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.4 1996/09/14 05:19:18 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 #if defined(SVR4) && !defined(SOL2)
84 #define ppp_ahdlcinfo phdlinfo
86 struct streamtab ppp_ahdlcinfo = {
87 &rinit, &winit, NULL, NULL
92 typedef struct ahdlc_state {
103 struct pppstat stats;
106 /* Values for flags */
107 #define ESCAPED 0x100 /* last saw escape char on input */
108 #define IFLUSH 0x200 /* flushing input due to error */
110 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
111 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
114 * FCS lookup table as calculated by genfcstab.
116 static u_short fcstab[256] = {
117 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
118 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
119 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
120 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
121 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
122 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
123 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
124 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
125 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
126 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
127 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
128 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
129 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
130 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
131 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
132 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
133 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
134 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
135 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
136 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
137 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
138 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
139 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
140 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
141 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
142 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
143 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
144 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
145 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
146 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
147 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
148 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
152 * STREAMS module entry points.
159 sp = (ahdlc_state_t *) ALLOC_SLEEP(sizeof(ahdlc_state_t));
162 bzero((caddr_t) sp, sizeof(ahdlc_state_t));
163 q->q_ptr = (caddr_t) sp;
164 WR(q)->q_ptr = (caddr_t) sp;
166 sp->xaccm[3] = 0x60000000;
174 MOD_CLOSE(ahdlc_close)
176 ahdlc_state_t *state;
180 state = (ahdlc_state_t *) q->q_ptr;
181 if (state->cur_frame != 0) {
182 freemsg(state->cur_frame);
183 state->cur_frame = 0;
185 FREE(q->q_ptr, sizeof(ahdlc_state_t));
196 ahdlc_state_t *state;
200 struct ppp_stats *psp;
202 state = (ahdlc_state_t *) q->q_ptr;
203 switch (mp->b_datap->db_type) {
206 * A data packet - do character-stuffing and FCS, and
214 iop = (struct iocblk *) mp->b_rptr;
216 switch (iop->ioc_cmd) {
218 if (iop->ioc_count < sizeof(u_int32_t)
219 || iop->ioc_count > sizeof(ext_accm))
221 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
223 state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
224 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
230 if (iop->ioc_count != sizeof(u_int32_t))
232 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
239 np = allocb(sizeof(int), BPRI_HI);
247 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
248 np->b_wptr += sizeof(int);
249 iop->ioc_count = sizeof(int);
254 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
262 psp = (struct ppp_stats *) np->b_wptr;
263 np->b_wptr += sizeof(struct ppp_stats);
264 bzero((caddr_t)psp, sizeof(struct ppp_stats));
265 psp->p = state->stats;
266 iop->ioc_count = sizeof(struct ppp_stats);
271 /* we knew this anyway */
282 else if (error == 0) {
283 mp->b_datap->db_type = M_IOCACK;
286 mp->b_datap->db_type = M_IOCNAK;
288 iop->ioc_error = error;
294 switch (*mp->b_rptr) {
296 state->mtu = ((unsigned short *)mp->b_rptr)[1];
300 state->mru = ((unsigned short *)mp->b_rptr)[1];
304 state->unit = mp->b_rptr[1];
324 ahdlc_state_t *state;
326 switch (mp->b_datap->db_type) {
328 unstuff_chars(q, mp);
333 state = (ahdlc_state_t *) q->q_ptr;
334 if (state->cur_frame != 0) {
335 /* XXX would like to send this up for debugging */
336 freemsg(state->cur_frame);
337 state->cur_frame = 0;
341 state->flags = IFLUSH;
351 /* Extract bit c from map m, to determine if c needs to be escaped. */
352 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
359 ahdlc_state_t *state;
360 int ilen, olen, c, extra, i, code;
361 mblk_t *omsg, *op, *np;
362 uchar_t *sp, *sp0, *dp, *dp0, *spend;
364 u_int32_t *xaccm, lcp_xaccm[8];
365 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
366 uchar_t ppphdr[PPP_HDRLEN];
368 state = (ahdlc_state_t *) q->q_ptr;
372 * We estimate the length of the output packet as
373 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
375 olen = ilen + (ilen >> 2) + 16;
376 if (olen > OFRAME_BSIZE)
378 omsg = op = allocb(olen, BPRI_MED);
383 * Put in an initial flag for now. We'll remove it later
384 * if we decide we don't need it.
391 * For LCP packets with code values between 1 and 7 (Conf-Req
392 * to Code-Rej), we must escape all control characters.
394 xaccm = state->xaccm;
395 if (MSG_BYTE(mp, 0) == PPP_ALLSTATIONS
396 && MSG_BYTE(mp, 1) == PPP_UI
397 && MSG_BYTE(mp, 2) == (PPP_LCP >> 8)
398 && MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)
399 && LCP_USE_DFLT(mp)) {
400 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
409 extra = sp + olen - spend;
415 * We can safely process the input up to `spend'
416 * without overrunning the output, provided we don't
417 * hit more than `extra' characters which need to be escaped.
423 if (ESCAPE(c, xaccm)) {
426 else if (sp < spend - 1)
430 fcs = PPP_FCS(fcs, c);
434 fcs = PPP_FCS(fcs, c);
442 * At this point, we have emptied an input block
443 * and/or filled an output block.
445 if (sp >= mp->b_wptr) {
447 * We've emptied an input block. Advance to the next.
451 break; /* all done */
456 * The output block is full. Allocate a new one.
460 if (olen > OFRAME_BSIZE)
462 np = allocb(olen, BPRI_MED);
472 * Append the FCS and closing flag.
473 * This could require up to 5 characters.
476 /* Sigh. Need another block. */
478 np = allocb(5, BPRI_MED);
486 if (ESCAPE(c, xaccm)) {
491 c = (~fcs >> 8) & 0xff;
492 if (ESCAPE(c, xaccm)) {
501 * Remove the initial flag, if possible.
503 if (qsize(q->q_next) > 0)
509 state->stats.ppp_obytes += msgdsize(omsg);
510 state->stats.ppp_opackets++;
521 state->stats.ppp_oerrors++;
522 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
525 #define UPDATE_FLAGS(c) { \
527 state->flags |= RCV_B7_1; \
529 state->flags |= RCV_B7_0; \
530 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
531 state->flags |= RCV_ODDP; \
533 state->flags |= RCV_EVNP; \
537 * Process received characters.
544 ahdlc_state_t *state;
546 uchar_t *cp, *cpend, *dp, *dp0;
547 int c, len, extra, offset;
550 state = (ahdlc_state_t *) q->q_ptr;
551 state->stats.ppp_ibytes += msgdsize(mp);
555 * Advance to next input block if necessary.
557 if (cp >= mp->b_wptr) {
565 if ((state->flags & (IFLUSH|ESCAPED)) == 0
566 && state->inlen > 0 && (om = state->cur_blk) != 0) {
568 * Process bulk chars as quickly as possible.
571 len = om->b_datap->db_lim - dp; /* max # output bytes */
572 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
574 len += extra; /* we'll run out of input first */
586 if (c == PPP_ESCAPE) {
591 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
592 state->flags |= ESCAPED;
600 fcs = PPP_FCS(fcs, c);
602 state->inlen += dp - dp0;
605 if (cp >= mp->b_wptr)
606 continue; /* advance to the next mblk */
614 * If the ESCAPE flag is set, the frame ended with
615 * the frame abort sequence "}~".
617 om = state->cur_frame;
619 state->cur_frame = 0;
621 if (len == 0 && (state->flags & IFLUSH) == 0)
623 state->stats.ppp_ipackets++;
624 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
625 && len > PPP_FCSLEN) {
626 if (state->infcs == PPP_GOODFCS) {
627 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
628 putnext(q, om); /* bombs away! */
631 DPRINT2("ppp%d: bad fcs (len=%d)\n", state->unit, len);
635 state->flags &= ~(IFLUSH|ESCAPED);
636 state->stats.ppp_ierrors++;
637 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
641 if (state->flags & IFLUSH)
643 if (state->flags & ESCAPED) {
645 state->flags &= ~ESCAPED;
646 } else if (c == PPP_ESCAPE) {
647 state->flags |= ESCAPED;
650 if (state->inlen == 0) {
652 * First byte of the frame: allocate the first message block.
654 om = allocb(IFRAME_BSIZE, BPRI_MED);
656 state->flags |= IFLUSH;
659 state->cur_frame = om;
661 state->infcs = PPP_INITFCS;
664 if (om->b_wptr >= om->b_datap->db_lim) {
666 * Current message block is full. Allocate another one,
667 * unless we have run out of MRU.
669 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
670 state->flags |= IFLUSH;
671 DPRINT2("ppp%d: frame too long (%d)\n",
672 state->unit, state->inlen);
675 om = allocb(IFRAME_BSIZE, BPRI_MED);
677 state->flags |= IFLUSH;
680 state->cur_blk->b_cont = om;
685 if (state->inlen == 0) {
687 * We don't do address/control & protocol decompression here,
688 * but we try to put the first byte at an offset such that
689 * the info field starts on a word boundary. The code here
690 * will do this except for packets with protocol compression
691 * but not address/control compression.
693 if (c != PPP_ALLSTATIONS) {
697 om->b_rptr = om->b_wptr;
703 state->infcs = PPP_FCS(state->infcs, c);
712 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
716 return mp->b_rptr[i];