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.8 1998/05/04 06:11:50 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, unless the serial driver currently has
399 * packets still to be transmitted in its queue.
402 if (qsize(q->q_next) == 0) {
408 * For LCP packets with code values between 1 and 7 (Conf-Req
409 * to Code-Rej), we must escape all control characters.
411 xaccm = state->xaccm;
412 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
413 && MSG_BYTE(mp, 1) == PPP_UI
414 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
415 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
416 && LCP_USE_DFLT(mp)) {
417 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
426 extra = sp + olen - spend;
432 * We can safely process the input up to `spend'
433 * without overrunning the output, provided we don't
434 * hit more than `extra' characters which need to be escaped.
440 if (ESCAPE(c, xaccm)) {
443 else if (sp < spend - 1)
447 fcs = PPP_FCS(fcs, c);
451 fcs = PPP_FCS(fcs, c);
459 * At this point, we have emptied an input block
460 * and/or filled an output block.
462 if (sp >= mp->b_wptr) {
464 * We've emptied an input block. Advance to the next.
468 break; /* all done */
473 * The output block is full. Allocate a new one.
477 if (olen > OFRAME_BSIZE)
479 np = allocb(olen, BPRI_MED);
489 * Append the FCS and closing flag.
490 * This could require up to 5 characters.
493 /* Sigh. Need another block. */
495 np = allocb(5, BPRI_MED);
503 if (ESCAPE(c, xaccm)) {
508 c = (~fcs >> 8) & 0xff;
509 if (ESCAPE(c, xaccm)) {
520 state->stats.ppp_obytes += msgdsize(omsg);
521 state->stats.ppp_opackets++;
532 state->stats.ppp_oerrors++;
533 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
536 #define UPDATE_FLAGS(c) { \
538 state->flags |= RCV_B7_1; \
540 state->flags |= RCV_B7_0; \
541 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
542 state->flags |= RCV_ODDP; \
544 state->flags |= RCV_EVNP; \
548 * Process received characters.
555 ahdlc_state_t *state;
557 uchar_t *cp, *cpend, *dp, *dp0;
558 int c, len, extra, offset;
561 state = (ahdlc_state_t *) q->q_ptr;
562 state->stats.ppp_ibytes += msgdsize(mp);
566 * Advance to next input block if necessary.
568 if (cp >= mp->b_wptr) {
576 if ((state->flags & (IFLUSH|ESCAPED)) == 0
577 && state->inlen > 0 && (om = state->cur_blk) != 0) {
579 * Process bulk chars as quickly as possible.
582 len = om->b_datap->db_lim - dp; /* max # output bytes */
583 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
585 len += extra; /* we'll run out of input first */
597 if (c == PPP_ESCAPE) {
602 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
603 state->flags |= ESCAPED;
611 fcs = PPP_FCS(fcs, c);
613 state->inlen += dp - dp0;
616 if (cp >= mp->b_wptr)
617 continue; /* advance to the next mblk */
625 * If the ESCAPE flag is set, the frame ended with
626 * the frame abort sequence "}~".
628 om = state->cur_frame;
630 state->cur_frame = 0;
632 if (len == 0 && (state->flags & IFLUSH) == 0)
634 state->stats.ppp_ipackets++;
635 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
636 && len > PPP_FCSLEN) {
637 if (state->infcs == PPP_GOODFCS) {
638 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
639 putnext(q, om); /* bombs away! */
642 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
646 state->flags &= ~(IFLUSH|ESCAPED);
647 state->stats.ppp_ierrors++;
648 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
652 if (state->flags & IFLUSH)
654 if (state->flags & ESCAPED) {
656 state->flags &= ~ESCAPED;
657 } else if (c == PPP_ESCAPE) {
658 state->flags |= ESCAPED;
661 if (state->inlen == 0) {
663 * First byte of the frame: allocate the first message block.
665 om = allocb(IFRAME_BSIZE, BPRI_MED);
667 state->flags |= IFLUSH;
670 state->cur_frame = om;
672 state->infcs = PPP_INITFCS;
675 if (om->b_wptr >= om->b_datap->db_lim) {
677 * Current message block is full. Allocate another one,
678 * unless we have run out of MRU.
680 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
681 state->flags |= IFLUSH;
682 DPRINT2("ppp%d: frame too long (%d)\n",
683 state->unit, state->inlen);
686 om = allocb(IFRAME_BSIZE, BPRI_MED);
688 state->flags |= IFLUSH;
691 state->cur_blk->b_cont = om;
696 if (state->inlen == 0) {
698 * We don't do address/control & protocol decompression here,
699 * but we try to put the first byte at an offset such that
700 * the info field starts on a word boundary. The code here
701 * will do this except for packets with protocol compression
702 * but not address/control compression.
704 if (c != PPP_ALLSTATIONS) {
708 om->b_rptr = om->b_wptr;
714 state->infcs = PPP_FCS(state->infcs, c);
723 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
727 return mp->b_rptr[i];