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.9 1999/02/26 10:52:07 paulus Exp $
31 * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
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>
46 #include <sys/cmn_err.h>
50 #include <net/ppp_defs.h>
51 #include <net/pppio.h>
54 #define IFRAME_BSIZE 512 /* Block size to allocate for input */
55 #define OFRAME_BSIZE 4096 /* Don't allocb more than this for output */
57 MOD_OPEN_DECL(ahdlc_open);
58 MOD_CLOSE_DECL(ahdlc_close);
59 static int ahdlc_wput __P((queue_t *, mblk_t *));
60 static int ahdlc_rput __P((queue_t *, mblk_t *));
61 static void stuff_frame __P((queue_t *, mblk_t *));
62 static void unstuff_chars __P((queue_t *, mblk_t *));
63 static int msg_byte __P((mblk_t *, unsigned int));
65 /* Extract byte i of message mp. */
66 #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
69 /* Is this LCP packet one we have to transmit using LCP defaults? */
70 #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
72 #define PPP_AHDL_ID 0x7d23
73 static struct module_info minfo = {
75 PPP_AHDL_ID, "ppp_ahdl", 0, INFPSZ, 640, 512
77 PPP_AHDL_ID, "ppp_ahdl", 0, INFPSZ, 4096, 128
81 static struct qinit rinit = {
82 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
85 static struct qinit winit = {
86 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
89 #if defined(SVR4) && !defined(SOL2)
91 #define ppp_ahdlcinfo phdlinfo
93 struct streamtab ppp_ahdlcinfo = {
94 &rinit, &winit, NULL, NULL
97 typedef struct ahdlc_state {
108 struct pppstat stats;
111 /* Values for flags */
112 #define ESCAPED 0x100 /* last saw escape char on input */
113 #define IFLUSH 0x200 /* flushing input due to error */
115 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
116 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
119 * FCS lookup table as calculated by genfcstab.
121 static u_short fcstab[256] = {
122 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
123 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
124 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
125 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
126 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
127 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
128 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
129 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
130 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
131 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
132 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
133 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
134 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
135 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
136 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
137 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
138 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
139 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
140 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
141 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
142 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
143 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
144 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
145 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
146 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
147 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
148 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
149 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
150 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
151 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
152 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
153 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
157 * STREAMS module entry points.
164 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
167 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
168 q->q_ptr = (caddr_t) sp;
169 WR(q)->q_ptr = (caddr_t) sp;
171 sp->xaccm[3] = 0x60000000;
178 MOD_CLOSE(ahdlc_close)
180 ahdlc_state_t *state;
184 state = (ahdlc_state_t *) q->q_ptr;
185 if (state->cur_frame != 0) {
186 freemsg(state->cur_frame);
187 state->cur_frame = 0;
189 FREE(q->q_ptr, sizeof(ahdlc_state_t));
191 OTHERQ(q)->q_ptr = NULL;
201 ahdlc_state_t *state;
205 struct ppp_stats *psp;
207 state = (ahdlc_state_t *) q->q_ptr;
209 DPRINT("state == 0 in ahdlc_wput\n");
214 switch (mp->b_datap->db_type) {
217 * A data packet - do character-stuffing and FCS, and
225 iop = (struct iocblk *) mp->b_rptr;
227 switch (iop->ioc_cmd) {
229 if (iop->ioc_count < sizeof(u_int32_t)
230 || iop->ioc_count > sizeof(ext_accm))
232 if (mp->b_cont == 0) {
233 DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
236 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
238 state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
239 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
245 if (iop->ioc_count != sizeof(u_int32_t))
247 if (mp->b_cont == 0) {
248 DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
251 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
258 np = allocb(sizeof(int), BPRI_HI);
266 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
267 np->b_wptr += sizeof(int);
268 iop->ioc_count = sizeof(int);
273 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
281 psp = (struct ppp_stats *) np->b_wptr;
282 np->b_wptr += sizeof(struct ppp_stats);
283 bzero((caddr_t)psp, sizeof(struct ppp_stats));
284 psp->p = state->stats;
285 iop->ioc_count = sizeof(struct ppp_stats);
290 /* we knew this anyway */
301 else if (error == 0) {
302 mp->b_datap->db_type = M_IOCACK;
305 mp->b_datap->db_type = M_IOCNAK;
307 iop->ioc_error = error;
313 switch (*mp->b_rptr) {
315 state->mtu = ((unsigned short *)mp->b_rptr)[1];
319 state->mru = ((unsigned short *)mp->b_rptr)[1];
323 state->unit = mp->b_rptr[1];
344 ahdlc_state_t *state;
346 state = (ahdlc_state_t *) q->q_ptr;
348 DPRINT("state == 0 in ahdlc_rput\n");
353 switch (mp->b_datap->db_type) {
355 unstuff_chars(q, mp);
360 if (state->cur_frame != 0) {
361 /* XXX would like to send this up for debugging */
362 freemsg(state->cur_frame);
363 state->cur_frame = 0;
367 state->flags = IFLUSH;
377 /* Extract bit c from map m, to determine if c needs to be escaped. */
378 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
385 ahdlc_state_t *state;
386 int ilen, olen, c, extra, i, code;
387 mblk_t *omsg, *op, *np;
388 uchar_t *sp, *sp0, *dp, *dp0, *spend;
390 u_int32_t *xaccm, lcp_xaccm[8];
391 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
392 uchar_t ppphdr[PPP_HDRLEN];
394 state = (ahdlc_state_t *) q->q_ptr;
398 * We estimate the length of the output packet as
399 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
401 olen = ilen + (ilen >> 2) + 16;
402 if (olen > OFRAME_BSIZE)
404 omsg = op = allocb(olen, BPRI_MED);
409 * Put in an initial flag, unless the serial driver currently has
410 * packets still to be transmitted in its queue.
413 if (qsize(q->q_next) == 0) {
419 * For LCP packets with code values between 1 and 7 (Conf-Req
420 * to Code-Rej), we must escape all control characters.
422 xaccm = state->xaccm;
423 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
424 && MSG_BYTE(mp, 1) == PPP_UI
425 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
426 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
427 && LCP_USE_DFLT(mp)) {
428 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
437 extra = sp + olen - spend;
443 * We can safely process the input up to `spend'
444 * without overrunning the output, provided we don't
445 * hit more than `extra' characters which need to be escaped.
451 if (ESCAPE(c, xaccm)) {
454 else if (sp < spend - 1)
458 fcs = PPP_FCS(fcs, c);
462 fcs = PPP_FCS(fcs, c);
470 * At this point, we have emptied an input block
471 * and/or filled an output block.
473 if (sp >= mp->b_wptr) {
475 * We've emptied an input block. Advance to the next.
479 break; /* all done */
484 * The output block is full. Allocate a new one.
488 if (olen > OFRAME_BSIZE)
490 np = allocb(olen, BPRI_MED);
500 * Append the FCS and closing flag.
501 * This could require up to 5 characters.
504 /* Sigh. Need another block. */
506 np = allocb(5, BPRI_MED);
514 if (ESCAPE(c, xaccm)) {
519 c = (~fcs >> 8) & 0xff;
520 if (ESCAPE(c, xaccm)) {
531 state->stats.ppp_obytes += msgdsize(omsg);
532 state->stats.ppp_opackets++;
543 state->stats.ppp_oerrors++;
544 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
547 #define UPDATE_FLAGS(c) { \
549 state->flags |= RCV_B7_1; \
551 state->flags |= RCV_B7_0; \
552 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
553 state->flags |= RCV_ODDP; \
555 state->flags |= RCV_EVNP; \
559 * Process received characters.
566 ahdlc_state_t *state;
568 uchar_t *cp, *cpend, *dp, *dp0;
569 int c, len, extra, offset;
572 state = (ahdlc_state_t *) q->q_ptr;
573 state->stats.ppp_ibytes += msgdsize(mp);
577 * Advance to next input block if necessary.
579 if (cp >= mp->b_wptr) {
587 if ((state->flags & (IFLUSH|ESCAPED)) == 0
588 && state->inlen > 0 && (om = state->cur_blk) != 0) {
590 * Process bulk chars as quickly as possible.
593 len = om->b_datap->db_lim - dp; /* max # output bytes */
594 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
596 len += extra; /* we'll run out of input first */
608 if (c == PPP_ESCAPE) {
613 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
614 state->flags |= ESCAPED;
622 fcs = PPP_FCS(fcs, c);
624 state->inlen += dp - dp0;
627 if (cp >= mp->b_wptr)
628 continue; /* advance to the next mblk */
636 * If the ESCAPE flag is set, the frame ended with
637 * the frame abort sequence "}~".
639 om = state->cur_frame;
641 state->cur_frame = 0;
643 if (len == 0 && (state->flags & IFLUSH) == 0)
645 state->stats.ppp_ipackets++;
646 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
647 && len > PPP_FCSLEN) {
648 if (state->infcs == PPP_GOODFCS) {
649 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
650 putnext(q, om); /* bombs away! */
653 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
657 state->flags &= ~(IFLUSH|ESCAPED);
658 state->stats.ppp_ierrors++;
659 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
663 if (state->flags & IFLUSH)
665 if (state->flags & ESCAPED) {
667 state->flags &= ~ESCAPED;
668 } else if (c == PPP_ESCAPE) {
669 state->flags |= ESCAPED;
672 if (state->inlen == 0) {
674 * First byte of the frame: allocate the first message block.
676 om = allocb(IFRAME_BSIZE, BPRI_MED);
678 state->flags |= IFLUSH;
681 state->cur_frame = om;
683 state->infcs = PPP_INITFCS;
686 if (om->b_wptr >= om->b_datap->db_lim) {
688 * Current message block is full. Allocate another one,
689 * unless we have run out of MRU.
691 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
692 state->flags |= IFLUSH;
693 DPRINT2("ppp%d: frame too long (%d)\n",
694 state->unit, state->inlen);
697 om = allocb(IFRAME_BSIZE, BPRI_MED);
699 state->flags |= IFLUSH;
702 state->cur_blk->b_cont = om;
707 if (state->inlen == 0) {
709 * We don't do address/control & protocol decompression here,
710 * but we try to put the first byte at an offset such that
711 * the info field starts on a word boundary. The code here
712 * will do this except for packets with protocol compression
713 * but not address/control compression.
715 if (c != PPP_ALLSTATIONS) {
719 om->b_rptr = om->b_wptr;
725 state->infcs = PPP_FCS(state->infcs, c);
734 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
738 return mp->b_rptr[i];