2 * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
4 * Re-written by Adi Masputra <adi.masputra@sun.com>, based on
5 * the original ppp_ahdlc.c
7 * Copyright (c) 2000 by Sun Microsystems, Inc.
10 * Permission to use, copy, modify, and distribute this software and its
11 * documentation is hereby granted, provided that the above copyright
12 * notice appears in all copies.
14 * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
15 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
16 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
17 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
18 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
19 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
21 * Copyright (c) 1994 Paul Mackerras. All rights reserved.
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer in
32 * the documentation and/or other materials provided with the
35 * 3. The name(s) of the authors of this software must not be used to
36 * endorse or promote products derived from this software without
37 * prior written permission.
39 * 4. Redistributions of any form whatsoever must retain the following
41 * "This product includes software developed by Paul Mackerras
42 * <paulus@samba.org>".
44 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
45 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
46 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
47 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
48 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
49 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
50 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
52 * $Id: ppp_ahdlc.c,v 1.5 2005/06/27 00:59:57 carlsonj Exp $
56 * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
58 #include <sys/types.h>
59 #include <sys/param.h>
60 #include <sys/stream.h>
61 #include <sys/stropts.h>
62 #include <sys/errno.h>
67 #include <sys/cmn_err.h>
72 #include <sys/cmn_err.h>
76 #include <net/ppp_defs.h>
77 #include <net/pppio.h>
81 * Right now, mutex is only enabled for Solaris 2.x
88 #define MUTEX_ENTER(x) mutex_enter(x)
89 #define MUTEX_EXIT(x) mutex_exit(x)
91 #define MUTEX_ENTER(x)
96 * intpointer_t and uintpointer_t are signed and unsigned integer types
97 * large enough to hold any data pointer; that is, data pointers can be
98 * assigned into or from these integer types without losing precision.
99 * On recent Solaris releases, these types are defined in sys/int_types.h,
100 * but not on SunOS 4.x or the earlier Solaris versions.
102 #if defined(_LP64) || defined(_I32LPx)
103 typedef long intpointer_t;
104 typedef unsigned long uintpointer_t;
106 typedef int intpointer_t;
107 typedef unsigned int uintpointer_t;
110 MOD_OPEN_DECL(ahdlc_open);
111 MOD_CLOSE_DECL(ahdlc_close);
112 static int ahdlc_wput __P((queue_t *, mblk_t *));
113 static int ahdlc_rput __P((queue_t *, mblk_t *));
114 static void ahdlc_encode __P((queue_t *, mblk_t *));
115 static void ahdlc_decode __P((queue_t *, mblk_t *));
116 static int msg_byte __P((mblk_t *, unsigned int));
120 * Don't send HDLC start flag is last transmit is within 1.5 seconds -
121 * FLAG_TIME is defined is microseconds
123 #define FLAG_TIME 1500
124 #define ABS(x) (x >= 0 ? x : (-x))
128 * Extract byte i of message mp
130 #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
134 * Is this LCP packet one we have to transmit using LCP defaults?
136 #define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
139 * Standard STREAMS declarations
141 static struct module_info minfo = {
142 0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512
145 static struct qinit rinit = {
146 ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
149 static struct qinit winit = {
150 ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
153 #if defined(SVR4) && !defined(SOL2)
155 #define ppp_ahdlcinfo phdlinfo
156 #endif /* defined(SVR4) && !defined(SOL2) */
158 struct streamtab ppp_ahdlcinfo = {
159 &rinit, /* ptr to st_rdinit */
160 &winit, /* ptr to st_wrinit */
161 NULL, /* ptr to st_muxrinit */
162 NULL, /* ptr to st_muxwinit */
164 NULL /* ptr to ptr to st_modlist */
169 int ppp_ahdlc_count = 0; /* open counter */
173 * Per-stream state structure
175 typedef struct ahdlc_state {
176 #if defined(USE_MUTEX)
177 kmutex_t lock; /* lock for this structure */
178 #endif /* USE_MUTEX */
179 int flags; /* link flags */
180 mblk_t *rx_buf; /* ptr to receive buffer */
181 int rx_buf_size; /* receive buffer size */
182 ushort_t infcs; /* calculated rx HDLC FCS */
183 u_int32_t xaccm[8]; /* 256-bit xmit ACCM */
184 u_int32_t raccm; /* 32-bit rcv ACCM */
185 int mtu; /* interface MTU */
186 int mru; /* link MRU */
187 int unit; /* current PPP unit number */
188 struct pppstat stats; /* statistic structure */
190 clock_t flag_time; /* time in usec between flags */
191 clock_t lbolt; /* last updated lbolt */
198 #define ESCAPED 0x100 /* last saw escape char on input */
199 #define IFLUSH 0x200 /* flushing input due to error */
202 * RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also.
204 #define RCV_FLAGS (RCV_B7_1|RCV_B7_0|RCV_ODDP|RCV_EVNP)
207 * FCS lookup table as calculated by genfcstab.
209 static u_short fcstab[256] = {
210 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
211 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
212 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
213 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
214 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
215 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
216 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
217 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
218 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
219 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
220 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
221 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
222 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
223 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
224 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
225 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
226 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
227 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
228 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
229 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
230 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
231 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
232 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
233 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
234 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
235 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
236 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
237 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
238 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
239 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
240 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
241 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
244 static u_int32_t paritytab[8] =
246 0x96696996, 0x69969669, 0x69969669, 0x96696996,
247 0x69969669, 0x96696996, 0x96696996, 0x69969669
251 * STREAMS module open (entry) point
255 ahdlc_state_t *state;
259 * Return if it's already opened
266 * This can only be opened as a module
268 if (sflag != MODOPEN) {
272 state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
275 bzero((caddr_t) state, sizeof(ahdlc_state_t));
277 q->q_ptr = (caddr_t) state;
278 WR(q)->q_ptr = (caddr_t) state;
280 #if defined(USE_MUTEX)
281 mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
282 #endif /* USE_MUTEX */
284 state->xaccm[0] = ~0; /* escape 0x00 through 0x1f */
285 state->xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */
286 state->mru = PPP_MRU; /* default of 1500 bytes */
288 state->flag_time = drv_usectohz(FLAG_TIME);
297 if ((mp = allocb(1, BPRI_HI)) != NULL) {
298 mp->b_datap->db_type = M_FLUSH;
299 *mp->b_wptr++ = FLUSHR;
307 * STREAMS module close (exit) point
309 MOD_CLOSE(ahdlc_close)
311 ahdlc_state_t *state;
315 state = (ahdlc_state_t *) q->q_ptr;
318 DPRINT("state == 0 in ahdlc_close\n");
322 if (state->rx_buf != 0) {
323 freemsg(state->rx_buf);
327 #if defined(USE_MUTEX)
328 mutex_destroy(&state->lock);
329 #endif /* USE_MUTEX */
331 FREE(q->q_ptr, sizeof(ahdlc_state_t));
333 OTHERQ(q)->q_ptr = NULL;
344 * Write side put routine
351 ahdlc_state_t *state;
355 struct ppp_stats *psp;
357 state = (ahdlc_state_t *) q->q_ptr;
359 DPRINT("state == 0 in ahdlc_wput\n");
364 switch (mp->b_datap->db_type) {
367 * A data packet - do character-stuffing and FCS, and
375 iop = (struct iocblk *) mp->b_rptr;
377 switch (iop->ioc_cmd) {
379 if ((iop->ioc_count < sizeof(u_int32_t)) ||
380 (iop->ioc_count > sizeof(ext_accm))) {
383 if (mp->b_cont == 0) {
384 DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
387 MUTEX_ENTER(&state->lock);
388 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)state->xaccm,
390 state->xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
391 state->xaccm[3] |= 0x60000000; /* do escape 0x7d, 0x7e */
392 MUTEX_EXIT(&state->lock);
398 if (iop->ioc_count != sizeof(u_int32_t))
400 if (mp->b_cont == 0) {
401 DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
404 MUTEX_ENTER(&state->lock);
405 bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
407 MUTEX_EXIT(&state->lock);
413 np = allocb(sizeof(int), BPRI_HI);
421 MUTEX_ENTER(&state->lock);
422 *(int *)np->b_wptr = state->flags & RCV_FLAGS;
423 MUTEX_EXIT(&state->lock);
424 np->b_wptr += sizeof(int);
425 iop->ioc_count = sizeof(int);
430 np = allocb(sizeof(struct ppp_stats), BPRI_HI);
438 psp = (struct ppp_stats *) np->b_wptr;
439 np->b_wptr += sizeof(struct ppp_stats);
440 bzero((caddr_t)psp, sizeof(struct ppp_stats));
441 psp->p = state->stats;
442 iop->ioc_count = sizeof(struct ppp_stats);
447 /* we knew this anyway */
458 else if (error == 0) {
459 mp->b_datap->db_type = M_IOCACK;
462 mp->b_datap->db_type = M_IOCNAK;
464 iop->ioc_error = error;
470 switch (*mp->b_rptr) {
472 MUTEX_ENTER(&state->lock);
473 state->mtu = ((unsigned short *)mp->b_rptr)[1];
474 MUTEX_EXIT(&state->lock);
477 MUTEX_ENTER(&state->lock);
478 state->mru = ((unsigned short *)mp->b_rptr)[1];
479 MUTEX_EXIT(&state->lock);
482 MUTEX_ENTER(&state->lock);
483 state->unit = mp->b_rptr[1];
484 MUTEX_EXIT(&state->lock);
501 * Read side put routine
508 ahdlc_state_t *state;
510 state = (ahdlc_state_t *) q->q_ptr;
512 DPRINT("state == 0 in ahdlc_rput\n");
517 switch (mp->b_datap->db_type) {
523 MUTEX_ENTER(&state->lock);
524 if (state->rx_buf != 0) {
525 /* XXX would like to send this up for debugging */
526 freemsg(state->rx_buf);
529 state->flags = IFLUSH;
530 MUTEX_EXIT(&state->lock);
541 * Extract bit c from map m, to determine if c needs to be escaped
543 #define IN_TX_MAP(c, m) ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
550 ahdlc_state_t *state;
551 u_int32_t *xaccm, loc_xaccm[8];
555 uchar_t *dp, fcs_val;
561 if (msgdsize(mp) < 4) {
565 state = (ahdlc_state_t *)q->q_ptr;
566 MUTEX_ENTER(&state->lock);
569 * Allocate an output buffer large enough to handle a case where all
570 * characters need to be escaped
572 outmp_len = (msgdsize(mp) << 1) + /* input block x 2 */
573 (sizeof(fcs) << 2) + /* HDLC FCS x 4 */
574 (sizeof(uchar_t) << 1); /* HDLC flags x 2 */
576 outmp = allocb(outmp_len, BPRI_MED);
578 state->stats.ppp_oerrors++;
579 MUTEX_EXIT(&state->lock);
580 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
586 * Check if our last transmit happenned within flag_time, using
587 * the system's LBOLT value in clock ticks
589 if (drv_getparm(LBOLT, &lbolt) != -1) {
590 if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
591 *outmp->b_wptr++ = PPP_FLAG;
593 state->lbolt = lbolt;
595 *outmp->b_wptr++ = PPP_FLAG;
599 * If the driver below still has a message to process, skip the
600 * HDLC flag, otherwise, put one in the beginning
602 if (qsize(q->q_next) == 0) {
603 *outmp->b_wptr++ = PPP_FLAG;
608 * All control characters must be escaped for LCP packets with code
609 * values between 1 (Conf-Req) and 7 (Code-Rej).
611 is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) &&
612 (MSG_BYTE(mp, 1) == PPP_UI) &&
613 (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
614 (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
617 xaccm = state->xaccm;
619 bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
620 loc_xaccm[0] = ~0; /* force escape on 0x00 through 0x1f */
624 fcs = PPP_INITFCS; /* Initial FCS is 0xffff */
627 * Process this block and the rest (if any) attached to the this one
629 for (tmp = mp; tmp; tmp = tmp->b_cont) {
630 if (tmp->b_datap->db_type == M_DATA) {
631 for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
632 fcs = PPP_FCS(fcs, *dp);
633 if (IN_TX_MAP(*dp, xaccm)) {
634 *outmp->b_wptr++ = PPP_ESCAPE;
635 *outmp->b_wptr++ = *dp ^ PPP_TRANS;
637 *outmp->b_wptr++ = *dp;
641 continue; /* skip if db_type is something other than M_DATA */
646 * Append the HDLC FCS, making sure that escaping is done on any
649 fcs_val = (fcs ^ 0xffff) & 0xff;
650 if (IN_TX_MAP(fcs_val, xaccm)) {
651 *outmp->b_wptr++ = PPP_ESCAPE;
652 *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
654 *outmp->b_wptr++ = fcs_val;
657 fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
658 if (IN_TX_MAP(fcs_val, xaccm)) {
659 *outmp->b_wptr++ = PPP_ESCAPE;
660 *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
662 *outmp->b_wptr++ = fcs_val;
666 * And finally, append the HDLC flag, and send it away
668 *outmp->b_wptr++ = PPP_FLAG;
670 state->stats.ppp_obytes += msgdsize(outmp);
671 state->stats.ppp_opackets++;
673 MUTEX_EXIT(&state->lock);
679 * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
681 #define IN_RX_MAP(c, m) ((((unsigned int) (uchar_t) (c)) < 0x20) && \
686 * Process received characters.
693 ahdlc_state_t *state;
697 state = (ahdlc_state_t *) q->q_ptr;
699 MUTEX_ENTER(&state->lock);
701 state->stats.ppp_ibytes += msgdsize(mp);
703 for (; mp != 0; om = mp->b_cont, freeb(mp), mp = om)
704 for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
707 * This should detect the lack of 8-bit communication channel
708 * which is necessary for PPP to work. In addition, it also
709 * checks on the parity.
712 state->flags |= RCV_B7_1;
714 state->flags |= RCV_B7_0;
716 if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
717 state->flags |= RCV_ODDP;
719 state->flags |= RCV_EVNP;
722 * So we have a HDLC flag ...
724 if (*dp == PPP_FLAG) {
727 * If we think that it marks the beginning of the frame,
728 * then continue to process the next octects
730 if ((state->flags & IFLUSH) ||
731 (state->rx_buf == 0) ||
732 (msgdsize(state->rx_buf) == 0)) {
734 state->flags &= ~IFLUSH;
739 * We get here because the above condition isn't true,
740 * in which case the HDLC flag was there to mark the end
741 * of the frame (or so we think)
745 if (state->infcs == PPP_GOODFCS) {
746 state->stats.ppp_ipackets++;
747 adjmsg(om, -PPP_FCSLEN);
750 DPRINT2("ppp%d: bad fcs (len=%d)\n",
751 state->unit, msgdsize(state->rx_buf));
752 freemsg(state->rx_buf);
753 state->flags &= ~(IFLUSH | ESCAPED);
754 state->stats.ppp_ierrors++;
755 putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
762 if (state->flags & IFLUSH) {
767 * Allocate a receive buffer, large enough to store a frame (after
768 * un-escaping) of at least 1500 octets. If MRU is negotiated to
769 * be more than the default, then allocate that much. In addition,
770 * we add an extra 32-bytes for a fudge factor
772 if (state->rx_buf == 0) {
773 state->rx_buf_size = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
774 state->rx_buf_size += (sizeof(u_int32_t) << 3);
775 state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
778 * If allocation fails, try again on the next frame
780 if (state->rx_buf == 0) {
781 state->flags |= IFLUSH;
784 state->flags &= ~(IFLUSH | ESCAPED);
785 state->infcs = PPP_INITFCS;
788 if (*dp == PPP_ESCAPE) {
789 state->flags |= ESCAPED;
794 * Make sure we un-escape the necessary characters, as well as the
795 * ones in our receive async control character map
797 if (state->flags & ESCAPED) {
799 state->flags &= ~ESCAPED;
800 } else if (IN_RX_MAP(*dp, state->raccm))
804 * Unless the peer lied to us about the negotiated MRU, we should
805 * never get a frame which is too long. If it happens, toss it away
806 * and grab the next incoming one
808 if (msgdsize(state->rx_buf) < state->rx_buf_size) {
809 state->infcs = PPP_FCS(state->infcs, *dp);
810 *state->rx_buf->b_wptr++ = *dp;
812 DPRINT2("ppp%d: frame too long (%d)\n",
813 state->unit, msgdsize(state->rx_buf));
814 freemsg(state->rx_buf);
816 state->flags |= IFLUSH;
820 MUTEX_EXIT(&state->lock);
828 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
832 return mp->b_rptr[i];