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.2 1995/05/19 02:18:34 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>
37 #include <sys/modctl.h>
41 #include <sys/sunddi.h>
42 #include <sys/errno.h>
43 #include <sys/cmn_err.h>
44 #include <net/ppp_defs.h>
45 #include <net/pppio.h>
47 #define IFRAME_BSIZE 512 /* Block size to allocate for input */
48 #define OFRAME_BSIZE 4096 /* Don't allocb more than this for output */
50 static int ahdlc_open __P((queue_t *, dev_t *, int, int, cred_t *));
51 static int ahdlc_close __P((queue_t *, int, cred_t *));
52 static int ahdlc_wput __P((queue_t *, mblk_t *));
53 static int ahdlc_rput __P((queue_t *, mblk_t *));
54 static void stuff_frame __P((queue_t *, mblk_t *));
55 static void unstuff_chars __P((queue_t *, mblk_t *));
57 static struct module_info minfo = {
58 0x7d23, "ppp_ahdl", 0, INFPSZ, 4096, 128
61 static struct qinit rinit = {
62 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
65 static struct qinit winit = {
66 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
69 static struct streamtab ahdlc_info = {
70 &rinit, &winit, NULL, NULL
73 static struct fmodsw fsw = {
76 D_NEW | D_MP | D_MTQPAIR
79 extern struct mod_ops mod_strmodops;
81 static struct modlstrmod modlstrmod = {
83 "PPP async HDLC module",
87 static struct modlinkage modlinkage = {
106 /* Values for flags */
107 #define ESCAPED 1 /* last saw escape char on input */
108 #define IFLUSH 2 /* flushing input due to error */
111 * FCS lookup table as calculated by genfcstab.
113 static u_short fcstab[256] = {
114 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
115 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
116 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
117 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
118 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
119 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
120 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
121 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
122 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
123 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
124 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
125 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
126 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
127 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
128 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
129 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
130 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
131 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
132 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
133 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
134 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
135 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
136 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
137 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
138 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
139 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
140 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
141 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
142 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
143 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
144 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
145 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
149 * Entry points for modloading.
154 return mod_install(&modlinkage);
160 return mod_remove(&modlinkage);
167 return mod_info(&modlinkage, mip);
171 * STREAMS module entry points.
174 ahdlc_open(q, devp, flag, sflag, credp)
180 struct ahdlc_state *sp;
183 sp = (struct ahdlc_state *) kmem_zalloc(sizeof(struct ahdlc_state),
190 sp->xaccm[3] = 0x60000000;
198 ahdlc_close(q, flag, credp)
203 struct ahdlc_state *state;
207 state = (struct ahdlc_state *) q->q_ptr;
208 if (state->cur_frame != 0) {
209 freemsg(state->cur_frame);
210 state->cur_frame = 0;
212 kmem_free(q->q_ptr, sizeof(struct ahdlc_state));
222 struct ahdlc_state *state;
226 state = (struct ahdlc_state *) q->q_ptr;
227 switch (mp->b_datap->db_type) {
230 * A data packet - do character-stuffing and FCS, and
238 iop = (struct iocblk *) mp->b_rptr;
240 switch (iop->ioc_cmd) {
242 if (iop->ioc_count < sizeof(u_int32_t)
243 || iop->ioc_count > sizeof(ext_accm))
245 bcopy(mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count);
246 state->xaccm[2] &= 0x40000000; /* don't escape 0x5e */
247 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
253 if (iop->ioc_count != sizeof(u_int32_t))
255 bcopy(mp->b_cont->b_rptr, (caddr_t)&state->raccm,
268 else if (error == 0) {
269 mp->b_datap->db_type = M_IOCACK;
272 mp->b_datap->db_type = M_IOCNAK;
274 iop->ioc_error = error;
280 switch (*mp->b_rptr) {
282 state->mtu = ((unsigned short *)mp->b_rptr)[1];
286 state->mru = ((unsigned short *)mp->b_rptr)[1];
290 state->unit = mp->b_rptr[1];
310 struct ahdlc_state *state;
312 switch (mp->b_datap->db_type) {
314 unstuff_chars(q, mp);
319 state = (struct ahdlc_state *) q->q_ptr;
320 if (state->cur_frame != 0) {
321 /* XXX would like to send this up for debugging */
322 freemsg(state->cur_frame);
323 state->cur_frame = 0;
327 state->flags = IFLUSH;
337 /* Extract bit c from map m, to determine if c needs to be escaped. */
338 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
345 struct ahdlc_state *state;
346 int ilen, olen, c, extra;
347 mblk_t *omsg, *np, *op;
348 uchar_t *sp, *sp0, *dp, *dp0, *spend;
350 u_int32_t *xaccm, lcp_xaccm[8];
353 * We estimate the length of the output packet as
354 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
356 state = (struct ahdlc_state *) q->q_ptr;
358 olen = ilen + (ilen >> 2) + 16;
359 if (olen > OFRAME_BSIZE)
361 omsg = op = allocb(olen, BPRI_MED);
366 * Put in an initial flag for now. We'll remove it later
367 * if we decide we don't need it.
374 * For LCP packets, we must escape all control characters.
375 * LCP packets must not be A/C or protocol compressed.
377 xaccm = state->xaccm;
378 if (ilen >= PPP_HDRLEN) {
379 if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
380 || pullupmsg(mp, PPP_HDRLEN)) {
381 if (PPP_ADDRESS(mp->b_rptr) == PPP_ALLSTATIONS
382 && PPP_CONTROL(mp->b_rptr) == PPP_UI
383 && PPP_PROTOCOL(mp->b_rptr) == PPP_LCP) {
384 bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm,
396 extra = sp + olen - spend;
402 * We can safely process the input up to `spend'
403 * without overrunning the output, provided we don't
404 * hit more than `extra' characters which need to be escaped.
410 if (ESCAPE(c, xaccm)) {
413 else if (sp < spend - 1)
417 fcs = PPP_FCS(fcs, c);
421 fcs = PPP_FCS(fcs, c);
429 * At this point, we have emptied an input block
430 * and/or filled an output block.
432 if (sp >= mp->b_wptr) {
434 * We've emptied an input block. Advance to the next.
438 break; /* all done */
443 * The output block is full. Allocate a new one.
447 if (olen > OFRAME_BSIZE)
449 np = allocb(olen, BPRI_MED);
461 * Append the FCS and closing flag.
462 * This could require up to 5 characters.
465 /* Sigh. Need another block. */
467 np = allocb(5, BPRI_MED);
477 if (ESCAPE(c, xaccm)) {
482 c = (~fcs >> 8) & 0xff;
483 if (ESCAPE(c, xaccm)) {
492 * Remove the initial flag, if possible.
494 if (qsize(q->q_next) > 0)
505 struct ahdlc_state *state;
507 uchar_t *cp, *cpend, *dp, *dp0;
508 int c, len, extra, offset;
511 state = (struct ahdlc_state *) q->q_ptr;
515 * Advance to next input block if necessary.
517 if (cp >= mp->b_wptr) {
525 if ((state->flags & (IFLUSH|ESCAPED)) == 0
526 && state->inlen >= PPP_HDRLEN
527 && (om = state->cur_blk) != 0) {
529 * Process bulk chars as quickly as possible.
532 len = om->b_datap->db_lim - dp; /* max # output bytes */
533 extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
535 len += extra; /* we'll run out of input first */
546 if (c == PPP_ESCAPE) {
550 } else if (cp >= cpend) {
551 state->flags |= ESCAPED;
561 fcs = PPP_FCS(fcs, c);
563 state->inlen += dp - dp0;
567 continue; /* go back and check cp again */
574 * If the ESCAPE flag is set, the frame ended with
575 * the frame abort sequence "}~".
577 om = state->cur_frame;
579 state->cur_frame = 0;
583 if (!(state->flags & (IFLUSH|ESCAPED) || len < PPP_FCSLEN)) {
584 if (state->infcs == PPP_GOODFCS) {
585 adjmsg(om, -PPP_FCSLEN); /* chop off fcs */
586 putnext(q, om); /* bombs away! */
589 /* incr bad fcs stats */
591 cmn_err(CE_CONT, "ppp_ahdl: bad fcs %x\n", state->infcs);
595 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
599 if (state->flags & IFLUSH)
601 if (state->flags & ESCAPED) {
603 state->flags &= ~ESCAPED;
604 } else if (c == PPP_ESCAPE) {
605 state->flags |= ESCAPED;
608 if (state->inlen == 0) {
610 * First byte of the frame: allocate the first message block.
612 om = allocb(IFRAME_BSIZE, BPRI_MED);
614 state->flags |= IFLUSH;
617 state->cur_frame = om;
619 state->infcs = PPP_INITFCS;
622 if (om->b_wptr >= om->b_datap->db_lim) {
624 * Current message block is full. Allocate another one,
625 * unless we have run out of MRU.
627 if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
629 cmn_err(CE_CONT, "ppp_ahdl: frame too long (%d)\n",
632 state->flags |= IFLUSH;
635 om = allocb(IFRAME_BSIZE, BPRI_MED);
637 state->flags |= IFLUSH;
640 state->cur_blk->b_cont = om;
646 state->infcs = PPP_FCS(state->infcs, c);
648 if (state->inlen == PPP_HDRLEN) {
650 * We don't do address/control & protocol decompression here,
651 * but we do leave space for the decompressed fields and
652 * arrange for the info field to start on a word boundary.
655 if (PPP_ADDRESS(dp) == PPP_ALLSTATIONS
656 && PPP_CONTROL(dp) == PPP_UI)
660 /* dp is now pointing at the last byte of the ppp protocol field */
661 offset = 3 - ((unsigned)dp & 3);
667 } while (dp > om->b_rptr);
668 om->b_rptr += offset;
669 om->b_wptr += offset;