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.10 1999/04/12 06:20:21 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
));
194 OTHERQ(q
)->q_ptr
= NULL
;
205 ahdlc_state_t
*state
;
209 struct ppp_stats
*psp
;
211 state
= (ahdlc_state_t
*) q
->q_ptr
;
213 DPRINT("state == 0 in ahdlc_wput\n");
218 switch (mp
->b_datap
->db_type
) {
221 * A data packet - do character-stuffing and FCS, and
229 iop
= (struct iocblk
*) mp
->b_rptr
;
231 switch (iop
->ioc_cmd
) {
233 if (iop
->ioc_count
< sizeof(u_int32_t
)
234 || iop
->ioc_count
> sizeof(ext_accm
))
236 if (mp
->b_cont
== 0) {
237 DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state
->unit
);
240 bcopy((caddr_t
)mp
->b_cont
->b_rptr
, (caddr_t
)state
->xaccm
,
242 state
->xaccm
[2] &= ~0x40000000; /* don't escape 0x5e */
243 state
->xaccm
[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
249 if (iop
->ioc_count
!= sizeof(u_int32_t
))
251 if (mp
->b_cont
== 0) {
252 DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state
->unit
);
255 bcopy((caddr_t
)mp
->b_cont
->b_rptr
, (caddr_t
)&state
->raccm
,
262 np
= allocb(sizeof(int), BPRI_HI
);
270 *(int *)np
->b_wptr
= state
->flags
& RCV_FLAGS
;
271 np
->b_wptr
+= sizeof(int);
272 iop
->ioc_count
= sizeof(int);
277 np
= allocb(sizeof(struct ppp_stats
), BPRI_HI
);
285 psp
= (struct ppp_stats
*) np
->b_wptr
;
286 np
->b_wptr
+= sizeof(struct ppp_stats
);
287 bzero((caddr_t
)psp
, sizeof(struct ppp_stats
));
288 psp
->p
= state
->stats
;
289 iop
->ioc_count
= sizeof(struct ppp_stats
);
294 /* we knew this anyway */
305 else if (error
== 0) {
306 mp
->b_datap
->db_type
= M_IOCACK
;
309 mp
->b_datap
->db_type
= M_IOCNAK
;
311 iop
->ioc_error
= error
;
317 switch (*mp
->b_rptr
) {
319 state
->mtu
= ((unsigned short *)mp
->b_rptr
)[1];
323 state
->mru
= ((unsigned short *)mp
->b_rptr
)[1];
327 state
->unit
= mp
->b_rptr
[1];
348 ahdlc_state_t
*state
;
350 state
= (ahdlc_state_t
*) q
->q_ptr
;
352 DPRINT("state == 0 in ahdlc_rput\n");
357 switch (mp
->b_datap
->db_type
) {
359 unstuff_chars(q
, mp
);
364 if (state
->cur_frame
!= 0) {
365 /* XXX would like to send this up for debugging */
366 freemsg(state
->cur_frame
);
367 state
->cur_frame
= 0;
371 state
->flags
= IFLUSH
;
381 /* Extract bit c from map m, to determine if c needs to be escaped. */
382 #define ESCAPE(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
389 ahdlc_state_t
*state
;
390 int ilen
, olen
, c
, extra
, i
, code
;
391 mblk_t
*omsg
, *op
, *np
;
392 uchar_t
*sp
, *sp0
, *dp
, *dp0
, *spend
;
394 u_int32_t
*xaccm
, lcp_xaccm
[8];
395 static uchar_t lcphdr
[PPP_HDRLEN
] = { 0xff, 0x03, 0xc0, 0x21 };
396 uchar_t ppphdr
[PPP_HDRLEN
];
398 state
= (ahdlc_state_t
*) q
->q_ptr
;
402 * We estimate the length of the output packet as
403 * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
405 olen
= ilen
+ (ilen
>> 2) + 16;
406 if (olen
> OFRAME_BSIZE
)
408 omsg
= op
= allocb(olen
, BPRI_MED
);
413 * Put in an initial flag, unless the serial driver currently has
414 * packets still to be transmitted in its queue.
417 if (qsize(q
->q_next
) == 0) {
423 * For LCP packets with code values between 1 and 7 (Conf-Req
424 * to Code-Rej), we must escape all control characters.
426 xaccm
= state
->xaccm
;
427 if (MSG_BYTE(mp
, 0) == PPP_ALLSTATIONS
428 && MSG_BYTE(mp
, 1) == PPP_UI
429 && MSG_BYTE(mp
, 2) == (PPP_LCP
>> 8)
430 && MSG_BYTE(mp
, 3) == (PPP_LCP
& 0xFF)
431 && LCP_USE_DFLT(mp
)) {
432 bcopy((caddr_t
) state
->xaccm
, (caddr_t
) lcp_xaccm
, sizeof(lcp_xaccm
));
441 extra
= sp
+ olen
- spend
;
447 * We can safely process the input up to `spend'
448 * without overrunning the output, provided we don't
449 * hit more than `extra' characters which need to be escaped.
455 if (ESCAPE(c
, xaccm
)) {
458 else if (sp
< spend
- 1)
462 fcs
= PPP_FCS(fcs
, c
);
466 fcs
= PPP_FCS(fcs
, c
);
474 * At this point, we have emptied an input block
475 * and/or filled an output block.
477 if (sp
>= mp
->b_wptr
) {
479 * We've emptied an input block. Advance to the next.
483 break; /* all done */
488 * The output block is full. Allocate a new one.
492 if (olen
> OFRAME_BSIZE
)
494 np
= allocb(olen
, BPRI_MED
);
504 * Append the FCS and closing flag.
505 * This could require up to 5 characters.
508 /* Sigh. Need another block. */
510 np
= allocb(5, BPRI_MED
);
518 if (ESCAPE(c
, xaccm
)) {
523 c
= (~fcs
>> 8) & 0xff;
524 if (ESCAPE(c
, xaccm
)) {
535 state
->stats
.ppp_obytes
+= msgdsize(omsg
);
536 state
->stats
.ppp_opackets
++;
547 state
->stats
.ppp_oerrors
++;
548 putctl1(RD(q
)->q_next
, M_CTL
, PPPCTL_OERROR
);
551 #define UPDATE_FLAGS(c) { \
553 state->flags |= RCV_B7_1; \
555 state->flags |= RCV_B7_0; \
556 if (0x6996 & (1 << ((((c) >> 4) ^ (c)) & 0xf))) \
557 state->flags |= RCV_ODDP; \
559 state->flags |= RCV_EVNP; \
563 * Process received characters.
570 ahdlc_state_t
*state
;
572 uchar_t
*cp
, *cpend
, *dp
, *dp0
;
573 int c
, len
, extra
, offset
;
576 state
= (ahdlc_state_t
*) q
->q_ptr
;
577 state
->stats
.ppp_ibytes
+= msgdsize(mp
);
581 * Advance to next input block if necessary.
583 if (cp
>= mp
->b_wptr
) {
591 if ((state
->flags
& (IFLUSH
|ESCAPED
)) == 0
592 && state
->inlen
> 0 && (om
= state
->cur_blk
) != 0) {
594 * Process bulk chars as quickly as possible.
597 len
= om
->b_datap
->db_lim
- dp
; /* max # output bytes */
598 extra
= (mp
->b_wptr
- cp
) - len
;/* #input chars - #output bytes */
600 len
+= extra
; /* we'll run out of input first */
612 if (c
== PPP_ESCAPE
) {
617 if (cp
>= cpend
|| (c
= *cp
) == PPP_FLAG
) {
618 state
->flags
|= ESCAPED
;
626 fcs
= PPP_FCS(fcs
, c
);
628 state
->inlen
+= dp
- dp0
;
631 if (cp
>= mp
->b_wptr
)
632 continue; /* advance to the next mblk */
640 * If the ESCAPE flag is set, the frame ended with
641 * the frame abort sequence "}~".
643 om
= state
->cur_frame
;
645 state
->cur_frame
= 0;
647 if (len
== 0 && (state
->flags
& IFLUSH
) == 0)
649 state
->stats
.ppp_ipackets
++;
650 if (om
!= 0 && (state
->flags
& (IFLUSH
|ESCAPED
)) == 0
651 && len
> PPP_FCSLEN
) {
652 if (state
->infcs
== PPP_GOODFCS
) {
653 adjmsg(om
, -PPP_FCSLEN
); /* chop off fcs */
654 putnext(q
, om
); /* bombs away! */
657 DPRINT2("ppp%d: bad fcs (len=%d)\n", state
->unit
, len
);
661 state
->flags
&= ~(IFLUSH
|ESCAPED
);
662 state
->stats
.ppp_ierrors
++;
663 putctl1(q
->q_next
, M_CTL
, PPPCTL_IERROR
);
667 if (state
->flags
& IFLUSH
)
669 if (state
->flags
& ESCAPED
) {
671 state
->flags
&= ~ESCAPED
;
672 } else if (c
== PPP_ESCAPE
) {
673 state
->flags
|= ESCAPED
;
676 if (state
->inlen
== 0) {
678 * First byte of the frame: allocate the first message block.
680 om
= allocb(IFRAME_BSIZE
, BPRI_MED
);
682 state
->flags
|= IFLUSH
;
685 state
->cur_frame
= om
;
687 state
->infcs
= PPP_INITFCS
;
690 if (om
->b_wptr
>= om
->b_datap
->db_lim
) {
692 * Current message block is full. Allocate another one,
693 * unless we have run out of MRU.
695 if (state
->inlen
>= state
->mru
+ PPP_HDRLEN
+ PPP_FCSLEN
) {
696 state
->flags
|= IFLUSH
;
697 DPRINT2("ppp%d: frame too long (%d)\n",
698 state
->unit
, state
->inlen
);
701 om
= allocb(IFRAME_BSIZE
, BPRI_MED
);
703 state
->flags
|= IFLUSH
;
706 state
->cur_blk
->b_cont
= om
;
711 if (state
->inlen
== 0) {
713 * We don't do address/control & protocol decompression here,
714 * but we try to put the first byte at an offset such that
715 * the info field starts on a word boundary. The code here
716 * will do this except for packets with protocol compression
717 * but not address/control compression.
719 if (c
!= PPP_ALLSTATIONS
) {
723 om
->b_rptr
= om
->b_wptr
;
729 state
->infcs
= PPP_FCS(state
->infcs
, c
);
738 while (mp
!= 0 && i
>= mp
->b_wptr
- mp
->b_rptr
)
742 return mp
->b_rptr
[i
];