2 * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
4 * Copyright (c) 1994 The Australian National University.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation is hereby granted, provided that the above copyright
9 * notice appears in all copies. This software is provided without any
10 * warranty, express or implied. The Australian National University
11 * makes no representations about the suitability of this software for
14 * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
15 * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
17 * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
20 * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
24 * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
27 * $Id: ppp_ahdlc.c,v 1.1 1995/12/11 05:06:42 paulus Exp $
31 * This file is used under Solaris 2, SVR4, SunOS 4, and OSF/1.
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/stream.h>
36 #include <sys/errno.h>
41 #include <sys/cmn_err.h>
47 #include <net/ppp_defs.h>
48 #include <net/pppio.h>
51 #define IFRAME_BSIZE 512 /* Block size to allocate for input */
52 #define OFRAME_BSIZE 4096 /* Don't allocb more than this for output */
54 MOD_OPEN_DECL(ahdlc_open);
55 MOD_CLOSE_DECL(ahdlc_close);
56 static int ahdlc_wput __P((queue_t *, mblk_t *));
57 static int ahdlc_rput __P((queue_t *, mblk_t *));
58 static void stuff_frame __P((queue_t *, mblk_t *));
59 static void unstuff_chars __P((queue_t *, mblk_t *));
60 static int msg_byte __P((mblk_t *, unsigned int));
62 /* Extract byte i of message mp. */
63 #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
66 /* Is this LCP packet one we have to transmit using LCP defaults? */
67 #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
69 static struct module_info minfo = {
70 0x7d23, "ppp_ahdl", 0, INFPSZ, 4096, 128
73 static struct qinit rinit = {
74 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
77 static struct qinit winit = {
78 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
81 struct streamtab ppp_ahdlcinfo = {
82 &rinit, &winit, NULL, NULL
87 typedef struct ahdlc_state {
101 /* Values for flags */
102 #define ESCAPED 0x100 /* last saw escape char on input */
103 #define IFLUSH 0x200 /* flushing input due to error */
105 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
106 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
109 * FCS lookup table as calculated by genfcstab.
111 static u_short fcstab[256] = {
112 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
113 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
114 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
115 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
116 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
117 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
118 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
119 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
120 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
121 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
122 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
123 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
124 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
125 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
126 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
127 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
128 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
129 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
130 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
131 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
132 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
133 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
134 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
135 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
136 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
137 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
138 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
139 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
140 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
141 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
142 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
143 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
147 * STREAMS module entry points.
154 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
157 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
158 q->q_ptr = (caddr_t) sp;
159 WR(q)->q_ptr = (caddr_t) sp;
161 sp->xaccm[3] = 0x60000000;
169 MOD_CLOSE(ahdlc_close)
171 ahdlc_state_t *state;
175 state = (ahdlc_state_t *) q->q_ptr;
176 if (state->cur_frame != 0) {
177 freemsg(state->cur_frame);
178 state->cur_frame = 0;
180 FREE(q->q_ptr, sizeof(ahdlc_state_t));
191 ahdlc_state_t *state;
195 struct ppp_stats *psp;
197 state = (ahdlc_state_t *) q->q_ptr;
198 switch (mp->b_datap->db_type) {
201 * A data packet - do character-stuffing and FCS, and
209 iop = (struct iocblk *) mp->b_rptr;
211 switch (iop->ioc_cmd) {
213 if (iop->ioc_count < sizeof(u_int32_t)
214 || iop->ioc_count > sizeof(ext_accm))
216 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
218 state->xaccm[2] &= 0x40000000; /* don't escape 0x5e */
219 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
225 if (iop->ioc_count != sizeof(u_int32_t))
227 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
234 np = allocb(sizeof(int), BPRI_HI);
242 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
243 np->b_wptr += sizeof(int);
244 iop->ioc_count = sizeof(int);
249 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
257 psp = (struct ppp_stats *) np->b_wptr;
258 np->b_wptr += sizeof(struct ppp_stats);
259 bzero((caddr_t)psp, sizeof(struct ppp_stats));
260 psp->p = state->stats;
261 iop->ioc_count = sizeof(struct ppp_stats);
266 /* we knew this anyway */
277 else if (error == 0) {
278 mp->b_datap->db_type = M_IOCACK;
281 mp->b_datap->db_type = M_IOCNAK;
283 iop->ioc_error = error;
289 switch (*mp->b_rptr) {
291 state->mtu = ((unsigned short *)mp->b_rptr)[1];
295 state->mru = ((unsigned short *)mp->b_rptr)[1];
299 state->unit = mp->b_rptr[1];
319 ahdlc_state_t *state;
321 switch (mp->b_datap->db_type) {
323 unstuff_chars(q, mp);
328 state = (ahdlc_state_t *) q->q_ptr;
329 if (state->cur_frame != 0) {
330 /* XXX would like to send this up for debugging */
331 freemsg(state->cur_frame);
332 state->cur_frame = 0;
336 state->flags = IFLUSH;
346 /* Extract bit c from map m, to determine if c needs to be escaped. */
347 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
354 ahdlc_state_t *state;
355 int ilen, olen, c, extra, i, code;
356 mblk_t *omsg, *op, *np;
357 uchar_t *sp, *sp0, *dp, *dp0, *spend;
359 u_int32_t *xaccm, lcp_xaccm[8];
360 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
361 uchar_t ppphdr[PPP_HDRLEN];
363 state = (ahdlc_state_t *) q->q_ptr;
367 * We estimate the length of the output packet as
368 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
370 olen = ilen + (ilen >> 2) + 16;
371 if (olen > OFRAME_BSIZE)
373 omsg = op = allocb(olen, BPRI_MED);
378 * Put in an initial flag for now. We'll remove it later
379 * if we decide we don't need it.
386 * For LCP packets with code values between 1 and 7 (Conf-Req
387 * to Code-Rej), we must escape all control characters.
389 xaccm = state->xaccm;
390 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
391 && MSG_BYTE(mp, 1) == PPP_UI
392 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
393 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
394 && LCP_USE_DFLT(mp)) {
395 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
404 extra = sp + olen - spend;
410 * We can safely process the input up to `spend'
411 * without overrunning the output, provided we don't
412 * hit more than `extra' characters which need to be escaped.
418 if (ESCAPE(c, xaccm)) {
421 else if (sp < spend - 1)
425 fcs = PPP_FCS(fcs, c);
429 fcs = PPP_FCS(fcs, c);
437 * At this point, we have emptied an input block
438 * and/or filled an output block.
440 if (sp >= mp->b_wptr) {
442 * We've emptied an input block. Advance to the next.
446 break; /* all done */
451 * The output block is full. Allocate a new one.
455 if (olen > OFRAME_BSIZE)
457 np = allocb(olen, BPRI_MED);
467 * Append the FCS and closing flag.
468 * This could require up to 5 characters.
471 /* Sigh. Need another block. */
473 np = allocb(5, BPRI_MED);
481 if (ESCAPE(c, xaccm)) {
486 c = (~fcs >> 8) & 0xff;
487 if (ESCAPE(c, xaccm)) {
496 * Remove the initial flag, if possible.
498 if (qsize(q->q_next) > 0)
504 state->stats.ppp_obytes += msgdsize(omsg);
505 state->stats.ppp_opackets++;
516 state->stats.ppp_oerrors++;
517 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
520 #define UPDATE_FLAGS(c) { \
522 state->flags |= RCV_B7_1; \
524 state->flags |= RCV_B7_0; \
525 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
526 state->flags |= RCV_ODDP; \
528 state->flags |= RCV_EVNP; \
532 * Process received characters.
539 ahdlc_state_t *state;
541 uchar_t *cp, *cpend, *dp, *dp0;
542 int c, len, extra, offset;
545 state = (ahdlc_state_t *) q->q_ptr;
546 state->stats.ppp_ibytes += msgdsize(mp);
550 * Advance to next input block if necessary.
552 if (cp >= mp->b_wptr) {
560 if ((state->flags & (IFLUSH|ESCAPED)) == 0
561 && state->inlen > 0 && (om = state->cur_blk) != 0) {
563 * Process bulk chars as quickly as possible.
566 len = om->b_datap->db_lim - dp; /* max # output bytes */
567 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
569 len += extra; /* we'll run out of input first */
581 if (c == PPP_ESCAPE) {
586 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
587 state->flags |= ESCAPED;
595 fcs = PPP_FCS(fcs, c);
597 state->inlen += dp - dp0;
600 if (cp >= mp->b_wptr)
601 continue; /* advance to the next mblk */
609 * If the ESCAPE flag is set, the frame ended with
610 * the frame abort sequence "}~".
612 om = state->cur_frame;
614 state->cur_frame = 0;
616 if (len == 0 && (state->flags & IFLUSH) == 0)
618 state->stats.ppp_ipackets++;
619 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
620 && len > PPP_FCSLEN) {
621 if (state->infcs == PPP_GOODFCS) {
622 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
623 putnext(q, om); /* bombs away! */
626 DPRINT1("ppp_ahdl: bad fcs %x\n", state->infcs);
630 state->stats.ppp_ierrors++;
631 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
635 if (state->flags & IFLUSH)
637 if (state->flags & ESCAPED) {
639 state->flags &= ~ESCAPED;
640 } else if (c == PPP_ESCAPE) {
641 state->flags |= ESCAPED;
644 if (state->inlen == 0) {
646 * First byte of the frame: allocate the first message block.
648 om = allocb(IFRAME_BSIZE, BPRI_MED);
650 state->flags |= IFLUSH;
653 state->cur_frame = om;
655 state->infcs = PPP_INITFCS;
658 if (om->b_wptr >= om->b_datap->db_lim) {
660 * Current message block is full. Allocate another one,
661 * unless we have run out of MRU.
663 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
664 state->flags |= IFLUSH;
665 DPRINT1("ppp_ahdl: frame too long (%d)\n", state->inlen);
668 om = allocb(IFRAME_BSIZE, BPRI_MED);
670 state->flags |= IFLUSH;
673 state->cur_blk->b_cont = om;
678 if (state->inlen == 0) {
680 * We don't do address/control & protocol decompression here,
681 * but we try to put the first byte at an offset such that
682 * the info field starts on a word boundary. The code here
683 * will do this except for packets with protocol compression
684 * but not address/control compression.
686 if (c != PPP_ALLSTATIONS) {
690 om->b_rptr = om->b_wptr;
696 state->infcs = PPP_FCS(state->infcs, c);
705 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
709 return mp->b_rptr[i];