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.7 1998/03/24 23:52:35 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
99 typedef struct ahdlc_state {
110 struct pppstat stats;
113 /* Values for flags */
114 #define ESCAPED 0x100 /* last saw escape char on input */
115 #define IFLUSH 0x200 /* flushing input due to error */
117 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
118 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
121 * FCS lookup table as calculated by genfcstab.
123 static u_short fcstab[256] = {
124 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
125 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
126 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
127 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
128 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
129 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
130 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
131 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
132 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
133 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
134 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
135 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
136 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
137 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
138 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
139 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
140 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
141 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
142 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
143 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
144 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
145 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
146 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
147 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
148 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
149 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
150 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
151 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
152 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
153 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
154 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
155 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
159 * STREAMS module entry points.
166 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
169 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
170 q->q_ptr = (caddr_t) sp;
171 WR(q)->q_ptr = (caddr_t) sp;
173 sp->xaccm[3] = 0x60000000;
181 MOD_CLOSE(ahdlc_close)
183 ahdlc_state_t *state;
187 state = (ahdlc_state_t *) q->q_ptr;
188 if (state->cur_frame != 0) {
189 freemsg(state->cur_frame);
190 state->cur_frame = 0;
192 FREE(q->q_ptr, sizeof(ahdlc_state_t));
203 ahdlc_state_t *state;
207 struct ppp_stats *psp;
209 state = (ahdlc_state_t *) q->q_ptr;
210 switch (mp->b_datap->db_type) {
213 * A data packet - do character-stuffing and FCS, and
221 iop = (struct iocblk *) mp->b_rptr;
223 switch (iop->ioc_cmd) {
225 if (iop->ioc_count < sizeof(u_int32_t)
226 || iop->ioc_count > sizeof(ext_accm))
228 if (mp->b_cont == 0) {
229 DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
232 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
234 state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
235 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
241 if (iop->ioc_count != sizeof(u_int32_t))
243 if (mp->b_cont == 0) {
244 DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
247 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
254 np = allocb(sizeof(int), BPRI_HI);
262 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
263 np->b_wptr += sizeof(int);
264 iop->ioc_count = sizeof(int);
269 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
277 psp = (struct ppp_stats *) np->b_wptr;
278 np->b_wptr += sizeof(struct ppp_stats);
279 bzero((caddr_t)psp, sizeof(struct ppp_stats));
280 psp->p = state->stats;
281 iop->ioc_count = sizeof(struct ppp_stats);
286 /* we knew this anyway */
297 else if (error == 0) {
298 mp->b_datap->db_type = M_IOCACK;
301 mp->b_datap->db_type = M_IOCNAK;
303 iop->ioc_error = error;
309 switch (*mp->b_rptr) {
311 state->mtu = ((unsigned short *)mp->b_rptr)[1];
315 state->mru = ((unsigned short *)mp->b_rptr)[1];
319 state->unit = mp->b_rptr[1];
339 ahdlc_state_t *state;
341 switch (mp->b_datap->db_type) {
343 unstuff_chars(q, mp);
348 state = (ahdlc_state_t *) q->q_ptr;
349 if (state->cur_frame != 0) {
350 /* XXX would like to send this up for debugging */
351 freemsg(state->cur_frame);
352 state->cur_frame = 0;
356 state->flags = IFLUSH;
366 /* Extract bit c from map m, to determine if c needs to be escaped. */
367 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
374 ahdlc_state_t *state;
375 int ilen, olen, c, extra, i, code;
376 mblk_t *omsg, *op, *np;
377 uchar_t *sp, *sp0, *dp, *dp0, *spend;
379 u_int32_t *xaccm, lcp_xaccm[8];
380 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
381 uchar_t ppphdr[PPP_HDRLEN];
383 state = (ahdlc_state_t *) q->q_ptr;
387 * We estimate the length of the output packet as
388 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
390 olen = ilen + (ilen >> 2) + 16;
391 if (olen > OFRAME_BSIZE)
393 omsg = op = allocb(olen, BPRI_MED);
398 * Put in an initial flag for now. We'll remove it later
399 * if we decide we don't need it.
406 * For LCP packets with code values between 1 and 7 (Conf-Req
407 * to Code-Rej), we must escape all control characters.
409 xaccm = state->xaccm;
410 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
411 && MSG_BYTE(mp, 1) == PPP_UI
412 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
413 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
414 && LCP_USE_DFLT(mp)) {
415 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
424 extra = sp + olen - spend;
430 * We can safely process the input up to `spend'
431 * without overrunning the output, provided we don't
432 * hit more than `extra' characters which need to be escaped.
438 if (ESCAPE(c, xaccm)) {
441 else if (sp < spend - 1)
445 fcs = PPP_FCS(fcs, c);
449 fcs = PPP_FCS(fcs, c);
457 * At this point, we have emptied an input block
458 * and/or filled an output block.
460 if (sp >= mp->b_wptr) {
462 * We've emptied an input block. Advance to the next.
466 break; /* all done */
471 * The output block is full. Allocate a new one.
475 if (olen > OFRAME_BSIZE)
477 np = allocb(olen, BPRI_MED);
487 * Append the FCS and closing flag.
488 * This could require up to 5 characters.
491 /* Sigh. Need another block. */
493 np = allocb(5, BPRI_MED);
501 if (ESCAPE(c, xaccm)) {
506 c = (~fcs >> 8) & 0xff;
507 if (ESCAPE(c, xaccm)) {
516 * Remove the initial flag, if possible.
518 if (qsize(q->q_next) > 0)
524 state->stats.ppp_obytes += msgdsize(omsg);
525 state->stats.ppp_opackets++;
536 state->stats.ppp_oerrors++;
537 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
540 #define UPDATE_FLAGS(c) { \
542 state->flags |= RCV_B7_1; \
544 state->flags |= RCV_B7_0; \
545 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
546 state->flags |= RCV_ODDP; \
548 state->flags |= RCV_EVNP; \
552 * Process received characters.
559 ahdlc_state_t *state;
561 uchar_t *cp, *cpend, *dp, *dp0;
562 int c, len, extra, offset;
565 state = (ahdlc_state_t *) q->q_ptr;
566 state->stats.ppp_ibytes += msgdsize(mp);
570 * Advance to next input block if necessary.
572 if (cp >= mp->b_wptr) {
580 if ((state->flags & (IFLUSH|ESCAPED)) == 0
581 && state->inlen > 0 && (om = state->cur_blk) != 0) {
583 * Process bulk chars as quickly as possible.
586 len = om->b_datap->db_lim - dp; /* max # output bytes */
587 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
589 len += extra; /* we'll run out of input first */
601 if (c == PPP_ESCAPE) {
606 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
607 state->flags |= ESCAPED;
615 fcs = PPP_FCS(fcs, c);
617 state->inlen += dp - dp0;
620 if (cp >= mp->b_wptr)
621 continue; /* advance to the next mblk */
629 * If the ESCAPE flag is set, the frame ended with
630 * the frame abort sequence "}~".
632 om = state->cur_frame;
634 state->cur_frame = 0;
636 if (len == 0 && (state->flags & IFLUSH) == 0)
638 state->stats.ppp_ipackets++;
639 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
640 && len > PPP_FCSLEN) {
641 if (state->infcs == PPP_GOODFCS) {
642 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
643 putnext(q, om); /* bombs away! */
646 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
650 state->flags &= ~(IFLUSH|ESCAPED);
651 state->stats.ppp_ierrors++;
652 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
656 if (state->flags & IFLUSH)
658 if (state->flags & ESCAPED) {
660 state->flags &= ~ESCAPED;
661 } else if (c == PPP_ESCAPE) {
662 state->flags |= ESCAPED;
665 if (state->inlen == 0) {
667 * First byte of the frame: allocate the first message block.
669 om = allocb(IFRAME_BSIZE, BPRI_MED);
671 state->flags |= IFLUSH;
674 state->cur_frame = om;
676 state->infcs = PPP_INITFCS;
679 if (om->b_wptr >= om->b_datap->db_lim) {
681 * Current message block is full. Allocate another one,
682 * unless we have run out of MRU.
684 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
685 state->flags |= IFLUSH;
686 DPRINT2("ppp%d: frame too long (%d)\n",
687 state->unit, state->inlen);
690 om = allocb(IFRAME_BSIZE, BPRI_MED);
692 state->flags |= IFLUSH;
695 state->cur_blk->b_cont = om;
700 if (state->inlen == 0) {
702 * We don't do address/control & protocol decompression here,
703 * but we try to put the first byte at an offset such that
704 * the info field starts on a word boundary. The code here
705 * will do this except for packets with protocol compression
706 * but not address/control compression.
708 if (c != PPP_ALLSTATIONS) {
712 om->b_rptr = om->b_wptr;
718 state->infcs = PPP_FCS(state->infcs, c);
727 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
731 return mp->b_rptr[i];