/*
* spppasyn.c - STREAMS module for doing PPP asynchronous HDLC.
*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies.
*
* SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
*
* Copyright (c) 1994 The Australian National University.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies. This software is provided without any
* warranty, express or implied. The Australian National University
* makes no representations about the suitability of this software for
* any purpose.
*
* IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
* PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
* OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
*
* $Id: ppp_ahdlc.c,v 1.16 2000/03/06 19:38:12 masputra Exp $
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/crc32.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <net/ppp_defs.h>
#include <net/pppio.h>
#include "s_common.h"
#ifdef DEBUG
#define REPORT_CRC_TYPE
#endif
#include "spppasyn.h"
/*
* This is used to tag official Solaris sources. Please do not define
* "INTERNAL_BUILD" when building this software outside of Sun
* Microsystems.
*/
#ifdef INTERNAL_BUILD
/* MODINFO is limited to 32 characters. */
const char spppasyn_module_description[] = "PPP 4.0 AHDLC";
#else /* INTERNAL_BUILD */
const char spppasyn_module_description[] = "ANU PPP AHDLC $Revision: 1.16$";
/* LINTED */
static const char buildtime[] = "Built " __DATE__ " at " __TIME__
#ifdef DEBUG
" DEBUG"
#endif
"\n";
#endif /* INTERNAL_BUILD */
static int spppasyn_open(queue_t *, dev_t *, int, int, cred_t *);
static int spppasyn_close(queue_t *, int, cred_t *);
static int spppasyn_wput(queue_t *, mblk_t *);
static int spppasyn_rput(queue_t *, mblk_t *);
static mblk_t *ahdlc_encode(queue_t *, mblk_t *);
static mblk_t *ahdlc_decode(queue_t *, mblk_t *);
static void spppasyn_timer(void *);
static mblk_t *spppasyn_inpkt(queue_t *, mblk_t *);
static mblk_t *spppasyn_muxencode(queue_t *, mblk_t *);
#define RESET_MUX_VALUES(x) { \
x->sa_mqhead = x->sa_mqtail = NULL; \
x->sa_proto = 0; \
x->sa_mqlen = 0; \
}
#define IS_XMUX_ENABLED(x) \
((x)->sa_flags & X_MUXMASK)
#define IS_RMUX_ENABLED(x) \
((x)->sa_flags & R_MUXMASK)
#define IS_COMP_AC(x) \
((x)->sa_flags & SAF_XCOMP_AC)
#define IS_COMP_PROT(x) \
((x)->sa_flags & SAF_XCOMP_PROT)
#define IS_DECOMP_PROT(x) \
((x)->sa_flags & SAF_RDECOMP_PROT)
/*
* Don't send HDLC start flag if last transmit is within 1.5 seconds -
* FLAG_TIME is defined in nanoseconds.
*/
#define FLAG_TIME 1500000000ul
/*
* The usual AHDLC implementation enables the default escaping for all
* LCP frames. LCP_USE_DFLT() is used in this implementation to
* modify this rule slightly. If the code number happens to be
* Echo-Request, Echo-Reply, or Discard-Request (each of which may be
* sent only when LCP is in Opened state), then one may also use the
* negotiated ACCM; the RFC is silent on this. The theory is that
* pppd can construct Echo-Request messages that are guaranteed to
* fail if the negotiated ACCM is bad.
*/
#define LCP_USE_DFLT(mp) ((code = MSG_BYTE((mp), 4)) < 9 || code > 11)
/*
* Extract bit c from map m, to determine if character c needs to be
* escaped. Map 'm' is a pointer to a 256 bit map; 8 words of 32 bits
* each.
*/
#define IN_TX_MAP(c, m) \
((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
/*
* Checks the 32-bit receive ACCM to see if the byte should have been
* escaped by peer.
*/
#define IN_RX_MAP(c, m) (((c) < 0x20) && ((m) & (1 << (c))))
static struct module_info spppasyn_modinfo = {
AHDLC_MOD_ID, /* mi_idnum */
AHDLC_MOD_NAME, /* mi_idname */
0, /* mi_minpsz */
INFPSZ, /* mi_maxpsz */
0, /* mi_hiwat */
0 /* mi_lowat */
};
static struct qinit spppasyn_rinit = {
spppasyn_rput, /* qi_putp */
NULL, /* qi_srvp */
spppasyn_open, /* qi_qopen */
spppasyn_close, /* qi_qclose */
NULL, /* qi_qadmin */
&spppasyn_modinfo, /* qi_minfo */
NULL /* qi_mstat */
};
static struct qinit spppasyn_winit = {
spppasyn_wput, /* qi_putp */
NULL, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&spppasyn_modinfo, /* qi_minfo */
NULL /* qi_mstat */
};
struct streamtab spppasyn_tab = {
&spppasyn_rinit, /* st_rdinit */
&spppasyn_winit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL, /* st_muxwinit */
};
/* Matches above structure. */
static const char *kstat_names[] = {
"ioctls", "ioctlsfwd", "ioctlserr", "ctls",
"ctlsfwd", "ctlserr", "inbadchars", "inbadcharmask",
"inaborts", "inrunts", "inallocfails", "intoolongs",
"outrunts", "outallocfails", "incrcerrs", "unknownwrs",
"unknownrds", "hangups", "datain", "dataout",
"extrabufs", "sentmux", "recvmux", "inmuxerrs",
#ifdef REPORT_CRC_TYPE
"incrctype", "outcrctype",
#endif
};
/* So. This is why we have optimizing compilers. */
#define KVAL(vn) state->sa_kstats.vn.value.ui32
#define KSET(vn, v) KVAL(vn) = (v)
#define KADD(vn, v) KSET(vn, KVAL(vn) + (v))
#define KOR(vn, v) KSET(vn, KVAL(vn) | (v))
#define KINCR(vn) KADD(vn, 1)
static void ppp_dump_frame(sppp_ahdlc_t *state, mblk_t *mptr,
const char *msg);
/*
* RCV_B7_1, etc., defined in net/pppio.h, are stored in flags also.
*/
#define RCV_FLAGS (RCV_B7_1 | RCV_B7_0 | RCV_ODDP | RCV_EVNP)
/*
* FCS lookup table as calculated by genfcstab.
*/
static ushort_t fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
/*
* Per-character flags for accumulating input errors. Flags are
* accumulated for bit 7 set to 0, bit 7 set to 1, even parity
* characters, and odd parity characters. The link should see all
* four in the very first LCP Configure-Request if all is ok. (C0 is
* even parity and has bit 7 set to 1, and 23 is odd parity and has
* bit 7 set to 0.)
*/
static uchar_t charflags[256] = {
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_ODDP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_0|RCV_EVNP,
RCV_B7_0|RCV_EVNP, RCV_B7_0|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_EVNP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP, RCV_B7_1|RCV_ODDP, RCV_B7_1|RCV_ODDP,
RCV_B7_1|RCV_EVNP
};
/*
* Append two lists; preserve message boundaries.
* Warning: uses b_next.
*/
static mblk_t *
sppp_mappend(mblk_t *m1, mblk_t *m2)
{
mblk_t *mret;
if (m1 == NULL)
return (m2);
if (m2 == NULL)
return (m1);
mret = m1;
while (m1->b_next != NULL)
m1 = m1->b_next;
m1->b_next = m2;
return (mret);
}
/*
* Concatenate two mblk lists.
*/
static mblk_t *
sppp_mcat(mblk_t *m1, mblk_t *m2)
{
mblk_t *mret;
if (m1 == NULL)
return (m2);
if (m2 == NULL)
return (m1);
mret = m1;
while (m1->b_cont != NULL)
m1 = m1->b_cont;
m1->b_cont = m2;
return (mret);
}
/*
* spppasyn_open()
*
* STREAMS module open (entry) point. Called when spppasyn is pushed
* onto an asynchronous serial stream.
*/
/* ARGSUSED */
static int
spppasyn_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
sppp_ahdlc_t *state;
ASSERT(q != NULL);
if (q->q_ptr != NULL) {
return (0); /* return if already opened */
}
if (sflag != MODOPEN) {
return (EINVAL); /* only open as a module */
}
state = (sppp_ahdlc_t *)kmem_zalloc(sizeof (sppp_ahdlc_t), KM_SLEEP);
ASSERT(state != NULL);
q->q_ptr = (caddr_t)state;
WR(q)->q_ptr = (caddr_t)state;
state->sa_xaccm[0] = 0xffffffff; /* escape 0x00 through 0x1f */
state->sa_xaccm[3] = 0x60000000; /* escape 0x7d and 0x7e */
state->sa_mru = PPP_MRU; /* default of 1500 bytes */
qprocson(q);
return (0);
}
/*
* spppasyn_close()
*
* STREAMS module close (exit) point
*/
/* ARGSUSED */
static int
spppasyn_close(queue_t *q, int flag, cred_t *credp)
{
sppp_ahdlc_t *state;
ASSERT(q != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
/* We're leaving now. No more calls, please. */
qprocsoff(q);
if (state->sa_rx_buf != NULL) {
freemsg(state->sa_rx_buf);
state->sa_rx_buf = NULL;
}
if (state->sa_ksp != NULL) {
kstat_delete(state->sa_ksp);
state->sa_ksp = NULL;
}
if (state->sa_mqhead != NULL)
freemsg(state->sa_mqhead);
/* remove the time out routine */
if (state->sa_timeout_id != 0)
(void) quntimeout(q, state->sa_timeout_id);
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
kmem_free(state, sizeof (sppp_ahdlc_t));
return (0);
}
/*
* Create the standard kernel statistics structure and attach it to
* the current state structure. This can be called only after
* assigning the unit number.
*/
static void
create_kstats(sppp_ahdlc_t *state)
{
kstat_t *ksp;
char unitname[KSTAT_STRLEN];
int nstat, i;
kstat_named_t *knt;
nstat = sizeof (state->sa_kstats) / sizeof (kstat_named_t);
knt = (kstat_named_t *)&state->sa_kstats;
for (i = 0; i < nstat; i++, knt++) {
#ifdef DEBUG
/* Just in case I do something silly here. */
if (i >= sizeof (kstat_names) / sizeof (kstat_names[0]))
(void) sprintf(knt->name, "unknown%d", i);
else
#endif
(void) strncpy(knt->name, kstat_names[i],
sizeof (knt->name));
knt->data_type = KSTAT_DATA_UINT32;
}
/*
* sprintf is known to be safe here because KSTAT_STRLEN is
* 31, the maximum module name length is 8, and the maximum
* string length from %d is 11. This was once snprintf, but
* that's not backward-compatible with Solaris 2.6.
*/
(void) sprintf(unitname, "%s" "%d", AHDLC_MOD_NAME, state->sa_unit);
ksp = kstat_create(AHDLC_MOD_NAME, state->sa_unit, unitname, "net",
KSTAT_TYPE_NAMED, nstat, KSTAT_FLAG_VIRTUAL);
if (ksp != NULL) {
ksp->ks_data = (void *)&state->sa_kstats;
kstat_install(ksp);
}
state->sa_ksp = ksp;
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 16);
KSET(pks_incrctype, 16);
#endif
}
/*
* spppasyn_inner_ioctl
*
* MT-Perimeters:
* exclusive inner
*
* Handle state-affecting ioctls.
*/
static void
spppasyn_inner_ioctl(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
struct iocblk *iop;
int error;
int flagval;
int len;
uint32_t mux_flags;
uint32_t mask;
int flagmask;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
iop = (struct iocblk *)mp->b_rptr;
ASSERT(state != NULL && iop != NULL);
error = EINVAL;
len = 0;
switch (iop->ioc_cmd) {
case PPPIO_XFCS:
/* Check for valid option length */
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
/* Grab flag value */
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval < PPPFCS_16 || flagval > PPPFCS_NONE)
break;
state->sa_flags &= ~SAF_XMITCRC32 & ~SAF_XMITCRCNONE;
if (flagval == PPPFCS_32) {
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 32);
#endif
state->sa_flags |= SAF_XMITCRC32;
} else if (flagval == PPPFCS_NONE) {
#ifdef REPORT_CRC_TYPE
KSET(pks_outcrctype, 0);
#endif
state->sa_flags |= SAF_XMITCRCNONE;
}
#ifdef REPORT_CRC_TYPE
else {
KSET(pks_outcrctype, 16);
}
#endif
/* Return success */
error = 0;
break;
case PPPIO_RFCS:
/* Check for valid option length */
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
/* Grab flag value */
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval < PPPFCS_16 || flagval > PPPFCS_NONE)
break;
state->sa_flags &= ~SAF_RECVCRC32 & ~SAF_RECVCRCNONE;
if (flagval == PPPFCS_32) {
#ifdef REPORT_CRC_TYPE
KSET(pks_incrctype, 32);
#endif
state->sa_flags |= SAF_RECVCRC32;
} else if (flagval == PPPFCS_NONE) {
#ifdef REPORT_CRC_TYPE
KSET(pks_incrctype, 0);
#endif
state->sa_flags |= SAF_RECVCRCNONE;
}
#ifdef REPORT_CRC_TYPE
else {
KSET(pks_incrctype, 16);
}
#endif
/* Return success */
error = 0;
break;
case PPPIO_XACCM:
/* Check for valid asyncmap length */
if (iop->ioc_count < sizeof (uint32_t) ||
iop->ioc_count > sizeof (ext_accm) ||
mp->b_cont == NULL)
break;
/* Copy user's asyncmap into our state structure. */
bcopy((caddr_t)mp->b_cont->b_rptr,
(caddr_t)state->sa_xaccm, iop->ioc_count);
state->sa_xaccm[2] &= ~0x40000000; /* don't escape 0x5e */
state->sa_xaccm[3] |= 0x60000000; /* escape 0x7d, 0x7e */
error = 0;
break;
case PPPIO_RACCM:
/* Check for valid asyncmap length (only ctrl chars) */
if (iop->ioc_count != sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
state->sa_raccm = *(uint32_t *)mp->b_cont->b_rptr;
error = 0;
break;
case PPPIO_LASTMOD:
/* We already know this. */
state->sa_flags |= SAF_LASTMOD;
error = 0;
break;
case PPPIO_MUX:
/* set the compression flags */
if (iop->ioc_count != 2 * sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
/* set the mux flags */
mux_flags = ((uint32_t *)mp->b_cont->b_rptr)[0];
mask = ((uint32_t *)mp->b_cont->b_rptr)[1];
if (mux_flags != 0)
state->sa_flags = (state->sa_flags & ~mask) | (mask);
/* set the multiplexing timer value */
if (mask & R_MUXMASK)
state->sa_timeout_usec = mux_flags;
error = 0;
break;
case PPPIO_CFLAGS:
if (iop->ioc_count != 2 * sizeof (uint32_t) ||
mp->b_cont == NULL)
break;
flagval = (((uint32_t *)mp->b_cont->b_rptr)[0] << 20) &
(SAF_RDECOMP_PROT | SAF_RDECOMP_AC | SAF_XCOMP_PROT |
SAF_XCOMP_AC);
flagmask = (((uint32_t *)mp->b_cont->b_rptr)[1] << 20) &
(SAF_RDECOMP_PROT | SAF_RDECOMP_AC | SAF_XCOMP_PROT |
SAF_XCOMP_AC);
state->sa_flags = flagval | (state->sa_flags & ~flagmask);
*(uint32_t *)mp->b_cont->b_rptr = state->sa_flags >> 20;
len = sizeof (uint32_t);
error = 0;
break;
case PPPIO_DEBUG:
if (iop->ioc_count != sizeof (uint32_t) || mp->b_cont == NULL)
break;
flagval = *(uint32_t *)mp->b_cont->b_rptr;
if (flagval != PPPDBG_LOG + PPPDBG_AHDLC) {
putnext(q, mp);
return;
}
cmn_err(CE_CONT, AHDLC_MOD_NAME "%d: debug log enabled\n",
state->sa_unit);
state->sa_flags |= SAF_XMITDUMP | SAF_RECVDUMP;
error = 0;
break;
}
if (error == 0) {
/* Success; tell the user */
if (mp->b_cont == NULL)
len = 0;
else
mp->b_cont->b_wptr = mp->b_cont->b_rptr + len;
miocack(q, mp, len, 0);
} else {
/* Failure; send error back upstream. */
KINCR(pks_ioctlserr);
miocnak(q, mp, 0, error);
}
}
/*
* spppasyn_inner_mctl
*
* MT-Perimeters:
* exclusive inner
*
* Handle state-affecting M_CTL messages.
*/
static void
spppasyn_inner_mctl(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
int msglen;
int error;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
msglen = MBLKL(mp);
error = 0;
switch (*mp->b_rptr) {
case PPPCTL_MTU:
/* Just ignore the MTU */
break;
case PPPCTL_MRU:
if (msglen != 4)
error = EINVAL;
else
state->sa_mru =
((ushort_t *)mp->b_rptr)[1];
break;
case PPPCTL_UNIT:
if (state->sa_ksp != NULL) {
error = EINVAL;
break;
}
if (msglen == 2)
state->sa_unit = mp->b_rptr[1];
else if (msglen == 8)
state->sa_unit =
((uint32_t *)mp->b_rptr)[1];
else
error = EINVAL;
if (error == 0 && state->sa_ksp == NULL)
create_kstats(state);
break;
}
if (error > 0) {
KINCR(pks_ctlserr);
}
if (state->sa_flags & SAF_LASTMOD) {
freemsg(mp);
} else {
KINCR(pks_ctlsfwd);
putnext(q, mp);
}
}
/*
* spppasyn_wput()
*
* MT-Perimeters:
* exclusive inner.
*
* Write side put routine. This called by the modules above us (likely to
* be the compression module) to transmit data or pass along ioctls.
*/
static int
spppasyn_wput(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
struct iocblk *iop;
int error;
mblk_t *np;
struct ppp_stats64 *psp;
int msglen;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
switch (MTYPE(mp)) {
case M_DATA:
/*
* A data packet - do character-stuffing and FCS, and
* send it onwards. The blocks are freed as we go.
*/
if (IS_XMUX_ENABLED(state))
mp = spppasyn_muxencode(q, mp);
else
mp = ahdlc_encode(q, mp);
if (mp != NULL)
putnext(q, mp);
break;
case M_IOCTL:
KINCR(pks_ioctls);
iop = (struct iocblk *)mp->b_rptr;
msglen = 0;
switch (iop->ioc_cmd) {
case PPPIO_XFCS:
case PPPIO_RFCS:
case PPPIO_XACCM:
case PPPIO_RACCM:
case PPPIO_LASTMOD:
case PPPIO_DEBUG:
case PPPIO_MUX:
case PPPIO_CFLAGS:
spppasyn_inner_ioctl(q, mp);
return (0);
case PPPIO_GCLEAN:
np = allocb(sizeof (uint32_t), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
*(uint32_t *)np->b_wptr = state->sa_flags & RCV_FLAGS;
msglen = sizeof (uint32_t);
np->b_wptr += msglen;
error = 0;
break;
case PPPIO_GETSTAT:
error = EINVAL;
break;
case PPPIO_GETSTAT64:
np = allocb(sizeof (*psp), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
psp = (struct ppp_stats64 *)np->b_wptr;
bzero((caddr_t)psp, sizeof (*psp));
psp->p = state->sa_stats;
msglen = sizeof (*psp);
np->b_wptr += msglen;
error = 0;
break;
case PPPIO_GTYPE:
np = allocb(sizeof (uint32_t), BPRI_HI);
if (np == NULL) {
error = ENOSR;
break;
}
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
}
mp->b_cont = np;
*(uint32_t *)np->b_wptr = PPPTYP_AHDLC;
msglen = sizeof (uint32_t);
np->b_wptr += msglen;
error = 0;
break;
default:
/* Unknown ioctl -- forward along */
KINCR(pks_ioctlsfwd);
putnext(q, mp);
return (0);
}
if (error == 0) {
/* Success; tell the user */
miocack(q, mp, msglen, 0);
} else {
/* Failure; send error back upstream. */
KINCR(pks_ioctlserr);
miocnak(q, mp, 0, error);
}
break;
case M_CTL:
KINCR(pks_ctls);
spppasyn_inner_mctl(q, mp);
break;
default:
if (state->sa_flags & (SAF_XMITDUMP|SAF_RECVDUMP))
cmn_err(CE_CONT,
"spppasyn_wpur: unknown buffer type %d",
MTYPE(mp));
KINCR(pks_unknownwrs);
putnext(q, mp);
break;
}
return (0);
}
/*
* spppasyn_rput()
*
* MT-Perimeters:
* exclusive inner.
*
* Read side put routine. This is called by the async serial driver
* below us to handle received data and returned signals (like
* hang-up).
*/
static int
spppasyn_rput(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
mblk_t *mpnext;
ASSERT(q != NULL && mp != NULL);
state = (sppp_ahdlc_t *)q->q_ptr;
ASSERT(state != NULL);
switch (MTYPE(mp)) {
case M_DATA:
/* Note -- decoder frees the buffers */
mp = ahdlc_decode(q, mp);
while (mp != NULL) {
mpnext = mp->b_next;
mp->b_next = NULL;
putnext(q, mp);
mp = mpnext;
}
break;
case M_HANGUP:
KINCR(pks_hangups);
state->sa_flags |= SAF_IFLUSH;
putnext(q, mp);
break;
default:
if (state->sa_flags & (SAF_XMITDUMP|SAF_RECVDUMP)) {
if (MTYPE(mp) == M_IOCTL)
cmn_err(CE_CONT,
"spppasyn_rput: unexpected ioctl %X",
((struct iocblk *)mp->b_rptr)->ioc_cmd);
else
cmn_err(CE_CONT,
"spppasyn_rput: unknown buffer type %d",
MTYPE(mp));
}
KINCR(pks_unknownrds);
putnext(q, mp);
break;
}
return (0);
}
/*
* ahdlc_encode
*
* Perform asynchronous HDLC framing on a given buffer and transmit
* the result. The state structure must be valid. The input buffers
* are freed as we go.
*
* This function is called by wput and just encodes the data. Wput
* then calls putnext directly. There's no service routine for this
* module, so flow control is asserted by the module below us up to
* our caller by the STREAMS framework. This is by design -- this
* module does not queue anything so that other modules can make QoS
* decisions.
*/
static mblk_t *
ahdlc_encode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
uint32_t loc_xaccm[8];
ushort_t fcs16;
uint32_t fcs32;
size_t msglen;
size_t outmp_len;
mblk_t *outmp;
mblk_t *curout;
mblk_t *tmp;
uchar_t *ep;
uchar_t *dp;
uchar_t *tp;
uchar_t *tpmax;
#if defined(lint) || defined(_lint)
uchar_t chr; /* lint likes this */
#else
int chr; /* not uchar_t; more efficient this way */
/* with WorkShop compiler */
#endif
int is_lcp, is_ctrl;
int code;
hrtime_t hrtime;
uint32_t flags; /* sampled copy of flags */
state = (sppp_ahdlc_t *)q->q_ptr;
/* Don't transmit anything obviously silly. */
msglen = msgsize(mp);
if (msglen < 4) {
KINCR(pks_outrunts);
freemsg(mp);
(void) putnextctl1(RD(q), M_CTL, PPPCTL_OERROR);
return (NULL);
}
/*
* Allocate an output buffer just large enough for most cases.
* Based on original work in the ppp-2.2 AIX PPP driver, we
* estimate the output size as 1.25 * input message length
* plus 16. If this turns out to be too small, then we'll
* allocate exactly one additional buffer with two times the
* remaining input length (the maximum that could possibly be
* required).
*/
outmp_len = msglen + (msglen >> 2) + 16;
outmp = allocb(outmp_len, BPRI_MED);
if (outmp == NULL)
goto outallocfail;
tp = outmp->b_wptr;
/*
* Check if our last transmit happened within FLAG_TIME, using
* the system's hrtime.
*/
hrtime = gethrtime();
if (ABS(hrtime - state->sa_hrtime) > FLAG_TIME) {
*tp++ = PPP_FLAG;
}
state->sa_hrtime = hrtime;
bcopy((caddr_t)state->sa_xaccm, (caddr_t)loc_xaccm, sizeof (loc_xaccm));
flags = state->sa_flags;
/*
* LCP messages must be sent using the default escaping
* (ACCM). We bend this rule a little to allow LCP
* Echo-Request through with the negotiated escaping so that
* we can detect bad negotiated ACCM values. If the ACCM is
* bad, echos will fail and take down the link.
*/
is_lcp = is_ctrl = 0;
code = MSG_BYTE(mp, 0);
if (code == PPP_ALLSTATIONS) {
if (MSG_BYTE(mp, 1) == PPP_UI) {
code = MSG_BYTE(mp, 2);
if (code == (PPP_LCP >> 8) &&
MSG_BYTE(mp, 3) == (PPP_LCP & 0xFF)) {
if (LCP_USE_DFLT(mp))
is_lcp = 2;
else
is_lcp = 1; /* Echo-Request */
} else if (!(code & 1) && code > 0x3F)
is_ctrl = 1;
}
} else if (!(code & 1) && code > 0x3F)
is_ctrl = 1;
/*
* If it's LCP and not just an LCP Echo-Request, then we need
* to drop back to default escaping rules temporarily.
*/
if (is_lcp > 1) {
/*
* force escape on 0x00 through 0x1f
* and, for RFC 1662 (and ISO 3309:1991), 0x80-0x9f.
*/
loc_xaccm[0] = 0xffffffff;
loc_xaccm[4] = 0xffffffff;
}
fcs16 = PPPINITFCS16; /* Initial FCS is 0xffff */
fcs32 = PPPINITFCS32;
/*
* Process this block and the rest (if any) attached to this
* one. Note that we quite intentionally ignore the type of
* the buffer. The caller has checked that the first buffer
* is M_DATA; all others must be so, and any that are not are
* harmless driver errors.
*/
curout = outmp;
tpmax = outmp->b_datap->db_lim;
do {
dp = mp->b_rptr;
while (dp < (ep = mp->b_wptr)) {
/*
* Calculate maximum safe run length for inner loop,
* regardless of escaping.
*/
outmp_len = (tpmax - tp) / 2;
if (dp + outmp_len < ep)
ep = dp + outmp_len;
/*
* Select out on CRC type here to make the
* inner byte loop more efficient. (We could
* do both CRCs at all times if we wanted, but
* that ends up taking an extra 8 cycles per
* byte -- 47% overhead!)
*/
if (flags & SAF_XMITCRC32) {
while (dp < ep) {
chr = *dp++;
fcs32 = PPPFCS32(fcs32, chr);
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
} else {
while (dp < ep) {
chr = *dp++;
fcs16 = PPPFCS16(fcs16, chr);
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
}
/*
* If we limited our run length and we're now low
* on output space, then allocate a new output buffer.
* This should rarely happen, unless the output data
* has a lot of escapes.
*/
if (ep != mp->b_wptr && tpmax - tp < 5) {
KINCR(pks_extrabufs);
/* Get remaining message length */
outmp_len = (mp->b_wptr - dp) +
msgsize(mp->b_cont);
/* Calculate maximum required space */
outmp_len = (outmp_len + PPP_FCS32LEN) * 2 + 1;
curout = allocb(outmp_len, BPRI_MED);
if ((outmp->b_cont = curout) == NULL)
goto outallocfail;
outmp->b_wptr = tp;
tp = curout->b_wptr;
tpmax = curout->b_datap->db_lim;
}
}
tmp = mp->b_cont;
freeb(mp);
mp = tmp;
} while (mp != NULL);
/*
* Make sure we have enough remaining room to add the CRC (if
* any) and a trailing flag byte.
*/
outmp_len = PPP_FCS32LEN * 2 + 1;
if (tpmax - tp < outmp_len) {
KINCR(pks_extrabufs);
curout = allocb(outmp_len, BPRI_MED);
if ((outmp->b_cont = curout) == NULL)
goto outallocfail;
outmp->b_wptr = tp;
tp = curout->b_wptr;
tpmax = curout->b_datap->db_lim;
}
/*
* Network layer data is the only thing that can be sent with
* no CRC at all.
*/
if ((flags & SAF_XMITCRCNONE) && !is_lcp && !is_ctrl)
goto nocrc;
if (!(flags & SAF_XMITCRC32))
fcs32 = fcs16;
/*
* Append the HDLC FCS, making sure that escaping is done on any
* necessary bytes. Note that the FCS bytes are in little-endian.
*/
fcs32 = ~fcs32;
chr = fcs32 & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
chr = (fcs32 >> 8) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
if (flags & SAF_XMITCRC32) {
chr = (fcs32 >> 16) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
chr = (fcs32 >> 24) & 0xff;
if (IN_TX_MAP(chr, loc_xaccm)) {
*tp++ = PPP_ESCAPE;
chr ^= PPP_TRANS;
}
*tp++ = chr;
}
nocrc:
/*
* And finally append the HDLC flag, and send it away
*/
*tp++ = PPP_FLAG;
ASSERT(tp < tpmax);
curout->b_wptr = tp;
state->sa_stats.ppp_obytes += msgsize(outmp);
state->sa_stats.ppp_opackets++;
if (state->sa_flags & SAF_XMITDUMP)
ppp_dump_frame(state, outmp, "sent");
KINCR(pks_dataout);
return (outmp);
outallocfail:
KINCR(pks_outallocfails);
state->sa_stats.ppp_oerrors++;
freemsg(outmp);
freemsg(mp);
(void) putnextctl1(RD(q), M_CTL, PPPCTL_OERROR);
return (NULL);
}
/*
* Handle end-of-frame excitement. This is here mostly because the Solaris
* C style rules require tab for indent and prohibit excessive indenting.
*/
static mblk_t *
receive_frame(queue_t *q, mblk_t *outmp, ushort_t fcs16, uint32_t fcs32)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
uchar_t *cp, *ep;
int is_lcp, is_ctrl, crclen;
ushort_t proto;
int i;
cp = outmp->b_rptr;
if (cp[0] == PPP_ALLSTATIONS && cp[1] == PPP_UI)
cp += 2;
proto = *cp++;
if ((proto & 1) == 0)
proto = (proto << 8) + *cp++;
is_lcp = (proto == PPP_LCP);
is_ctrl = (proto >= 0x4000);
/*
* To allow for renegotiation, LCP accepts good CRCs of either
* type at any time. Other control (non-network) packets must
* have either CRC-16 or CRC-32, as negotiated. Network layer
* packets may additionally omit the CRC entirely, if that was
* negotiated.
*/
if ((is_lcp && (fcs16 == PPPGOODFCS16 || fcs32 == PPPGOODFCS32)) ||
((fcs16 == PPPGOODFCS16 && !(state->sa_flags & SAF_RECVCRC32)) ||
(fcs32 == PPPGOODFCS32 &&
(state->sa_flags & SAF_RECVCRC32))) ||
(!is_ctrl && !is_lcp && (state->sa_flags & SAF_RECVCRCNONE))) {
state->sa_stats.ppp_ipackets++;
if (is_lcp) {
crclen = (fcs16 == PPPGOODFCS16) ?
PPP_FCSLEN : PPP_FCS32LEN;
} else {
crclen = (state->sa_flags & SAF_RECVCRC32) ?
PPP_FCS32LEN : PPP_FCSLEN;
if (!is_ctrl && (state->sa_flags & SAF_RECVCRCNONE))
crclen = 0;
}
if (crclen != 0) {
i = adjmsg(outmp, -crclen);
ASSERT(i != 0);
#if defined(lint) || defined(_lint)
/* lint is happier this way in a non-DEBUG build */
i = i;
#endif
}
if (proto == PPP_MUX) {
/* spppasyn_inpkt checks for PPP_MUX packets */
KINCR(pks_recvmux);
/* Remove headers */
outmp->b_rptr = cp;
return (spppasyn_inpkt(q, outmp));
}
/*
* Sniff the received data stream. If we see an LCP
* Configure-Ack, then pick out the ACCM setting, if
* any, and configure now. This allows us to stay in
* sync in case the peer is already out of Establish
* phase.
*/
if (is_lcp && *cp == 2) {
ep = outmp->b_wptr;
i = (cp[2] << 8) | cp[3];
if (i > ep - cp)
ep = cp; /* Discard junk */
else if (i < ep - cp)
ep = cp + i;
cp += 4;
while (cp + 2 < ep) {
if ((i = cp[1]) < 2)
i = 2;
if (cp + i > ep)
i = ep - cp;
if (cp[0] == 2 && i >= 6) {
state->sa_raccm = (cp[2] << 24) |
(cp[3] << 16) | (cp[4] << 8) |
cp[5];
break;
}
cp += i;
}
}
return (outmp);
} else {
KINCR(pks_incrcerrs);
cmn_err(CE_CONT, PPP_DRV_NAME "%d: bad fcs (len=%ld)\n",
state->sa_unit, msgsize(outmp));
if (state->sa_flags & SAF_RECVDUMP)
ppp_dump_frame(state, outmp, "bad data");
freemsg(outmp);
state->sa_stats.ppp_ierrors++;
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
return (NULL);
}
}
/*
* ahdlc_decode()
*
* Process received characters.
*
* This is handled as exclusive inner so that we don't get confused
* about the state. Returns a list of packets linked by b_next.
*/
static mblk_t *
ahdlc_decode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state;
mblk_t *retmp; /* list of packets to return */
mblk_t *outmp; /* buffer for decoded data */
mblk_t *mpnext; /* temporary ptr for unlinking */
uchar_t *dp; /* pointer to input data */
uchar_t *dpend; /* end of input data */
uchar_t *tp; /* pointer to decoded output data */
uchar_t *tpmax; /* output buffer limit */
int flagtmp; /* temporary cache of flags */
#if defined(lint) || defined(_lint)
uchar_t chr; /* lint likes this */
#else
int chr; /* not uchar_t; more efficient this way */
/* with WorkShop compiler */
#endif
ushort_t fcs16; /* running CRC-16 */
uint32_t fcs32; /* running CRC-32 */
#ifdef HANDLE_ZERO_LENGTH
size_t nprocessed;
#endif
state = (sppp_ahdlc_t *)q->q_ptr;
KINCR(pks_datain);
state->sa_stats.ppp_ibytes += msgsize(mp);
if (state->sa_flags & SAF_RECVDUMP)
ppp_dump_frame(state, mp, "rcvd");
flagtmp = state->sa_flags;
fcs16 = state->sa_infcs16;
fcs32 = state->sa_infcs32;
outmp = state->sa_rx_buf;
if (outmp == NULL) {
tp = tpmax = NULL;
} else {
tp = outmp->b_wptr;
tpmax = outmp->b_datap->db_lim;
}
#ifdef HANDLE_ZERO_LENGTH
nprocessed = 0;
#endif
/*
* Main input processing loop. Loop over received buffers and
* each byte in each buffer. Note that we quite intentionally
* ignore the type of the buffer. The caller has checked that
* the first buffer is M_DATA; all others must be so, and any
* that are not are harmless driver errors.
*/
retmp = NULL;
while (mp != NULL) {
/* Innermost loop -- examine bytes in buffer. */
dpend = mp->b_wptr;
dp = mp->b_rptr;
#ifdef HANDLE_ZERO_LENGTH
nprocessed += dpend - dp;
#endif
for (; dp < dpend; dp++) {
chr = *dp;
/*
* This should detect the lack of an 8-bit
* communication channel, which is necessary
* for PPP to work.
*/
flagtmp |= charflags[chr];
/*
* So we have a HDLC flag ...
*/
if (chr == PPP_FLAG) {
/*
* If there's no received buffer, then
* just ignore this frame marker.
*/
if ((flagtmp & SAF_IFLUSH) || outmp == NULL) {
flagtmp &= ~SAF_IFLUSH & ~SAF_ESCAPED;
continue;
}
/*
* Per RFC 1662 -- silently discard
* runt frames (fewer than 4 octets
* with 16 bit CRC) and frames that
* end in 7D 7E (abort sequence).
* These are not counted as errors.
*
* (We could just reset the pointers
* and reuse the buffer, but this is a
* rarely used error path and not
* worth the optimization.)
*/
if ((flagtmp & SAF_ESCAPED) ||
tp - outmp->b_rptr < 2 + PPP_FCSLEN) {
if (flagtmp & SAF_ESCAPED)
KINCR(pks_inaborts);
else
KINCR(pks_inrunts);
if (state->sa_flags & SAF_RECVDUMP) {
outmp->b_wptr = tp;
ppp_dump_frame(state, outmp,
"runt");
}
freemsg(outmp);
flagtmp &= ~SAF_ESCAPED;
} else {
/* Handle the received frame */
outmp->b_wptr = tp;
outmp = receive_frame(q, outmp, fcs16,
fcs32);
retmp = sppp_mappend(retmp, outmp);
}
outmp = NULL;
tp = tpmax = NULL;
continue;
}
/* If we're waiting for a new frame, then drop data. */
if (flagtmp & SAF_IFLUSH) {
continue;
}
/*
* Start of new frame. Allocate a receive
* buffer large enough to store a frame (after
* un-escaping) of at least 1500 octets plus
* the CRC. If MRU is negotiated to be more
* than the default, then allocate that much.
* In addition, we add an extra 32-bytes for a
* fudge factor, in case the peer doesn't do
* arithmetic very well.
*/
if (outmp == NULL) {
int maxlen;
if ((maxlen = state->sa_mru) < PPP_MRU)
maxlen = PPP_MRU;
maxlen += PPP_FCS32LEN + 32;
outmp = allocb(maxlen, BPRI_MED);
/*
* If allocation fails, try again on
* the next frame. (Go into discard
* mode.)
*/
if (outmp == NULL) {
KINCR(pks_inallocfails);
flagtmp |= SAF_IFLUSH;
continue;
}
tp = outmp->b_wptr;
tpmax = outmp->b_datap->db_lim;
/* Neither flag can possibly be set here. */
flagtmp &= ~(SAF_IFLUSH | SAF_ESCAPED);
fcs16 = PPPINITFCS16;
fcs32 = PPPINITFCS32;
}
/*
* If the peer sends us a character that's in
* our receive character map, then that's
* junk. Discard it without changing state.
* If he previously sent us an escape
* character, then toggle this one and
* continue. Otherwise, if he's now sending
* escape, set the flag for next time.
*/
if (IN_RX_MAP(chr, state->sa_raccm)) {
KINCR(pks_inbadchars);
KOR(pks_inbadcharmask, 1 << chr);
continue;
}
if (flagtmp & SAF_ESCAPED) {
chr ^= PPP_TRANS;
flagtmp &= ~SAF_ESCAPED;
} else if (chr == PPP_ESCAPE) {
flagtmp |= SAF_ESCAPED;
continue;
}
/*
* Unless the peer is confused about the
* negotiated MRU, we should never get a frame
* that is too long. If it happens, toss it
* away and begin discarding data until we see
* the end of the frame.
*/
if (tp < tpmax) {
fcs16 = PPPFCS16(fcs16, chr);
fcs32 = PPPFCS32(fcs32, chr);
*tp++ = chr;
} else {
KINCR(pks_intoolongs);
cmn_err(CE_CONT, PPP_DRV_NAME
"%d: frame too long (%d bytes)\n",
state->sa_unit,
(int)(tpmax - outmp->b_rptr));
freemsg(outmp);
outmp = NULL;
tp = tpmax = NULL;
flagtmp |= SAF_IFLUSH;
}
}
/*
* Free the buffer we just processed and move on to
* the next one.
*/
mpnext = mp->b_cont;
freeb(mp);
mp = mpnext;
}
state->sa_flags = flagtmp;
if ((state->sa_rx_buf = outmp) != NULL)
outmp->b_wptr = tp;
state->sa_infcs16 = fcs16;
state->sa_infcs32 = fcs32;
#ifdef HANDLE_ZERO_LENGTH
if (nprocessed <= 0) {
outmp = allocb(0, BPRI_MED);
if (outmp != NULL) {
outmp->b_datap->db_type = M_HANGUP;
retmp = sppp_mappend(retmp, outmp);
}
}
#endif
return (retmp);
}
/*
* Nifty packet dumper; copied from AIX 4.1 port. This routine dumps
* the raw received and transmitted data through syslog. This allows
* debug of communications problems without resorting to a line
* analyzer.
*
* The expression "3*BYTES_PER_LINE" used frequently here represents
* the size of each hex value printed -- two hex digits and a space.
*/
#define BYTES_PER_LINE 8
static void
ppp_dump_frame(sppp_ahdlc_t *state, mblk_t *mptr, const char *msg)
{
/*
* Buffer is big enough for hex digits, two spaces, ASCII output,
* and one NUL byte.
*/
char buf[3 * BYTES_PER_LINE + 2 + BYTES_PER_LINE + 1];
uchar_t *rptr, *eptr;
int i, chr;
char *bp;
static const char digits[] = "0123456789abcdef";
cmn_err(CE_CONT, "!ppp_async%d: %s %ld bytes\n", state->sa_unit,
msg, msgsize(mptr));
i = 0;
bp = buf;
/* Add filler spaces between hex output and ASCII */
buf[3 * BYTES_PER_LINE] = ' ';
buf[3 * BYTES_PER_LINE + 1] = ' ';
/* Add NUL byte at end */
buf[sizeof (buf) - 1] = '\0';
while (mptr != NULL) {
rptr = mptr->b_rptr; /* get pointer to beginning */
eptr = mptr->b_wptr;
while (rptr < eptr) {
chr = *rptr++;
/* convert byte to ascii hex */
*bp++ = digits[chr >> 4];
*bp++ = digits[chr & 0xf];
*bp++ = ' ';
/* Insert ASCII past hex output and filler */
buf[3 * BYTES_PER_LINE + 2 + i] =
(chr >= 0x20 && chr <= 0x7E) ? (char)chr : '.';
i++;
if (i >= BYTES_PER_LINE) {
cmn_err(CE_CONT, "!ppp%d: %s\n", state->sa_unit,
buf);
bp = buf;
i = 0;
}
}
mptr = mptr->b_cont;
}
if (bp > buf) {
/* fill over unused hex display positions */
while (bp < buf + 3 * BYTES_PER_LINE)
*bp++ = ' ';
/* terminate ASCII string at right position */
buf[3 * BYTES_PER_LINE + 2 + i] = '\0';
cmn_err(CE_CONT, "!ppp%d: %s\n", state->sa_unit, buf);
}
}
static mblk_t *
spppasyn_muxencode(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
uint32_t len;
uint32_t nlen;
ushort_t protolen;
uint32_t hdrlen;
ushort_t proto;
mblk_t *new_frame;
mblk_t *tmp;
mblk_t *send_frame;
ushort_t i;
len = msgdsize(mp);
i = 0;
protolen = 1;
proto = MSG_BYTE(mp, i);
if (proto == PPP_ALLSTATIONS) {
len -= 2;
i += 2;
proto = MSG_BYTE(mp, i);
}
++i;
if ((proto & 1) == 0) {
proto = (proto << 8) + MSG_BYTE(mp, i);
protolen++;
}
hdrlen = i - 1;
send_frame = NULL;
if (len > PPP_MAX_MUX_LEN || (proto & 0x8000)) {
/* send the queued frames */
if (state->sa_mqhead != NULL) {
/* increment counter if it is MUX pkt */
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
send_frame = ahdlc_encode(q, state->sa_mqhead);
}
/* send the current frame */
mp = ahdlc_encode(q, mp);
send_frame = sppp_mcat(send_frame, mp);
/* reset the state values over here */
RESET_MUX_VALUES(state);
return (send_frame);
}
/* len + 1 , since we add the mux overhead */
nlen = len + 1;
/* subtract the protocol length if protocol matches */
if (state->sa_proto == proto)
nlen -= protolen;
send_frame = NULL;
if ((state->sa_mqlen + nlen) >= state->sa_mru) {
/* send the existing queued frames */
if (state->sa_mqhead != NULL) {
/* increment counter if it is MUX pkt */
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
send_frame = ahdlc_encode(q, state->sa_mqhead);
}
/* reset state values */
RESET_MUX_VALUES(state);
}
/* add the current frame to the queue */
if (state->sa_mqhead != NULL) {
if (state->sa_mqtail == NULL) {
/*
* this is the first mblk in the queue create
* a new frame to hold the PPP MUX header
*/
if ((new_frame = allocb(PPP_HDRLEN+1,
BPRI_MED)) == NULL) {
return (send_frame);
}
if (!IS_COMP_AC(state)) {
/* add the header */
*new_frame->b_wptr++ = PPP_ALLSTATIONS;
*new_frame->b_wptr++ = PPP_UI;
}
/* do protocol compression */
if (IS_COMP_PROT(state)) {
*new_frame->b_wptr++ = PPP_MUX;
} else {
*new_frame->b_wptr++ = 0;
*new_frame->b_wptr++ = PPP_MUX;
}
*new_frame->b_wptr++ = PFF |
(state->sa_mqlen - protolen - 1);
if (DB_REF(mp) > 1) {
tmp = copymsg(state->sa_mqhead);
freemsg(state->sa_mqhead);
if ((state->sa_mqhead = tmp) == NULL) {
return (send_frame);
}
}
if (state->sa_mqhead->b_rptr[0] == PPP_ALLSTATIONS)
state->sa_mqhead->b_rptr += 2;
linkb(new_frame, state->sa_mqhead);
state->sa_mqtail = state->sa_mqhead;
/* point mqtail to the last mblk_t */
while (state->sa_mqtail->b_cont != NULL)
state->sa_mqtail = state->sa_mqtail->b_cont;
/* change state->sa_mqhead */
state->sa_mqhead = new_frame;
}
if (state->sa_proto == proto) {
/* Check if the mblk_t is being referenced */
if (DB_REF(mp) > 1) {
tmp = copymsg(mp);
freemsg(mp);
if ((mp = tmp) == NULL) {
return (send_frame);
}
}
/*
* match,can remove the protocol field
* and write data there
*/
mp->b_rptr += hdrlen;
/*
* protolen - 1 ,because the the byte with
* the PFF bit and the length field have
* to added
*/
mp->b_rptr += (protolen - 1);
*mp->b_rptr = (len - protolen) & 0xff;
} else {
/*
* no match, there are three options
* 1. write in mp
* 2. write in mqtail
* 3. alloc a new blk for just one byte
*/
/* Check if the mblk_t is being referenced */
if (DB_REF(mp) > 1) {
tmp = copymsg(mp);
freemsg(mp);
if ((mp = tmp) == NULL) {
return (send_frame);
}
}
if (hdrlen != 0) {
mp->b_rptr += (hdrlen-1);
*mp->b_rptr = PFF | (len);
} else if (state->sa_mqtail->b_wptr <
DB_LIM(state->sa_mqtail)) {
*state->sa_mqtail->b_wptr++ = PFF |len;
} else {
/* allocate a new mblk & add the byte */
/* write the data */
if ((new_frame = allocb(1, BPRI_MED))
== NULL) {
freemsg(mp);
return (send_frame);
}
*new_frame->b_wptr++ = PFF | (len);
linkb(state->sa_mqtail, new_frame);
}
/* update proto */
state->sa_proto = proto;
}
linkb(state->sa_mqtail, mp);
state->sa_mqtail = mp;
while (state->sa_mqtail->b_cont != NULL)
state->sa_mqtail = state->sa_mqtail->b_cont;
state->sa_mqlen += nlen;
} else {
state->sa_mqhead = mp;
state->sa_mqlen = len + protolen + 1;
state->sa_proto = proto;
}
if (state->sa_timeout_id == 0) {
state->sa_timeout_id = qtimeout(q, spppasyn_timer, q,
(drv_usectohz(state->sa_timeout_usec)));
}
return (send_frame);
}
/*
* Called from receive frame, this routine checks if it is a PPP_MUX
* packet and demuxes it. The returned pointer is a chain of mblks
* using b_next and representing the demultiplexed packets.
*/
static mblk_t *
spppasyn_inpkt(queue_t *q, mblk_t *mp)
{
sppp_ahdlc_t *state = (sppp_ahdlc_t *)q->q_ptr;
ushort_t proto;
ushort_t prev_proto;
uint32_t len; /* length of subframe */
uchar_t muxhdr;
mblk_t *hdrmp;
mblk_t *subframe;
mblk_t *retmp;
if (!(mp->b_rptr[0] & PFF)) {
KINCR(pks_inmuxerrs);
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
freemsg(mp);
return (NULL);
}
/* initialise the Last protocol and protocol length */
prev_proto = 0;
/*
* Taking into granted that the decoded frame is contiguous
*/
retmp = NULL;
while (mp->b_rptr < mp->b_wptr) {
/*
* get the last protocol, protocol length
* and the length of the message
*/
/* protocol field flag and length */
muxhdr = mp->b_rptr[0];
len = muxhdr & ~PFF;
mp->b_rptr++;
/* check if there and enough bytes left in pkt */
if (MBLKL(mp) < len) {
KINCR(pks_inmuxerrs);
(void) putnextctl1(q, M_CTL, PPPCTL_IERROR);
break;
}
/* allocate memory for the header length */
if ((hdrmp = allocb(PPP_HDRLEN, BPRI_MED)) == NULL) {
KINCR(pks_inallocfails);
break;
}
/* add the ppp header to the pkt */
*hdrmp->b_wptr++ = PPP_ALLSTATIONS;
*hdrmp->b_wptr++ = PPP_UI;
/* check if the protocol field flag is set */
if (muxhdr & PFF) {
/* get the protocol */
proto = MSG_BYTE(mp, 0);
if ((proto & 1) == 0)
proto = (proto << 8) + MSG_BYTE(mp, 1);
/* reset values */
prev_proto = proto;
} else {
if (!IS_DECOMP_PROT(state))
*hdrmp->b_wptr++ = prev_proto >> 8;
*hdrmp->b_wptr++ = (prev_proto & 0xff);
}
/* get the payload from the MUXed packet */
subframe = dupmsg(mp);
subframe->b_wptr = mp->b_rptr + len;
/* link the subframe to the new frame */
linkb(hdrmp, subframe);
/* do a putnext */
retmp = sppp_mappend(retmp, hdrmp);
/* move the read pointer beyond this subframe */
mp->b_rptr += len;
}
freemsg(mp);
return (retmp);
}
/*
* timer routine which sends out the queued pkts *
*/
static void
spppasyn_timer(void *arg)
{
queue_t *q;
sppp_ahdlc_t *state;
mblk_t *mp;
ASSERT(arg);
q = (queue_t *)arg;
state = (sppp_ahdlc_t *)q->q_ptr;
if (state->sa_mqhead != NULL) {
/* increment counter */
if (state->sa_mqtail != NULL)
KINCR(pks_sentmux);
if ((mp = ahdlc_encode(q, state->sa_mqhead)) != NULL)
putnext(q, mp);
/* reset the state values over here */
RESET_MUX_VALUES(state);
}
/* clear timeout_id */
state->sa_timeout_id = 0;
}