]> git.ozlabs.org Git - ppp.git/blobdiff - svr4/ppp_ahdlc.c
align first byte to allow for A/C and proto compression
[ppp.git] / svr4 / ppp_ahdlc.c
index 2e62d21b88f5a1d9b0ff320dc3f0a340ffa1d556..ffbe12078fa435a1b7b38af1a98adcd3b2cae75f 100644 (file)
@@ -1,11 +1,48 @@
+/*
+ * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
+ *
+ * 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 HAVE 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.5 1995/10/27 03:57:10 paulus Exp $
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/stream.h>
-#include <sys/modctl.h>
 #include <sys/conf.h>
 #include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
 #include <sys/ddi.h>
+#ifdef sun
+#include <sys/modctl.h>
 #include <sys/sunddi.h>
+#endif
 #include <net/ppp_defs.h>
 #include <net/pppio.h>
 
@@ -18,6 +55,7 @@ static int ahdlc_wput __P((queue_t *, mblk_t *));
 static int ahdlc_rput __P((queue_t *, mblk_t *));
 static void stuff_frame __P((queue_t *, mblk_t *));
 static void unstuff_chars __P((queue_t *, mblk_t *));
+static int msg_copy __P((uchar_t *, mblk_t *, int));
 
 static struct module_info minfo = {
     0x7d23, "ppp_ahdl", 0, INFPSZ, 4096, 128
@@ -34,7 +72,8 @@ static struct qinit winit = {
 static struct streamtab ahdlc_info = {
     &rinit, &winit, NULL, NULL
 };
-    
+
+#ifdef sun
 static struct fmodsw fsw = {
     "ppp_ahdl",
     &ahdlc_info,
@@ -54,8 +93,9 @@ static struct modlinkage modlinkage = {
     (void *) &modlstrmod,
     NULL
 };
+#endif /* sun */
 
