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 HAVE 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 1995/08/10 06:54:20 paulus Exp $
31 * This file is used under Solaris 2.
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/stream.h>
39 #include <sys/errno.h>
40 #include <sys/cmn_err.h>
43 #include <sys/modctl.h>
44 #include <sys/sunddi.h>
46 #include <net/ppp_defs.h>
47 #include <net/pppio.h>
49 #define IFRAME_BSIZE 512 /* Block size to allocate for input */
50 #define OFRAME_BSIZE 4096 /* Don't allocb more than this for output */
52 static int ahdlc_open __P((queue_t *, dev_t *, int, int, cred_t *));
53 static int ahdlc_close __P((queue_t *, int, cred_t *));
54 static int ahdlc_wput __P((queue_t *, mblk_t *));
55 static int ahdlc_rput __P((queue_t *, mblk_t *));
56 static void stuff_frame __P((queue_t *, mblk_t *));
57 static void unstuff_chars __P((queue_t *, mblk_t *));
58 static int msg_copy __P((uchar_t *, mblk_t *, int));
60 static struct module_info minfo = {
61 0x7d23, "ppp_ahdl", 0, INFPSZ, 4096, 128
64 static struct qinit rinit = {
65 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
68 static struct qinit winit = {
69 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
72 static struct streamtab ahdlc_info = {
73 &rinit, &winit, NULL, NULL
77 static struct fmodsw fsw = {
80 D_NEW | D_MP | D_MTQPAIR
83 extern struct mod_ops mod_strmodops;
85 static struct modlstrmod modlstrmod = {
87 "PPP async HDLC module",
91 static struct modlinkage modlinkage = {
98 typedef struct ahdlc_state {
109 struct pppstat stats;
112 /* Values for flags */
113 #define ESCAPED 0x100 /* last saw escape char on input */
114 #define IFLUSH 0x200 /* flushing input due to error */
116 /* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also. */
117 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
121 # define qprocsoff(q)
125 * FCS lookup table as calculated by genfcstab.
127 static u_short fcstab[256] = {
128 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
129 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
130 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
131 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
132 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
133 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
134 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
135 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
136 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
137 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
138 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
139 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
140 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
141 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
142 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
143 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
144 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
145 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
146 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
147 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
148 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
149 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
150 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
151 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
152 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
153 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
154 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
155 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
156 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
157 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
158 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
159 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
164 * Entry points for modloading.
169 return mod_install(&modlinkage);
175 return mod_remove(&modlinkage);
182 return mod_info(&modlinkage, mip);
187 * STREAMS module entry points.
190 ahdlc_open(q, devp, flag, sflag, credp)
199 sp = (ahdlc_state_t *) kmem_zalloc(sizeof(ahdlc_state_t), KM_SLEEP);
205 sp->xaccm[3] = 0x60000000;
213 ahdlc_close(q, flag, credp)
218 ahdlc_state_t *state;
222 state = (ahdlc_state_t *) q->q_ptr;
223 if (state->cur_frame != 0) {
224 freemsg(state->cur_frame);
225 state->cur_frame = 0;
227 kmem_free(q->q_ptr, sizeof(ahdlc_state_t));
237 ahdlc_state_t *state;
241 struct ppp_stats *psp;
243 state = (ahdlc_state_t *) q->q_ptr;
244 switch (mp->b_datap->db_type) {
247 * A data packet - do character-stuffing and FCS, and
255 iop = (struct iocblk *) mp->b_rptr;
257 switch (iop->ioc_cmd) {
259 if (iop->ioc_count < sizeof(u_int32_t)
260 || iop->ioc_count > sizeof(ext_accm))
262 bcopy(mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count);
263 state->xaccm[2] &= 0x40000000; /* don't escape 0x5e */
264 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
270 if (iop->ioc_count != sizeof(u_int32_t))
272 bcopy(mp->b_cont->b_rptr, (caddr_t)&state->raccm,
279 np = allocb(sizeof(int), BPRI_HI);
287 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
288 np->b_wptr += sizeof(int);
289 iop->ioc_count = sizeof(int);
294 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
302 psp = (struct ppp_stats *) np->b_wptr;
303 np->b_wptr += sizeof(struct ppp_stats);
304 bzero((caddr_t)psp, sizeof(struct ppp_stats));
305 psp->p = state->stats;
306 iop->ioc_count = sizeof(struct ppp_stats);
311 /* we knew this anyway */
322 else if (error == 0) {
323 mp->b_datap->db_type = M_IOCACK;
326 mp->b_datap->db_type = M_IOCNAK;
328 iop->ioc_error = error;
334 switch (*mp->b_rptr) {
336 state->mtu = ((unsigned short *)mp->b_rptr)[1];
340 state->mru = ((unsigned short *)mp->b_rptr)[1];
344 state->unit = mp->b_rptr[1];
364 ahdlc_state_t *state;
366 switch (mp->b_datap->db_type) {
368 unstuff_chars(q, mp);
373 state = (ahdlc_state_t *) q->q_ptr;
374 if (state->cur_frame != 0) {
375 /* XXX would like to send this up for debugging */
376 freemsg(state->cur_frame);
377 state->cur_frame = 0;
381 state->flags = IFLUSH;
391 /* Extract bit c from map m, to determine if c needs to be escaped. */
392 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
399 ahdlc_state_t *state;
400 int ilen, olen, c, extra, i;
401 mblk_t *omsg, *op, *np;
402 uchar_t *sp, *sp0, *dp, *dp0, *spend;
404 u_int32_t *xaccm, lcp_xaccm[8];
405 static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
406 uchar_t ppphdr[PPP_HDRLEN];
408 state = (ahdlc_state_t *) q->q_ptr;
412 * We estimate the length of the output packet as
413 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
415 olen = ilen + (ilen >> 2) + 16;
416 if (olen > OFRAME_BSIZE)
418 omsg = op = allocb(olen, BPRI_MED);
423 * Put in an initial flag for now. We'll remove it later
424 * if we decide we don't need it.
431 * For LCP packets, we must escape all control characters.
432 * Inspect the first PPP_HDRLEN bytes of the message to
433 * see whether it is an LCP packet.
434 * LCP packets must not be A/C or protocol compressed.
436 xaccm = state->xaccm;
437 if (msg_copy(ppphdr, mp, PPP_HDRLEN) == PPP_HDRLEN
438 && bcmp((caddr_t) ppphdr, (caddr_t) lcphdr, PPP_HDRLEN) == 0) {
439 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
448 extra = sp + olen - spend;
454 * We can safely process the input up to `spend'
455 * without overrunning the output, provided we don't
456 * hit more than `extra' characters which need to be escaped.
462 if (ESCAPE(c, xaccm)) {
465 else if (sp < spend - 1)
469 fcs = PPP_FCS(fcs, c);
473 fcs = PPP_FCS(fcs, c);
481 * At this point, we have emptied an input block
482 * and/or filled an output block.
484 if (sp >= mp->b_wptr) {
486 * We've emptied an input block. Advance to the next.
490 break; /* all done */
495 * The output block is full. Allocate a new one.
499 if (olen > OFRAME_BSIZE)
501 np = allocb(olen, BPRI_MED);
511 * Append the FCS and closing flag.
512 * This could require up to 5 characters.
515 /* Sigh. Need another block. */
517 np = allocb(5, BPRI_MED);
525 if (ESCAPE(c, xaccm)) {
530 c = (~fcs >> 8) & 0xff;
531 if (ESCAPE(c, xaccm)) {
540 * Remove the initial flag, if possible.
542 if (qsize(q->q_next) > 0)
548 state->stats.ppp_obytes += msgdsize(omsg);
549 state->stats.ppp_opackets++;
560 state->stats.ppp_oerrors++;
561 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
564 static unsigned paritytab[8] = {
565 0x96696996, 0x69969669, 0x69969669, 0x96696996,
566 0x69969669, 0x96696996, 0x96696996, 0x69969669
569 #define UPDATE_FLAGS(c) { \
571 state->flags |= RCV_B7_1; \
573 state->flags |= RCV_B7_0; \
574 if (paritytab[(c) >> 5] & (1 << ((c) & 0x1F))) \
575 state->flags |= RCV_ODDP; \
577 state->flags |= RCV_EVNP; \
581 * Process received characters.
588 ahdlc_state_t *state;
590 uchar_t *cp, *cpend, *dp, *dp0;
591 int c, len, extra, offset;
594 state = (ahdlc_state_t *) q->q_ptr;
595 state->stats.ppp_ibytes += msgdsize(mp);
599 * Advance to next input block if necessary.
601 if (cp >= mp->b_wptr) {
609 if ((state->flags & (IFLUSH|ESCAPED)) == 0
610 && state->inlen >= PPP_HDRLEN
611 && (om = state->cur_blk) != 0) {
613 * Process bulk chars as quickly as possible.
616 len = om->b_datap->db_lim - dp; /* max # output bytes */
617 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
619 len += extra; /* we'll run out of input first */
631 if (c == PPP_ESCAPE) {
636 if (cp >= cpend || (c = *cp) == PPP_FLAG) {
637 state->flags |= ESCAPED;
645 fcs = PPP_FCS(fcs, c);
647 state->inlen += dp - dp0;
650 if (cp >= mp->b_wptr)
651 continue; /* advance to the next mblk */
659 * If the ESCAPE flag is set, the frame ended with
660 * the frame abort sequence "}~".
662 om = state->cur_frame;
664 state->cur_frame = 0;
666 if (len == 0 && (state->flags & IFLUSH) == 0)
668 state->stats.ppp_ipackets++;
669 if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
670 && len > PPP_FCSLEN) {
671 if (state->infcs == PPP_GOODFCS) {
672 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
673 putnext(q, om); /* bombs away! */
677 cmn_err(CE_CONT, "ppp_ahdl: bad fcs %x\n", state->infcs);
682 state->stats.ppp_ierrors++;
683 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
687 if (state->flags & IFLUSH)
689 if (state->flags & ESCAPED) {
691 state->flags &= ~ESCAPED;
692 } else if (c == PPP_ESCAPE) {
693 state->flags |= ESCAPED;
696 if (state->inlen == 0) {
698 * First byte of the frame: allocate the first message block.
700 om = allocb(IFRAME_BSIZE, BPRI_MED);
702 state->flags |= IFLUSH;
705 state->cur_frame = om;
707 state->infcs = PPP_INITFCS;
710 if (om->b_wptr >= om->b_datap->db_lim) {
712 * Current message block is full. Allocate another one,
713 * unless we have run out of MRU.
715 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
717 cmn_err(CE_CONT, "ppp_ahdl: frame too long (%d)\n",
720 state->flags |= IFLUSH;
723 om = allocb(IFRAME_BSIZE, BPRI_MED);
725 state->flags |= IFLUSH;
728 state->cur_blk->b_cont = om;
734 state->infcs = PPP_FCS(state->infcs, c);
736 if (state->inlen == PPP_HDRLEN) {
738 * We don't do address/control & protocol decompression here,
739 * but we do leave space for the decompressed fields and
740 * arrange for the info field to start on a word boundary.
743 if (PPP_ADDRESS(dp) == PPP_ALLSTATIONS
744 && PPP_CONTROL(dp) == PPP_UI)
748 /* dp is now pointing at the last byte of the ppp protocol field */
749 offset = 3 - ((unsigned)dp & 3);
755 } while (dp > om->b_rptr);
756 om->b_rptr += offset;
757 om->b_wptr += offset;
773 for (i = 0; i < n; ++i) {
774 if (cp >= mp->b_wptr) {
776 if ((mp = mp->b_cont) == 0)
778 } while (mp->b_rptr >= mp->b_wptr);