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.2 1996/06/26 00:54:01 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>
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 #define PPP_AHDL_ID 0x7d23
70 static struct module_info minfo = {
71 PPP_AHDL_ID, "ppp_ahdl", 0, INFPSZ, 4096, 128
74 static struct qinit rinit = {
75 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
78 static struct qinit winit = {
79 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
82 struct streamtab ppp_ahdlcinfo = {
83 &rinit, &winit, NULL, NULL
88 typedef struct ahdlc_state {
102 /* Values for flags */
103 #define ESCAPED 0x100 /* last saw escape char on input */
104 #define IFLUSH 0x200 /* flushing input due to error */
106 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
107 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
110 * FCS lookup table as calculated by genfcstab.
112 static u_short fcstab[256] = {
113 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
114 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
115 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
116 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
117 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
118 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
119 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
120 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
121 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
122 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
123 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
124 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
125 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
126 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
127 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
128 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
129 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
130 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
131 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
132 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
133 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
134 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
135 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
136 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
137 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
138 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
139 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
140 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
141 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
142 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
143 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
144 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
148 * STREAMS module entry points.
155 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
158 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
159 q->q_ptr = (caddr_t) sp;
160 WR(q)->q_ptr = (caddr_t) sp;
162 sp->xaccm[3] = 0x60000000;
170 MOD_CLOSE(ahdlc_close)
172 ahdlc_state_t *state;
176 state = (ahdlc_state_t *) q->q_ptr;
177 if (state->cur_frame != 0) {
178 freemsg(state->cur_frame);
179 state->cur_frame = 0;
181 FREE(q->q_ptr, sizeof(ahdlc_state_t));
192 ahdlc_state_t *state;
196 struct ppp_stats *psp;
198 state = (ahdlc_state_t *) q->q_ptr;
199 switch (mp->b_datap->db_type) {
202 * A data packet - do character-stuffing and FCS, and
210 iop = (struct iocblk *) mp->b_rptr;
212 switch (iop->ioc_cmd) {
214 if (iop->ioc_count < sizeof(u_int32_t)
215 || iop->ioc_count > sizeof(ext_accm))
217 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
219 state->xaccm[2] &= 0x40000000; /* don't escape 0x5e */
220 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
226 if (iop->ioc_count != sizeof(u_int32_t))
228 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
235 np = allocb(sizeof(int), BPRI_HI);
243 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
244 np->b_wptr += sizeof(int);
245 iop->ioc_count = sizeof(int);
250 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
258 psp = (struct ppp_stats *) np->b_wptr;
259 np->b_wptr += sizeof(struct ppp_stats);
260 bzero((caddr_t)psp, sizeof(struct ppp_stats));
261 psp->p = state->stats;
262 iop->ioc_count = sizeof(struct ppp_stats);
267 /* we knew this anyway */
278 else if (error == 0) {
279 mp->b_datap->db_type = M_IOCACK;
282 mp->b_datap->db_type = M_IOCNAK;
284 iop->ioc_error = error;
290 switch (*mp->b_rptr) {
292 state->mtu = ((unsigned short *)mp->b_rptr)[1];
296 state->mru = ((unsigned short *)mp->b_rptr)[1];
300 state->unit = mp->b_rptr[1];
320 ahdlc_state_t *state;
322 switch (mp->b_datap->db_type) {
324 unstuff_chars(q, mp);
329 state = (ahdlc_state_t *) q->q_ptr;
330 if (state->cur_frame != 0) {
331 /* XXX would like to send this up for debugging */
332 freemsg(state->cur_frame);
333 state->cur_frame = 0;
337 state->flags = IFLUSH;
347 /* Extract bit c from map m, to determine if c needs to be escaped. */
348 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
355 ahdlc_state_t *state;
356 int ilen, olen, c, extra, i, code;
357 mblk_t *omsg, *op, *np;
358 uchar_t *sp, *sp0, *dp, *dp0, *spend;
360 u_int32_t *xaccm, lcp_xaccm[8];
361 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
362 uchar_t ppphdr[PPP_HDRLEN];
364 state = (ahdlc_state_t *) q->q_ptr;
368 * We estimate the length of the output packet as
369 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
371 olen = ilen + (ilen >> 2) + 16;
372 if (olen > OFRAME_BSIZE)
374 omsg = op = allocb(olen, BPRI_MED);
379 * Put in an initial flag for now. We'll remove it later
380 * if we decide we don't need it.
387 * For LCP packets with code values between 1 and 7 (Conf-Req
388 * to Code-Rej), we must escape all control characters.
390 xaccm = state->xaccm;
391 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
392 && MSG_BYTE(mp, 1) == PPP_UI
393 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
394 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
395 && LCP_USE_DFLT(mp)) {
396 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
405 extra = sp + olen - spend;
411 * We can safely process the input up to `spend'
412 * without overrunning the output, provided we don't
413 * hit more than `extra' characters which need to be escaped.
419 if (ESCAPE(c, xaccm)) {
422 else if (sp < spend - 1)
426 fcs = PPP_FCS(fcs, c);
430 fcs = PPP_FCS(fcs, c);
438 * At this point, we have emptied an input block
439 * and/or filled an output block.
441 if (sp >= mp->b_wptr) {
443 * We've emptied an input block. Advance to the next.
447 break; /* all done */
452 * The output block is full. Allocate a new one.
456 if (olen > OFRAME_BSIZE)
458 np = allocb(olen, BPRI_MED);
468 * Append the FCS and closing flag.
469 * This could require up to 5 characters.
472 /* Sigh. Need another block. */
474 np = allocb(5, BPRI_MED);
482 if (ESCAPE(c, xaccm)) {
487 c = (~fcs >> 8) & 0xff;
488 if (ESCAPE(c, xaccm)) {
497 * Remove the initial flag, if possible.
499 if (qsize(q->q_next) > 0)
505 state->stats.ppp_obytes += msgdsize(omsg);
506 state->stats.ppp_opackets++;
517 state->stats.ppp_oerrors++;
518 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
521 #define UPDATE_FLAGS(c) { \
523 state->flags |= RCV_B7_1; \
525 state->flags |= RCV_B7_0; \
526 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
527 state->flags |= RCV_ODDP; \
529 state->flags |= RCV_EVNP; \
533 * Process received characters.
540 ahdlc_state_t *state;
542 uchar_t *cp, *cpend, *dp, *dp0;
543 int c, len, extra, offset;
546 state = (ahdlc_state_t *) q->q_ptr;
547 state->stats.ppp_ibytes += msgdsize(mp);
551 * Advance to next input block if necessary.
553 if (cp >= mp->b_wptr) {
561 if ((state->flags & (IFLUSH|ESCAPED)) == 0
562 && state->inlen > 0 && (om = state->cur_blk) != 0) {
564 * Process bulk chars as quickly as possible.
567 len = om->b_datap->db_lim - dp; /* max # output bytes */
568 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
570 len += extra; /* we'll run out of input first */
582 if (c == PPP_ESCAPE) {
587 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
588 state->flags |= ESCAPED;
596 fcs = PPP_FCS(fcs, c);
598 state->inlen += dp - dp0;
601 if (cp >= mp->b_wptr)
602 continue; /* advance to the next mblk */
610 * If the ESCAPE flag is set, the frame ended with
611 * the frame abort sequence "}~".
613 om = state->cur_frame;
615 state->cur_frame = 0;
617 if (len == 0 && (state->flags & IFLUSH) == 0)
619 state->stats.ppp_ipackets++;
620 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
621 && len > PPP_FCSLEN) {
622 if (state->infcs == PPP_GOODFCS) {
623 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
624 putnext(q, om); /* bombs away! */
627 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
631 state->flags &= ~(IFLUSH|ESCAPED);
632 state->stats.ppp_ierrors++;
633 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
637 if (state->flags & IFLUSH)
639 if (state->flags & ESCAPED) {
641 state->flags &= ~ESCAPED;
642 } else if (c == PPP_ESCAPE) {
643 state->flags |= ESCAPED;
646 if (state->inlen == 0) {
648 * First byte of the frame: allocate the first message block.
650 om = allocb(IFRAME_BSIZE, BPRI_MED);
652 state->flags |= IFLUSH;
655 state->cur_frame = om;
657 state->infcs = PPP_INITFCS;
660 if (om->b_wptr >= om->b_datap->db_lim) {
662 * Current message block is full. Allocate another one,
663 * unless we have run out of MRU.
665 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
666 state->flags |= IFLUSH;
667 DPRINT2("ppp%d: frame too long (%d)\n",
668 state->unit, state->inlen);
671 om = allocb(IFRAME_BSIZE, BPRI_MED);
673 state->flags |= IFLUSH;
676 state->cur_blk->b_cont = om;
681 if (state->inlen == 0) {
683 * We don't do address/control & protocol decompression here,
684 * but we try to put the first byte at an offset such that
685 * the info field starts on a word boundary. The code here
686 * will do this except for packets with protocol compression
687 * but not address/control compression.
689 if (c != PPP_ALLSTATIONS) {
693 om->b_rptr = om->b_wptr;
699 state->infcs = PPP_FCS(state->infcs, c);
708 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
712 return mp->b_rptr[i];