-struct ahdlc_state {
+typedef struct ahdlc_state {
     int flags;
     mblk_t *cur_frame;
     mblk_t *cur_blk;
@@ -65,11 +105,21 @@ struct ahdlc_state {
     u_int32_t raccm;
     int mtu;
     int mru;
-};
+    int unit;
+    struct pppstat stats;
+} ahdlc_state_t;
 
 /* Values for flags */
-#define ESCAPED                1       /* last saw escape char on input */
-#define IFLUSH         2       /* flushing input due to error */
+#define ESCAPED                0x100   /* last saw escape char on input */
+#define IFLUSH         0x200   /* flushing input due to error */
+
+/* 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)
+
+#ifndef sun
+# define qprocson(q)
+# define qprocsoff(q)
+#endif
 
 /*
  * FCS lookup table as calculated by genfcstab.
@@ -109,6 +159,7 @@ static u_short fcstab[256] = {
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
 
+#ifdef sun
 /*
  * Entry points for modloading.
  */
@@ -130,6 +181,7 @@ _info(mip)
 {
     return mod_info(&modlinkage, mip);
 }
+#endif
 
 /*
  * STREAMS module entry points.
@@ -141,11 +193,10 @@ ahdlc_open(q, devp, flag, sflag, credp)
     int flag, sflag;
     cred_t *credp;
 {
-    struct ahdlc_state *sp;
+    ahdlc_state_t *sp;
 
     if (q->q_ptr == 0) {
-       sp = (struct ahdlc_state *) kmem_zalloc(sizeof(struct ahdlc_state),
-                                               KM_SLEEP);
+       sp = (ahdlc_state_t *) kmem_zalloc(sizeof(ahdlc_state_t), KM_SLEEP);
        if (sp == 0)
            return ENOSR;
        q->q_ptr = sp;
@@ -164,16 +215,16 @@ ahdlc_close(q, flag, credp)
     int flag;
     cred_t *credp;
 {
-    struct ahdlc_state *state;
+    ahdlc_state_t *state;
 
     qprocsoff(q);
     if (q->q_ptr != 0) {
-       state = (struct ahdlc_state *) q->q_ptr;
+       state = (ahdlc_state_t *) q->q_ptr;
        if (state->cur_frame != 0) {
            freemsg(state->cur_frame);
            state->cur_frame = 0;
        }
-       kmem_free(q->q_ptr, sizeof(struct ahdlc_state));
+       kmem_free(q->q_ptr, sizeof(ahdlc_state_t));
     }
     return 0;
 }
@@ -183,10 +234,13 @@ ahdlc_wput(q, mp)
     queue_t *q;
     mblk_t *mp;
 {
-    struct ahdlc_state *state;
+    ahdlc_state_t *state;
     struct iocblk *iop;
+    int error;
+    mblk_t *np;
+    struct ppp_stats *psp;
 
-    state = (struct ahdlc_state *) q->q_ptr;
+    state = (ahdlc_state_t *) q->q_ptr;
     switch (mp->b_datap->db_type) {
     case M_DATA:
        /*
@@ -199,36 +253,100 @@ ahdlc_wput(q, mp)
 
     case M_IOCTL:
        iop = (struct iocblk *) mp->b_rptr;
+       error = EINVAL;
        switch (iop->ioc_cmd) {
        case PPPIO_XACCM:
-           if (iop->ioc_count != sizeof(ext_accm))
-               goto iocnak;
-           bcopy(mp->b_cont->b_rptr, (caddr_t)state->xaccm, sizeof(ext_accm));
+           if (iop->ioc_count < sizeof(u_int32_t)
+               || iop->ioc_count > sizeof(ext_accm))
+               break;
+           bcopy(mp->b_cont->b_rptr, (caddr_t)state->xaccm, iop->ioc_count);
            state->xaccm[2] &= 0x40000000;      /* don't escape 0x5e */
            state->xaccm[3] |= 0x60000000;      /* do escape 0x7d, 0x7e */
-           goto iocack;
+           iop->ioc_count = 0;
+           error = 0;
+           break;
 
        case PPPIO_RACCM:
            if (iop->ioc_count != sizeof(u_int32_t))
-               goto iocnak;
-           state->raccm = *(u_int32_t *)mp->b_cont->b_rptr;
-           goto iocack;
+               break;
+           bcopy(mp->b_cont->b_rptr, (caddr_t)&state->raccm,
+                 sizeof(u_int32_t));
+           iop->ioc_count = 0;
+           error = 0;
+           break;
+
+       case PPPIO_GCLEAN:
+           np = allocb(sizeof(int), BPRI_HI);
+           if (np == 0) {
+               error = ENOSR;
+               break;
+           }
+           if (mp->b_cont != 0)
+               freemsg(mp->b_cont);
+           mp->b_cont = np;
+           *(int *)np->b_wptr = state->flags & RCV_FLAGS;
+           np->b_wptr += sizeof(int);
+           iop->ioc_count = sizeof(int);
+           error = 0;
+           break;
+
+       case PPPIO_GETSTAT:
+           np = allocb(sizeof(struct ppp_stats), BPRI_HI);
+           if (np == 0) {
+               error = ENOSR;
+               break;
+           }
+           if (mp->b_cont != 0)
+               freemsg(mp->b_cont);
+           mp->b_cont = np;
+           psp = (struct ppp_stats *) np->b_wptr;
+           np->b_wptr += sizeof(struct ppp_stats);
+           bzero((caddr_t)psp, sizeof(struct ppp_stats));
+           psp->p = state->stats;
+           iop->ioc_count = sizeof(struct ppp_stats);
+           error = 0;
+           break;
+
+       case PPPIO_LASTMOD:
+           /* we knew this anyway */
+           error = 0;
+           break;
 
        default:
-           putnext(q, mp);
+           error = -1;
            break;
+       }
 
-       iocack:
-           iop->ioc_count = 0;
+       if (error < 0)
+           putnext(q, mp);
+       else if (error == 0) {
            mp->b_datap->db_type = M_IOCACK;
            qreply(q, mp);
-           break;
-
-       iocnak:
+       } else {
            mp->b_datap->db_type = M_IOCNAK;
+           iop->ioc_count = 0;
+           iop->ioc_error = error;
            qreply(q, mp);
+       }
+       break;
+
+    case M_CTL:
+       switch (*mp->b_rptr) {
+       case PPPCTL_MTU:
+           state->mtu = ((unsigned short *)mp->b_rptr)[1];
+           freemsg(mp);
+           break;
+       case PPPCTL_MRU:
+           state->mru = ((unsigned short *)mp->b_rptr)[1];
+           freemsg(mp);
            break;
+       case PPPCTL_UNIT:
+           state->unit = mp->b_rptr[1];
+           break;
+       default:
+           putnext(q, mp);
        }
+       break;
 
     default:
        putnext(q, mp);
@@ -243,7 +361,7 @@ ahdlc_rput(q, mp)
 {
     mblk_t *np;
     uchar_t *cp;
-    struct ahdlc_state *state;
+    ahdlc_state_t *state;
 
     switch (mp->b_datap->db_type) {
     case M_DATA:
@@ -252,7 +370,7 @@ ahdlc_rput(q, mp)
        break;
 
     case M_HANGUP:
-       state = (struct ahdlc_state *) q->q_ptr;
+       state = (ahdlc_state_t *) q->q_ptr;
        if (state->cur_frame != 0) {
            /* XXX would like to send this up for debugging */
            freemsg(state->cur_frame);
@@ -278,25 +396,28 @@ stuff_frame(q, mp)
     queue_t *q;
     mblk_t *mp;
 {
-    struct ahdlc_state *state;
-    int ilen, olen, c, extra;
-    mblk_t *omsg, *np, *op;
+    ahdlc_state_t *state;
+    int ilen, olen, c, extra, i;
+    mblk_t *omsg, *op, *np;
     uchar_t *sp, *sp0, *dp, *dp0, *spend;
     ushort_t fcs;
     u_int32_t *xaccm, lcp_xaccm[8];
+    static uchar_t lcphdr[PPP_HDRLEN] = { 0xff, 0x03, 0xc0, 0x21 };
+    uchar_t ppphdr[PPP_HDRLEN];
+
+    state = (ahdlc_state_t *) q->q_ptr;
+    ilen = msgdsize(mp);
 
     /*
      * We estimate the length of the output packet as
      * 1.25 * input length + 16 (for initial flag, FCS, final flag, slop).
      */
-    state = (struct ahdlc_state *) q->q_ptr;
-    ilen = msgdsize(mp);
     olen = ilen + (ilen >> 2) + 16;
     if (olen > OFRAME_BSIZE)
        olen = OFRAME_BSIZE;
     omsg = op = allocb(olen, BPRI_MED);
     if (omsg == 0)
-       return;
+       goto bomb;
 
     /*
      * Put in an initial flag for now.  We'll remove it later
@@ -308,13 +429,17 @@ stuff_frame(q, mp)
 
     /*
      * For LCP packets, we must escape all control characters.
+     * Inspect the first PPP_HDRLEN bytes of the message to
+     * see whether it is an LCP packet.
+     * LCP packets must not be A/C or protocol compressed.
      */
-    if (proto == PPP_LCP) {
-       bcopy(state->xaccm, lcp_xaccm, sizeof(lcp_xaccm));
+    xaccm = state->xaccm;
+    if (msg_copy(ppphdr, mp, PPP_HDRLEN) == PPP_HDRLEN
+       && bcmp((caddr_t) ppphdr, (caddr_t) lcphdr, PPP_HDRLEN) == 0) {
+       bcopy((caddr_t) state->xaccm, (caddr_t) lcp_xaccm, sizeof(lcp_xaccm));
        lcp_xaccm[0] = ~0;
        xaccm = lcp_xaccm;
-    } else
-       xaccm = state->xaccm;
+    }
 
     sp = mp->b_rptr;
     fcs = PPP_INITFCS;
@@ -374,10 +499,8 @@ stuff_frame(q, mp)
            if (olen > OFRAME_BSIZE)
                olen = OFRAME_BSIZE;
            np = allocb(olen, BPRI_MED);
-           if (np == 0) {
-               freemsg(omsg);
-               return;
-           }
+           if (np == 0)
+               goto bomb;
            op->b_cont = np;
            op = np;
            dp = op->b_wptr;
@@ -392,10 +515,8 @@ stuff_frame(q, mp)
        /* Sigh.  Need another block. */
        op->b_wptr = dp;
        np = allocb(5, BPRI_MED);
-       if (np == 0) {
-           freemsg(omsg);
-           return;
-       }
+       if (np == 0)
+           goto bomb;
        op->b_cont = np;
        op = np;
        dp = op->b_wptr;
@@ -421,21 +542,57 @@ stuff_frame(q, mp)
     if (qsize(q->q_next) > 0)
        ++omsg->b_rptr;
 
+    /*
+     * Update statistics.
+     */
+    state->stats.ppp_obytes += msgdsize(omsg);
+    state->stats.ppp_opackets++;
+
+    /*
+     * Send it on.
+     */
     putnext(q, omsg);
+    return;
+
+ bomb:
+    if (omsg != 0)
+       freemsg(omsg);
+    state->stats.ppp_oerrors++;
+    putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+}
+
+static unsigned paritytab[8] = {
+    0x96696996, 0x69969669, 0x69969669, 0x96696996,
+    0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+
+#define UPDATE_FLAGS(c)        {                               \
+    if ((c) & 0x80)                                    \
+       state->flags |= RCV_B7_1;                       \
+    else                                               \
+       state->flags |= RCV_B7_0;                       \
+    if (paritytab[(c) >> 5] & (1 << ((c) & 0x1F)))     \
+       state->flags |= RCV_ODDP;                       \
+    else                                               \
+       state->flags |= RCV_EVNP;                       \
 }
 
+/*
+ * Process received characters.
+ */
 static void
 unstuff_chars(q, mp)
     queue_t *q;
     mblk_t *mp;
 {
-    struct ahdlc_state *state;
+    ahdlc_state_t *state;
     mblk_t *om;
     uchar_t *cp, *cpend, *dp, *dp0;
     int c, len, extra, offset;
     ushort_t fcs;
 
-    state = (struct ahdlc_state *) q->q_ptr;
+    state = (ahdlc_state_t *) q->q_ptr;
+    state->stats.ppp_ibytes += msgdsize(mp);
     cp = mp->b_rptr;
     for (;;) {
        /*
@@ -450,14 +607,13 @@ unstuff_chars(q, mp)
        }
 
        if ((state->flags & (IFLUSH|ESCAPED)) == 0
-           && state->inlen >= PPP_HDRLEN
-           && (om = state->cur_blk) != 0) {
+           && state->inlen > 0 && (om = state->cur_blk) != 0) {
            /*
             * Process bulk chars as quickly as possible.
             */
            dp = om->b_wptr;
            len = om->b_datap->db_lim - dp; /* max # output bytes */
-           extra = (cpend - cp) - len;     /* #input chars - #output bytes */
+           extra = (mp->b_wptr - cp) - len;/* #input chars - #output bytes */
            if (extra < 0) {
                len += extra;               /* we'll run out of input first */
                extra = 0;
@@ -470,19 +626,18 @@ unstuff_chars(q, mp)
                if (c == PPP_FLAG)
                    break;
                ++cp;
+               UPDATE_FLAGS(c);
                if (c == PPP_ESCAPE) {
-                   if (extra > 0)
+                   if (extra > 0) {
                        --extra;
-                   else if (len > 1)
-                       --len;
-                   else {
+                       ++cpend;
+                   }
+                   if (cp >= cpend || (c = *cp) == PPP_FLAG) {
                        state->flags |= ESCAPED;
                        break;
                    }
-                   c = *cp;
-                   if (c == PPP_FLAG)
-                       break;
                    ++cp;
+                   UPDATE_FLAGS(c);
                    c ^= PPP_TRANS;
                }
                *dp++ = c;
@@ -491,11 +646,12 @@ unstuff_chars(q, mp)
            state->inlen += dp - dp0;
            state->infcs = fcs;
            om->b_wptr = dp;
-           if (len <= 0)
-               continue;       /* go back and check cp again */
+           if (cp >= mp->b_wptr)
+               continue;       /* advance to the next mblk */
        }
 
        c = *cp++;
+       UPDATE_FLAGS(c);
        if (c == PPP_FLAG) {
            /*
             * End of a frame.
@@ -506,22 +662,24 @@ unstuff_chars(q, mp)
            len = state->inlen;
            state->cur_frame = 0;
            state->inlen = 0;
-           if (om == 0)
-               continue;
-           if (state->flags & (IFLUSH|ESCAPED) || len < PPP_FCSLEN) {
-               /* XXX should send up ctl message to notify VJ decomp */
-               freemsg(om);
+           if (len == 0 && (state->flags & IFLUSH) == 0)
                continue;
+           state->stats.ppp_ipackets++;
+           if (om != 0 && (state->flags & (IFLUSH|ESCAPED)) == 0
+               && len > PPP_FCSLEN) {
+               if (state->infcs == PPP_GOODFCS) {
+                   adjmsg(om, -PPP_FCSLEN);    /* chop off fcs */
+                   putnext(q, om);             /* bombs away! */
+                   continue;
+               }
+#if DEBUG
+               cmn_err(CE_CONT, "ppp_ahdl: bad fcs %x\n", state->infcs);
+#endif
            }
-           if (state->infcs != PPP_GOODFCS) {
-               /* incr bad fcs stats */
-               /* would like to be able to pass this up for debugging */
-               /* XXX should send up ctl message to notify VJ decomp */
+           if (om != 0)
                freemsg(om);
-               continue;
-           }
-           adjmsg(om, -PPP_FCSLEN);    /* chop off fcs */
-           putnext(q, om);             /* bombs away! */
+           state->stats.ppp_ierrors++;
+           putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
            continue;
        }
 
@@ -529,6 +687,7 @@ unstuff_chars(q, mp)
            continue;
        if (state->flags & ESCAPED) {
            c ^= PPP_TRANS;
+           state->flags &= ~ESCAPED;
        } else if (c == PPP_ESCAPE) {
            state->flags |= ESCAPED;
            continue;
@@ -549,11 +708,19 @@ unstuff_chars(q, mp)
            om = state->cur_blk;
            if (om->b_wptr >= om->b_datap->db_lim) {
                /*
-                * Current message block is full.  Allocate another one.
+                * Current message block is full.  Allocate another one,
+                * unless we have run out of MRU.
                 */
+               if (state->inlen >= state->mru + PPP_HDRLEN + PPP_FCSLEN) {
+#if DEBUG
+                   cmn_err(CE_CONT, "ppp_ahdl: frame too long (%d)\n",
+                           state->inlen);
+#endif
+                   state->flags |= IFLUSH;
+                   continue;
+               }
                om = allocb(IFRAME_BSIZE, BPRI_MED);
                if (om == 0) {
-                   freemsg(state->cur_frame);
                    state->flags |= IFLUSH;
                    continue;
                }
@@ -561,32 +728,48 @@ unstuff_chars(q, mp)
                state->cur_blk = om;
            }
        }
-       *om->b_wptr++ = c;
-       ++state->inlen;
-       state->infcs = PPP_FCS(state->infcs, c);
 
-       if (state->inlen == PPP_HDRLEN) {
+       if (state->inlen == 0) {
            /*
             * We don't do address/control & protocol decompression here,
-            * but we do leave space for the decompressed fields and
-            * arrange for the info field to start on a word boundary.
+            * but we try to put the first byte at an offset such that
+            * the info field starts on a word boundary.  The code here
+            * will do this except for packets with protocol compression
+            * but not address/control compression.
             */
-           cp = om->b_rptr;
-           if (*cp == PPP_ALLSTATIONS)
-               cp += 2;
-           if ((*cp & 1) == 0)
-               ++cp;
-           /* cp is now pointing at the last byte of the ppp protocol field */
-           offset = 3 - ((unsigned)cp & 3);
-           if (offset > 0) {
-               do {
-                   cp[offset] = cp[0];
-                   --cp;
-               } while (cp >= om->b_rptr);
-               om->b_rptr += offset;
-               om->b_wptr += offset;
+           if (c != PPP_ALLSTATIONS) {
+               om->b_wptr += 2;
+               if (c & 1)
+                   ++om->b_wptr;
+               om->b_rptr = om->b_wptr;
            }
        }
+
+       *om->b_wptr++ = c;
+       ++state->inlen;
+       state->infcs = PPP_FCS(state->infcs, c);
     }
 }
 
+static int
+msg_copy(buf, mp, n)
+    uchar_t *buf;
+    mblk_t *mp;
+    int n;
+{
+    int i;
+    uchar_t *cp;
+
+    cp = mp->b_rptr;
+    for (i = 0; i < n; ++i) {
+       if (cp >= mp->b_wptr) {
+           do {
+               if ((mp = mp->b_cont) == 0)
+                   return i;
+           } while (mp->b_rptr >= mp->b_wptr);
+           cp = mp->b_rptr;
+       }
+       buf[i] = *cp++;
+    }
+    return n;
+}