1) Created a subdirectory called 'solaris'. Currently it contains a replica
authorAdi Masputra <adi.masputra@sun.com>
Tue, 18 Apr 2000 23:51:29 +0000 (23:51 +0000)
committerAdi Masputra <adi.masputra@sun.com>
Tue, 18 Apr 2000 23:51:29 +0000 (23:51 +0000)
   of the 'svr4' directory. However, over time, files in this directory
   will contain the same code as the kernel-portion of pppd in future
   releases of Solaris, hence they most probably will change in contents
   and/or sub-structure.
2) Changed the 'configure' script to not create symbolic link Makefiles
   when the OS is SunOS 4.x. Under 'SunOS' category, only SunOS 5.x (or
   Solaris 2.x) is currently enabled.
3) Changed the rest of the utilities + pppd daemon Makefile.sol2 to point
   to the solaris/Makedefs instead of the one in svr4 directory.

19 files changed:
chat/Makefile.sol2
configure
pppd/Makefile.sol2
pppd/sys-solaris.c [new file with mode: 0644]
pppdump/Makefile.sol2
pppstats/Makefile.sol2
solaris/Makedefs [new file with mode: 0644]
solaris/Makedefs.sol2 [new file with mode: 0644]
solaris/Makefile.sol2 [new file with mode: 0644]
solaris/Makefile.sol2-64 [new file with mode: 0644]
solaris/Makefile.top [new file with mode: 0644]
solaris/ppp.c [new file with mode: 0644]
solaris/ppp.conf [new file with mode: 0644]
solaris/ppp_ahdlc.c [new file with mode: 0644]
solaris/ppp_ahdlc_mod.c [new file with mode: 0644]
solaris/ppp_comp.c [new file with mode: 0644]
solaris/ppp_comp_mod.c [new file with mode: 0644]
solaris/ppp_mod.c [new file with mode: 0644]
solaris/ppp_mod.h [new file with mode: 0644]

index ee65fe61cbc071259bd69ae3d85a24cba6ca618d..f566cc68b49273068a52f3d579748aaf10ccfcca 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for chat on Solaris 2
 #
 
-include ../svr4/Makedefs
+include ../solaris/Makedefs
 
 CFLAGS = $(COPTS) -DNO_USLEEP -DSOL2
 
index db318e07e7a38a2003a9dfea4b68dc2753acdf39..1868308e908480970da92c1aef39b6c0989ead50 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: configure,v 1.24 2000/04/13 11:59:40 paulus Exp $
+# $Id: configure,v 1.25 2000/04/18 23:51:26 masputra Exp $
 
 #  if [ -d /NextApps ]; then
 #    system="NeXTStep"
@@ -18,10 +18,10 @@ case $system in
     state="known";;
   SunOS)
     case $release in
-      [0-3]*)  state="ancient";;
-      4*)      state="known"; ksrc="sunos4"; makext="sunos4";;
-      5.[1-6]*)        state="known"; ksrc="svr4"; makext="sol2";;
-      5.[789]*)        state="known"; ksrc="svr4"; makext="sol2";
+#      [0-3]*) state="ancient";;
+#      4*)     state="known"; ksrc="sunos4"; makext="sunos4";;
+      5.[1-6]*)        state="known"; ksrc="solaris"; makext="sol2";;
+      5.[7-9]*)        state="known"; ksrc="solaris"; makext="sol2";
               case $arch in
                sun4u)  lp64='y';;
                *)      ;;
@@ -118,7 +118,7 @@ if [ -d "$ksrc" ]; then
   rm -f Makefile
   ln -s $ksrc/Makefile.top Makefile
   echo "  Makefile -> $ksrc/Makefile.top"
-  if [ "$ksrc" = svr4 ]; then
+  if [ "$ksrc" = solaris ]; then
     # Point to 64-bit Makefile extension
     if [ "$lp64" = y ]; then 
       makext=$makext-64 
index 835acc215091394d65e1b3bba544b7f248c7f2b7..2f28fa4c1980965a58e415f5740c54a41a4016fa 100644 (file)
@@ -1,16 +1,16 @@
 #
 # Makefile for pppd under Solaris 2.
-# $Id: Makefile.sol2,v 1.18 2000/04/13 12:05:57 paulus Exp $
+# $Id: Makefile.sol2,v 1.19 2000/04/18 23:51:26 masputra Exp $
 #
 
-include ../svr4/Makedefs
+include ../solaris/Makedefs
 
 COPTS  += -xO2 -xspace -W0,-Lt
 CFLAGS =  -I../include -DSVR4 -DSOL2 $(COPTS)
 LIBS   = -lsocket -lnsl
 
 OBJS   =  main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o \
-       ccp.o auth.o options.o demand.o utils.o sys-svr4.o tdb.o
+       ccp.o auth.o options.o demand.o utils.o sys-solaris.o tdb.o
 
 #
 # uncomment the following to enable plugins
diff --git a/pppd/sys-solaris.c b/pppd/sys-solaris.c
new file mode 100644 (file)
index 0000000..923bce8
--- /dev/null
@@ -0,0 +1,2735 @@
+/*
+ * System-dependent procedures for pppd under Solaris 2.
+ *
+ * Parts re-written by Adi Masputra <adi.masputra@sun.com>, based on 
+ * the original sys-svr4.c
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * 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.  
+ *
+ * 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 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.
+ */
+
+#define RCSID  "$Id: sys-solaris.c,v 1.1 2000/04/18 23:51:26 masputra Exp $"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#ifndef CRTSCTS
+#include <sys/termiox.h>
+#endif
+#include <signal.h>
+#include <utmpx.h>
+#include <sys/types.h>
+#include <sys/ioccom.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/dlpi.h>
+#include <sys/stat.h>
+#include <sys/mkdev.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+#ifdef SOL2
+#include <sys/tihdr.h>
+#include <sys/tiuser.h>
+#include <inet/common.h>
+#include <inet/mib2.h>
+#include <sys/ethernet.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "ccp.h"
+
+#if !defined(PPP_DEV_NAME)
+#define PPP_DEV_NAME   "/dev/ppp"
+#endif /* !defined(PPP_DEV_NAME) */
+
+#if !defined(AHDLC_MOD_NAME)
+#define AHDLC_MOD_NAME "ppp_ahdl"
+#endif /* !defined(AHDLC_MOD_NAME) */
+
+#if !defined(COMP_MOD_NAME)
+#define COMP_MOD_NAME  "ppp_comp"
+#endif /* !defined(COMP_MOD_NAME) */
+
+#if !defined(IP_DEV_NAME)
+#define        IP_DEV_NAME     "/dev/ip"
+#endif /* !defined(IP_DEV_NAME) */
+
+#if !defined(IP_MOD_NAME)
+#define        IP_MOD_NAME     "ip"
+#endif /* !defined(IP_MOD_NAME) */
+
+#if !defined(UDP_DEV_NAME) && defined(SOL2)
+#define        UDP_DEV_NAME    "/dev/udp"
+#endif /* !defined(UDP_DEV_NAME) && defined(SOL2) */
+
+#if !defined(UDP6_DEV_NAME) && defined(SOL2)
+#define        UDP6_DEV_NAME   "/dev/udp6"
+#endif /* !defined(UDP6_DEV_NAME) && defined(SOL2) */
+
+static const char rcsid[] = RCSID;
+
+#if defined(SOL2)
+/*
+ * "/dev/udp" is used as a multiplexor to PLINK the interface stream
+ * under. It is used in place of "/dev/ip" since STREAMS will not let
+ * a driver be PLINK'ed under itself, and "/dev/ip" is typically the
+ * driver at the bottom of the tunneling interfaces stream.
+ */
+static char *mux_dev_name = UDP_DEV_NAME;
+#else
+static char *mux_dev_name = IP_DEV_NAME;
+#endif
+static int     pppfd;
+static int     fdmuxid = -1;
+static int     ipfd;
+static int     ipmuxid = -1;
+
+#if defined(INET6) && defined(SOL2)
+static int     ip6fd;          /* IP file descriptor */
+static int     ip6muxid = -1;  /* Multiplexer file descriptor */
+static int     if6_is_up = 0;  /* IPv6 interface has been marked up */
+
+#define _IN6_LLX_FROM_EUI64(l, s, eui64, as) do {      \
+       s->sin6_addr.s6_addr32[0] = htonl(as);  \
+       eui64_copy(eui64, s->sin6_addr.s6_addr32[2]);   \
+       s->sin6_family = AF_INET6;              \
+       l.lifr_addr.ss_family = AF_INET6;       \
+       l.lifr_addrlen = 10;                    \
+       l.lifr_addr = laddr;                    \
+       } while (0)
+
+#define IN6_LLADDR_FROM_EUI64(l, s, eui64)  \
+    _IN6_LLX_FROM_EUI64(l, s, eui64, 0xfe800000)
+
+#define IN6_LLTOKEN_FROM_EUI64(l, s, eui64) \
+    _IN6_LLX_FROM_EUI64(l, s, eui64, 0)
+
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if defined(INET6) && defined(SOL2)
+static char    first_ether_name[LIFNAMSIZ];    /* Solaris 8 and above */
+#else
+static char    first_ether_name[IFNAMSIZ];     /* Before Solaris 8 */
+#define MAXIFS         256                     /* Max # of interfaces */
+#endif /* defined(INET6) && defined(SOL2) */
+
+static int     restore_term;
+static struct termios inittermios;
+#ifndef CRTSCTS
+static struct termiox inittermiox;
+static int     termiox_ok;
+#endif
+static struct winsize wsinfo;  /* Initial window size info */
+static pid_t   tty_sid;        /* original session ID for terminal */
+
+extern u_char  inpacket_buf[]; /* borrowed from main.c */
+
+#define MAX_POLLFDS    32
+static struct pollfd pollfds[MAX_POLLFDS];
+static int n_pollfds;
+
+static int     link_mtu, link_mru;
+
+#define NMODULES       32
+static int     tty_nmodules;
+static char    tty_modules[NMODULES][FMNAMESZ+1];
+static int     tty_npushed;
+
+static int     if_is_up;       /* Interface has been marked up */
+static u_int32_t remote_addr;          /* IP address of peer */
+static u_int32_t default_route_gateway;        /* Gateway for default route added */
+static u_int32_t proxy_arp_addr;       /* Addr for proxy arp entry added */
+
+/* Prototypes for procedures local to this file. */
+static int translate_speed __P((int));
+static int baud_rate_of __P((int));
+static int get_ether_addr __P((u_int32_t, struct sockaddr *));
+static int get_hw_addr __P((char *, u_int32_t, struct sockaddr *));
+static int get_hw_addr_dlpi __P((char *, struct sockaddr *));
+static int dlpi_attach __P((int, int));
+static int dlpi_info_req __P((int));
+static int dlpi_get_reply __P((int, union DL_primitives *, int, int));
+static int strioctl __P((int, int, void *, int, int));
+
+#ifdef SOL2
+/*
+ * sifppa - Sets interface ppa
+ *
+ * without setting the ppa, ip module will return EINVAL upon setting the
+ * interface UP (SIOCSxIFFLAGS). This is because ip module in 2.8 expects
+ * two DLPI_INFO_REQ to be sent down to the driver (below ip) before
+ * IFF_UP can be set. Plumbing the device causes one DLPI_INFO_REQ to
+ * be sent down, and the second DLPI_INFO_REQ is sent upon receiving
+ * IF_UNITSEL (old) or SIOCSLIFNAME (new) ioctls. Such setting of the ppa
+ * is required because the ppp DLPI provider advertises itself as
+ * a DLPI style 2 type, which requires a point of attachment to be
+ * specified. The only way the user can specify a point of attachment
+ * is via SIOCSLIFNAME or IF_UNITSEL.
+ *
+ * Such changes in the behavior of ip module was made to meet new or
+ * evolving standards requirements.
+ *
+ */
+static int
+sifppa(fd, ppa)
+    int fd;
+    int ppa;
+{
+    return (int)ioctl(fd, IF_UNITSEL, (char *)&ppa);
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in 
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the lifreq version (Solaris 8 and above)
+ */
+char *
+get_first_ethernet()
+{
+    struct lifnum lifn;
+    struct lifconf lifc;
+    struct lifreq *plifreq;
+    struct lifreq lifr;
+    int        fd, num_ifs, i, found;
+    uint_t fl, req_size;
+    char *req;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+       return 0;
+    }
+
+    /*
+     * Find out how many interfaces are running
+     */
+    lifn.lifn_family = AF_UNSPEC;
+    lifn.lifn_flags = LIFC_NOXMIT;
+    if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) {
+       close(fd);
+       error("could not determine number of interfaces: %m");
+       return 0;
+    }
+
+    num_ifs = lifn.lifn_count;
+    req_size = num_ifs * sizeof(struct lifreq);
+    req = malloc(req_size);
+    if (req == NULL) {
+       close(fd);
+       error("out of memory");
+       return 0;
+    }
+
+    /*
+     * Get interface configuration info for all interfaces
+     */
+    lifc.lifc_family = AF_UNSPEC;
+    lifc.lifc_flags = LIFC_NOXMIT;
+    lifc.lifc_len = req_size;
+    lifc.lifc_buf = req;
+    if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0) {
+       close(fd);
+       free(req);
+       error("SIOCGLIFCONF: %m");
+       return 0;
+    }
+
+    /*
+     * And traverse each interface to look specifically for the first
+     * occurence of an Ethernet interface which has been marked up
+     */
+    plifreq = lifc.lifc_req;
+    found = 0;
+    for (i = lifc.lifc_len / sizeof(struct lifreq); i > 0; i--, plifreq++) {
+
+       if (strchr(plifreq->lifr_name, ':') != NULL)
+           continue;
+
+       memset(&lifr, 0, sizeof(lifr));
+       strncpy(lifr.lifr_name, plifreq->lifr_name, sizeof(lifr.lifr_name));
+       if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+           close(fd);
+           free(req);
+           error("SIOCGLIFFLAGS: %m");
+           return 0;
+       }
+       fl = lifr.lifr_flags;
+
+       if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+               != (IFF_UP | IFF_BROADCAST))
+           continue;
+
+       found = 1;
+       break;
+    }
+    free(req);
+    close(fd);
+
+    if (found) {
+       strncpy(first_ether_name, lifr.lifr_name, sizeof(first_ether_name));
+       return (char *)first_ether_name;
+    } else
+       return NULL;
+}
+#else
+/*
+ * get_first_ethernet - returns the first Ethernet interface name found in 
+ * the system, or NULL if none is found
+ *
+ * NOTE: This is the ifreq version (before Solaris 8). 
+ */
+char *
+get_first_ethernet()
+{
+    struct ifconf ifc;
+    struct ifreq *pifreq;
+    struct ifreq ifr;
+    int        fd, num_ifs, i, found;
+    uint_t fl, req_size;
+    char *req;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+       return 0;
+    }
+
+    /*
+     * Find out how many interfaces are running
+     */
+    if (ioctl(fd, SIOCGIFNUM, (char *)&num_ifs) < 0) {
+       num_ifs = MAXIFS;
+    }
+
+    req_size = num_ifs * sizeof(struct ifreq);
+    req = malloc(req_size);
+    if (req == NULL) {
+       close(fd);
+       error("out of memory");
+       return 0;
+    }
+
+    /*
+     * Get interface configuration info for all interfaces
+     */
+    ifc.ifc_len = req_size;
+    ifc.ifc_buf = req;
+    if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+       close(fd);
+       free(req);
+       error("SIOCGIFCONF: %m");
+       return 0;
+    }
+
+    /*
+     * And traverse each interface to look specifically for the first
+     * occurence of an Ethernet interface which has been marked up
+     */
+    pifreq = ifc.ifc_req;
+    found = 0;
+    for (i = ifc.ifc_len / sizeof(struct ifreq); i > 0; i--, pifreq++) {
+
+       if (strchr(pifreq->ifr_name, ':') != NULL)
+           continue;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, pifreq->ifr_name, sizeof(ifr.ifr_name));
+       if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
+           close(fd);
+           free(req);
+           error("SIOCGIFFLAGS: %m");
+           return 0;
+       }
+       fl = ifr.ifr_flags;
+
+       if ((fl & (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+               != (IFF_UP | IFF_BROADCAST))
+           continue;
+
+       found = 1;
+       break;
+    }
+    free(req);
+    close(fd);
+
+    if (found) {
+       strncpy(first_ether_name, ifr.ifr_name, sizeof(first_ether_name));
+       return (char *)first_ether_name;
+    } else
+       return NULL;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+#if defined(SOL2)
+/*
+ * get_if_hwaddr - get the hardware address for the specified
+ * network interface device.
+ */
+int
+get_if_hwaddr(u_char *addr, char *if_name)
+{
+    struct sockaddr s_eth_addr;
+    struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+
+    if (if_name == NULL)
+       return -1;
+
+    /*
+     * Send DL_INFO_REQ to the driver to solicit its MAC address
+     */
+    if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+       error("could not obtain hardware address for %s", if_name);
+       return -1;
+    }
+
+    memcpy(addr, eth_addr->ether_addr_octet, 6);
+    return 1;
+}
+#endif /* SOL2 */
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * slifname - Sets interface ppa and flags
+ *
+ * in addition to the comments stated in sifppa(), IFF_IPV6 bit must
+ * be set in order to declare this as an IPv6 interface
+ */
+static int
+slifname(fd, ppa)
+    int fd;
+    int ppa;
+{
+    struct  lifreq lifr;
+    int            ret;
+
+    memset(&lifr, 0, sizeof(lifr));
+    ret = ioctl(fd, SIOCGLIFFLAGS, &lifr);
+    if (ret < 0)
+       goto slifname_done;
+
+    lifr.lifr_flags |= IFF_IPV6;
+    lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
+    lifr.lifr_ppa = ppa;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+    ret = ioctl(fd, SIOCSLIFNAME, &lifr);
+
+slifname_done:
+    return ret;
+
+
+}
+
+
+/*
+ * ether_to_eui64 - Convert 48-bit Ethernet address into 64-bit EUI
+ *
+ * walks the list of valid ethernet interfaces, and convert the first
+ * found 48-bit MAC address into EUI 64. caller also assumes that
+ * the system has a properly configured Ethernet interface for this
+ * function to return non-zero.
+ */
+int
+ether_to_eui64(eui64_t *p_eui64)
+{
+    struct sockaddr s_eth_addr;
+    struct ether_addr *eth_addr = (struct ether_addr *)&s_eth_addr.sa_data;
+    char *if_name;
+
+    if ((if_name = get_first_ethernet()) == NULL) {
+       error("no persistent id can be found");
+       return 0;
+    }
+    /*
+     * Send DL_INFO_REQ to the driver to solicit its MAC address
+     */
+    if (!get_hw_addr_dlpi(if_name, &s_eth_addr)) {
+       error("could not obtain hardware address for %s", if_name);
+       return 0;
+    }
+
+    /*
+     * And convert the EUI-48 into EUI-64, per RFC 2472 [sec 4.1]
+     */
+    p_eui64->e8[0] = (eth_addr->ether_addr_octet[0] & 0xFF) | 0x02;
+    p_eui64->e8[1] = (eth_addr->ether_addr_octet[1] & 0xFF);
+    p_eui64->e8[2] = (eth_addr->ether_addr_octet[2] & 0xFF);
+    p_eui64->e8[3] = 0xFF;
+    p_eui64->e8[4] = 0xFE;
+    p_eui64->e8[5] = (eth_addr->ether_addr_octet[3] & 0xFF);
+    p_eui64->e8[6] = (eth_addr->ether_addr_octet[4] & 0xFF);
+    p_eui64->e8[7] = (eth_addr->ether_addr_octet[5] & 0xFF);
+
+    return 1;
+}
+#endif /* defined(SOL2) && defined(INET6) */
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+    int ifd, x;
+    struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+    int i6fd;
+    struct lifreq lifr;
+#endif /* defined(INET6) && defined(SOL2) */
+#if !defined(SOL2)
+    struct {
+       union DL_primitives prim;
+       char space[64];
+    } reply;
+#endif /* !defined(SOL2) */
+
+    ipfd = open(mux_dev_name, O_RDWR, 0);
+    if (ipfd < 0)
+       fatal("Couldn't open IP device: %m");
+
+#if defined(INET6) && defined(SOL2)
+    ip6fd = open(UDP6_DEV_NAME, O_RDWR, 0);
+    if (ip6fd < 0)
+       fatal("Couldn't open IP device (2): %m");
+#endif /* defined(INET6) && defined(SOL2) */
+
+    if (default_device && !notty)
+       tty_sid = getsid((pid_t)0);
+
+    pppfd = open(PPP_DEV_NAME, O_RDWR | O_NONBLOCK, 0);
+    if (pppfd < 0)
+       fatal("Can't open %s: %m", PPP_DEV_NAME);
+    if (kdebugflag & 1) {
+       x = PPPDBG_LOG + PPPDBG_DRIVER;
+       strioctl(pppfd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    if (strioctl(pppfd, PPPIO_NEWPPA, &ifunit, 0, sizeof(int)) < 0)
+       fatal("Can't create new PPP interface: %m");
+
+#if defined(SOL2)
+    /*
+     * Since sys_init() is called prior to ifname being set in main(),
+     * we need to get the ifname now, otherwise slifname(), and others,
+     * will fail, or maybe, I should move them to a later point ?
+     * <adi.masputra@sun.com>
+     */
+    sprintf(ifname, "ppp%d", ifunit);
+#endif /* defined(SOL2) */
+    /*
+     * Open the ppp device again and link it under the ip multiplexor.
+     * IP will assign a unit number which hopefully is the same as ifunit.
+     * I don't know any way to be certain they will be the same. :-(
+     */
+    ifd = open(PPP_DEV_NAME, O_RDWR, 0);
+    if (ifd < 0)
+       fatal("Can't open %s (2): %m", PPP_DEV_NAME);
+    if (kdebugflag & 1) {
+       x = PPPDBG_LOG + PPPDBG_DRIVER;
+       strioctl(ifd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+
+#if defined(INET6) && defined(SOL2)
+    i6fd = open(PPP_DEV_NAME, O_RDWR, 0);
+    if (i6fd < 0) {
+       close(ifd);
+       fatal("Can't open %s (3): %m", PPP_DEV_NAME);
+    }
+    if (kdebugflag & 1) {
+       x = PPPDBG_LOG + PPPDBG_DRIVER;
+       strioctl(i6fd, PPPIO_DEBUG, &x, sizeof(int), 0);
+    }
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if defined(SOL2)
+    if (ioctl(ifd, I_PUSH, IP_MOD_NAME) < 0) {
+       close(ifd);
+#if defined(INET6)
+       close(i6fd);
+#endif /* defined(INET6) */
+       fatal("Can't push IP module: %m");
+    }
+
+    /*
+     * Assign ppa according to the unit number returned by ppp device
+     * after plumbing is completed above.
+     */
+    if (sifppa(ifd, ifunit) < 0) {
+        close (ifd);
+#if defined(INET6)
+       close(i6fd);
+#endif /* defined(INET6) */
+        fatal("Can't set ppa for unit %d: %m", ifunit);
+    }
+
+#if defined(INET6)
+    /*
+     * An IPv6 interface is created anyway, even when the user does not 
+     * explicitly enable it. Note that the interface will be marked
+     * IPv6 during slifname().
+     */
+    if (ioctl(i6fd, I_PUSH, IP_MOD_NAME) < 0) {
+       close(ifd);
+       close(i6fd);
+       fatal("Can't push IP module (2): %m");
+    }
+
+    /*
+     * Assign ppa according to the unit number returned by ppp device
+     * after plumbing is completed above. In addition, mark the interface
+     * as an IPv6 interface.
+     */
+    if (slifname(i6fd, ifunit) < 0) {
+       close(ifd);
+       close(i6fd);
+       fatal("Can't set ifname for unit %d: %m", ifunit);
+    }
+#endif /* defined(INET6) */
+
+    ipmuxid = ioctl(ipfd, I_PLINK, ifd);
+    close(ifd);
+    if (ipmuxid < 0) {
+#if defined(INET6)
+       close(i6fd);
+#endif /* defined(INET6) */
+       fatal("Can't I_PLINK PPP device to IP: %m");
+    }
+
+    memset(&ifr, 0, sizeof(ifr));
+    sprintf(ifr.ifr_name, "%s", ifname);
+    ifr.ifr_ip_muxid = ipmuxid;
+
+    /*
+     * In Sol 8 and later, STREAMS dynamic module plumbing feature exists.
+     * This is so that an arbitrary module can be inserted, or deleted, 
+     * between ip module and the device driver without tearing down the 
+     * existing stream. Such feature requires the mux ids, which is set 
+     * by SIOCSIFMUXID (or SIOCLSIFMUXID).
+     */
+    if (ioctl(ipfd, SIOCSIFMUXID, &ifr) < 0) {
+       ioctl(ipfd, I_PUNLINK, ipmuxid);
+#if defined(INET6)
+       close(i6fd);
+#endif /* defined(INET6) */
+       fatal("SIOCSIFMUXID: %m");
+    }
+
+#else /* else if !defined(SOL2) */
+
+    if (dlpi_attach(ifd, ifunit) < 0 ||
+       dlpi_get_reply(ifd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0) {
+       close(ifd);
+       fatal("Can't attach to ppp%d: %m", ifunit);
+    }
+
+    ipmuxid = ioctl(ipfd, I_LINK, ifd);
+    close(ifd);
+    if (ipmuxid < 0)
+       fatal("Can't link PPP device to IP: %m");
+#endif /* defined(SOL2) */
+
+#if defined(INET6) && defined(SOL2)
+    ip6muxid = ioctl(ip6fd, I_PLINK, i6fd);
+    close(i6fd);
+    if (ip6muxid < 0) {
+       ioctl(ipfd, I_PUNLINK, ipmuxid);
+       fatal("Can't I_PLINK PPP device to IP (2): %m");
+    }
+
+    memset(&lifr, 0, sizeof(lifr));
+    sprintf(lifr.lifr_name, "%s", ifname);
+    lifr.lifr_ip_muxid = ip6muxid;
+
+    /*
+     * Let IP know of the mux id [see comment for SIOCSIFMUXID above]
+     */
+    if (ioctl(ip6fd, SIOCSLIFMUXID, &lifr) < 0) {
+       ioctl(ipfd, I_PUNLINK, ipmuxid);
+       ioctl(ip6fd, I_PUNLINK, ip6muxid);
+       fatal("Can't link PPP device to IP (2): %m");
+    }
+#endif /* defined(INET6) && defined(SOL2) */
+
+#if !defined(SOL2)
+    /* Set the interface name for the link. */
+    slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "ppp%d", ifunit);
+    ifr.ifr_metric = ipmuxid;
+    if (strioctl(ipfd, SIOCSIFNAME, (char *)&ifr, sizeof ifr, 0) < 0)
+       fatal("Can't set interface name %s: %m", ifr.ifr_name);
+#endif /* !defined(SOL2) */
+
+    n_pollfds = 0;
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This should call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+#if defined(SOL2)
+    struct ifreq ifr;
+#if defined(INET6)
+    struct lifreq lifr;
+#endif /* defined(INET6) */
+#endif /* defined(SOL2) */
+
+#if defined(SOL2) && defined(INET6)
+    if (if6_is_up)
+       sif6down(0);
+#endif /* defined(SOL2) && defined(INET6) */
+    if (if_is_up)
+       sifdown(0);
+    if (default_route_gateway)
+       cifdefaultroute(0, default_route_gateway, default_route_gateway);
+    if (proxy_arp_addr)
+       cifproxyarp(0, proxy_arp_addr);
+#if defined(SOL2)
+    /*
+     * Make sure we ask ip what the muxid, because 'ifconfig modlist' will
+     * unlink and re-link the modules, causing the muxid to change.
+     */
+    memset(&ifr, 0, sizeof(ifr));
+    sprintf(ifr.ifr_name, "%s", ifname);
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+       error("SIOCGIFFLAGS: %m");
+       return;
+    }
+
+    if (ioctl(ipfd, SIOCGIFMUXID, &ifr) < 0) {
+       error("SIOCGIFMUXID: %m");
+       return;
+    }
+
+    ipmuxid = ifr.ifr_ip_muxid;
+     
+    if (ioctl(ipfd, I_PUNLINK, ipmuxid) < 0) {
+       error("Can't I_PUNLINK PPP from IP: %m");
+       return;
+    }
+#if defined(INET6)
+    /*
+     * Make sure we ask ip what the muxid, because 'ifconfig modlist' will
+     * unlink and re-link the modules, causing the muxid to change.
+     */
+    memset(&lifr, 0, sizeof(lifr));
+    sprintf(lifr.lifr_name, "%s", ifname);
+    if (ioctl(ip6fd, SIOCGLIFFLAGS, &lifr) < 0) {
+       error("SIOCGLIFFLAGS: %m");
+       return;
+    }
+
+    if (ioctl(ip6fd, SIOCGLIFMUXID, &lifr) < 0) {
+       error("SIOCGLIFMUXID: %m");
+       return;
+    }
+
+    ip6muxid = lifr.lifr_ip_muxid;
+
+    if (ioctl(ip6fd, I_PUNLINK, ip6muxid) < 0) {
+       error("Can't I_PUNLINK PPP from IP (2): %m");
+    }
+#endif /* defined(INET6) */
+#endif /* defined(SOL2) */
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+    close(ipfd);
+#if defined(INET6) && defined(SOL2)
+    close(ip6fd);
+#endif /* defined(INET6) && defined(SOL2) */
+    if (pppfd >= 0)
+       close(pppfd);
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+int
+sys_check_options()
+{
+    return 1;
+}
+
+#if 0
+/*
+ * daemon - Detach us from controlling terminal session.
+ */
+int
+daemon(nochdir, noclose)
+    int nochdir, noclose;
+{
+    int pid;
+
+    if ((pid = fork()) < 0)
+       return -1;
+    if (pid != 0)
+       exit(0);                /* parent dies */
+    setsid();
+    if (!nochdir)
+       chdir("/");
+    if (!noclose) {
+       fclose(stdin);          /* don't need stdin, stdout, stderr */
+       fclose(stdout);
+       fclose(stderr);
+    }
+    return 0;
+}
+#endif
+
+/*
+ * ppp_available - check whether the system has any ppp interfaces
+ */
+int
+ppp_available()
+{
+    struct stat buf;
+
+    return stat(PPP_DEV_NAME, &buf) >= 0;
+}
+
+/*
+ * any_compressions - see if compression is enabled or not
+ *
+ * In the STREAMS implementation of kernel-portion pppd,
+ * the comp STREAMS module performs the ACFC, PFC, as well
+ * CCP and VJ compressions. However, if the user has explicitly
+ * declare to not enable them from the command line, there is
+ * no point of having the comp module be pushed on the stream.
+ */
+static int
+any_compressions()
+{
+    if ((!lcp_wantoptions[0].neg_accompression) &&
+       (!lcp_wantoptions[0].neg_pcompression) &&
+       (!ccp_protent.enabled_flag) &&
+       (!ipcp_wantoptions[0].neg_vj)) {
+           return 0;
+    }
+    return 1;
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+int
+establish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    /* Pop any existing modules off the tty stream. */
+    for (i = 0;; ++i)
+       if (ioctl(fd, I_LOOK, tty_modules[i]) < 0
+           || strcmp(tty_modules[i], "ptem") == 0
+           || ioctl(fd, I_POP, 0) < 0)
+           break;
+    tty_nmodules = i;
+
+    /* Push the async hdlc module and the compressor module. */
+    tty_npushed = 0;
+
+    if(!sync_serial) {
+        if (ioctl(fd, I_PUSH, AHDLC_MOD_NAME) < 0) {
+            error("Couldn't push PPP Async HDLC module: %m");
+           return -1;
+        }
+        ++tty_npushed;
+    }
+    if (kdebugflag & 4) {
+       i = PPPDBG_LOG + PPPDBG_AHDLC;
+       strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+    }
+    /*
+     * There's no need to push comp module if we don't intend
+     * to compress anything
+     */
+    if (any_compressions()) { 
+        if (ioctl(fd, I_PUSH, COMP_MOD_NAME) < 0)
+           error("Couldn't push PPP compression module: %m");
+       else
+           ++tty_npushed;
+    }
+
+    if (kdebugflag & 2) {
+       i = PPPDBG_LOG; 
+       if (any_compressions())
+           i += PPPDBG_COMP;
+       strioctl(pppfd, PPPIO_DEBUG, &i, sizeof(int), 0);
+    }
+
+    /* Link the serial port under the PPP multiplexor. */
+    if ((fdmuxid = ioctl(pppfd, I_LINK, fd)) < 0) {
+       error("Can't link tty to PPP mux: %m");
+       return -1;
+    }
+
+    return pppfd;
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ * This doesn't need to do anything because disestablish_ppp does it.
+ */
+void
+restore_loop()
+{
+}
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * It attempts to reconstruct the stream with the previously popped
+ * modules.  This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+    int fd;
+{
+    int i;
+
+    if (fdmuxid >= 0) {
+       if (ioctl(pppfd, I_UNLINK, fdmuxid) < 0) {
+           if (!hungup)
+               error("Can't unlink tty from PPP mux: %m");
+       }
+       fdmuxid = -1;
+
+       if (!hungup) {
+           while (tty_npushed > 0 && ioctl(fd, I_POP, 0) >= 0)
+               --tty_npushed;
+           for (i = tty_nmodules - 1; i >= 0; --i)
+               if (ioctl(fd, I_PUSH, tty_modules[i]) < 0)
+                   error("Couldn't restore tty module %s: %m",
+                          tty_modules[i]);
+       }
+       if (hungup && default_device && tty_sid > 0) {
+           /*
+            * If we have received a hangup, we need to send a SIGHUP
+            * to the terminal's controlling process.  The reason is
+            * that the original stream head for the terminal hasn't
+            * seen the M_HANGUP message (it went up through the ppp
+            * driver to the stream head for our fd to /dev/ppp).
+            */
+           kill(tty_sid, SIGHUP);
+       }
+    }
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+    int x;
+    char *s;
+
+    if (strioctl(pppfd, PPPIO_GCLEAN, &x, 0, sizeof(x)) < 0)
+       return;
+    s = NULL;
+    switch (~x) {
+    case RCV_B7_0:
+       s = "bit 7 set to 1";
+       break;
+    case RCV_B7_1:
+       s = "bit 7 set to 0";
+       break;
+    case RCV_EVNP:
+       s = "odd parity";
+       break;
+    case RCV_ODDP:
+       s = "even parity";
+       break;
+    }
+    if (s != NULL) {
+       warn("Serial link is not 8-bit clean:");
+       warn("All received characters had %s", s);
+    }
+}
+
+/*
+ * List of valid speeds.
+ */
+struct speed {
+    int speed_int, speed_val;
+} speeds[] = {
+#ifdef B50
+    { 50, B50 },
+#endif
+#ifdef B75
+    { 75, B75 },
+#endif
+#ifdef B110
+    { 110, B110 },
+#endif
+#ifdef B134
+    { 134, B134 },
+#endif
+#ifdef B150
+    { 150, B150 },
+#endif
+#ifdef B200
+    { 200, B200 },
+#endif
+#ifdef B300
+    { 300, B300 },
+#endif
+#ifdef B600
+    { 600, B600 },
+#endif
+#ifdef B1200
+    { 1200, B1200 },
+#endif
+#ifdef B1800
+    { 1800, B1800 },
+#endif
+#ifdef B2000
+    { 2000, B2000 },
+#endif
+#ifdef B2400
+    { 2400, B2400 },
+#endif
+#ifdef B3600
+    { 3600, B3600 },
+#endif
+#ifdef B4800
+    { 4800, B4800 },
+#endif
+#ifdef B7200
+    { 7200, B7200 },
+#endif
+#ifdef B9600
+    { 9600, B9600 },
+#endif
+#ifdef B19200
+    { 19200, B19200 },
+#endif
+#ifdef B38400
+    { 38400, B38400 },
+#endif
+#ifdef EXTA
+    { 19200, EXTA },
+#endif
+#ifdef EXTB
+    { 38400, EXTB },
+#endif
+#ifdef B57600
+    { 57600, B57600 },
+#endif
+#ifdef B76800
+    { 76800, B76800 },
+#endif
+#ifdef B115200
+    { 115200, B115200 },
+#endif
+#ifdef B153600
+    { 153600, B153600 },
+#endif
+#ifdef B230400
+    { 230400, B230400 },
+#endif
+#ifdef B307200
+    { 307200, B307200 },
+#endif
+#ifdef B460800
+    { 460800, B460800 },
+#endif
+    { 0, 0 }
+};
+
+/*
+ * Translate from bits/second to a speed_t.
+ */
+static int
+translate_speed(bps)
+    int bps;
+{
+    struct speed *speedp;
+
+    if (bps == 0)
+       return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+       if (bps == speedp->speed_int)
+           return speedp->speed_val;
+    warn("speed %d not supported", bps);
+    return 0;
+}
+
+/*
+ * Translate from a speed_t to bits/second.
+ */
+static int
+baud_rate_of(speed)
+    int speed;
+{
+    struct speed *speedp;
+
+    if (speed == 0)
+       return 0;
+    for (speedp = speeds; speedp->speed_int; speedp++)
+       if (speed == speedp->speed_val)
+           return speedp->speed_int;
+    return 0;
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc.  If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ */
+void
+set_up_tty(fd, local)
+    int fd, local;
+{
+    int speed;
+    struct termios tios;
+#if !defined (CRTSCTS)
+    struct termiox tiox;
+#endif
+
+    if (!sync_serial && tcgetattr(fd, &tios) < 0)
+       fatal("tcgetattr: %m");
+
+#ifndef CRTSCTS
+    termiox_ok = 1;
+    if (!sync_serial && ioctl (fd, TCGETX, &tiox) < 0) {
+       termiox_ok = 0;
+       if (errno != ENOTTY)
+           error("TCGETX: %m");
+    }
+#endif
+
+    if (!restore_term) {
+       inittermios = tios;
+#ifndef CRTSCTS
+       inittermiox = tiox;
+#endif
+       if (!sync_serial)
+           ioctl(fd, TIOCGWINSZ, &wsinfo);
+    }
+
+    tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+#ifdef CRTSCTS
+    if (crtscts > 0)
+       tios.c_cflag |= CRTSCTS;
+    else if (crtscts < 0)
+       tios.c_cflag &= ~CRTSCTS;
+#else
+    if (crtscts != 0 && !termiox_ok) {
+       error("Can't set RTS/CTS flow control");
+    } else if (crtscts > 0) {
+       tiox.x_hflag |= RTSXOFF|CTSXON;
+    } else if (crtscts < 0) {
+       tiox.x_hflag &= ~(RTSXOFF|CTSXON);
+    }
+#endif
+
+    tios.c_cflag |= CS8 | CREAD | HUPCL;
+    if (local || !modem)
+       tios.c_cflag |= CLOCAL;
+    tios.c_iflag = IGNBRK | IGNPAR;
+    tios.c_oflag = 0;
+    tios.c_lflag = 0;
+    tios.c_cc[VMIN] = 1;
+    tios.c_cc[VTIME] = 0;
+
+    if (crtscts == -2) {
+       tios.c_iflag |= IXON | IXOFF;
+       tios.c_cc[VSTOP] = 0x13;        /* DC3 = XOFF = ^S */
+       tios.c_cc[VSTART] = 0x11;       /* DC1 = XON  = ^Q */
+    }
+
+    speed = translate_speed(inspeed);
+    if (speed) {
+       cfsetospeed(&tios, speed);
+       cfsetispeed(&tios, speed);
+    } else {
+       speed = cfgetospeed(&tios);
+       /*
+        * We can't proceed if the serial port speed is 0,
+        * since that implies that the serial port is disabled.
+        */
+       if ((speed == B0) && !sync_serial)
+           fatal("Baud rate for %s is 0; need explicit baud rate", devnam);
+    }
+
+    if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &tios) < 0)
+       fatal("tcsetattr: %m");
+
+#ifndef CRTSCTS
+    if (!sync_serial && termiox_ok && ioctl (fd, TCSETXF, &tiox) < 0){
+       error("TCSETXF: %m");
+    }
+#endif
+
+    baud_rate = inspeed = baud_rate_of(speed);
+    if (!sync_serial)
+       restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+    int fd;
+{
+    if (restore_term) {
+       if (!default_device) {
+           /*
+            * Turn off echoing, because otherwise we can get into
+            * a loop with the tty and the modem echoing to each other.
+            * We presume we are the sole user of this tty device, so
+            * when we close it, it will revert to its defaults anyway.
+            */
+           inittermios.c_lflag &= ~(ECHO | ECHONL);
+       }
+       if (!sync_serial && tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+           if (!hungup && errno != ENXIO)
+               warn("tcsetattr: %m");
+#ifndef CRTSCTS
+       if (!sync_serial && ioctl (fd, TCSETXF, &inittermiox) < 0){
+           if (!hungup && errno != ENXIO)
+               error("TCSETXF: %m");
+       }
+#endif
+       if (!sync_serial)
+           ioctl(fd, TIOCSWINSZ, &wsinfo);
+       restore_term = 0;
+    }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+    int modembits = TIOCM_DTR;
+
+    ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+/*
+ * open_loopback - open the device we use for getting packets
+ * in demand mode.  Under Solaris 2, we use our existing fd
+ * to the ppp driver.
+ */
+int
+open_ppp_loopback()
+{
+    return pppfd;
+}
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    struct strbuf data;
+    int retries;
+    struct pollfd pfd;
+
+    if (debug)
+       dbglog("sent %P", p, len);
+
+    data.len = len;
+    data.buf = (caddr_t) p;
+    retries = 4;
+    while (putmsg(pppfd, NULL, &data, 0) < 0) {
+       if (--retries < 0 || (errno != EWOULDBLOCK && errno != EAGAIN)) {
+           if (errno != ENXIO)
+               error("Couldn't send packet: %m");
+           break;
+       }
+       pfd.fd = pppfd;
+       pfd.events = POLLOUT;
+       poll(&pfd, 1, 250);     /* wait for up to 0.25 seconds */
+    }
+}
+
+
+/*
+ * wait_input - wait until there is data available,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+    struct timeval *timo;
+{
+    int t;
+
+    t = timo == NULL? -1: timo->tv_sec * 1000 + timo->tv_usec / 1000;
+    if (poll(pollfds, n_pollfds, t) < 0 && errno != EINTR)
+       fatal("poll: %m");
+}
+
+/*
+ * add_fd - add an fd to the set that wait_input waits for.
+ */
+void add_fd(fd)
+    int fd;
+{
+    int n;
+
+    for (n = 0; n < n_pollfds; ++n)
+       if (pollfds[n].fd == fd)
+           return;
+    if (n_pollfds < MAX_POLLFDS) {
+       pollfds[n_pollfds].fd = fd;
+       pollfds[n_pollfds].events = POLLIN | POLLPRI | POLLHUP;
+       ++n_pollfds;
+    } else
+       error("Too many inputs!");
+}
+
+/*
+ * remove_fd - remove an fd from the set that wait_input waits for.
+ */
+void remove_fd(fd)
+    int fd;
+{
+    int n;
+
+    for (n = 0; n < n_pollfds; ++n) {
+       if (pollfds[n].fd == fd) {
+           while (++n < n_pollfds)
+               pollfds[n-1] = pollfds[n];
+           --n_pollfds;
+           break;
+       }
+    }
+}
+
+#if 0
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+    struct timeval *timo;
+{
+    wait_input(timo);
+}
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+    struct timeval *timo;
+{
+    int n;
+
+    n = select(0, NULL, NULL, NULL, timo);
+    if (n < 0 && errno != EINTR)
+       fatal("select: %m");
+}
+#endif
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+    u_char *buf;
+{
+    struct strbuf ctrl, data;
+    int flags, len;
+    unsigned char ctrlbuf[sizeof(union DL_primitives) + 64];
+
+    for (;;) {
+       data.maxlen = PPP_MRU + PPP_HDRLEN;
+       data.buf = (caddr_t) buf;
+       ctrl.maxlen = sizeof(ctrlbuf);
+       ctrl.buf = (caddr_t) ctrlbuf;
+       flags = 0;
+       len = getmsg(pppfd, &ctrl, &data, &flags);
+       if (len < 0) {
+           if (errno == EAGAIN || errno == EINTR)
+               return -1;
+           fatal("Error reading packet: %m");
+       }
+
+       if (ctrl.len <= 0)
+           return data.len;
+
+       /*
+        * Got a M_PROTO or M_PCPROTO message.  Interpret it
+        * as a DLPI primitive??
+        */
+       if (debug)
+           dbglog("got dlpi prim 0x%x, len=%d",
+                  ((union DL_primitives *)ctrlbuf)->dl_primitive, ctrl.len);
+
+    }
+}
+
+/*
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+    int len;
+    int rv = 0;
+
+    while ((len = read_packet(inpacket_buf)) > 0) {
+       if (loop_frame(inpacket_buf, len))
+           rv = 1;
+    }
+    return rv;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+    int unit, mtu;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+    struct ifreq ifr;
+#if defined(INET6) && defined(SOL2)
+    struct lifreq lifr;
+    int        fd;
+#endif /* defined(INET6) && defined(SOL2) */
+
+    link_mtu = mtu;
+    if (strioctl(pppfd, PPPIO_MTU, &mtu, sizeof(mtu), 0) < 0) {
+       if (hungup && errno == ENXIO)
+           return;
+       error("Couldn't set MTU: %m");
+    }
+    if (fdmuxid >= 0) {
+       if (!sync_serial) {
+           if (strioctl(pppfd, PPPIO_XACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+               error("Couldn't set transmit ACCM: %m");
+           }
+       }
+       cf[0] = (pcomp? COMP_PROT: 0) + (accomp? COMP_AC: 0);
+       cf[1] = COMP_PROT | COMP_AC;
+       if (any_compressions() &&
+           strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+           error("Couldn't set prot/AC compression: %m");
+       }
+    }
+
+    /* set the MTU for IP as well */
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+       error("Couldn't set IP MTU: %m");
+    }
+
+#if defined(INET6) && defined(SOL2) 
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+       error("Couldn't open IPv6 socket: %m");
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    lifr.lifr_mtu = link_mtu;
+    if (ioctl(fd, SIOCSLIFMTU, &lifr) < 0) {
+       close(fd);
+       error("Couldn't set IPv6 MTU: %m");
+    }
+    close(fd);
+#endif /* defined(INET6) && defined(SOL2) */
+}
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+    int unit;
+    ext_accm accm;
+{
+    if (sync_serial)
+       return;
+
+    if (fdmuxid >= 0
+       && strioctl(pppfd, PPPIO_XACCM, accm, sizeof(ext_accm), 0) < 0) {
+       if (!hungup || errno != ENXIO)
+           warn("Couldn't set extended ACCM: %m");
+    }
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+    int unit, mru;
+    u_int32_t asyncmap;
+    int pcomp, accomp;
+{
+    int cf[2];
+
+    link_mru = mru;
+    if (strioctl(pppfd, PPPIO_MRU, &mru, sizeof(mru), 0) < 0) {
+       if (hungup && errno == ENXIO)
+           return;
+       error("Couldn't set MRU: %m");
+    }
+    if (fdmuxid >= 0) {
+       if (!sync_serial) {
+           if (strioctl(pppfd, PPPIO_RACCM, &asyncmap, sizeof(asyncmap), 0) < 0) {
+               error("Couldn't set receive ACCM: %m");
+           }
+       }
+       cf[0] = (pcomp? DECOMP_PROT: 0) + (accomp? DECOMP_AC: 0);
+       cf[1] = DECOMP_PROT | DECOMP_AC;
+       if (any_compressions() &&
+           strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+           error("Couldn't set prot/AC decompression: %m");
+       }
+    }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+    int unit, opt_len, for_transmit;
+    u_char *opt_ptr;
+{
+    if (strioctl(pppfd, (for_transmit? PPPIO_XCOMP: PPPIO_RCOMP),
+                opt_ptr, opt_len, 0) >= 0)
+       return 1;
+    return (errno == ENOSR)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+    int unit, isopen, isup;
+{
+    int cf[2];
+
+    cf[0] = (isopen? CCP_ISOPEN: 0) + (isup? CCP_ISUP: 0);
+    cf[1] = CCP_ISOPEN | CCP_ISUP | CCP_ERROR | CCP_FATALERROR;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+       if (!hungup || errno != ENXIO)
+           error("Couldn't set kernel CCP state: %m");
+    }
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+    int u;
+    struct ppp_idle *ip;
+{
+    return strioctl(pppfd, PPPIO_GIDLE, ip, 0, sizeof(struct ppp_idle)) >= 0;
+}
+
+/*
+ * get_ppp_stats - return statistics for the link.
+ */
+int
+get_ppp_stats(u, stats)
+    int u;
+    struct pppd_stats *stats;
+{
+    struct ppp_stats s;
+
+    if (!sync_serial && 
+       strioctl(pppfd, PPPIO_GETSTAT, &s, 0, sizeof(s)) < 0) {
+       error("Couldn't get link statistics: %m");
+       return 0;
+    }
+    stats->bytes_in = s.p.ppp_ibytes;
+    stats->bytes_out = s.p.ppp_obytes;
+    return 1;
+}
+
+#if 0
+/*
+ * set_filters - transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+    struct bpf_program *pass, *active;
+{
+    int ret = 1;
+
+    if (pass->bf_len > 0) {
+       if (strioctl(pppfd, PPPIO_PASSFILT, pass,
+                    sizeof(struct bpf_program), 0) < 0) {
+           error("Couldn't set pass-filter in kernel: %m");
+           ret = 0;
+       }
+    }
+    if (active->bf_len > 0) {
+       if (strioctl(pppfd, PPPIO_ACTIVEFILT, active,
+                    sizeof(struct bpf_program), 0) < 0) {
+           error("Couldn't set active-filter in kernel: %m");
+           ret = 0;
+       }
+    }
+    return ret;
+}
+#endif
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise.  This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+    int unit;
+{
+    int cf[2];
+
+    cf[0] = cf[1] = 0;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+       if (errno != ENXIO && errno != EINVAL)
+           error("Couldn't get compression flags: %m");
+       return 0;
+    }
+    return cf[0] & CCP_FATALERROR;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, xcidcomp, xmaxcid)
+    int u, vjcomp, xcidcomp, xmaxcid;
+{
+    int cf[2];
+    char maxcid[2];
+
+    if (vjcomp) {
+       maxcid[0] = xcidcomp;
+       maxcid[1] = 15;         /* XXX should be rmaxcid */
+       if (strioctl(pppfd, PPPIO_VJINIT, maxcid, sizeof(maxcid), 0) < 0) {
+           error("Couldn't initialize VJ compression: %m");
+       }
+    }
+
+    cf[0] = (vjcomp? COMP_VJC + DECOMP_VJC: 0) /* XXX this is wrong */
+       + (xcidcomp? COMP_VJCCID + DECOMP_VJCCID: 0);
+    cf[1] = COMP_VJC + DECOMP_VJC + COMP_VJCCID + DECOMP_VJCCID;
+    if (strioctl(pppfd, PPPIO_CFLAGS, cf, sizeof(cf), sizeof(int)) < 0) {
+       if (vjcomp)
+           error("Couldn't enable VJ compression: %m");
+    }
+
+    return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+       error("Couldn't mark interface up (get): %m");
+       return 0;
+    }
+    ifr.ifr_flags |= IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+       error("Couldn't mark interface up (set): %m");
+       return 0;
+    }
+    if_is_up = 1;
+    return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+    int u;
+{
+    struct ifreq ifr;
+
+    if (ipmuxid < 0)
+       return 1;
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) < 0) {
+       error("Couldn't mark interface down (get): %m");
+       return 0;
+    }
+    ifr.ifr_flags &= ~IFF_UP;
+    if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+       error("Couldn't mark interface down (set): %m");
+       return 0;
+    }
+    if_is_up = 0;
+    return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+    int u;
+    int proto;
+    enum NPmode mode;
+{
+    int npi[2];
+
+    npi[0] = proto;
+    npi[1] = (int) mode;
+    if (strioctl(pppfd, PPPIO_NPMODE, &npi, 2 * sizeof(int), 0) < 0) {
+       error("ioctl(set NP %d mode to %d): %m", proto, mode);
+       return 0;
+    }
+    return 1;
+}
+
+#if defined(SOL2) && defined(INET6)
+/*
+ * sif6up - Config the IPv6 interface up and enable IPv6 packets to pass.
+ */
+int
+sif6up(u)
+    int u;
+{
+    struct lifreq lifr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0) {
+       return 0;
+    }
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    lifr.lifr_flags |= IFF_UP;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCSLIFFLAGS, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    if6_is_up = 1;
+    close(fd);
+    return 1;
+}
+
+/*
+ * sifdown - Config the IPv6 interface down and disable IPv6.
+ */
+int
+sif6down(u)
+    int u;
+{
+    struct lifreq lifr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+       return 0;
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    lifr.lifr_flags &= ~IFF_UP;
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    if (ioctl(fd, SIOCGLIFFLAGS, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    if6_is_up = 0;
+    close(fd);
+    return 1;
+}
+
+/*
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int
+sif6addr(u, o, h)
+    int u;
+    eui64_t o, h;
+{
+    struct lifreq lifr;
+    struct sockaddr_storage laddr;
+    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&laddr;
+    int fd;
+
+    fd = socket(AF_INET6, SOCK_DGRAM, 0);
+    if (fd < 0)
+       return 0;
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+
+    /*
+     * Do this because /dev/ppp responds to DL_PHYS_ADDR_REQ with
+     * zero values, hence the interface token came to be zero too,
+     * and without this, in.ndpd will complain
+     */
+    IN6_LLTOKEN_FROM_EUI64(lifr, sin6, o);
+    if (ioctl(fd, SIOCSLIFTOKEN, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    /*
+     * Set the interface address and destination address
+     */
+    IN6_LLADDR_FROM_EUI64(lifr, sin6, o);
+    if (ioctl(fd, SIOCSLIFADDR, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    memset(&lifr, 0, sizeof(lifr));
+    strlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
+    IN6_LLADDR_FROM_EUI64(lifr, sin6, h);
+    if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) < 0) {
+       close(fd);
+       return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * cif6addr - Remove the IPv6 address from interface
+ */
+int
+cif6addr(u, o, h)
+    int u;
+    eui64_t o, h;
+{
+    return 1;
+}
+
+#endif /* defined(SOL2) && defined(INET6) */
+
+
+#define INET_ADDR(x)   (((struct sockaddr_in *) &(x))->sin_addr.s_addr)
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+    int u;
+    u_int32_t o, h, m;
+{
+    struct ifreq ifr;
+    int ret = 1;
+
+    memset(&ifr, 0, sizeof(ifr));
+    strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = m;
+    if (ioctl(ipfd, SIOCSIFNETMASK, &ifr) < 0) {
+       error("Couldn't set IP netmask: %m");
+       ret = 0;
+    }
+    ifr.ifr_addr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_addr) = o;
+    if (ioctl(ipfd, SIOCSIFADDR, &ifr) < 0) {
+       error("Couldn't set local IP address: %m");
+       ret = 0;
+    }
+
+    /*
+     * On some systems, we have to explicitly set the point-to-point
+     * flag bit before we can set a destination address.
+     */
+    if (ioctl(ipfd, SIOCGIFFLAGS, &ifr) >= 0
+       && (ifr.ifr_flags & IFF_POINTOPOINT) == 0) {
+       ifr.ifr_flags |= IFF_POINTOPOINT;
+       if (ioctl(ipfd, SIOCSIFFLAGS, &ifr) < 0) {
+           error("Couldn't mark interface pt-to-pt: %m");
+           ret = 0;
+       }
+    }
+    ifr.ifr_dstaddr.sa_family = AF_INET;
+    INET_ADDR(ifr.ifr_dstaddr) = h;
+    if (ioctl(ipfd, SIOCSIFDSTADDR, &ifr) < 0) {
+       error("Couldn't set remote IP address: %m");
+       ret = 0;
+    }
+#if 0  /* now done in ppp_send_config */
+    ifr.ifr_metric = link_mtu;
+    if (ioctl(ipfd, SIOCSIFMTU, &ifr) < 0) {
+       error("Couldn't set IP MTU: %m");
+    }
+#endif
+
+    remote_addr = h;
+    return ret;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+    int u;
+    u_int32_t o, h;
+{
+#if defined(__USLC__)          /* was: #if 0 */
+    cifroute(unit, ouraddr, hisaddr);
+    if (ipmuxid >= 0) {
+       notice("Removing ppp interface unit");
+       if (ioctl(ipfd, I_UNLINK, ipmuxid) < 0) {
+           error("Can't remove ppp interface unit: %m");
+           return 0;
+       }
+       ipmuxid = -1;
+    }
+#endif
+    remote_addr = 0;
+    return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;                     /* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCADDRT, &rt) < 0) {
+       error("Can't add default route: %m");
+       return 0;
+    }
+
+    default_route_gateway = g;
+    return 1;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+    int u;
+    u_int32_t l, g;
+{
+    struct rtentry rt;
+
+#if defined(__USLC__)
+    g = l;                     /* use the local address as gateway */
+#endif
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = 0;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = g;
+    rt.rt_flags = RTF_GATEWAY;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+       error("Can't delete default route: %m");
+       return 0;
+    }
+
+    default_route_gateway = 0;
+    return 1;
+}
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    if (!get_ether_addr(hisaddr, &arpreq.arp_ha))
+       return 0;
+
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+    if (ioctl(ipfd, SIOCSARP, (caddr_t) &arpreq) < 0) {
+       error("Couldn't set proxy ARP entry: %m");
+       return 0;
+    }
+
+    proxy_arp_addr = hisaddr;
+    return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+    int unit;
+    u_int32_t hisaddr;
+{
+    struct arpreq arpreq;
+
+    memset(&arpreq, 0, sizeof(arpreq));
+    arpreq.arp_pa.sa_family = AF_INET;
+    INET_ADDR(arpreq.arp_pa) = hisaddr;
+    if (ioctl(ipfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+       error("Couldn't delete proxy ARP entry: %m");
+       return 0;
+    }
+
+    proxy_arp_addr = 0;
+    return 1;
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS                32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+    u_int32_t ipaddr;
+    struct sockaddr *hwaddr;
+{
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+    u_int32_t ina, mask;
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+       nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+       return 0;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+       warn("Couldn't get system interface list: %m");
+       free(ifc.ifc_buf);
+       return 0;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+       if (ifr->ifr_addr.sa_family != AF_INET)
+           continue;
+       /*
+        * Check that the interface is up, and not point-to-point or loopback.
+        */
+       strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+       if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+           continue;
+       if ((ifreq.ifr_flags &
+            (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+           != (IFF_UP|IFF_BROADCAST))
+           continue;
+       /*
+        * Get its netmask and check that it's on the right subnet.
+        */
+       if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+           continue;
+       ina = INET_ADDR(ifr->ifr_addr);
+       mask = INET_ADDR(ifreq.ifr_addr);
+       if ((ipaddr & mask) == (ina & mask))
+           break;
+    }
+
+    if (ifr >= ifend) {
+       warn("No suitable interface found for proxy ARP");
+       free(ifc.ifc_buf);
+       return 0;
+    }
+
+    info("found interface %s for proxy ARP", ifr->ifr_name);
+    if (!get_hw_addr(ifr->ifr_name, ina, hwaddr)) {
+       error("Couldn't get hardware address for %s", ifr->ifr_name);
+       free(ifc.ifc_buf);
+       return 0;
+    }
+
+    free(ifc.ifc_buf);
+    return 1;
+}
+
+/*
+ * get_hw_addr_dlpi - obtain the hardware address using DLPI
+ */
+static int
+get_hw_addr_dlpi(name, hwaddr)
+    char *name;
+    struct sockaddr *hwaddr;
+{
+    char *p, *q;
+    int unit, iffd, adrlen;
+    unsigned char *adrp;
+    char ifdev[24];
+    struct {
+       union DL_primitives prim;
+       char space[64];
+    } reply;
+
+    /*
+     * We have to open the device and ask it for its hardware address.
+     * First split apart the device name and unit.
+     */
+    slprintf(ifdev, sizeof(ifdev), "/dev/%s", name);
+    for (q = ifdev + strlen(ifdev); --q >= ifdev; )
+       if (!isdigit(*q))
+           break;
+    unit = atoi(q+1);
+    q[1] = 0;
+
+    /*
+     * Open the device and do a DLPI attach and phys_addr_req.
+     */
+    iffd = open(ifdev, O_RDWR);
+    if (iffd < 0) {
+       error("Can't open %s: %m", ifdev);
+       return 0;
+    }
+    if (dlpi_attach(iffd, unit) < 0
+       || dlpi_get_reply(iffd, &reply.prim, DL_OK_ACK, sizeof(reply)) < 0
+       || dlpi_info_req(iffd) < 0
+       || dlpi_get_reply(iffd, &reply.prim, DL_INFO_ACK, sizeof(reply)) < 0) {
+       close(iffd);
+       return 0;
+    }
+
+    adrlen = reply.prim.info_ack.dl_addr_length;
+    adrp = (unsigned char *)&reply + reply.prim.info_ack.dl_addr_offset;
+
+#if DL_CURRENT_VERSION >= 2
+    if (reply.prim.info_ack.dl_sap_length < 0)
+       adrlen += reply.prim.info_ack.dl_sap_length;
+    else
+       adrp += reply.prim.info_ack.dl_sap_length;
+#endif
+
+    hwaddr->sa_family = AF_UNSPEC;
+    memcpy(hwaddr->sa_data, adrp, adrlen);
+
+    return 1;
+}
+/*
+ * get_hw_addr - obtain the hardware address for a named interface.
+ */
+static int
+get_hw_addr(name, ina, hwaddr)
+    char *name;
+    u_int32_t ina;
+    struct sockaddr *hwaddr;
+{
+    /* New way - get the address by doing an arp request. */
+    int s;
+    struct arpreq req;
+
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (s < 0)
+       return 0;
+    memset(&req, 0, sizeof(req));
+    req.arp_pa.sa_family = AF_INET;
+    INET_ADDR(req.arp_pa) = ina;
+    if (ioctl(s, SIOCGARP, &req) < 0) {
+       error("Couldn't get ARP entry for %s: %m", ip_ntoa(ina));
+       return 0;
+    }
+    *hwaddr = req.arp_ha;
+    hwaddr->sa_family = AF_UNSPEC;
+
+    return 1;
+}
+
+static int
+dlpi_attach(fd, ppa)
+    int fd, ppa;
+{
+    dl_attach_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_ATTACH_REQ;
+    req.dl_ppa = ppa;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_info_req(fd)
+    int fd;
+{
+    dl_info_req_t req;
+    struct strbuf buf;
+
+    req.dl_primitive = DL_INFO_REQ;
+    buf.len = sizeof(req);
+    buf.buf = (void *) &req;
+    return putmsg(fd, &buf, NULL, RS_HIPRI);
+}
+
+static int
+dlpi_get_reply(fd, reply, expected_prim, maxlen)
+    union DL_primitives *reply;
+    int fd, expected_prim, maxlen;
+{
+    struct strbuf buf;
+    int flags, n;
+    struct pollfd pfd;
+
+    /*
+     * Use poll to wait for a message with a timeout.
+     */
+    pfd.fd = fd;
+    pfd.events = POLLIN | POLLPRI;
+    do {
+       n = poll(&pfd, 1, 1000);
+    } while (n == -1 && errno == EINTR);
+    if (n <= 0)
+       return -1;
+
+    /*
+     * Get the reply.
+     */
+    buf.maxlen = maxlen;
+    buf.buf = (void *) reply;
+    flags = 0;
+    if (getmsg(fd, &buf, NULL, &flags) < 0)
+       return -1;
+
+    if (buf.len < sizeof(ulong)) {
+       if (debug)
+           dbglog("dlpi response short (len=%d)\n", buf.len);
+       return -1;
+    }
+
+    if (reply->dl_primitive == expected_prim)
+       return 0;
+
+    if (debug) {
+       if (reply->dl_primitive == DL_ERROR_ACK) {
+           dbglog("dlpi error %d (unix errno %d) for prim %x\n",
+                  reply->error_ack.dl_errno, reply->error_ack.dl_unix_errno,
+                  reply->error_ack.dl_error_primitive);
+       } else {
+           dbglog("dlpi unexpected response prim %x\n",
+                  reply->dl_primitive);
+       }
+    }
+
+    return -1;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'.  If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+    u_int32_t addr;
+{
+    u_int32_t mask, nmask, ina;
+    struct ifreq *ifr, *ifend, ifreq;
+    int nif;
+    struct ifconf ifc;
+
+    addr = ntohl(addr);
+    if (IN_CLASSA(addr))       /* determine network mask for address class */
+       nmask = IN_CLASSA_NET;
+    else if (IN_CLASSB(addr))
+       nmask = IN_CLASSB_NET;
+    else
+       nmask = IN_CLASSC_NET;
+    /* class D nets are disallowed by bad_ip_adrs */
+    mask = netmask | htonl(nmask);
+
+    /*
+     * Scan through the system's network interfaces.
+     */
+#ifdef SIOCGIFNUM
+    if (ioctl(ipfd, SIOCGIFNUM, &nif) < 0)
+#endif
+       nif = MAX_IFS;
+    ifc.ifc_len = nif * sizeof(struct ifreq);
+    ifc.ifc_buf = (caddr_t) malloc(ifc.ifc_len);
+    if (ifc.ifc_buf == 0)
+       return mask;
+    if (ioctl(ipfd, SIOCGIFCONF, &ifc) < 0) {
+       warn("Couldn't get system interface list: %m");
+       free(ifc.ifc_buf);
+       return mask;
+    }
+    ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+    for (ifr = ifc.ifc_req; ifr < ifend; ++ifr) {
+       /*
+        * Check the interface's internet address.
+        */
+       if (ifr->ifr_addr.sa_family != AF_INET)
+           continue;
+       ina = INET_ADDR(ifr->ifr_addr);
+       if ((ntohl(ina) & nmask) != (addr & nmask))
+           continue;
+       /*
+        * Check that the interface is up, and not point-to-point or loopback.
+        */
+       strlcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+       if (ioctl(ipfd, SIOCGIFFLAGS, &ifreq) < 0)
+           continue;
+       if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+           != IFF_UP)
+           continue;
+       /*
+        * Get its netmask and OR it into our mask.
+        */
+       if (ioctl(ipfd, SIOCGIFNETMASK, &ifreq) < 0)
+           continue;
+       mask |= INET_ADDR(ifreq.ifr_addr);
+    }
+
+    free(ifc.ifc_buf);
+    return mask;
+}
+
+/*
+ * logwtmp - write an accounting record to the /var/adm/wtmp file.
+ */
+void
+logwtmp(line, name, host)
+    const char *line, *name, *host;
+{
+    static struct utmpx utmpx;
+
+    if (name[0] != 0) {
+       /* logging in */
+       strncpy(utmpx.ut_user, name, sizeof(utmpx.ut_user));
+       strncpy(utmpx.ut_id, ifname, sizeof(utmpx.ut_id));
+       strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
+       utmpx.ut_pid = getpid();
+       utmpx.ut_type = USER_PROCESS;
+    } else {
+       utmpx.ut_type = DEAD_PROCESS;
+    }
+    gettimeofday(&utmpx.ut_tv, NULL);
+    updwtmpx("/var/adm/wtmpx", &utmpx);
+}
+
+/*
+ * get_host_seed - return the serial number of this machine.
+ */
+int
+get_host_seed()
+{
+    char buf[32];
+
+    if (sysinfo(SI_HW_SERIAL, buf, sizeof(buf)) < 0) {
+       error("sysinfo: %m");
+       return 0;
+    }
+    return (int) strtoul(buf, NULL, 16);
+}
+
+static int
+strioctl(fd, cmd, ptr, ilen, olen)
+    int fd, cmd, ilen, olen;
+    void *ptr;
+{
+    struct strioctl str;
+
+    str.ic_cmd = cmd;
+    str.ic_timout = 0;
+    str.ic_len = ilen;
+    str.ic_dp = ptr;
+    if (ioctl(fd, I_STR, &str) == -1)
+       return -1;
+    if (str.ic_len != olen)
+       dbglog("strioctl: expected %d bytes, got %d for cmd %x\n",
+              olen, str.ic_len, cmd);
+    return 0;
+}
+
+#if 0
+/*
+ * lock - create a lock file for the named lock device
+ */
+
+#define LOCK_PREFIX    "/var/spool/locks/LK."
+static char lock_file[40];     /* name of lock file created */
+
+int
+lock(dev)
+    char *dev;
+{
+    int n, fd, pid;
+    struct stat sbuf;
+    char ascii_pid[12];
+
+    if (stat(dev, &sbuf) < 0) {
+       error("Can't get device number for %s: %m", dev);
+       return -1;
+    }
+    if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+       error("Can't lock %s: not a character device", dev);
+       return -1;
+    }
+    slprintf(lock_file, sizeof(lock_file), "%s%03d.%03d.%03d",
+            LOCK_PREFIX, major(sbuf.st_dev),
+            major(sbuf.st_rdev), minor(sbuf.st_rdev));
+
+    while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+       if (errno == EEXIST
+           && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+           /* Read the lock file to find out who has the device locked */
+           n = read(fd, ascii_pid, 11);
+           if (n <= 0) {
+               error("Can't read pid from lock file %s", lock_file);
+               close(fd);
+           } else {
+               ascii_pid[n] = 0;
+               pid = atoi(ascii_pid);
+               if (pid > 0 && kill(pid, 0) == -1 && errno == ESRCH) {
+                   /* pid no longer exists - remove the lock file */
+                   if (unlink(lock_file) == 0) {
+                       close(fd);
+                       notice("Removed stale lock on %s (pid %d)",
+                              dev, pid);
+                       continue;
+                   } else
+                       warn("Couldn't remove stale lock on %s",
+                              dev);
+               } else
+                   notice("Device %s is locked by pid %d",
+                          dev, pid);
+           }
+           close(fd);
+       } else
+           error("Can't create lock file %s: %m", lock_file);
+       lock_file[0] = 0;
+       return -1;
+    }
+
+    slprintf(ascii_pid, sizeof(ascii_pid), "%10d\n", getpid());
+    write(fd, ascii_pid, 11);
+
+    close(fd);
+    return 1;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+    if (lock_file[0]) {
+       unlink(lock_file);
+       lock_file[0] = 0;
+    }
+}
+#endif
+
+/*
+ * cifroute - delete a route through the addresses given.
+ */
+int
+cifroute(u, our, his)
+    int u;
+    u_int32_t our, his;
+{
+    struct rtentry rt;
+
+    memset(&rt, 0, sizeof(rt));
+    rt.rt_dst.sa_family = AF_INET;
+    INET_ADDR(rt.rt_dst) = his;
+    rt.rt_gateway.sa_family = AF_INET;
+    INET_ADDR(rt.rt_gateway) = our;
+    rt.rt_flags = RTF_HOST;
+
+    if (ioctl(ipfd, SIOCDELRT, &rt) < 0) {
+       error("Can't delete route: %m");
+       return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * have_route_to - determine if the system has a route to the specified
+ * IP address.  Returns 0 if not, 1 if so, -1 if we can't tell.
+ * `addr' is in network byte order.
+ * For demand mode to work properly, we have to ignore routes
+ * through our own interface.
+ */
+#ifndef T_CURRENT              /* needed for Solaris 2.5 */
+#define T_CURRENT      MI_T_CURRENT
+#endif
+
+int
+have_route_to(addr)
+    u_int32_t addr;
+{
+#ifdef SOL2
+    int fd, r, flags, i;
+    struct {
+       struct T_optmgmt_req req;
+       struct opthdr hdr;
+    } req;
+    union {
+       struct T_optmgmt_ack ack;
+       unsigned char space[64];
+    } ack;
+    struct opthdr *rh;
+    struct strbuf cbuf, dbuf;
+    int nroutes;
+    mib2_ipRouteEntry_t routes[8];
+    mib2_ipRouteEntry_t *rp;
+
+    fd = open(mux_dev_name, O_RDWR);
+    if (fd < 0) {
+       warn("have_route_to: couldn't open %s: %m", mux_dev_name);
+       return -1;
+    }
+
+    req.req.PRIM_type = T_OPTMGMT_REQ;
+    req.req.OPT_offset = (char *) &req.hdr - (char *) &req;
+    req.req.OPT_length = sizeof(req.hdr);
+    req.req.MGMT_flags = T_CURRENT;
+
+    req.hdr.level = MIB2_IP;
+    req.hdr.name = 0;
+    req.hdr.len = 0;
+
+    cbuf.buf = (char *) &req;
+    cbuf.len = sizeof(req);
+
+    if (putmsg(fd, &cbuf, NULL, 0) == -1) {
+       warn("have_route_to: putmsg: %m");
+       close(fd);
+       return -1;
+    }
+
+    for (;;) {
+       cbuf.buf = (char *) &ack;
+       cbuf.maxlen = sizeof(ack);
+       dbuf.buf = (char *) routes;
+       dbuf.maxlen = sizeof(routes);
+       flags = 0;
+       r = getmsg(fd, &cbuf, &dbuf, &flags);
+       if (r == -1) {
+           warn("have_route_to: getmsg: %m");
+           close(fd);
+           return -1;
+       }
+
+       if (cbuf.len < sizeof(struct T_optmgmt_ack)
+           || ack.ack.PRIM_type != T_OPTMGMT_ACK
+           || ack.ack.MGMT_flags != T_SUCCESS
+           || ack.ack.OPT_length < sizeof(struct opthdr)) {
+           dbglog("have_route_to: bad message len=%d prim=%d",
+                  cbuf.len, ack.ack.PRIM_type);
+           close(fd);
+           return -1;
+       }
+
+       rh = (struct opthdr *) ((char *)&ack + ack.ack.OPT_offset);
+       if (rh->level == 0 && rh->name == 0)
+           break;
+       if (rh->level != MIB2_IP || rh->name != MIB2_IP_21) {
+           while (r == MOREDATA)
+               r = getmsg(fd, NULL, &dbuf, &flags);
+           continue;
+       }
+
+       for (;;) {
+           nroutes = dbuf.len / sizeof(mib2_ipRouteEntry_t);
+           for (rp = routes, i = 0; i < nroutes; ++i, ++rp) {
+               if (rp->ipRouteMask != ~0) {
+                   dbglog("have_route_to: dest=%x gw=%x mask=%x\n",
+                          rp->ipRouteDest, rp->ipRouteNextHop,
+                          rp->ipRouteMask);
+                   if (((addr ^ rp->ipRouteDest) & rp->ipRouteMask) == 0
+                       && rp->ipRouteNextHop != remote_addr)
+                       return 1;
+               }
+           }
+           if (r == 0)
+               break;
+           r = getmsg(fd, NULL, &dbuf, &flags);
+       }
+    }
+    close(fd);
+    return 0;
+#else
+    return -1;
+#endif /* SOL2 */
+}
+
+/*
+ * get_pty - get a pty master/slave pair and chown the slave side to
+ * the uid given.  Assumes slave_name points to MAXPATHLEN bytes of space.
+ */
+int
+get_pty(master_fdp, slave_fdp, slave_name, uid)
+    int *master_fdp;
+    int *slave_fdp;
+    char *slave_name;
+    int uid;
+{
+    int mfd, sfd;
+    char *pty_name;
+    struct termios tios;
+
+    mfd = open("/dev/ptmx", O_RDWR);
+    if (mfd < 0) {
+       error("Couldn't open pty master: %m");
+       return 0;
+    }
+
+    pty_name = ptsname(mfd);
+    if (pty_name == NULL) {
+       error("Couldn't get name of pty slave");
+       close(mfd);
+       return 0;
+    }
+    if (chown(pty_name, uid, -1) < 0)
+       warn("Couldn't change owner of pty slave: %m");
+    if (chmod(pty_name, S_IRUSR | S_IWUSR) < 0)
+       warn("Couldn't change permissions on pty slave: %m");
+    if (unlockpt(mfd) < 0)
+       warn("Couldn't unlock pty slave: %m");
+
+    sfd = open(pty_name, O_RDWR);
+    if (sfd < 0) {
+       error("Couldn't open pty slave %s: %m", pty_name);
+       close(mfd);
+       return 0;
+    }
+    if (ioctl(sfd, I_PUSH, "ptem") < 0)
+       warn("Couldn't push ptem module on pty slave: %m");
+
+    dbglog("Using %s", pty_name);
+    strlcpy(slave_name, pty_name, MAXPATHLEN);
+    *master_fdp = mfd;
+    *slave_fdp = sfd;
+
+    return 1;
+}
index 7b77606028e2f2d47369d536708e1e61a9ffce60..5549bebdc827a7ae6ab9c7a1ebdeedd8e93e2933 100644 (file)
@@ -1,9 +1,9 @@
 #
 # pppdump Makefile for SVR4 systems
-# $Id: Makefile.sol2,v 1.2 1999/09/22 23:57:57 masputra Exp $
+# $Id: Makefile.sol2,v 1.3 2000/04/18 23:51:27 masputra Exp $
 #
 
-include ../svr4/Makedefs
+include ../solaris/Makedefs
 
 CFLAGS= $(COPTS) -I../include/net
 OBJS = pppdump.o bsd-comp.o deflate.o zlib.o
index be9a6d978434e9ece24efa3a87d5fc24edb0ca11..ace1a24e35cf5231df1c69415f5a74c3f61eb7b2 100644 (file)
@@ -1,9 +1,9 @@
 #
 # pppstats Makefile for SVR4 systems
-# $Id: Makefile.sol2,v 1.8 1998/03/25 02:21:24 paulus Exp $
+# $Id: Makefile.sol2,v 1.9 2000/04/18 23:51:27 masputra Exp $
 #
 
-include ../svr4/Makedefs
+include ../solaris/Makedefs
 
 CFLAGS = -DSTREAMS -I../include $(COPTS)
 
diff --git a/solaris/Makedefs b/solaris/Makedefs
new file mode 100644 (file)
index 0000000..81db8ab
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# defines common to several Makefiles
+#
+
+INSTALL= /usr/sbin/install
+
+BINDIR = /usr/local/bin
+MANDIR = /usr/local/man
+ETCDIR = /etc/ppp
+
+COPTS = -O -Xa
+
+# For compiling with gcc, comment out the COPTS definition above and
+# uncomment the next 2 definitions.
+#CC = gcc
+#COPTS = -O2
diff --git a/solaris/Makedefs.sol2 b/solaris/Makedefs.sol2
new file mode 100644 (file)
index 0000000..ee57da0
--- /dev/null
@@ -0,0 +1,59 @@
+#
+# Generic make definitions for Solaris 2
+#
+# $Id: Makedefs.sol2,v 1.1 2000/04/18 23:51:27 masputra Exp $
+#
+
+include ../solaris/Makedefs
+
+CPPFLAGS       = -D_KERNEL -DSVR4 -DSOL2 -DPRIOQ -DDEBUG -I../include
+CFLAGS         = $(CPPFLAGS) $(COPTS)
+
+# lint-specific variables
+LINT            = lint
+LINT_OPT_32     =
+LINT_OPT_64     = -Xarch=v9 -errchk=longptr64
+
+LINT_32        =
+LINT_32        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINT_32        += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
+LINT_32        += -erroff=E_SUSPICIOUS_COMPARISON
+LINT_32        += -erroff=E_CAST_UINT_TO_SIGNED_INT
+LINT_32        += -erroff=E_PASS_UINT_TO_SIGNED_INT
+LINT_32        += -erroff=E_INVALID_ANNOTATION_NAME
+LINT_32        += -erroff=E_FUNC_ARG_UNUSED
+# This might be needed, but zlib.c and vjcompress.c will squawk 
+# when not ignored
+LINT_32                += -erroff=E_CASE_FALLTHRU
+LINT_32                += -erroff=E_RET_INT_IMPLICITLY
+LINT_32                += -erroff=E_FUNC_NO_RET_VAL
+# Some STREAMS macros will be noisy too when this isn't ignored
+LINT_32                += -erroff=E_CONSTANT_CONDITION
+LINT_32                += -erroff=E_CONST_EXPR
+
+# Extra noise suppressant for 64-bit
+EXTRA_OFF      =
+EXTRA_OFF      += -erroff=E_CAST_INT_TO_SMALL_INT
+EXTRA_OFF      += -erroff=E_CAST_INT_CONST_TO_SMALL_INT
+EXTRA_OFF      += -erroff=E_CAST_TO_PTR_FROM_INT
+EXTRA_OFF      += -erroff=E_ASSIGN_INT_TO_SMALL_INT
+EXTRA_OFF      += -erroff=E_ASSIGN_INT_FROM_BIG_CONST
+EXTRA_OFF      += -erroff=E_CONST_PROMOTED_UNSIGNED_LL
+EXTRA_OFF      += -erroff=E_CONST_PROMOTED_LONG_LONG
+EXTRA_OFF      += -erroff=E_CONST_TRUNCATED_BY_ASSIGN
+EXTRA_OFF      += -erroff=E_PASS_INT_FROM_BIG_CONST
+EXTRA_OFF      += -erroff=E_COMP_INT_WITH_LARGE_INT
+EXTRA_OFF      += -erroff=E_ASSIGN_UINT_TO_SIGNED_INT
+EXTRA_OFF      += -erroff=E_ASSIGN_NARROW_CONV
+EXTRA_OFF      += -erroff=E_PASS_INT_TO_SMALL_INT
+EXTRA_OFF      += -erroff=E_PTR_CONV_LOSES_BITS
+
+LINT_64        = $(LINT_32)
+LINT_64        += $(EXTRA_OFF)
+
+LINTFLAGS64     = -Xa -nsxmuF -errtags=yes $(LINT_OPT_64) $(LINT_64)
+LINT64          = $(LINT) -c $(LINTFLAGS64) $(CPPFLAGS)
+
+LINTFLAGS32     = -Xa -nsxmuF -errtags=yes $(LINT_OPT_32) $(LINT_32)
+LINT32          = $(LINT) -c $(LINTFLAGS32) $(CPPFLAGS)
+
diff --git a/solaris/Makefile.sol2 b/solaris/Makefile.sol2
new file mode 100644 (file)
index 0000000..b5d8189
--- /dev/null
@@ -0,0 +1,66 @@
+#
+# Makefile for STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2,v 1.1 2000/04/18 23:51:27 masputra Exp $
+#
+
+include Makedefs.sol2
+
+COPTS += -xO2 -xspace -W0,-Lt
+
+COMP_OBJS = ppp_comp.o bsd-comp.o deflate.o zlib.o vjcompress.o \
+       ppp_comp_mod.o
+
+all:   ppp ppp_ahdl ppp_comp
+
+ppp:   ppp.o ppp_mod.o
+       ld -r -o $@ ppp.o ppp_mod.o
+       chmod +x $@
+
+ppp_ahdl: ppp_ahdlc.o ppp_ahdlc_mod.o
+       ld -r -o $@ ppp_ahdlc.o ppp_ahdlc_mod.o
+       chmod +x $@
+
+ppp_comp: $(COMP_OBJS)
+       ld -r -o $@ $(COMP_OBJS)
+       chmod +x $@
+
+bsd-comp.o:    ../modules/bsd-comp.c
+       $(CC) $(CFLAGS) -c $?
+deflate.o:     ../modules/deflate.c
+       $(CC) $(CFLAGS) -c $?
+ppp.o: ppp.c
+       $(CC) $(CFLAGS) -c $?
+ppp_mod.o:     ppp_mod.c
+       $(CC) $(CFLAGS) -c $?
+ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+       $(CC) $(CFLAGS) -c $?
+ppp_ahdlc.o: ppp_ahdlc.c
+       $(CC) $(CFLAGS) -c $?
+ppp_comp.o: ppp_comp.c
+       $(CC) $(CFLAGS) -c $?
+ppp_comp_mod.o:        ppp_comp_mod.c
+       $(CC) $(CFLAGS) -c $?
+vjcompress.o:  ../modules/vjcompress.c
+       $(CC) $(CFLAGS) -c $?
+zlib.o:        ../common/zlib.c
+       $(CC) $(CFLAGS) -c $?
+
+install:
+       cp ppp ppp.conf /kernel/drv
+       cp ppp_comp ppp_ahdl /kernel/strmod
+       if grep clone:ppp /etc/minor_perm; then :; else \
+         echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+       /usr/sbin/rem_drv ppp 2>/dev/null || true
+       /usr/sbin/add_drv ppp
+
+SRCS   = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+       ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+       ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+       $(LINT32) $(SRCS)
+
+clean:
+       rm -f ppp ppp_comp ppp_ahdl *.o *~ core
+       rm -f *.ln
diff --git a/solaris/Makefile.sol2-64 b/solaris/Makefile.sol2-64
new file mode 100644 (file)
index 0000000..b57f885
--- /dev/null
@@ -0,0 +1,85 @@
+#
+# Makefile for 64-bit STREAMS modules for Solaris 2.
+#
+# $Id: Makefile.sol2-64,v 1.1 2000/04/18 23:51:27 masputra Exp $
+#
+
+include Makedefs.sol2
+
+# Sun's cc flag for LP64 compilation / linkage
+COPTS          += -xchip=ultra -xarch=v9 -Wc,-xcode=abs32 -Wc,-Qiselect-regsym=0 -xO3 -xspace -W0,-Lt
+
+# subdirectory where 64-bit objects / binaries will be placed
+LP64DIR                = sparcv9
+
+# Name of legacy Makefile (for 32-bit binaries)
+STD_MAKE       = Makefile.sol2
+
+COMP_OBJS      = $(LP64DIR)/ppp_comp.o $(LP64DIR)/bsd-comp.o \
+               $(LP64DIR)/deflate.o $(LP64DIR)/zlib.o $(LP64DIR)/vjcompress.o \
+               $(LP64DIR)/ppp_comp_mod.o
+
+all:   std_objs $(LP64DIR) ppp ppp_ahdl ppp_comp
+
+std_objs:
+       $(MAKE) -f $(STD_MAKE) all
+
+ppp:   $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+       ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp.o $(LP64DIR)/ppp_mod.o
+       chmod +x $(LP64DIR)/$@
+
+ppp_ahdl: $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+       ld -r -o $(LP64DIR)/$@ $(LP64DIR)/ppp_ahdlc.o $(LP64DIR)/ppp_ahdlc_mod.o
+       chmod +x $(LP64DIR)/$@
+
+ppp_comp: $(COMP_OBJS)
+       ld -r -o $(LP64DIR)/$@ $(COMP_OBJS)
+       chmod +x $(LP64DIR)/$@
+
+$(LP64DIR)/bsd-comp.o: ../modules/bsd-comp.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/deflate.o: ../modules/deflate.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp.o:      ppp.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_mod.o:  ppp_mod.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc_mod.o: ppp_ahdlc_mod.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_ahdlc.o: ppp_ahdlc.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp.o: ppp_comp.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/ppp_comp_mod.o: ppp_comp_mod.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/vjcompress.o: ../modules/vjcompress.c
+       $(CC) $(CFLAGS) -c $? -o $@
+$(LP64DIR)/zlib.o:     ../common/zlib.c
+       $(CC) $(CFLAGS) -c $? -o $@
+
+$(LP64DIR):
+       mkdir -m 755 -p $@
+
+install:
+       cp ppp ppp.conf /kernel/drv
+       cp ppp_comp ppp_ahdl /kernel/strmod
+       cp $(LP64DIR)/ppp /kernel/drv/$(LP64DIR)
+       cp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl /kernel/strmod/$(LP64DIR)
+       if grep clone:ppp /etc/minor_perm; then :; else \
+         echo clone:ppp 0644 root sys >>/etc/minor_perm; fi
+       /usr/sbin/rem_drv ppp 2>/dev/null || true
+       /usr/sbin/add_drv ppp
+
+SRCS   = ppp.c ppp_mod.c ppp_ahdlc.c ppp_ahdlc_mod.c \
+       ppp_comp.c ../modules/bsd-comp.c ../modules/deflate.c \
+       ../common/zlib.c ../modules/vjcompress.c ppp_comp_mod.c
+
+lint:
+       $(LINT64) $(SRCS)
+
+lint-32:
+       $(LINT32) $(SRCS)
+
+clean:
+       $(MAKE) -f $(STD_MAKE) clean
+       rm -f $(LP64DIR)/ppp $(LP64DIR)/ppp_comp $(LP64DIR)/ppp_ahdl $(LP64DIR)/*.o $(LP64DIR)/*~ $(LP64DIR)/core
diff --git a/solaris/Makefile.top b/solaris/Makefile.top
new file mode 100644 (file)
index 0000000..0cf2a76
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# ppp top level makefile for SVR4 and Solaris 2
+#
+# $Id: Makefile.top,v 1.1 2000/04/18 23:51:28 masputra Exp $
+#
+
+include solaris/Makedefs
+
+all:
+       cd chat; $(MAKE) all
+       cd pppd; $(MAKE) all
+       cd pppstats; $(MAKE) all
+       cd pppdump; $(MAKE) all
+       cd solaris; $(MAKE) all
+
+install: $(BINDIR) $(MANDIR)/man8 install-progs install-etcppp
+
+install-progs:
+       cd chat; $(MAKE) install
+       cd pppd; $(MAKE) install
+       cd pppstats; $(MAKE) install
+       cd pppdump; $(MAKE) install
+       cd solaris; $(MAKE) install
+
+install-etcppp: $(ETCDIR) $(ETCDIR)/options $(ETCDIR)/pap-secrets \
+       $(ETCDIR)/chap-secrets
+
+$(ETCDIR)/options:
+       cp etc.ppp/options $@
+       chmod go-w $@
+$(ETCDIR)/pap-secrets:
+       $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/pap-secrets
+$(ETCDIR)/chap-secrets:
+       $(INSTALL) -f $(ETCDIR) -m 600 etc.ppp/chap-secrets
+
+$(BINDIR):
+       mkdir -m 755 -p $@
+$(MANDIR)/man8:
+       mkdir -m 755 -p $@
+$(ETCDIR):
+       mkdir -m 755 -p $@
+
+clean:
+       rm -f *~
+       cd chat; $(MAKE) clean
+       cd pppd; $(MAKE) clean
+       cd pppstats; $(MAKE) clean
+       cd pppdump; $(MAKE) clean
+       cd solaris; $(MAKE) clean
+
diff --git a/solaris/ppp.c b/solaris/ppp.c
new file mode 100644 (file)
index 0000000..123ade1
--- /dev/null
@@ -0,0 +1,2486 @@
+/*
+ * ppp.c - STREAMS multiplexing pseudo-device driver for PPP.
+ *
+ * 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.c,v 1.1 2000/04/18 23:51:28 masputra Exp $
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/errno.h>
+#ifdef __osf__
+#include <sys/ioctl.h>
+#include <sys/cmn_err.h>
+#define queclass(mp)   ((mp)->b_band & QPCTL)
+#else
+#include <sys/ioccom.h>
+#endif
+#include <sys/time.h>
+#ifdef SVR4
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/dlpi.h>
+#include <sys/ddi.h>
+#ifdef SOL2
+#include <sys/ksynch.h>
+#include <sys/kstat.h>
+#include <sys/sunddi.h>
+#include <sys/ethernet.h>
+#else
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#endif /* SOL2 */
+#else /* not SVR4 */
+#include <sys/user.h>
+#endif /* SVR4 */
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Modifications marked with #ifdef PRIOQ are for priority queueing of
+ * interactive traffic, and are due to Marko Zec <zec@japa.tel.fer.hr>.
+ */
+#ifdef PRIOQ
+#endif /* PRIOQ */
+
+#include <netinet/in.h>        /* leave this outside of PRIOQ for htons */
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/*
+ * The IP module may use this SAP value for IP packets.
+ */
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP   0x800
+#endif
+
+#if !defined(ETHERTYPE_IPV6) 
+#define ETHERTYPE_IPV6 0x86dd
+#endif /* !defined(ETHERTYPE_IPV6) */
+
+#if !defined(ETHERTYPE_ALLSAP) && defined(SOL2)
+#define ETHERTYPE_ALLSAP   0
+#endif /* !defined(ETHERTYPE_ALLSAP) && defined(SOL2) */
+
+#if !defined(PPP_ALLSAP) && defined(SOL2)
+#define PPP_ALLSAP  PPP_ALLSTATIONS
+#endif /* !defined(PPP_ALLSAP) && defined(SOL2) */
+
+extern time_t time;
+
+#ifdef SOL2
+/*
+ * We use this reader-writer lock to ensure that the lower streams
+ * stay connected to the upper streams while the lower-side put and
+ * service procedures are running.  Essentially it is an existence
+ * lock for the upper stream associated with each lower stream.
+ */
+krwlock_t ppp_lower_lock;
+#define LOCK_LOWER_W   rw_enter(&ppp_lower_lock, RW_WRITER)
+#define LOCK_LOWER_R   rw_enter(&ppp_lower_lock, RW_READER)
+#define TRYLOCK_LOWER_R        rw_tryenter(&ppp_lower_lock, RW_READER)
+#define UNLOCK_LOWER   rw_exit(&ppp_lower_lock)
+
+#define MT_ENTER(x)    mutex_enter(x)
+#define MT_EXIT(x)     mutex_exit(x)
+
+/*
+ * Notes on multithreaded implementation for Solaris 2:
+ *
+ * We use an inner perimeter around each queue pair and an outer
+ * perimeter around the whole driver.  The inner perimeter is
+ * entered exclusively for all entry points (open, close, put,
+ * service).  The outer perimeter is entered exclusively for open
+ * and close and shared for put and service.  This is all done for
+ * us by the streams framework.
+ *
+ * I used to think that the perimeters were entered for the lower
+ * streams' put and service routines as well as for the upper streams'.
+ * Because of problems experienced by people, and after reading the
+ * documentation more closely, I now don't think that is true.  So we
+ * now use ppp_lower_lock to give us an existence guarantee on the
+ * upper stream controlling each lower stream.
+ *
+ * Shared entry to the outer perimeter protects the existence of all
+ * the upper streams and their upperstr_t structures, and guarantees
+ * that the following fields of any upperstr_t won't change:
+ * nextmn, next, nextppa.  It guarantees that the lowerq field of an
+ * upperstr_t won't go from non-zero to zero, that the global `ppas'
+ * won't change and that the no lower stream will get unlinked.
+ *
+ * Shared (reader) access to ppa_lower_lock guarantees that no lower
+ * stream will be unlinked and that the lowerq field of all upperstr_t
+ * structures won't change.
+ */
+
+#else /* SOL2 */
+#define LOCK_LOWER_W   0
+#define LOCK_LOWER_R   0
+#define TRYLOCK_LOWER_R        1
+#define UNLOCK_LOWER   0
+#define MT_ENTER(x)    0
+#define MT_EXIT(x)     0
+
+#endif /* SOL2 */
+
+/*
+ * Private information; one per upper stream.
+ */
+typedef struct upperstr {
+    minor_t mn;                        /* minor device number */
+    struct upperstr *nextmn;   /* next minor device */
+    queue_t *q;                        /* read q associated with this upper stream */
+    int flags;                 /* flag bits, see below */
+    int state;                 /* current DLPI state */
+    int sap;                   /* service access point */
+    int req_sap;               /* which SAP the DLPI client requested */
+    struct upperstr *ppa;      /* control stream for our ppa */
+    struct upperstr *next;     /* next stream for this ppa */
+    uint ioc_id;               /* last ioctl ID for this stream */
+    enum NPmode npmode;                /* what to do with packets on this SAP */
+    unsigned char rblocked;    /* flow control has blocked upper read strm */
+       /* N.B. rblocked is only changed by control stream's put/srv procs */
+    /*
+     * There is exactly one control stream for each PPA.
+     * The following fields are only used for control streams.
+     */
+    int ppa_id;
+    queue_t *lowerq;           /* write queue attached below this PPA */
+    struct upperstr *nextppa;  /* next control stream */
+    int mru;
+    int mtu;
+    struct pppstat stats;      /* statistics */
+    time_t last_sent;          /* time last NP packet sent */
+    time_t last_recv;          /* time last NP packet rcvd */
+#ifdef SOL2
+    kmutex_t stats_lock;       /* lock for stats updates */
+    kstat_t *kstats;           /* stats for netstat */
+#endif /* SOL2 */
+#ifdef LACHTCP
+    int ifflags;
+    char ifname[IFNAMSIZ];
+    struct ifstats ifstats;
+#endif /* LACHTCP */
+} upperstr_t;
+
+/* Values for flags */
+#define US_PRIV                1       /* stream was opened by superuser */
+#define US_CONTROL     2       /* stream is a control stream */
+#define US_BLOCKED     4       /* flow ctrl has blocked lower write stream */
+#define US_LASTMOD     8       /* no PPP modules below us */
+#define US_DBGLOG      0x10    /* log various occurrences */
+#define US_RBLOCKED    0x20    /* flow ctrl has blocked upper read stream */
+
+#if defined(SOL2)
+#if DL_CURRENT_VERSION >= 2
+#define US_PROMISC     0x40    /* stream is promiscuous */
+#endif /* DL_CURRENT_VERSION >= 2 */
+#define US_RAWDATA     0x80    /* raw M_DATA, no DLPI header */
+#endif /* defined(SOL2) */
+
+#ifdef PRIOQ
+static u_char max_band=0;
+static u_char def_band=0;
+
+#define IPPORT_DEFAULT         65535
+
+/*
+ * Port priority table
+ * Highest priority ports are listed first, lowest are listed last.
+ * ICMP & packets using unlisted ports will be treated as "default".
+ * If IPPORT_DEFAULT is not listed here, "default" packets will be 
+ * assigned lowest priority.
+ * Each line should be terminated with "0".
+ * Line containing only "0" marks the end of the list.
+ */
+
+static u_short prioq_table[]= {
+    113, 53, 0,
+    22, 23, 513, 517, 518, 0,
+    514, 21, 79, 111, 0,
+    25, 109, 110, 0,
+    IPPORT_DEFAULT, 0,
+    20, 70, 80, 8001, 8008, 8080, 0, /* 8001,8008,8080 - common proxy ports */
+0 };
+
+#endif /* PRIOQ */
+
+
+static upperstr_t *minor_devs = NULL;
+static upperstr_t *ppas = NULL;
+
+#ifdef SVR4
+static int pppopen __P((queue_t *, dev_t *, int, int, cred_t *));
+static int pppclose __P((queue_t *, int, cred_t *));
+#else
+static int pppopen __P((queue_t *, int, int, int));
+static int pppclose __P((queue_t *, int));
+#endif /* SVR4 */
+static int pppurput __P((queue_t *, mblk_t *));
+static int pppuwput __P((queue_t *, mblk_t *));
+static int pppursrv __P((queue_t *));
+static int pppuwsrv __P((queue_t *));
+static int ppplrput __P((queue_t *, mblk_t *));
+static int ppplwput __P((queue_t *, mblk_t *));
+static int ppplrsrv __P((queue_t *));
+static int ppplwsrv __P((queue_t *));
+#ifndef NO_DLPI
+static void dlpi_request __P((queue_t *, mblk_t *, upperstr_t *));
+static void dlpi_error __P((queue_t *, upperstr_t *, int, int, int));
+static void dlpi_ok __P((queue_t *, int));
+#endif
+static int send_data __P((mblk_t *, upperstr_t *));
+static void new_ppa __P((queue_t *, mblk_t *));
+static void attach_ppa __P((queue_t *, mblk_t *));
+static void detach_ppa __P((queue_t *, mblk_t *));
+static void detach_lower __P((queue_t *, mblk_t *));
+static void debug_dump __P((queue_t *, mblk_t *));
+static upperstr_t *find_dest __P((upperstr_t *, int));
+#if defined(SOL2)
+static upperstr_t *find_promisc __P((upperstr_t *, int));
+static mblk_t *prepend_ether __P((upperstr_t *, mblk_t *, int));
+static mblk_t *prepend_udind __P((upperstr_t *, mblk_t *, int));
+static void promisc_sendup __P((upperstr_t *, mblk_t *, int, int));
+#endif /* defined(SOL2) */
+static int putctl2 __P((queue_t *, int, int, int));
+static int putctl4 __P((queue_t *, int, int, int));
+static int pass_packet __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#ifdef FILTER_PACKETS
+static int ip_hard_filter __P((upperstr_t *ppa, mblk_t *mp, int outbound));
+#endif /* FILTER_PACKETS */
+
+#define PPP_ID 0xb1a6
+static struct module_info ppp_info = {
+#ifdef PRIOQ
+    PPP_ID, "ppp", 0, 512, 512, 384
+#else
+    PPP_ID, "ppp", 0, 512, 512, 128
+#endif /* PRIOQ */
+};
+
+static struct qinit pppurint = {
+    pppurput, pppursrv, pppopen, pppclose, NULL, &ppp_info, NULL
+};
+
+static struct qinit pppuwint = {
+    pppuwput, pppuwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplrint = {
+    ppplrput, ppplrsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+static struct qinit ppplwint = {
+    ppplwput, ppplwsrv, NULL, NULL, NULL, &ppp_info, NULL
+};
+
+#ifdef LACHTCP
+extern struct ifstats *ifstats;
+int pppdevflag = 0;
+#endif
+
+struct streamtab pppinfo = {
+    &pppurint, &pppuwint,
+    &ppplrint, &ppplwint
+};
+
+int ppp_count;
+
+/*
+ * How we maintain statistics.
+ */
+#ifdef SOL2
+#define INCR_IPACKETS(ppa)                             \
+       if (ppa->kstats != 0) {                         \
+           KSTAT_NAMED_PTR(ppa->kstats)[0].value.ul++; \
+       }
+#define INCR_IERRORS(ppa)                              \
+       if (ppa->kstats != 0) {                         \
+           KSTAT_NAMED_PTR(ppa->kstats)[1].value.ul++; \
+       }
+#define INCR_OPACKETS(ppa)                             \
+       if (ppa->kstats != 0) {                         \
+           KSTAT_NAMED_PTR(ppa->kstats)[2].value.ul++; \
+       }
+#define INCR_OERRORS(ppa)                              \
+       if (ppa->kstats != 0) {                         \
+           KSTAT_NAMED_PTR(ppa->kstats)[3].value.ul++; \
+       }
+#endif
+
+#ifdef LACHTCP
+#define INCR_IPACKETS(ppa)     ppa->ifstats.ifs_ipackets++;
+#define INCR_IERRORS(ppa)      ppa->ifstats.ifs_ierrors++;
+#define INCR_OPACKETS(ppa)     ppa->ifstats.ifs_opackets++;
+#define INCR_OERRORS(ppa)      ppa->ifstats.ifs_oerrors++;
+#endif
+
+/*
+ * STREAMS driver entry points.
+ */
+static int
+#ifdef SVR4
+pppopen(q, devp, oflag, sflag, credp)
+    queue_t *q;
+    dev_t *devp;
+    int oflag, sflag;
+    cred_t *credp;
+#else
+pppopen(q, dev, oflag, sflag)
+    queue_t *q;
+    int dev;                   /* really dev_t */
+    int oflag, sflag;
+#endif
+{
+    upperstr_t *up;
+    upperstr_t **prevp;
+    minor_t mn;
+#ifdef PRIOQ
+    u_short *ptr;
+    u_char new_band;
+#endif /* PRIOQ */
+
+    if (q->q_ptr)
+       DRV_OPEN_OK(dev);       /* device is already open */
+
+#ifdef PRIOQ
+    /* Calculate max_bband & def_band from definitions in prioq.h
+       This colud be done at some more approtiate time (less often)
+       but this way it works well so I'll just leave it here */
+
+    max_band = 1;
+    def_band = 0;
+    ptr = prioq_table;
+    while (*ptr) {
+        new_band = 1;
+        while (*ptr)
+           if (*ptr++ == IPPORT_DEFAULT) {
+               new_band = 0;
+               def_band = max_band;
+           }
+        max_band += new_band;
+        ptr++;
+    }
+    if (def_band)
+        def_band = max_band - def_band;
+    --max_band;
+#endif /* PRIOQ */
+
+    if (sflag == CLONEOPEN) {
+       mn = 0;
+       for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+           if (up->mn != mn)
+               break;
+           ++mn;
+       }
+    } else {
+#ifdef SVR4
+       mn = getminor(*devp);
+#else
+       mn = minor(dev);
+#endif
+       for (prevp = &minor_devs; (up = *prevp) != 0; prevp = &up->nextmn) {
+           if (up->mn >= mn)
+               break;
+       }
+       if (up->mn == mn) {
+           /* this can't happen */
+           q->q_ptr = WR(q)->q_ptr = (caddr_t) up;
+           DRV_OPEN_OK(dev);
+       }
+    }
+
+    /*
+     * Construct a new minor node.
+     */
+    up = (upperstr_t *) ALLOC_SLEEP(sizeof(upperstr_t));
+    bzero((caddr_t) up, sizeof(upperstr_t));
+    if (up == 0) {
+       DPRINT("pppopen: out of kernel memory\n");
+       OPEN_ERROR(ENXIO);
+    }
+    up->nextmn = *prevp;
+    *prevp = up;
+    up->mn = mn;
+#ifdef SVR4
+    *devp = makedevice(getmajor(*devp), mn);
+#endif
+    up->q = q;
+    if (NOTSUSER() == 0)
+       up->flags |= US_PRIV;
+#ifndef NO_DLPI
+    up->state = DL_UNATTACHED;
+#endif
+#ifdef LACHTCP
+    up->ifflags = IFF_UP | IFF_POINTOPOINT;
+#endif
+    up->sap = -1;
+    up->last_sent = up->last_recv = time;
+    up->npmode = NPMODE_DROP;
+    q->q_ptr = (caddr_t) up;
+    WR(q)->q_ptr = (caddr_t) up;
+    noenable(WR(q));
+#ifdef SOL2
+    mutex_init(&up->stats_lock, NULL, MUTEX_DRIVER, NULL);
+#endif
+    ++ppp_count;
+
+    qprocson(q);
+    DRV_OPEN_OK(makedev(major(dev), mn));
+}
+
+static int
+#ifdef SVR4
+pppclose(q, flag, credp)
+    queue_t *q;
+    int flag;
+    cred_t *credp;
+#else
+pppclose(q, flag)
+    queue_t *q;
+    int flag;
+#endif
+{
+    upperstr_t *up, **upp;
+    upperstr_t *as, *asnext;
+    upperstr_t **prevp;
+
+    qprocsoff(q);
+
+    up = (upperstr_t *) q->q_ptr;
+    if (up == 0) {
+       DPRINT("pppclose: q_ptr = 0\n");
+       return 0;
+    }
+    if (up->flags & US_DBGLOG)
+       DPRINT2("ppp/%d: close, flags=%x\n", up->mn, up->flags);
+    if (up->flags & US_CONTROL) {
+#ifdef LACHTCP
+       struct ifstats *ifp, *pifp;
+#endif
+       if (up->lowerq != 0) {
+           /* Gack! the lower stream should have be unlinked earlier! */
+           DPRINT1("ppp%d: lower stream still connected on close?\n",
+                   up->mn);
+           LOCK_LOWER_W;
+           up->lowerq->q_ptr = 0;
+           RD(up->lowerq)->q_ptr = 0;
+           up->lowerq = 0;
+           UNLOCK_LOWER;
+       }
+
+       /*
+        * This stream represents a PPA:
+        * For all streams attached to the PPA, clear their
+        * references to this PPA.
+        * Then remove this PPA from the list of PPAs.
+        */
+       for (as = up->next; as != 0; as = asnext) {
+           asnext = as->next;
+           as->next = 0;
+           as->ppa = 0;
+           if (as->flags & US_BLOCKED) {
+               as->flags &= ~US_BLOCKED;
+               flushq(WR(as->q), FLUSHDATA);
+           }
+       }
+       for (upp = &ppas; *upp != 0; upp = &(*upp)->nextppa)
+           if (*upp == up) {
+               *upp = up->nextppa;
+               break;
+           }
+#ifdef LACHTCP
+       /* Remove the statistics from the active list.  */
+       for (ifp = ifstats, pifp = 0; ifp; ifp = ifp->ifs_next) {
+           if (ifp == &up->ifstats) {
+               if (pifp)
+                   pifp->ifs_next = ifp->ifs_next;
+               else
+                   ifstats = ifp->ifs_next;
+               break;
+           }
+           pifp = ifp;
+       }
+#endif
+    } else {
+       /*
+        * If this stream is attached to a PPA,
+        * remove it from the PPA's list.
+        */
+       if ((as = up->ppa) != 0) {
+           for (; as->next != 0; as = as->next)
+               if (as->next == up) {
+                   as->next = up->next;
+                   break;
+               }
+       }
+    }
+
+#ifdef SOL2
+    if (up->kstats)
+       kstat_delete(up->kstats);
+    mutex_destroy(&up->stats_lock);
+#endif
+
+    q->q_ptr = NULL;
+    WR(q)->q_ptr = NULL;
+
+    for (prevp = &minor_devs; *prevp != 0; prevp = &(*prevp)->nextmn) {
+       if (*prevp == up) {
+           *prevp = up->nextmn;
+           break;
+       }
+    }
+    FREE(up, sizeof(upperstr_t));
+    --ppp_count;
+
+    return 0;
+}
+
+/*
+ * A message from on high.  We do one of three things:
+ *     - qreply()
+ *     - put the message on the lower write stream
+ *     - queue it for our service routine
+ */
+static int
+pppuwput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *ppa, *nps;
+    struct iocblk *iop;
+    struct linkblk *lb;
+#ifdef LACHTCP
+    struct ifreq *ifr;
+    int i;
+#endif
+    queue_t *lq;
+    int error, n, sap;
+    mblk_t *mq;
+    struct ppp_idle *pip;
+#ifdef PRIOQ
+    queue_t *tlq;
+#endif /* PRIOQ */
+#ifdef NO_DLPI
+    upperstr_t *os;
+#endif
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("pppuwput: q_ptr = 0!\n");
+       return 0;
+    }
+    if (mp == 0) {
+       DPRINT1("pppuwput/%d: mp = 0!\n", us->mn);
+       return 0;
+    }
+    if (mp->b_datap == 0) {
+       DPRINT1("pppuwput/%d: mp->b_datap = 0!\n", us->mn);
+       return 0;
+    }
+    switch (mp->b_datap->db_type) {
+#ifndef NO_DLPI
+    case M_PCPROTO:
+    case M_PROTO:
+       dlpi_request(q, mp, us);
+       break;
+#endif /* NO_DLPI */
+
+    case M_DATA:
+       if (us->flags & US_DBGLOG)
+           DPRINT3("ppp/%d: uwput M_DATA len=%d flags=%x\n",
+                   us->mn, msgdsize(mp), us->flags);
+       if (us->ppa == 0 || msgdsize(mp) > us->ppa->mtu + PPP_HDRLEN
+#ifndef NO_DLPI
+           || (us->flags & US_CONTROL) == 0
+#endif /* NO_DLPI */
+           ) {
+           DPRINT1("pppuwput: junk data len=%d\n", msgdsize(mp));
+           freemsg(mp);
+           break;
+       }
+#ifdef NO_DLPI
+       if ((us->flags & US_CONTROL) == 0 && !pass_packet(us, mp, 1))
+           break;
+#endif
+       if (!send_data(mp, us))
+           putq(q, mp);
+       break;
+
+    case M_IOCTL:
+       iop = (struct iocblk *) mp->b_rptr;
+       error = EINVAL;
+       if (us->flags & US_DBGLOG)
+           DPRINT3("ppp/%d: ioctl %x count=%d\n",
+                   us->mn, iop->ioc_cmd, iop->ioc_count);
+       switch (iop->ioc_cmd) {
+#if defined(SOL2)
+       case DLIOCRAW:      /* raw M_DATA mode */
+           us->flags |= US_RAWDATA;
+           error = 0;
+           break;
+#endif /* defined(SOL2) */
+       case I_LINK:
+           if ((us->flags & US_CONTROL) == 0 || us->lowerq != 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl I_LINK b_cont = 0!\n", us->mn);
+               break;
+           }
+           lb = (struct linkblk *) mp->b_cont->b_rptr;
+           lq = lb->l_qbot;
+           if (lq == 0) {
+               DPRINT1("pppuwput/%d: ioctl I_LINK l_qbot = 0!\n", us->mn);
+               break;
+           }
+           LOCK_LOWER_W;
+           us->lowerq = lq;
+           lq->q_ptr = (caddr_t) q;
+           RD(lq)->q_ptr = (caddr_t) us->q;
+           UNLOCK_LOWER;
+           iop->ioc_count = 0;
+           error = 0;
+           us->flags &= ~US_LASTMOD;
+           /* Unblock upper streams which now feed this lower stream. */
+           qenable(q);
+           /* Send useful information down to the modules which
+              are now linked below us. */
+           putctl2(lq, M_CTL, PPPCTL_UNIT, us->ppa_id);
+           putctl4(lq, M_CTL, PPPCTL_MRU, us->mru);
+           putctl4(lq, M_CTL, PPPCTL_MTU, us->mtu);
+#ifdef PRIOQ
+            /* Lower tty driver's queue hiwat/lowat from default 4096/128
+               to 256/128 since we don't want queueing of data on
+               output to physical device */
+
+            freezestr(lq);
+            for (tlq = lq; tlq->q_next != NULL; tlq = tlq->q_next)
+               ;
+            strqset(tlq, QHIWAT, 0, 256);
+            strqset(tlq, QLOWAT, 0, 128);
+            unfreezestr(lq);
+#endif /* PRIOQ */
+           break;
+
+       case I_UNLINK:
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl I_UNLINK b_cont = 0!\n", us->mn);
+               break;
+           }
+           lb = (struct linkblk *) mp->b_cont->b_rptr;
+#if DEBUG
+           if (us->lowerq != lb->l_qbot) {
+               DPRINT2("ppp unlink: lowerq=%x qbot=%x\n",
+                       us->lowerq, lb->l_qbot);
+               break;
+           }
+#endif
+           iop->ioc_count = 0;
+           qwriter(q, mp, detach_lower, PERIM_OUTER);
+           error = -1;
+           break;
+
+       case PPPIO_NEWPPA:
+           if (us->flags & US_CONTROL)
+               break;
+           if ((us->flags & US_PRIV) == 0) {
+               error = EPERM;
+               break;
+           }
+           /* Arrange to return an int */
+           if ((mq = mp->b_cont) == 0
+               || mq->b_datap->db_lim - mq->b_rptr < sizeof(int)) {
+               mq = allocb(sizeof(int), BPRI_HI);
+               if (mq == 0) {
+                   error = ENOSR;
+                   break;
+               }
+               if (mp->b_cont != 0)
+                   freemsg(mp->b_cont);
+               mp->b_cont = mq;
+               mq->b_cont = 0;
+           }
+           iop->ioc_count = sizeof(int);
+           mq->b_wptr = mq->b_rptr + sizeof(int);
+           qwriter(q, mp, new_ppa, PERIM_OUTER);
+           error = -1;
+           break;
+
+       case PPPIO_ATTACH:
+           /* like dlpi_attach, for programs which can't write to
+              the stream (like pppstats) */
+           if (iop->ioc_count != sizeof(int) || us->ppa != 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_ATTACH b_cont = 0!\n", us->mn);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+               if (ppa->ppa_id == n)
+                   break;
+           if (ppa == 0)
+               break;
+           us->ppa = ppa;
+           iop->ioc_count = 0;
+           qwriter(q, mp, attach_ppa, PERIM_OUTER);
+           error = -1;
+           break;
+
+#ifdef NO_DLPI
+       case PPPIO_BIND:
+           /* Attach to a given SAP. */
+           if (iop->ioc_count != sizeof(int) || us->ppa == 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_BIND b_cont = 0!\n", us->mn);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           /* n must be a valid PPP network protocol number. */
+           if (n < 0x21 || n > 0x3fff || (n & 0x101) != 1)
+               break;
+           /* check that no other stream is bound to this sap already. */
+           for (os = us->ppa; os != 0; os = os->next)
+               if (os->sap == n)
+                   break;
+           if (os != 0)
+               break;
+           us->sap = n;
+           iop->ioc_count = 0;
+           error = 0;
+           break;
+#endif /* NO_DLPI */
+
+       case PPPIO_MRU:
+           if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_MRU b_cont = 0!\n", us->mn);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           if (n <= 0 || n > PPP_MAXMRU)
+               break;
+           if (n < PPP_MRU)
+               n = PPP_MRU;
+           us->mru = n;
+           if (us->lowerq)
+               putctl4(us->lowerq, M_CTL, PPPCTL_MRU, n);
+           error = 0;
+           iop->ioc_count = 0;
+           break;
+
+       case PPPIO_MTU:
+           if (iop->ioc_count != sizeof(int) || (us->flags & US_CONTROL) == 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_MTU b_cont = 0!\n", us->mn);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           if (n <= 0 || n > PPP_MAXMTU)
+               break;
+           us->mtu = n;
+#ifdef LACHTCP
+           /* The MTU reported in netstat, not used as IP max packet size! */
+           us->ifstats.ifs_mtu = n;
+#endif
+           if (us->lowerq)
+               putctl4(us->lowerq, M_CTL, PPPCTL_MTU, n);
+           error = 0;
+           iop->ioc_count = 0;
+           break;
+
+       case PPPIO_LASTMOD:
+           us->flags |= US_LASTMOD;
+           error = 0;
+           break;
+
+       case PPPIO_DEBUG:
+           if (iop->ioc_count != sizeof(int))
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_DEBUG b_cont = 0!\n", us->mn);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           if (n == PPPDBG_DUMP + PPPDBG_DRIVER) {
+               qwriter(q, NULL, debug_dump, PERIM_OUTER);
+               iop->ioc_count = 0;
+               error = -1;
+           } else if (n == PPPDBG_LOG + PPPDBG_DRIVER) {
+               DPRINT1("ppp/%d: debug log enabled\n", us->mn);
+               us->flags |= US_DBGLOG;
+               iop->ioc_count = 0;
+               error = 0;
+           } else {
+               if (us->ppa == 0 || us->ppa->lowerq == 0)
+                   break;
+               putnext(us->ppa->lowerq, mp);
+               error = -1;
+           }
+           break;
+
+       case PPPIO_NPMODE:
+           if (iop->ioc_count != 2 * sizeof(int))
+               break;
+           if ((us->flags & US_CONTROL) == 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("pppuwput/%d: ioctl PPPIO_NPMODE b_cont = 0!\n", us->mn);
+               break;
+           }
+           sap = ((int *)mp->b_cont->b_rptr)[0];
+           for (nps = us->next; nps != 0; nps = nps->next) {
+               if (us->flags & US_DBGLOG)
+                   DPRINT2("us = 0x%x, us->next->sap = 0x%x\n", nps, nps->sap);
+               if (nps->sap == sap)
+                   break;
+           }
+           if (nps == 0) {
+               if (us->flags & US_DBGLOG)
+                   DPRINT2("ppp/%d: no stream for sap %x\n", us->mn, sap);
+               break;
+           }
+           /* XXX possibly should use qwriter here */
+           nps->npmode = (enum NPmode) ((int *)mp->b_cont->b_rptr)[1];
+           if (nps->npmode != NPMODE_QUEUE && (nps->flags & US_BLOCKED) != 0)
+               qenable(WR(nps->q));
+           iop->ioc_count = 0;
+           error = 0;
+           break;
+
+       case PPPIO_GIDLE:
+           if ((ppa = us->ppa) == 0)
+               break;
+           mq = allocb(sizeof(struct ppp_idle), BPRI_HI);
+           if (mq == 0) {
+               error = ENOSR;
+               break;
+           }
+           if (mp->b_cont != 0)
+               freemsg(mp->b_cont);
+           mp->b_cont = mq;
+           mq->b_cont = 0;
+           pip = (struct ppp_idle *) mq->b_wptr;
+           pip->xmit_idle = time - ppa->last_sent;
+           pip->recv_idle = time - ppa->last_recv;
+           mq->b_wptr += sizeof(struct ppp_idle);
+           iop->ioc_count = sizeof(struct ppp_idle);
+           error = 0;
+           break;
+
+#ifdef LACHTCP
+       case SIOCSIFNAME:
+           /* Sent from IP down to us.  Attach the ifstats structure.  */
+           if (iop->ioc_count != sizeof(struct ifreq) || us->ppa == 0)
+               break;
+           ifr = (struct ifreq *)mp->b_cont->b_rptr;
+           /* Find the unit number in the interface name.  */
+           for (i = 0; i < IFNAMSIZ; i++) {
+               if (ifr->ifr_name[i] == 0 ||
+                   (ifr->ifr_name[i] >= '0' &&
+                    ifr->ifr_name[i] <= '9'))
+                   break;
+               else
+                   us->ifname[i] = ifr->ifr_name[i];
+           }
+           us->ifname[i] = 0;
+
+           /* Convert the unit number to binary.  */
+           for (n = 0; i < IFNAMSIZ; i++) {
+               if (ifr->ifr_name[i] == 0) {
+                   break;
+               }
+               else {
+                   n = n * 10 + ifr->ifr_name[i] - '0';
+               }
+           }
+
+           /* Verify the ppa.  */
+           if (us->ppa->ppa_id != n)
+               break;
+           ppa = us->ppa;
+
+           /* Set up the netstat block.  */
+           strncpy (ppa->ifname, us->ifname, IFNAMSIZ);
+
+           ppa->ifstats.ifs_name = ppa->ifname;
+           ppa->ifstats.ifs_unit = n;
+           ppa->ifstats.ifs_active = us->state != DL_UNBOUND;
+           ppa->ifstats.ifs_mtu = ppa->mtu;
+
+           /* Link in statistics used by netstat.  */
+           ppa->ifstats.ifs_next = ifstats;
+           ifstats = &ppa->ifstats;
+
+           iop->ioc_count = 0;
+           error = 0;
+           break;
+
+       case SIOCGIFFLAGS:
+           if (!(us->flags & US_CONTROL)) {
+               if (us->ppa)
+                   us = us->ppa;
+               else
+                   break;
+           }
+           ((struct iocblk_in *)iop)->ioc_ifflags = us->ifflags;
+           error = 0;
+           break;
+
+       case SIOCSIFFLAGS:
+           if (!(us->flags & US_CONTROL)) {
+               if (us->ppa)
+                   us = us->ppa;
+               else
+                   break;
+           }
+           us->ifflags = ((struct iocblk_in *)iop)->ioc_ifflags;
+           error = 0;
+           break;
+
+       case SIOCSIFADDR:
+           if (!(us->flags & US_CONTROL)) {
+               if (us->ppa)
+                   us = us->ppa;
+               else
+                   break;
+           }
+           us->ifflags |= IFF_RUNNING;
+           ((struct iocblk_in *)iop)->ioc_ifflags |= IFF_RUNNING;
+           error = 0;
+           break;
+
+       case SIOCSIFMTU:
+           /*
+            * Vanilla SVR4 systems don't handle SIOCSIFMTU, rather
+            * they take the MTU from the DL_INFO_ACK we sent in response
+            * to their DL_INFO_REQ.  Fortunately, they will update the
+            * MTU if we send an unsolicited DL_INFO_ACK up.
+            */
+           if ((mq = allocb(sizeof(dl_info_req_t), BPRI_HI)) == 0)
+               break;          /* should do bufcall */
+           ((union DL_primitives *)mq->b_rptr)->dl_primitive = DL_INFO_REQ;
+           mq->b_wptr = mq->b_rptr + sizeof(dl_info_req_t);
+           dlpi_request(q, mq, us);
+           error = 0;
+           break;
+
+       case SIOCGIFNETMASK:
+       case SIOCSIFNETMASK:
+       case SIOCGIFADDR:
+       case SIOCGIFDSTADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCGIFMETRIC:
+           error = 0;
+           break;
+#endif /* LACHTCP */
+
+       default:
+           if (us->ppa == 0 || us->ppa->lowerq == 0)
+               break;
+           us->ioc_id = iop->ioc_id;
+           error = -1;
+           switch (iop->ioc_cmd) {
+           case PPPIO_GETSTAT:
+           case PPPIO_GETCSTAT:
+               if (us->flags & US_LASTMOD) {
+                   error = EINVAL;
+                   break;
+               }
+               putnext(us->ppa->lowerq, mp);
+               break;
+           default:
+               if (us->flags & US_PRIV)
+                   putnext(us->ppa->lowerq, mp);
+               else {
+                   DPRINT1("ppp ioctl %x rejected\n", iop->ioc_cmd);
+                   error = EPERM;
+               }
+               break;
+           }
+           break;
+       }
+
+       if (error > 0) {
+           iop->ioc_error = error;
+           mp->b_datap->db_type = M_IOCNAK;
+           qreply(q, mp);
+       } else if (error == 0) {
+           mp->b_datap->db_type = M_IOCACK;
+           qreply(q, mp);
+       }
+       break;
+
+    case M_FLUSH:
+       if (us->flags & US_DBGLOG)
+           DPRINT2("ppp/%d: flush %x\n", us->mn, *mp->b_rptr);
+       if (*mp->b_rptr & FLUSHW)
+           flushq(q, FLUSHDATA);
+       if (*mp->b_rptr & FLUSHR) {
+           *mp->b_rptr &= ~FLUSHW;
+           qreply(q, mp);
+       } else
+           freemsg(mp);
+       break;
+
+    default:
+       freemsg(mp);
+       break;
+    }
+    return 0;
+}
+
+#ifndef NO_DLPI
+static void
+dlpi_request(q, mp, us)
+    queue_t *q;
+    mblk_t *mp;
+    upperstr_t *us;
+{
+    union DL_primitives *d = (union DL_primitives *) mp->b_rptr;
+    int size = mp->b_wptr - mp->b_rptr;
+    mblk_t *reply, *np;
+    upperstr_t *ppa, *os;
+    int sap, len;
+    dl_info_ack_t *info;
+    dl_bind_ack_t *ackp;
+#if DL_CURRENT_VERSION >= 2
+    dl_phys_addr_ack_t *paddrack;
+    static struct ether_addr eaddr = {0};
+#endif
+
+    if (us->flags & US_DBGLOG)
+       DPRINT3("ppp/%d: dlpi prim %x len=%d\n", us->mn,
+               d->dl_primitive, size);
+    switch (d->dl_primitive) {
+    case DL_INFO_REQ:
+       if (size < sizeof(dl_info_req_t))
+           goto badprim;
+       if ((reply = allocb(sizeof(dl_info_ack_t), BPRI_HI)) == 0)
+           break;              /* should do bufcall */
+       reply->b_datap->db_type = M_PCPROTO;
+       info = (dl_info_ack_t *) reply->b_wptr;
+       reply->b_wptr += sizeof(dl_info_ack_t);
+       bzero((caddr_t) info, sizeof(dl_info_ack_t));
+       info->dl_primitive = DL_INFO_ACK;
+       info->dl_max_sdu = us->ppa? us->ppa->mtu: PPP_MAXMTU;
+       info->dl_min_sdu = 1;
+       info->dl_addr_length = sizeof(uint);
+       info->dl_mac_type = DL_ETHER;   /* a bigger lie */
+       info->dl_current_state = us->state;
+       info->dl_service_mode = DL_CLDLS;
+       info->dl_provider_style = DL_STYLE2;
+#if DL_CURRENT_VERSION >= 2
+       info->dl_sap_length = sizeof(uint);
+       info->dl_version = DL_CURRENT_VERSION;
+#endif
+       qreply(q, reply);
+       break;
+
+    case DL_ATTACH_REQ:
+       if (size < sizeof(dl_attach_req_t))
+           goto badprim;
+       if (us->state != DL_UNATTACHED || us->ppa != 0) {
+           dlpi_error(q, us, DL_ATTACH_REQ, DL_OUTSTATE, 0);
+           break;
+       }
+       for (ppa = ppas; ppa != 0; ppa = ppa->nextppa)
+           if (ppa->ppa_id == d->attach_req.dl_ppa)
+               break;
+       if (ppa == 0) {
+           dlpi_error(q, us, DL_ATTACH_REQ, DL_BADPPA, 0);
+           break;
+       }
+       us->ppa = ppa;
+       qwriter(q, mp, attach_ppa, PERIM_OUTER);
+       return;
+
+    case DL_DETACH_REQ:
+       if (size < sizeof(dl_detach_req_t))
+           goto badprim;
+       if (us->state != DL_UNBOUND || us->ppa == 0) {
+           dlpi_error(q, us, DL_DETACH_REQ, DL_OUTSTATE, 0);
+           break;
+       }
+       qwriter(q, mp, detach_ppa, PERIM_OUTER);
+       return;
+
+    case DL_BIND_REQ:
+       if (size < sizeof(dl_bind_req_t))
+           goto badprim;
+       if (us->state != DL_UNBOUND || us->ppa == 0) {
+           dlpi_error(q, us, DL_BIND_REQ, DL_OUTSTATE, 0);
+           break;
+       }
+#if 0
+       /* apparently this test fails (unnecessarily?) on some systems */
+       if (d->bind_req.dl_service_mode != DL_CLDLS) {
+           dlpi_error(q, us, DL_BIND_REQ, DL_UNSUPPORTED, 0);
+           break;
+       }
+#endif
+
+       /* saps must be valid PPP network protocol numbers,
+          except that we accept ETHERTYPE_IP in place of PPP_IP. */
+       sap = d->bind_req.dl_sap;
+       us->req_sap = sap;
+
+#if defined(SOL2)
+       if (us->flags & US_DBGLOG)
+           DPRINT2("DL_BIND_REQ: ip gives sap = 0x%x, us = 0x%x", sap, us);
+
+       if (sap == ETHERTYPE_IP)            /* normal IFF_IPV4 */
+           sap = PPP_IP;
+       else if (sap == ETHERTYPE_IPV6)     /* when IFF_IPV6 is set */
+           sap = PPP_IPV6;
+       else if (sap == ETHERTYPE_ALLSAP)   /* snoop gives sap of 0 */
+           sap = PPP_ALLSAP;
+       else {
+           DPRINT2("DL_BIND_REQ: unrecognized sap = 0x%x, us = 0x%x", sap, us);
+           dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+           break;
+       }
+#else
+       if (sap == ETHERTYPE_IP)
+           sap = PPP_IP;
+       if (sap < 0x21 || sap > 0x3fff || (sap & 0x101) != 1) {
+           dlpi_error(q, us, DL_BIND_REQ, DL_BADADDR, 0);
+           break;
+       }
+#endif /* defined(SOL2) */
+
+       /* check that no other stream is bound to this sap already. */
+       for (os = us->ppa; os != 0; os = os->next)
+           if (os->sap == sap)
+               break;
+       if (os != 0) {
+           dlpi_error(q, us, DL_BIND_REQ, DL_NOADDR, 0);
+           break;
+       }
+
+       us->sap = sap;
+       us->state = DL_IDLE;
+
+       if ((reply = allocb(sizeof(dl_bind_ack_t) + sizeof(uint),
+                           BPRI_HI)) == 0)
+           break;              /* should do bufcall */
+       ackp = (dl_bind_ack_t *) reply->b_wptr;
+       reply->b_wptr += sizeof(dl_bind_ack_t) + sizeof(uint);
+       reply->b_datap->db_type = M_PCPROTO;
+       bzero((caddr_t) ackp, sizeof(dl_bind_ack_t));
+       ackp->dl_primitive = DL_BIND_ACK;
+       ackp->dl_sap = sap;
+       ackp->dl_addr_length = sizeof(uint);
+       ackp->dl_addr_offset = sizeof(dl_bind_ack_t);
+       *(uint *)(ackp+1) = sap;
+       qreply(q, reply);
+       break;
+
+    case DL_UNBIND_REQ:
+       if (size < sizeof(dl_unbind_req_t))
+           goto badprim;
+       if (us->state != DL_IDLE) {
+           dlpi_error(q, us, DL_UNBIND_REQ, DL_OUTSTATE, 0);
+           break;
+       }
+       us->sap = -1;
+       us->state = DL_UNBOUND;
+#ifdef LACHTCP
+       us->ppa->ifstats.ifs_active = 0;
+#endif
+       dlpi_ok(q, DL_UNBIND_REQ);
+       break;
+
+    case DL_UNITDATA_REQ:
+       if (size < sizeof(dl_unitdata_req_t))
+           goto badprim;
+       if (us->state != DL_IDLE) {
+           dlpi_error(q, us, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
+           break;
+       }
+       if ((ppa = us->ppa) == 0) {
+           cmn_err(CE_CONT, "ppp: in state dl_idle but ppa == 0?\n");
+           break;
+       }
+       len = mp->b_cont == 0? 0: msgdsize(mp->b_cont);
+       if (len > ppa->mtu) {
+           DPRINT2("dlpi data too large (%d > %d)\n", len, ppa->mtu);
+           break;
+       }
+
+#if defined(SOL2)
+       /*
+        * Should there be any promiscuous stream(s), send the data
+        * up for each promiscuous stream that we recognize.
+        */
+       if (mp->b_cont)
+           promisc_sendup(ppa, mp->b_cont, us->sap, 0);
+#endif /* defined(SOL2) */
+
+       mp->b_band = 0;
+#ifdef PRIOQ
+        /* Extract s_port & d_port from IP-packet, the code is a bit
+           dirty here, but so am I, too... */
+        if (mp->b_datap->db_type == M_PROTO && us->sap == PPP_IP
+           && mp->b_cont != 0) {
+           u_char *bb, *tlh;
+           int iphlen, len;
+           u_short *ptr;
+           u_char band_unset, cur_band, syn;
+           u_short s_port, d_port;
+
+            bb = mp->b_cont->b_rptr; /* bb points to IP-header*/
+           len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
+            syn = 0;
+           s_port = IPPORT_DEFAULT;
+           d_port = IPPORT_DEFAULT;
+           if (len >= 20) {    /* 20 = minimum length of IP header */
+               iphlen = (bb[0] & 0x0f) * 4;
+               tlh = bb + iphlen;
+               len -= iphlen;
+               switch (bb[9]) {
+               case IPPROTO_TCP:
+                   if (len >= 20) {          /* min length of TCP header */
+                       s_port = (tlh[0] << 8) + tlh[1];
+                       d_port = (tlh[2] << 8) + tlh[3];
+                       syn = tlh[13] & 0x02;
+                   }
+                   break;
+               case IPPROTO_UDP:
+                   if (len >= 8) {           /* min length of UDP header */
+                       s_port = (tlh[0] << 8) + tlh[1];
+                       d_port = (tlh[2] << 8) + tlh[3];
+                   }
+                   break;
+               }
+           }
+
+            /*
+            * Now calculate b_band for this packet from the
+            * port-priority table.
+            */
+            ptr = prioq_table;
+            cur_band = max_band;
+            band_unset = 1;
+            while (*ptr) {
+                while (*ptr && band_unset)
+                    if (s_port == *ptr || d_port == *ptr++) {
+                        mp->b_band = cur_band;
+                        band_unset = 0;
+                        break;
+                   }
+                ptr++;
+                cur_band--;
+           }
+            if (band_unset)
+               mp->b_band = def_band;
+            /* It may be usable to urge SYN packets a bit */
+            if (syn)
+               mp->b_band++;
+       }
+#endif /* PRIOQ */
+       /* this assumes PPP_HDRLEN <= sizeof(dl_unitdata_req_t) */
+       if (mp->b_datap->db_ref > 1) {
+           np = allocb(PPP_HDRLEN, BPRI_HI);
+           if (np == 0)
+               break;          /* gak! */
+           np->b_cont = mp->b_cont;
+           mp->b_cont = 0;
+           freeb(mp);
+           mp = np;
+       } else
+           mp->b_datap->db_type = M_DATA;
+       /* XXX should use dl_dest_addr_offset/length here,
+          but we would have to translate ETHERTYPE_IP -> PPP_IP */
+       mp->b_wptr = mp->b_rptr + PPP_HDRLEN;
+       mp->b_rptr[0] = PPP_ALLSTATIONS;
+       mp->b_rptr[1] = PPP_UI;
+       mp->b_rptr[2] = us->sap >> 8;
+       mp->b_rptr[3] = us->sap;
+       if (pass_packet(us, mp, 1)) {
+           if (!send_data(mp, us))
+               putq(q, mp);
+       }
+       return;
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_PHYS_ADDR_REQ:
+       if (size < sizeof(dl_phys_addr_req_t))
+           goto badprim;
+
+       /*
+        * Don't check state because ifconfig sends this one down too
+        */
+
+       if ((reply = allocb(sizeof(dl_phys_addr_ack_t)+ETHERADDRL, 
+                       BPRI_HI)) == 0)
+           break;              /* should do bufcall */
+       reply->b_datap->db_type = M_PCPROTO;
+       paddrack = (dl_phys_addr_ack_t *) reply->b_wptr;
+       reply->b_wptr += sizeof(dl_phys_addr_ack_t);
+       bzero((caddr_t) paddrack, sizeof(dl_phys_addr_ack_t)+ETHERADDRL);
+       paddrack->dl_primitive = DL_PHYS_ADDR_ACK;
+       paddrack->dl_addr_length = ETHERADDRL;
+       paddrack->dl_addr_offset = sizeof(dl_phys_addr_ack_t);
+       bcopy(&eaddr, reply->b_wptr, ETHERADDRL);
+       reply->b_wptr += ETHERADDRL;
+       qreply(q, reply);
+       break;
+
+#if defined(SOL2)
+    case DL_PROMISCON_REQ:
+       if (size < sizeof(dl_promiscon_req_t))
+           goto badprim;
+       us->flags |= US_PROMISC;
+       dlpi_ok(q, DL_PROMISCON_REQ);
+       break;
+
+    case DL_PROMISCOFF_REQ:
+       if (size < sizeof(dl_promiscoff_req_t))
+           goto badprim;
+       us->flags &= ~US_PROMISC;
+       dlpi_ok(q, DL_PROMISCOFF_REQ);
+       break;
+#else
+    case DL_PROMISCON_REQ:         /* fall thru */
+    case DL_PROMISCOFF_REQ:        /* fall thru */
+#endif /* defined(SOL2) */
+#endif /* DL_CURRENT_VERSION >= 2 */
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_SET_PHYS_ADDR_REQ:
+    case DL_SUBS_BIND_REQ:
+    case DL_SUBS_UNBIND_REQ:
+    case DL_ENABMULTI_REQ:
+    case DL_DISABMULTI_REQ:
+    case DL_XID_REQ:
+    case DL_TEST_REQ:
+    case DL_REPLY_UPDATE_REQ:
+    case DL_REPLY_REQ:
+    case DL_DATA_ACK_REQ:
+#endif
+    case DL_CONNECT_REQ:
+    case DL_TOKEN_REQ:
+       dlpi_error(q, us, d->dl_primitive, DL_NOTSUPPORTED, 0);
+       break;
+
+    case DL_CONNECT_RES:
+    case DL_DISCONNECT_REQ:
+    case DL_RESET_REQ:
+    case DL_RESET_RES:
+       dlpi_error(q, us, d->dl_primitive, DL_OUTSTATE, 0);
+       break;
+
+    case DL_UDQOS_REQ:
+       dlpi_error(q, us, d->dl_primitive, DL_BADQOSTYPE, 0);
+       break;
+
+#if DL_CURRENT_VERSION >= 2
+    case DL_TEST_RES:
+    case DL_XID_RES:
+       break;
+#endif
+
+    default:
+       cmn_err(CE_CONT, "ppp: unknown dlpi prim 0x%x\n", d->dl_primitive);
+       /* fall through */
+    badprim:
+       dlpi_error(q, us, d->dl_primitive, DL_BADPRIM, 0);
+       break;
+    }
+    freemsg(mp);
+}
+
+static void
+dlpi_error(q, us, prim, err, uerr)
+    queue_t *q;
+    upperstr_t *us;
+    int prim, err, uerr;
+{
+    mblk_t *reply;
+    dl_error_ack_t *errp;
+
+    if (us->flags & US_DBGLOG)
+        DPRINT3("ppp/%d: dlpi error, prim=%x, err=%x\n", us->mn, prim, err);
+    reply = allocb(sizeof(dl_error_ack_t), BPRI_HI);
+    if (reply == 0)
+       return;                 /* XXX should do bufcall */
+    reply->b_datap->db_type = M_PCPROTO;
+    errp = (dl_error_ack_t *) reply->b_wptr;
+    reply->b_wptr += sizeof(dl_error_ack_t);
+    errp->dl_primitive = DL_ERROR_ACK;
+    errp->dl_error_primitive = prim;
+    errp->dl_errno = err;
+    errp->dl_unix_errno = uerr;
+    qreply(q, reply);
+}
+
+static void
+dlpi_ok(q, prim)
+    queue_t *q;
+    int prim;
+{
+    mblk_t *reply;
+    dl_ok_ack_t *okp;
+
+    reply = allocb(sizeof(dl_ok_ack_t), BPRI_HI);
+    if (reply == 0)
+       return;                 /* XXX should do bufcall */
+    reply->b_datap->db_type = M_PCPROTO;
+    okp = (dl_ok_ack_t *) reply->b_wptr;
+    reply->b_wptr += sizeof(dl_ok_ack_t);
+    okp->dl_primitive = DL_OK_ACK;
+    okp->dl_correct_primitive = prim;
+    qreply(q, reply);
+}
+#endif /* NO_DLPI */
+
+static int
+pass_packet(us, mp, outbound)
+    upperstr_t *us;
+    mblk_t *mp;
+    int outbound;
+{
+    int pass;
+    upperstr_t *ppa;
+
+    if ((ppa = us->ppa) == 0) {
+       freemsg(mp);
+       return 0;
+    }
+
+#ifdef FILTER_PACKETS
+    pass = ip_hard_filter(us, mp, outbound);
+#else
+    /*
+     * Here is where we might, in future, decide whether to pass
+     * or drop the packet, and whether it counts as link activity.
+     */
+    pass = 1;
+#endif /* FILTER_PACKETS */
+
+    if (pass < 0) {
+       /* pass only if link already up, and don't update time */
+       if (ppa->lowerq == 0) {
+           freemsg(mp);
+           return 0;
+       }
+       pass = 1;
+    } else if (pass) {
+       if (outbound)
+           ppa->last_sent = time;
+       else
+           ppa->last_recv = time;
+    }
+
+    return pass;
+}
+
+/*
+ * We have some data to send down to the lower stream (or up the
+ * control stream, if we don't have a lower stream attached).
+ * Returns 1 if the message was dealt with, 0 if it wasn't able
+ * to be sent on and should therefore be queued up.
+ */
+static int
+send_data(mp, us)
+    mblk_t *mp;
+    upperstr_t *us;
+{
+    upperstr_t *ppa;
+
+    if ((us->flags & US_BLOCKED) || us->npmode == NPMODE_QUEUE)
+       return 0;
+    ppa = us->ppa;
+    if (ppa == 0 || us->npmode == NPMODE_DROP || us->npmode == NPMODE_ERROR) {
+       if (us->flags & US_DBGLOG)
+           DPRINT2("ppp/%d: dropping pkt (npmode=%d)\n", us->mn, us->npmode);
+       freemsg(mp);
+       return 1;
+    }
+    if (ppa->lowerq == 0) {
+       /* try to send it up the control stream */
+        if (bcanputnext(ppa->q, mp->b_band)) {
+           /*
+            * The message seems to get corrupted for some reason if
+            * we just send the message up as it is, so we send a copy.
+            */
+           mblk_t *np = copymsg(mp);
+           freemsg(mp);
+           if (np != 0)
+               putnext(ppa->q, np);
+           return 1;
+       }
+    } else {
+        if (bcanputnext(ppa->lowerq, mp->b_band)) {
+           MT_ENTER(&ppa->stats_lock);
+           ppa->stats.ppp_opackets++;
+           ppa->stats.ppp_obytes += msgdsize(mp);
+#ifdef INCR_OPACKETS
+           INCR_OPACKETS(ppa);
+#endif
+           MT_EXIT(&ppa->stats_lock);
+           /*
+            * The lower queue is only ever detached while holding an
+            * exclusive lock on the whole driver.  So we can be confident
+            * that the lower queue is still there.
+            */
+           putnext(ppa->lowerq, mp);
+           return 1;
+       }
+    }
+    us->flags |= US_BLOCKED;
+    return 0;
+}
+
+/*
+ * Allocate a new PPA id and link this stream into the list of PPAs.
+ * This procedure is called with an exclusive lock on all queues in
+ * this driver.
+ */
+static void
+new_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *up, **usp;
+    int ppa_id;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("new_ppa: q_ptr = 0!\n");
+       return;
+    }
+
+    usp = &ppas;
+    ppa_id = 0;
+    while ((up = *usp) != 0 && ppa_id == up->ppa_id) {
+       ++ppa_id;
+       usp = &up->nextppa;
+    }
+    us->ppa_id = ppa_id;
+    us->ppa = us;
+    us->next = 0;
+    us->nextppa = *usp;
+    *usp = us;
+    us->flags |= US_CONTROL;
+    us->npmode = NPMODE_PASS;
+
+    us->mtu = PPP_MTU;
+    us->mru = PPP_MRU;
+
+#ifdef SOL2
+    /*
+     * Create a kstats record for our statistics, so netstat -i works.
+     */
+    if (us->kstats == 0) {
+       char unit[32];
+
+       sprintf(unit, "ppp%d", us->ppa->ppa_id);
+       us->kstats = kstat_create("ppp", us->ppa->ppa_id, unit,
+                                 "net", KSTAT_TYPE_NAMED, 4, 0);
+       if (us->kstats != 0) {
+           kstat_named_t *kn = KSTAT_NAMED_PTR(us->kstats);
+
+           strcpy(kn[0].name, "ipackets");
+           kn[0].data_type = KSTAT_DATA_ULONG;
+           strcpy(kn[1].name, "ierrors");
+           kn[1].data_type = KSTAT_DATA_ULONG;
+           strcpy(kn[2].name, "opackets");
+           kn[2].data_type = KSTAT_DATA_ULONG;
+           strcpy(kn[3].name, "oerrors");
+           kn[3].data_type = KSTAT_DATA_ULONG;
+           kstat_install(us->kstats);
+       }
+    }
+#endif /* SOL2 */
+
+    *(int *)mp->b_cont->b_rptr = ppa_id;
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+static void
+attach_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *t;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("attach_ppa: q_ptr = 0!\n");
+       return;
+    }
+
+#ifndef NO_DLPI
+    us->state = DL_UNBOUND;
+#endif
+    for (t = us->ppa; t->next != 0; t = t->next)
+       ;
+    t->next = us;
+    us->next = 0;
+    if (mp->b_datap->db_type == M_IOCTL) {
+       mp->b_datap->db_type = M_IOCACK;
+       qreply(q, mp);
+    } else {
+#ifndef NO_DLPI
+       dlpi_ok(q, DL_ATTACH_REQ);
+#endif
+    }
+}
+
+static void
+detach_ppa(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us, *t;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("detach_ppa: q_ptr = 0!\n");
+       return;
+    }
+
+    for (t = us->ppa; t->next != 0; t = t->next)
+       if (t->next == us) {
+           t->next = us->next;
+           break;
+       }
+    us->next = 0;
+    us->ppa = 0;
+#ifndef NO_DLPI
+    us->state = DL_UNATTACHED;
+    dlpi_ok(q, DL_DETACH_REQ);
+#endif
+}
+
+/*
+ * We call this with qwriter in order to give the upper queue procedures
+ * the guarantee that the lower queue is not going to go away while
+ * they are executing.
+ */
+static void
+detach_lower(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("detach_lower: q_ptr = 0!\n");
+       return;
+    }
+
+    LOCK_LOWER_W;
+    us->lowerq->q_ptr = 0;
+    RD(us->lowerq)->q_ptr = 0;
+    us->lowerq = 0;
+    UNLOCK_LOWER;
+
+    /* Unblock streams which now feed back up the control stream. */
+    qenable(us->q);
+
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+static int
+pppuwsrv(q)
+    queue_t *q;
+{
+    upperstr_t *us, *as;
+    mblk_t *mp;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("pppuwsrv: q_ptr = 0!\n");
+       return 0;
+    }
+
+    /*
+     * If this is a control stream, then this service procedure
+     * probably got enabled because of flow control in the lower
+     * stream being enabled (or because of the lower stream going
+     * away).  Therefore we enable the service procedure of all
+     * attached upper streams.
+     */
+    if (us->flags & US_CONTROL) {
+       for (as = us->next; as != 0; as = as->next)
+           qenable(WR(as->q));
+    }
+
+    /* Try to send on any data queued here. */
+    us->flags &= ~US_BLOCKED;
+    while ((mp = getq(q)) != 0) {
+       if (!send_data(mp, us)) {
+           putbq(q, mp);
+           break;
+       }
+    }
+
+    return 0;
+}
+
+/* should never get called... */
+static int
+ppplwput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    putnext(q, mp);
+    return 0;
+}
+
+static int
+ppplwsrv(q)
+    queue_t *q;
+{
+    queue_t *uq;
+
+    /*
+     * Flow control has back-enabled this stream:
+     * enable the upper write service procedure for
+     * the upper control stream for this lower stream.
+     */
+    LOCK_LOWER_R;
+    uq = (queue_t *) q->q_ptr;
+    if (uq != 0)
+       qenable(uq);
+    UNLOCK_LOWER;
+    return 0;
+}
+
+/*
+ * This should only get called for control streams.
+ */
+static int
+pppurput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *ppa, *us;
+    int proto, len;
+    struct iocblk *iop;
+
+    ppa = (upperstr_t *) q->q_ptr;
+    if (ppa == 0) {
+       DPRINT("pppurput: q_ptr = 0!\n");
+       return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_CTL:
+       MT_ENTER(&ppa->stats_lock);
+       switch (*mp->b_rptr) {
+       case PPPCTL_IERROR:
+#ifdef INCR_IERRORS
+           INCR_IERRORS(ppa);
+#endif
+           ppa->stats.ppp_ierrors++;
+           break;
+       case PPPCTL_OERROR:
+#ifdef INCR_OERRORS
+           INCR_OERRORS(ppa);
+#endif
+           ppa->stats.ppp_oerrors++;
+           break;
+       }
+       MT_EXIT(&ppa->stats_lock);
+       freemsg(mp);
+       break;
+
+    case M_IOCACK:
+    case M_IOCNAK:
+       /*
+        * Attempt to match up the response with the stream
+        * that the request came from.
+        */
+       iop = (struct iocblk *) mp->b_rptr;
+       for (us = ppa; us != 0; us = us->next)
+           if (us->ioc_id == iop->ioc_id)
+               break;
+       if (us == 0)
+           freemsg(mp);
+       else
+           putnext(us->q, mp);
+       break;
+
+    case M_HANGUP:
+       /*
+        * The serial device has hung up.  We don't want to send
+        * the M_HANGUP message up to pppd because that will stop
+        * us from using the control stream any more.  Instead we
+        * send a zero-length message as an end-of-file indication.
+        */
+       freemsg(mp);
+       mp = allocb(1, BPRI_HI);
+       if (mp == 0) {
+           DPRINT1("ppp/%d: couldn't allocate eof message!\n", ppa->mn);
+           break;
+       }
+       putnext(ppa->q, mp);
+       break;
+
+    default:
+       if (mp->b_datap->db_type == M_DATA) {
+           len = msgdsize(mp);
+           if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN) {
+               PULLUP(mp, PPP_HDRLEN);
+               if (mp == 0) {
+                   DPRINT1("ppp_urput: msgpullup failed (len=%d)\n", len);
+                   break;
+               }
+           }
+           MT_ENTER(&ppa->stats_lock);
+           ppa->stats.ppp_ipackets++;
+           ppa->stats.ppp_ibytes += len;
+#ifdef INCR_IPACKETS
+           INCR_IPACKETS(ppa);
+#endif
+           MT_EXIT(&ppa->stats_lock);
+
+           proto = PPP_PROTOCOL(mp->b_rptr);
+
+#if defined(SOL2)
+           /*
+            * Should there be any promiscuous stream(s), send the data
+            * up for each promiscuous stream that we recognize.
+            */
+           promisc_sendup(ppa, mp, proto, 1);
+#endif /* defined(SOL2) */
+
+           if (proto < 0x8000 && (us = find_dest(ppa, proto)) != 0) {
+               /*
+                * A data packet for some network protocol.
+                * Queue it on the upper stream for that protocol.
+                * XXX could we just putnext it?  (would require thought)
+                * The rblocked flag is there to ensure that we keep
+                * messages in order for each network protocol.
+                */
+               if (!pass_packet(us, mp, 0))
+                   break;
+               if (!us->rblocked && !canput(us->q))
+                   us->rblocked = 1;
+               if (!us->rblocked)
+                   putq(us->q, mp);
+               else
+                   putq(q, mp);
+               break;
+           }
+       }
+       /*
+        * A control frame, a frame for an unknown protocol,
+        * or some other message type.
+        * Send it up to pppd via the control stream.
+        */
+       if (queclass(mp) == QPCTL || canputnext(ppa->q))
+           putnext(ppa->q, mp);
+       else
+           putq(q, mp);
+       break;
+    }
+
+    return 0;
+}
+
+static int
+pppursrv(q)
+    queue_t *q;
+{
+    upperstr_t *us, *as;
+    mblk_t *mp, *hdr;
+#ifndef NO_DLPI
+    dl_unitdata_ind_t *ud;
+#endif
+    int proto;
+
+    us = (upperstr_t *) q->q_ptr;
+    if (us == 0) {
+       DPRINT("pppursrv: q_ptr = 0!\n");
+       return 0;
+    }
+
+    if (us->flags & US_CONTROL) {
+       /*
+        * A control stream.
+        * If there is no lower queue attached, run the write service
+        * routines of other upper streams attached to this PPA.
+        */
+       if (us->lowerq == 0) {
+           as = us;
+           do {
+               if (as->flags & US_BLOCKED)
+                   qenable(WR(as->q));
+               as = as->next;
+           } while (as != 0);
+       }
+
+       /*
+        * Messages get queued on this stream's read queue if they
+        * can't be queued on the read queue of the attached stream
+        * that they are destined for.  This is for flow control -
+        * when this queue fills up, the lower read put procedure will
+        * queue messages there and the flow control will propagate
+        * down from there.
+        */
+       while ((mp = getq(q)) != 0) {
+           proto = PPP_PROTOCOL(mp->b_rptr);
+           if (proto < 0x8000 && (as = find_dest(us, proto)) != 0) {
+               if (!canput(as->q))
+                   break;
+               putq(as->q, mp);
+           } else {
+               if (!canputnext(q))
+                   break;
+               putnext(q, mp);
+           }
+       }
+       if (mp) {
+           putbq(q, mp);
+       } else {
+           /* can now put stuff directly on network protocol streams again */
+           for (as = us->next; as != 0; as = as->next)
+               as->rblocked = 0;
+       }
+
+       /*
+        * If this stream has a lower stream attached,
+        * enable the read queue's service routine.
+        * XXX we should really only do this if the queue length
+        * has dropped below the low-water mark.
+        */
+       if (us->lowerq != 0)
+           qenable(RD(us->lowerq));
+               
+    } else {
+       /*
+        * A network protocol stream.  Put a DLPI header on each
+        * packet and send it on.
+        * (Actually, it seems that the IP module will happily
+        * accept M_DATA messages without the DL_UNITDATA_IND header.)
+        */
+       while ((mp = getq(q)) != 0) {
+           if (!canputnext(q)) {
+               putbq(q, mp);
+               break;
+           }
+#ifndef NO_DLPI
+           proto = PPP_PROTOCOL(mp->b_rptr);
+           mp->b_rptr += PPP_HDRLEN;
+           hdr = allocb(sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint),
+                        BPRI_MED);
+           if (hdr == 0) {
+               /* XXX should put it back and use bufcall */
+               freemsg(mp);
+               continue;
+           }
+           hdr->b_datap->db_type = M_PROTO;
+           ud = (dl_unitdata_ind_t *) hdr->b_wptr;
+           hdr->b_wptr += sizeof(dl_unitdata_ind_t) + 2 * sizeof(uint);
+           hdr->b_cont = mp;
+           ud->dl_primitive = DL_UNITDATA_IND;
+           ud->dl_dest_addr_length = sizeof(uint);
+           ud->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+           ud->dl_src_addr_length = sizeof(uint);
+           ud->dl_src_addr_offset = ud->dl_dest_addr_offset + sizeof(uint);
+#if DL_CURRENT_VERSION >= 2
+           ud->dl_group_address = 0;
+#endif
+           /* Send the DLPI client the data with the SAP they requested,
+              (e.g. ETHERTYPE_IP) rather than the PPP protocol number
+              (e.g. PPP_IP) */
+           ((uint *)(ud + 1))[0] = us->req_sap;        /* dest SAP */
+           ((uint *)(ud + 1))[1] = us->req_sap;        /* src SAP */
+           putnext(q, hdr);
+#else /* NO_DLPI */
+           putnext(q, mp);
+#endif /* NO_DLPI */
+       }
+       /*
+        * Now that we have consumed some packets from this queue,
+        * enable the control stream's read service routine so that we
+        * can process any packets for us that might have got queued
+        * there for flow control reasons.
+        */
+       if (us->ppa)
+           qenable(us->ppa->q);
+    }
+
+    return 0;
+}
+
+static upperstr_t *
+find_dest(ppa, proto)
+    upperstr_t *ppa;
+    int proto;
+{
+    upperstr_t *us;
+
+    for (us = ppa->next; us != 0; us = us->next)
+       if (proto == us->sap)
+           break;
+    return us;
+}
+
+#if defined (SOL2)
+/*
+ * Test upstream promiscuous conditions. As of now, only pass IPv4 and
+ * Ipv6 packets upstream (let PPP packets be decoded elsewhere).
+ */
+static upperstr_t *
+find_promisc(us, proto)
+    upperstr_t *us;
+    int proto;
+{
+
+    if ((proto != PPP_IP) && (proto != PPP_IPV6))
+       return (upperstr_t *)0;
+
+    for ( ; us; us = us->next) {
+       if ((us->flags & US_PROMISC) && (us->state == DL_IDLE))
+           return us;
+    }
+
+    return (upperstr_t *)0;
+}
+
+/*
+ * Prepend an empty Ethernet header to msg for snoop, et al.
+ */
+static mblk_t *
+prepend_ether(us, mp, proto)
+    upperstr_t *us;
+    mblk_t *mp;
+    int proto;
+{
+    mblk_t *eh;
+    int type;
+
+    if ((eh = allocb(sizeof(struct ether_header), BPRI_HI)) == 0) {
+       freemsg(mp);
+       return (mblk_t *)0;
+    }
+
+    if (proto == PPP_IP)
+       type = ETHERTYPE_IP;
+    else if (proto == PPP_IPV6)
+       type = ETHERTYPE_IPV6;
+    else 
+       type = proto;       /* What else? Let decoder decide */
+
+    eh->b_wptr += sizeof(struct ether_header);
+    bzero((caddr_t)eh->b_rptr, sizeof(struct ether_header));
+    ((struct ether_header *)eh->b_rptr)->ether_type = htons((short)type);
+    eh->b_cont = mp;
+    return (eh);
+}
+
+/*
+ * Prepend DL_UNITDATA_IND mblk to msg
+ */
+static mblk_t *
+prepend_udind(us, mp, proto)
+    upperstr_t *us;
+    mblk_t *mp;
+    int proto;
+{
+    dl_unitdata_ind_t *dlu;
+    mblk_t *dh;
+    size_t size;
+
+    size = sizeof(dl_unitdata_ind_t);
+    if ((dh = allocb(size, BPRI_MED)) == 0) {
+       freemsg(mp);
+       return (mblk_t *)0;
+    }
+
+    dh->b_datap->db_type = M_PROTO;
+    dh->b_wptr = dh->b_datap->db_lim;
+    dh->b_rptr = dh->b_wptr - size;
+
+    dlu = (dl_unitdata_ind_t *)dh->b_rptr;
+    dlu->dl_primitive = DL_UNITDATA_IND;
+    dlu->dl_dest_addr_length = 0;
+    dlu->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
+    dlu->dl_src_addr_length = 0;
+    dlu->dl_src_addr_offset = sizeof(dl_unitdata_ind_t);
+    dlu->dl_group_address = 0;
+
+    dh->b_cont = mp;
+    return (dh);
+}
+
+/*
+ * For any recognized promiscuous streams, send data upstream
+ */
+static void
+promisc_sendup(ppa, mp, proto, skip)
+    upperstr_t *ppa;
+    mblk_t *mp;
+    int proto, skip;
+{
+    mblk_t *dup_mp, *dup_dup_mp;
+    upperstr_t *prus, *nprus;
+
+    if ((prus = find_promisc(ppa, proto)) != 0) {
+       if (dup_mp = dupmsg(mp)) {
+
+           if (skip)
+               dup_mp->b_rptr += PPP_HDRLEN;
+
+           for ( ; nprus = find_promisc(prus->next, proto); 
+                   prus = nprus) {
+
+               if (dup_dup_mp = dupmsg(dup_mp)) {
+                   if (canputnext(prus->q)) {
+                       if (prus->flags & US_RAWDATA) {
+                           dup_dup_mp = prepend_ether(prus, dup_dup_mp, proto);
+                           putnext(prus->q, dup_dup_mp);
+                       } else {
+                           dup_dup_mp = prepend_udind(prus, dup_dup_mp, proto);
+                           putnext(prus->q, dup_dup_mp);
+                       }
+                   } else {
+                       DPRINT("ppp_urput: data to promisc q dropped\n");
+                       freemsg(dup_dup_mp);
+                   }
+               }
+           }
+
+           if (canputnext(prus->q)) {
+               if (prus->flags & US_RAWDATA) {
+                   dup_mp = prepend_ether(prus, dup_mp, proto);
+                   putnext(prus->q, dup_mp);
+               } else {
+                   dup_mp = prepend_udind(prus, dup_mp, proto);
+                   putnext(prus->q, dup_mp);
+               }
+           } else {
+               DPRINT("ppp_urput: data to promisc q dropped\n");
+               freemsg(dup_mp);
+           }
+       }
+    }
+}
+#endif /* defined(SOL2) */
+
+/*
+ * We simply put the message on to the associated upper control stream
+ * (either here or in ppplrsrv).  That way we enter the perimeters
+ * before looking through the list of attached streams to decide which
+ * stream it should go up.
+ */
+static int
+ppplrput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    queue_t *uq;
+    struct iocblk *iop;
+
+    switch (mp->b_datap->db_type) {
+    case M_IOCTL:
+       iop = (struct iocblk *) mp->b_rptr;
+       iop->ioc_error = EINVAL;
+       mp->b_datap->db_type = M_IOCNAK;
+       qreply(q, mp);
+       return 0;
+    case M_FLUSH:
+       if (*mp->b_rptr & FLUSHR)
+           flushq(q, FLUSHDATA);
+       if (*mp->b_rptr & FLUSHW) {
+           *mp->b_rptr &= ~FLUSHR;
+           qreply(q, mp);
+       } else
+           freemsg(mp);
+       return 0;
+    }
+
+    /*
+     * If we can't get the lower lock straight away, queue this one
+     * rather than blocking, to avoid the possibility of deadlock.
+     */
+    if (!TRYLOCK_LOWER_R) {
+       putq(q, mp);
+       return 0;
+    }
+
+    /*
+     * Check that we're still connected to the driver.
+     */
+    uq = (queue_t *) q->q_ptr;
+    if (uq == 0) {
+       UNLOCK_LOWER;
+       DPRINT1("ppplrput: q = %x, uq = 0??\n", q);
+       freemsg(mp);
+       return 0;
+    }
+
+    /*
+     * Try to forward the message to the put routine for the upper
+     * control stream for this lower stream.
+     * If there are already messages queued here, queue this one so
+     * they don't get out of order.
+     */
+    if (queclass(mp) == QPCTL || (qsize(q) == 0 && canput(uq)))
+       put(uq, mp);
+    else
+       putq(q, mp);
+
+    UNLOCK_LOWER;
+    return 0;
+}
+
+static int
+ppplrsrv(q)
+    queue_t *q;
+{
+    mblk_t *mp;
+    queue_t *uq;
+
+    /*
+     * Packets get queued here for flow control reasons
+     * or if the lrput routine couldn't get the lower lock
+     * without blocking.
+     */
+    LOCK_LOWER_R;
+    uq = (queue_t *) q->q_ptr;
+    if (uq == 0) {
+       UNLOCK_LOWER;
+       flushq(q, FLUSHALL);
+       DPRINT1("ppplrsrv: q = %x, uq = 0??\n", q);
+       return 0;
+    }
+    while ((mp = getq(q)) != 0) {
+       if (queclass(mp) == QPCTL || canput(uq))
+           put(uq, mp);
+       else {
+           putbq(q, mp);
+           break;
+       }
+    }
+    UNLOCK_LOWER;
+    return 0;
+}
+
+static int
+putctl2(q, type, code, val)
+    queue_t *q;
+    int type, code, val;
+{
+    mblk_t *mp;
+
+    mp = allocb(2, BPRI_HI);
+    if (mp == 0)
+       return 0;
+    mp->b_datap->db_type = type;
+    mp->b_wptr[0] = code;
+    mp->b_wptr[1] = val;
+    mp->b_wptr += 2;
+    putnext(q, mp);
+    return 1;
+}
+
+static int
+putctl4(q, type, code, val)
+    queue_t *q;
+    int type, code, val;
+{
+    mblk_t *mp;
+
+    mp = allocb(4, BPRI_HI);
+    if (mp == 0)
+       return 0;
+    mp->b_datap->db_type = type;
+    mp->b_wptr[0] = code;
+    ((short *)mp->b_wptr)[1] = val;
+    mp->b_wptr += 4;
+    putnext(q, mp);
+    return 1;
+}
+
+static void
+debug_dump(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    upperstr_t *us;
+    queue_t *uq, *lq;
+
+    DPRINT("ppp upper streams:\n");
+    for (us = minor_devs; us != 0; us = us->nextmn) {
+       uq = us->q;
+       DPRINT3(" %d: q=%x rlev=%d",
+               us->mn, uq, (uq? qsize(uq): 0));
+       DPRINT3(" wlev=%d flags=0x%b", (uq? qsize(WR(uq)): 0),
+               us->flags, "\020\1priv\2control\3blocked\4last");
+       DPRINT3(" state=%x sap=%x req_sap=%x", us->state, us->sap,
+               us->req_sap);
+       if (us->ppa == 0)
+           DPRINT(" ppa=?\n");
+       else
+           DPRINT1(" ppa=%d\n", us->ppa->ppa_id);
+       if (us->flags & US_CONTROL) {
+           lq = us->lowerq;
+           DPRINT3("    control for %d lq=%x rlev=%d",
+                   us->ppa_id, lq, (lq? qsize(RD(lq)): 0));
+           DPRINT3(" wlev=%d mru=%d mtu=%d\n",
+                   (lq? qsize(lq): 0), us->mru, us->mtu);
+       }
+    }
+    mp->b_datap->db_type = M_IOCACK;
+    qreply(q, mp);
+}
+
+#ifdef FILTER_PACKETS
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+
+#define MAX_IPHDR    128     /* max TCP/IP header size */
+
+
+/* The following table contains a hard-coded list of protocol/port pairs.
+ * Any matching packets are either discarded unconditionally, or, 
+ * if ok_if_link_up is non-zero when a connection does not currently exist
+ * (i.e., they go through if the connection is present, but never initiate
+ * a dial-out).
+ * This idea came from a post by dm@garage.uun.org (David Mazieres)
+ */
+static struct pktfilt_tab { 
+       int proto; 
+       u_short port; 
+       u_short ok_if_link_up; 
+} pktfilt_tab[] = {
+       { IPPROTO_UDP,  520,    1 },    /* RIP, ok to pass if link is up */
+       { IPPROTO_UDP,  123,    1 },    /* NTP, don't keep up the link for it */
+       { -1,           0,      0 }     /* terminator entry has port == -1 */
+};
+
+
+static int
+ip_hard_filter(us, mp, outbound)
+    upperstr_t *us;
+    mblk_t *mp;
+    int outbound;
+{
+    struct ip *ip;
+    struct pktfilt_tab *pft;
+    mblk_t *temp_mp;
+    int proto;
+    int len, hlen;
+
+
+    /* Note, the PPP header has already been pulled up in all cases */
+    proto = PPP_PROTOCOL(mp->b_rptr);
+    if (us->flags & US_DBGLOG)
+        DPRINT3("ppp/%d: filter, proto=0x%x, out=%d\n", us->mn, proto, outbound);
+
+    switch (proto)
+    {
+    case PPP_IP:
+       if ((mp->b_wptr - mp->b_rptr) == PPP_HDRLEN && mp->b_cont != 0) {
+           temp_mp = mp->b_cont;
+           len = msgdsize(temp_mp);
+           hlen = (len < MAX_IPHDR) ? len : MAX_IPHDR;
+           PULLUP(temp_mp, hlen);
+           if (temp_mp == 0) {
+               DPRINT2("ppp/%d: filter, pullup next failed, len=%d\n", 
+                       us->mn, hlen);
+               mp->b_cont = 0;         /* PULLUP() freed the rest */
+               freemsg(mp);
+               return 0;
+           }
+           ip = (struct ip *)mp->b_cont->b_rptr;
+       }
+       else {
+           len = msgdsize(mp);
+           hlen = (len < (PPP_HDRLEN+MAX_IPHDR)) ? len : (PPP_HDRLEN+MAX_IPHDR);
+           PULLUP(mp, hlen);
+           if (mp == 0) {
+               DPRINT2("ppp/%d: filter, pullup failed, len=%d\n", 
+                       us->mn, hlen);
+               return 0;
+           }
+           ip = (struct ip *)(mp->b_rptr + PPP_HDRLEN);
+       }
+
+       /* For IP traffic, certain packets (e.g., RIP) may be either
+        *   1.  ignored - dropped completely
+        *   2.  will not initiate a connection, but
+        *       will be passed if a connection is currently up.
+        */
+       for (pft=pktfilt_tab; pft->proto != -1; pft++) {
+           if (ip->ip_p == pft->proto) {
+               switch(pft->proto) {
+               case IPPROTO_UDP:
+                   if (((struct udphdr *) &((int *)ip)[ip->ip_hl])->uh_dport
+                               == htons(pft->port)) goto endfor;
+                   break;
+               case IPPROTO_TCP:
+                   if (((struct tcphdr *) &((int *)ip)[ip->ip_hl])->th_dport
+                               == htons(pft->port)) goto endfor;
+                   break;
+               }       
+           }
+       }
+       endfor:
+       if (pft->proto != -1) {
+           if (us->flags & US_DBGLOG)
+               DPRINT3("ppp/%d: found IP pkt, proto=0x%x (%d)\n", 
+                               us->mn, pft->proto, pft->port);
+           /* Discard if not connected, or if not pass_with_link_up */
+           /* else, if link is up let go by, but don't update time */
+           return pft->ok_if_link_up? -1: 0;
+       }
+        break;
+    } /* end switch (proto) */
+
+    return 1;
+}
+#endif /* FILTER_PACKETS */
+
diff --git a/solaris/ppp.conf b/solaris/ppp.conf
new file mode 100644 (file)
index 0000000..e443a7a
--- /dev/null
@@ -0,0 +1 @@
+name="ppp" parent="pseudo" instance=0;
diff --git a/solaris/ppp_ahdlc.c b/solaris/ppp_ahdlc.c
new file mode 100644 (file)
index 0000000..a58d955
--- /dev/null
@@ -0,0 +1,878 @@
+/*
+ * ppp_ahdlc.c - STREAMS module for doing PPP asynchronous HDLC.
+ *
+ * Re-written by Adi Masputra <adi.masputra@sun.com>, based on 
+ * the original ppp_ahdlc.c
+ *
+ * Copyright (c) 2000 by Sun Microsystems, Inc.
+ * 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.  
+ *
+ * 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.1 2000/04/18 23:51:28 masputra Exp $
+ */
+
+/*
+ * This file is used under Solaris 2, SVR4, SunOS 4, and Digital UNIX.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/errno.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+/*
+ * Right now, mutex is only enabled for Solaris 2.x
+ */
+#if defined(SOL2)
+#define USE_MUTEX
+#endif /* SOL2 */
+
+/*
+ * intpointer_t and uintpointer_t are signed and unsigned integer types 
+ * large enough to hold any data pointer; that is, data pointers can be 
+ * assigned into or from these integer types without losing precision.
+ * On recent Solaris releases, these types are defined in sys/int_types.h,
+ * but not on SunOS 4.x or the earlier Solaris versions.
+ */
+#if defined(_LP64) || defined(_I32LPx)
+typedef long                    intpointer_t;
+typedef unsigned long           uintpointer_t;
+#else
+typedef int                     intpointer_t;
+typedef unsigned int            uintpointer_t;
+#endif
+
+MOD_OPEN_DECL(ahdlc_open);
+MOD_CLOSE_DECL(ahdlc_close);
+static int ahdlc_wput __P((queue_t *, mblk_t *));
+static int ahdlc_rput __P((queue_t *, mblk_t *));
+static void ahdlc_encode __P((queue_t *, mblk_t *));
+static void ahdlc_decode __P((queue_t *, mblk_t *));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+#if defined(SOL2)
+/*
+ * Don't send HDLC start flag is last transmit is within 1.5 seconds -
+ * FLAG_TIME is defined is microseconds
+ */
+#define FLAG_TIME   1500
+#define ABS(x)     (x >= 0 ? x : (-x))
+#endif /* SOL2 */
+
+/*
+ * Extract byte i of message mp 
+ */
+#define MSG_BYTE(mp, i)        ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+                        msg_byte((mp), (i)))
+
+/* 
+ * Is this LCP packet one we have to transmit using LCP defaults? 
+ */
+#define LCP_USE_DFLT(mp)       (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+/*
+ * Standard STREAMS declarations
+ */
+static struct module_info minfo = {
+    0x7d23, "ppp_ahdl", 0, INFPSZ, 32768, 512
+};
+
+static struct qinit rinit = {
+    ahdlc_rput, NULL, ahdlc_open, ahdlc_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+    ahdlc_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int phdldevflag = 0;
+#define ppp_ahdlcinfo phdlinfo
+#endif /* defined(SVR4) && !defined(SOL2) */
+
+struct streamtab ppp_ahdlcinfo = {
+    &rinit,                        /* ptr to st_rdinit */
+    &winit,                        /* ptr to st_wrinit */
+    NULL,                          /* ptr to st_muxrinit */
+    NULL,                          /* ptr to st_muxwinit */
+#if defined(SUNOS4)
+    NULL                           /* ptr to ptr to st_modlist */
+#endif /* SUNOS4 */
+};
+
+#if defined(SUNOS4)
+int ppp_ahdlc_count = 0;           /* open counter */
+#endif /* SUNOS4 */
+
+/*
+ * Per-stream state structure
+ */
+typedef struct ahdlc_state {
+#if defined(USE_MUTEX)
+    kmutex_t       lock;                   /* lock for this structure */
+#endif /* USE_MUTEX */
+    int                    flags;                  /* link flags */
+    mblk_t         *rx_buf;                /* ptr to receive buffer */
+    int                    rx_buf_size;            /* receive buffer size */
+    ushort_t       infcs;                  /* calculated rx HDLC FCS */
+    u_int32_t      xaccm[8];               /* 256-bit xmit ACCM */
+    u_int32_t      raccm;                  /* 32-bit rcv ACCM */
+    int                    mtu;                    /* interface MTU */
+    int                    mru;                    /* link MRU */
+    int                    unit;                   /* current PPP unit number */
+    struct pppstat  stats;                 /* statistic structure */
+#if defined(SOL2)
+    clock_t        flag_time;              /* time in usec between flags */
+    clock_t        lbolt;                  /* last updated lbolt */
+#endif /* SOL2 */
+} ahdlc_state_t;
+
+/*
+ * Values for flags 
+ */
+#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)
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short 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
+};
+
+static u_int32_t paritytab[8] =
+{
+       0x96696996, 0x69969669, 0x69969669, 0x96696996,
+       0x69969669, 0x96696996, 0x96696996, 0x69969669
+};
+
+/*
+ * STREAMS module open (entry) point
+ */
+MOD_OPEN(ahdlc_open)
+{
+    ahdlc_state_t   *state;
+
+    /*
+     * Return if it's already opened
+     */
+    if (q->q_ptr) {
+       return 0;
+    }
+
+    /*
+     * This can only be opened as a module
+     */
+    if (sflag != MODOPEN) {
+       return 0;
+    }
+
+    state = (ahdlc_state_t *) ALLOC_NOSLEEP(sizeof(ahdlc_state_t));
+    if (state == 0)
+       OPEN_ERROR(ENOSR);
+    bzero((caddr_t) state, sizeof(ahdlc_state_t));
+
+    q->q_ptr    = (caddr_t) state;
+    WR(q)->q_ptr = (caddr_t) state;
+
+#if defined(USE_MUTEX)
+    mutex_init(&state->lock, NULL, MUTEX_DEFAULT, NULL);
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    state->xaccm[0] = ~0;          /* escape 0x00 through 0x1f */
+    state->xaccm[3] = 0x60000000;   /* escape 0x7d and 0x7e */
+    state->mru     = PPP_MRU;      /* default of 1500 bytes */
+#if defined(SOL2)
+    state->flag_time = drv_usectohz(FLAG_TIME);
+#endif /* SOL2 */
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+#endif /* USE_MUTEX */ 
+
+#if defined(SUNOS4)
+    ppp_ahdlc_count++;
+#endif /* SUNOS4 */
+
+    qprocson(q);
+    
+    return 0;
+}
+
+/*
+ * STREAMS module close (exit) point
+ */
+MOD_CLOSE(ahdlc_close)
+{
+    ahdlc_state_t   *state;
+
+    qprocsoff(q);
+
+    state = (ahdlc_state_t *) q->q_ptr;
+
+    if (state == 0) {
+       DPRINT("state == 0 in ahdlc_close\n");
+       return 0;
+    }
+
+#if defined(USE_MUTEX)
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    if (state->rx_buf != 0) {
+       freemsg(state->rx_buf);
+       state->rx_buf = 0;
+    }
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+    mutex_destroy(&state->lock);
+#endif /* USE_MUTEX */
+
+    FREE(q->q_ptr, sizeof(ahdlc_state_t));
+    q->q_ptr        = NULL;
+    OTHERQ(q)->q_ptr = NULL;
+
+#if defined(SUNOS4)
+    if (ppp_ahdlc_count)
+       ppp_ahdlc_count--;
+#endif /* SUNOS4 */
+    
+    return 0;
+}
+
+/*
+ * Write side put routine
+ */
+static int
+ahdlc_wput(q, mp)
+    queue_t    *q;
+    mblk_t     *mp;
+{
+    ahdlc_state_t      *state;
+    struct iocblk      *iop;
+    int                        error;
+    mblk_t             *np;
+    struct ppp_stats   *psp;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+    if (state == 0) {
+       DPRINT("state == 0 in ahdlc_wput\n");
+       freemsg(mp);
+       return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+       /*
+        * A data packet - do character-stuffing and FCS, and
+        * send it onwards.
+        */
+       ahdlc_encode(q, mp);
+       freemsg(mp);
+       break;
+
+    case M_IOCTL:
+       iop = (struct iocblk *) mp->b_rptr;
+       error = EINVAL;
+       switch (iop->ioc_cmd) {
+       case PPPIO_XACCM:
+           if ((iop->ioc_count < sizeof(u_int32_t)) || 
+               (iop->ioc_count > sizeof(ext_accm))) {
+               break;
+           }
+           if (mp->b_cont == 0) {
+               DPRINT1("ahdlc_wput/%d: PPPIO_XACCM b_cont = 0!\n", state->unit);
+               break;
+           }
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           bcopy((caddr_t)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 */
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           iop->ioc_count = 0;
+           error = 0;
+           break;
+
+       case PPPIO_RACCM:
+           if (iop->ioc_count != sizeof(u_int32_t))
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("ahdlc_wput/%d: PPPIO_RACCM b_cont = 0!\n", state->unit);
+               break;
+           }
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           bcopy((caddr_t)mp->b_cont->b_rptr, (caddr_t)&state->raccm,
+                 sizeof(u_int32_t));
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           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;
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           *(int *)np->b_wptr = state->flags & RCV_FLAGS;
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           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:
+           error = -1;
+           break;
+       }
+
+       if (error < 0)
+           putnext(q, mp);
+       else if (error == 0) {
+           mp->b_datap->db_type = M_IOCACK;
+           qreply(q, mp);
+       } 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:
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           state->mtu = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           freemsg(mp);
+           break;
+       case PPPCTL_MRU:
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           state->mru = ((unsigned short *)mp->b_rptr)[1];
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           freemsg(mp);
+           break;
+       case PPPCTL_UNIT:
+#if defined(USE_MUTEX)
+           mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+           state->unit = mp->b_rptr[1];
+#if defined(USE_MUTEX)
+           mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+           break;
+       default:
+           putnext(q, mp);
+       }
+       break;
+
+    default:
+       putnext(q, mp);
+    }
+
+    return 0;
+}
+
+/*
+ * Read side put routine
+ */
+static int
+ahdlc_rput(q, mp)
+    queue_t *q;
+    mblk_t  *mp;
+{
+    ahdlc_state_t *state;
+
+    state = (ahdlc_state_t *) q->q_ptr;
+    if (state == 0) {
+       DPRINT("state == 0 in ahdlc_rput\n");
+       freemsg(mp);
+       return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+       ahdlc_decode(q, mp);
+       freemsg(mp);
+       break;
+
+    case M_HANGUP:
+#if defined(USE_MUTEX)
+       mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+       if (state->rx_buf != 0) {
+           /* XXX would like to send this up for debugging */
+           freemsg(state->rx_buf);
+           state->rx_buf = 0;
+       }
+       state->flags = IFLUSH;
+#if defined(USE_MUTEX)
+       mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+       putnext(q, mp);
+       break;
+
+    default:
+       putnext(q, mp);
+    }
+    return 0;
+}
+
+/*
+ * Extract bit c from map m, to determine if c needs to be escaped
+ */
+#define IN_TX_MAP(c, m)        ((m)[(c) >> 5] & (1 << ((c) & 0x1f)))
+
+static void
+ahdlc_encode(q, mp)
+    queue_t    *q;
+    mblk_t     *mp;
+{
+    ahdlc_state_t      *state;
+    u_int32_t          *xaccm, loc_xaccm[8];
+    ushort_t           fcs;
+    size_t             outmp_len;
+    mblk_t             *outmp, *tmp;
+    uchar_t            *dp, fcs_val;
+    int                        is_lcp, code;
+#if defined(SOL2)
+    clock_t            lbolt;
+#endif /* SOL2 */
+
+    if (msgdsize(mp) < 4) {
+       return;
+    }
+
+    state = (ahdlc_state_t *)q->q_ptr;
+#if defined(USE_MUTEX)
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    /*
+     * Allocate an output buffer large enough to handle a case where all
+     * characters need to be escaped
+     */
+    outmp_len = (msgdsize(mp)   << 1) +                /* input block x 2 */
+               (sizeof(fcs)     << 2) +                /* HDLC FCS x 4 */
+               (sizeof(uchar_t) << 1);                 /* HDLC flags x 2 */
+
+    outmp = allocb(outmp_len, BPRI_MED);
+    if (outmp == NULL) {
+       state->stats.ppp_oerrors++;
+#if defined(USE_MUTEX)
+       mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+       putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+       return;
+    }
+
+#if defined(SOL2)
+    /*
+     * Check if our last transmit happenned within flag_time, using
+     * the system's LBOLT value in clock ticks
+     */
+    if (drv_getparm(LBOLT, &lbolt) != -1) {
+       if (ABS((clock_t)lbolt - state->lbolt) > state->flag_time) {
+           *outmp->b_wptr++ = PPP_FLAG;
+       } 
+       state->lbolt = lbolt;
+    } else {
+       *outmp->b_wptr++ = PPP_FLAG;
+    }
+#else
+    /*
+     * If the driver below still has a message to process, skip the
+     * HDLC flag, otherwise, put one in the beginning
+     */
+    if (qsize(q->q_next) == 0) {
+       *outmp->b_wptr++ = PPP_FLAG;
+    }
+#endif
+
+    /*
+     * All control characters must be escaped for LCP packets with code
+     * values between 1 (Conf-Req) and 7 (Code-Rej).
+     */
+    is_lcp = ((MSG_BYTE(mp, 0) == PPP_ALLSTATIONS) && 
+             (MSG_BYTE(mp, 1) == PPP_UI) && 
+             (MSG_BYTE(mp, 2) == (PPP_LCP >> 8)) &&
+             (MSG_BYTE(mp, 3) == (PPP_LCP & 0xff)) &&
+             LCP_USE_DFLT(mp));
+
+    xaccm = state->xaccm;
+    if (is_lcp) {
+       bcopy((caddr_t)state->xaccm, (caddr_t)loc_xaccm, sizeof(loc_xaccm));
+       loc_xaccm[0] = ~0;      /* force escape on 0x00 through 0x1f */
+       xaccm = loc_xaccm;
+    }
+
+    fcs = PPP_INITFCS;         /* Initial FCS is 0xffff */
+
+    /*
+     * Process this block and the rest (if any) attached to the this one
+     */
+    for (tmp = mp; tmp; tmp = tmp->b_cont) {
+       if (tmp->b_datap->db_type == M_DATA) {
+           for (dp = tmp->b_rptr; dp < tmp->b_wptr; dp++) {
+               fcs = PPP_FCS(fcs, *dp);
+               if (IN_TX_MAP(*dp, xaccm)) {
+                   *outmp->b_wptr++ = PPP_ESCAPE;
+                   *outmp->b_wptr++ = *dp ^ PPP_TRANS;
+               } else {
+                   *outmp->b_wptr++ = *dp;
+               }
+           }
+       } else {
+           continue;   /* skip if db_type is something other than M_DATA */
+       }
+    }
+
+    /*
+     * Append the HDLC FCS, making sure that escaping is done on any
+     * necessary bytes
+     */
+    fcs_val = (fcs ^ 0xffff) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+       *outmp->b_wptr++ = PPP_ESCAPE;
+       *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+       *outmp->b_wptr++ = fcs_val;
+    }
+
+    fcs_val = ((fcs ^ 0xffff) >> 8) & 0xff;
+    if (IN_TX_MAP(fcs_val, xaccm)) {
+       *outmp->b_wptr++ = PPP_ESCAPE;
+       *outmp->b_wptr++ = fcs_val ^ PPP_TRANS;
+    } else {
+       *outmp->b_wptr++ = fcs_val;
+    }
+
+    /*
+     * And finally, append the HDLC flag, and send it away
+     */
+    *outmp->b_wptr++ = PPP_FLAG;
+
+    state->stats.ppp_obytes += msgdsize(outmp);
+    state->stats.ppp_opackets++;
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+
+    putnext(q, outmp);
+    return;
+}
+
+/*
+ * Checks the 32-bit receive ACCM to see if the byte needs un-escaping
+ */
+#define IN_RX_MAP(c, m)        ((((unsigned int) (uchar_t) (c)) < 0x20) && \
+                       (m) & (1 << (c)))
+
+
+/*
+ * Process received characters.
+ */
+static void
+ahdlc_decode(q, mp)
+    queue_t *q;
+    mblk_t  *mp;
+{
+    ahdlc_state_t   *state;
+    mblk_t         *om;
+    uchar_t        *dp;
+    ushort_t       fcs;
+#if defined(SOL2)
+    mblk_t         *zmp;
+#endif /* SOL2 */
+
+#if defined(SOL2)
+    /*
+     * In case the driver (or something below) doesn't send
+     * data upstream in one message block, concatenate everything
+     */
+    if (!((mp->b_wptr - mp->b_rptr == msgdsize(mp)) && 
+         ((intpointer_t)mp->b_rptr % sizeof(intpointer_t) == 0))) {
+
+       zmp = msgpullup(mp, -1);
+       freemsg(mp);
+       mp = zmp;
+       if (mp == 0)
+           return; 
+    }
+#endif /* SOL2 */
+
+    state = (ahdlc_state_t *) q->q_ptr;
+
+#if defined(USE_MUTEX)
+    mutex_enter(&state->lock);
+#endif /* USE_MUTEX */
+
+    state->stats.ppp_ibytes += msgdsize(mp);
+
+    for (dp = mp->b_rptr; dp < mp->b_wptr; dp++) {
+
+       /*
+        * This should detect the lack of 8-bit communication channel
+        * which is necessary for PPP to work. In addition, it also
+        * checks on the parity.
+        */
+       if (*dp & 0x80)
+           state->flags |= RCV_B7_1;
+       else
+           state->flags |= RCV_B7_0;
+
+       if (paritytab[*dp >> 5] & (1 << (*dp & 0x1f)))
+           state->flags |= RCV_ODDP;
+       else
+           state->flags |= RCV_EVNP;
+
+       /*
+        * So we have a HDLC flag ...
+        */
+       if (*dp == PPP_FLAG) {
+
+           /*
+            * If we think that it marks the beginning of the frame,
+            * then continue to process the next octects
+            */
+           if ((state->flags & IFLUSH) ||
+               (state->rx_buf == 0) ||
+               (msgdsize(state->rx_buf) == 0)) {
+
+               state->flags &= ~IFLUSH;
+               continue;
+           }
+
+           /*
+            * We get here because the above condition isn't true,
+            * in which case the HDLC flag was there to mark the end
+            * of the frame (or so we think)
+            */
+           om = state->rx_buf;
+
+           if (state->infcs == PPP_GOODFCS) {
+               state->stats.ppp_ipackets++;
+               adjmsg(om, -PPP_FCSLEN);
+               putnext(q, om);
+           } else {
+               DPRINT2("ppp%d: bad fcs (len=%d)\n",
+                    state->unit, msgdsize(state->rx_buf));
+               freemsg(state->rx_buf);
+               state->flags &= ~(IFLUSH | ESCAPED);
+               state->stats.ppp_ierrors++;
+               putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+           }
+
+           state->rx_buf = 0;
+           continue;
+       }
+
+       if (state->flags & IFLUSH) {
+           continue;
+       }
+
+       /*
+        * Allocate a receive buffer, large enough to store a frame (after
+        * un-escaping) of at least 1500 octets. 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
+        */ 
+       if (state->rx_buf == 0) {
+           state->rx_buf_size  = (state->mru < PPP_MRU ? PPP_MRU : state->mru);
+           state->rx_buf_size += (sizeof(u_int32_t) << 3);
+           state->rx_buf = allocb(state->rx_buf_size, BPRI_MED);
+
+           /*
+            * If allocation fails, try again on the next frame
+            */
+           if (state->rx_buf == 0) {
+               state->flags |= IFLUSH;
+               continue;
+           }
+           state->flags &= ~(IFLUSH | ESCAPED);
+           state->infcs  = PPP_INITFCS;
+       }
+
+       if (*dp == PPP_ESCAPE) {
+           state->flags |= ESCAPED;
+           continue;
+       }
+
+       /*
+        * Make sure we un-escape the necessary characters, as well as the
+        * ones in our receive async control character map
+        */
+       if (state->flags & ESCAPED) {
+           *dp ^= PPP_TRANS;
+           state->flags &= ~ESCAPED;
+       } else if (IN_RX_MAP(*dp, state->raccm)) 
+           continue;
+
+       /*
+        * Unless the peer lied to us about the negotiated MRU, we should
+        * never get a frame which is too long. If it happens, toss it away
+        * and grab the next incoming one
+        */
+       if (msgdsize(state->rx_buf) < state->rx_buf_size) {
+           state->infcs = PPP_FCS(state->infcs, *dp);
+           *state->rx_buf->b_wptr++ = *dp;
+       } else {
+           DPRINT2("ppp%d: frame too long (%d)\n",
+               state->unit, msgdsize(state->rx_buf));
+           freemsg(state->rx_buf);
+           state->rx_buf     = 0;
+           state->flags     |= IFLUSH;
+       }
+    }
+
+#if defined(USE_MUTEX)
+    mutex_exit(&state->lock);
+#endif /* USE_MUTEX */
+}
+
+static int
+msg_byte(mp, i)
+    mblk_t *mp;
+    unsigned int i;
+{
+    while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+       mp = mp->b_cont;
+    if (mp == 0)
+       return -1;
+    return mp->b_rptr[i];
+}
diff --git a/solaris/ppp_ahdlc_mod.c b/solaris/ppp_ahdlc_mod.c
new file mode 100644 (file)
index 0000000..f81be8a
--- /dev/null
@@ -0,0 +1,49 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_ahdlcinfo;
+
+static struct fmodsw fsw = {
+    "ppp_ahdl",
+    &ppp_ahdlcinfo,
+    D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+    &mod_strmodops,
+    "PPP async HDLC module",
+    &fsw
+};
+
+static struct modlinkage modlinkage = {
+    MODREV_1,
+    (void *) &modlstrmod,
+    NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+    return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+    return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+    struct modinfo *mip;
+{
+    return mod_info(&modlinkage, mip);
+}
diff --git a/solaris/ppp_comp.c b/solaris/ppp_comp.c
new file mode 100644 (file)
index 0000000..8177b83
--- /dev/null
@@ -0,0 +1,1126 @@
+/*
+ * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
+ *
+ * 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_comp.c,v 1.1 2000/04/18 23:51:28 masputra Exp $
+ */
+
+/*
+ * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+
+#ifdef SVR4
+#include <sys/conf.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#else
+#include <sys/user.h>
+#ifdef __osf__
+#include <sys/cmn_err.h>
+#endif
+#endif /* SVR4 */
+
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include "ppp_mod.h"
+
+#ifdef __osf__
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/vjcompress.h>
+
+#define PACKETPTR      mblk_t *
+#include <net/ppp-comp.h>
+
+MOD_OPEN_DECL(ppp_comp_open);
+MOD_CLOSE_DECL(ppp_comp_close);
+static int ppp_comp_rput __P((queue_t *, mblk_t *));
+static int ppp_comp_rsrv __P((queue_t *));
+static int ppp_comp_wput __P((queue_t *, mblk_t *));
+static int ppp_comp_wsrv __P((queue_t *));
+static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
+static int msg_byte __P((mblk_t *, unsigned int));
+
+/* Extract byte i of message mp. */
+#define MSG_BYTE(mp, i)        ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
+                        msg_byte((mp), (i)))
+
+/* Is this LCP packet one we have to transmit using LCP defaults? */
+#define LCP_USE_DFLT(mp)       (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
+
+#define PPP_COMP_ID 0xbadf
+static struct module_info minfo = {
+#ifdef PRIOQ
+    PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384,
+#else
+    PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
+#endif
+};
+
+static struct qinit r_init = {
+    ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
+    NULL, &minfo, NULL
+};
+
+static struct qinit w_init = {
+    ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
+};
+
+#if defined(SVR4) && !defined(SOL2)
+int pcmpdevflag = 0;
+#define ppp_compinfo pcmpinfo
+#endif
+struct streamtab ppp_compinfo = {
+    &r_init, &w_init, NULL, NULL
+};
+
+int ppp_comp_count;            /* number of module instances in use */
+
+#ifdef __osf__
+
+static void ppp_comp_alloc __P((comp_state_t *));
+typedef struct memreq {
+    unsigned char comp_opts[20];
+    int cmd;
+    int thread_status;
+    char *returned_mem;
+} memreq_t;
+
+#endif
+
+typedef struct comp_state {
+    int                flags;
+    int                mru;
+    int                mtu;
+    int                unit;
+    struct compressor *xcomp;
+    void       *xstate;
+    struct compressor *rcomp;
+    void       *rstate;
+    struct vjcompress vj_comp;
+    int                vj_last_ierrors;
+    struct pppstat stats;
+#ifdef __osf__
+    memreq_t   memreq;
+    thread_t   thread;
+#endif
+} comp_state_t;
+
+
+#ifdef __osf__
+extern task_t first_task;
+#endif
+
+/* Bits in flags are as defined in pppio.h. */
+#define CCP_ERR                (CCP_ERROR | CCP_FATALERROR)
+#define LAST_MOD       0x1000000       /* no ppp modules below us */
+#define DBGLOG         0x2000000       /* log debugging stuff */
+
+#define MAX_IPHDR      128     /* max TCP/IP header size */
+#define MAX_VJHDR      20      /* max VJ compressed header size (?) */
+
+#undef MIN             /* just in case */
+#define MIN(a, b)      ((a) < (b)? (a): (b))
+
+/*
+ * List of compressors we know about.
+ */
+
+#if DO_BSD_COMPRESS
+extern struct compressor ppp_bsd_compress;
+#endif
+#if DO_DEFLATE
+extern struct compressor ppp_deflate, ppp_deflate_draft;
+#endif
+
+struct compressor *ppp_compressors[] = {
+#if DO_BSD_COMPRESS
+    &ppp_bsd_compress,
+#endif
+#if DO_DEFLATE
+    &ppp_deflate,
+    &ppp_deflate_draft,
+#endif
+    NULL
+};
+
+/*
+ * STREAMS module entry points.
+ */
+MOD_OPEN(ppp_comp_open)
+{
+    comp_state_t *cp;
+#ifdef __osf__
+    thread_t thread;
+#endif
+
+    if (q->q_ptr == NULL) {
+       cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
+       if (cp == NULL)
+           OPEN_ERROR(ENOSR);
+       bzero((caddr_t)cp, sizeof(comp_state_t));
+       WR(q)->q_ptr = q->q_ptr = (caddr_t) cp;
+       cp->mru = PPP_MRU;
+       cp->mtu = PPP_MTU;
+       cp->xstate = NULL;
+       cp->rstate = NULL;
+       vj_compress_init(&cp->vj_comp, -1);
+#ifdef __osf__
+       if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
+               OPEN_ERROR(ENOSR);
+       cp->thread = thread;
+#endif
+       ++ppp_comp_count;
+       qprocson(q);
+    }
+    return 0;
+}
+
+MOD_CLOSE(ppp_comp_close)
+{
+    comp_state_t *cp;
+
+    qprocsoff(q);
+    cp = (comp_state_t *) q->q_ptr;
+    if (cp != NULL) {
+       if (cp->xstate != NULL)
+           (*cp->xcomp->comp_free)(cp->xstate);
+       if (cp->rstate != NULL)
+           (*cp->rcomp->decomp_free)(cp->rstate);
+#ifdef __osf__
+       if (!cp->thread)
+           printf("ppp_comp_close: NULL thread!\n");
+       else
+           thread_terminate(cp->thread);
+#endif
+       FREE(cp, sizeof(comp_state_t));
+       q->q_ptr = NULL;
+       OTHERQ(q)->q_ptr = NULL;
+       --ppp_comp_count;
+    }
+    return 0;
+}
+
+#ifdef __osf__
+
+/* thread for calling back to a compressor's memory allocator
+ * Needed for Digital UNIX since it's VM can't handle requests
+ * for large amounts of memory without blocking.  The thread
+ * provides a context in which we can call a memory allocator
+ * that may block.
+ */
+static void
+ppp_comp_alloc(comp_state_t *cp)
+{
+    int len, cmd;
+    unsigned char *compressor_options;
+    thread_t thread;
+    void *(*comp_allocator)();
+
+
+#if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2)
+
+    /* In 2.x and earlier the argument gets passed
+     * in the thread structure itself.  Yuck.
+     */
+    thread = current_thread();
+    cp = thread->reply_port;
+    thread->reply_port = PORT_NULL;
+
+#endif
+
+    for (;;) {
+       assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE);
+       thread_block();
+
+       if (thread_should_halt(current_thread()))
+           thread_halt_self();
+       cmd = cp->memreq.cmd;
+       compressor_options = &cp->memreq.comp_opts[0];
+       len = compressor_options[1];
+       if (cmd == PPPIO_XCOMP) {
+           cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len);
+           if (!cp->memreq.returned_mem) {
+               cp->memreq.thread_status = ENOSR;
+           } else {
+               cp->memreq.thread_status = 0;
+           }
+       } else {
+           cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len);
+           if (!cp->memreq.returned_mem) {
+               cp->memreq.thread_status = ENOSR;
+           } else {
+               cp->memreq.thread_status = 0;
+           }
+       }
+    }
+}
+
+#endif /* __osf__ */
+
+/* here's the deal with memory allocation under Digital UNIX.
+ * Some other may also benefit from this...
+ * We can't ask for huge chunks of memory in a context where
+ * the caller can't be put to sleep (like, here.)  The alloc
+ * is likely to fail.  Instead we do this: the first time we
+ * get called, kick off a thread to do the allocation.  Return
+ * immediately to the caller with EAGAIN, as an indication that
+ * they should send down the ioctl again.  By the time the
+ * second call comes in it's likely that the memory allocation
+ * thread will have returned with the requested memory.  We will
+ * continue to return EAGAIN however until the thread has completed.
+ * When it has, we return zero (and the memory) if the allocator
+ * was successful and ENOSR otherwise.
+ *
+ * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
+ * required) to loop for some number of iterations with a small
+ * delay in the loop body (for instance a 1/10-th second "sleep"
+ * via select.)
+ */
+static int
+ppp_comp_wput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    struct iocblk *iop;
+    comp_state_t *cp;
+    int error, len, n;
+    int flags, mask;
+    mblk_t *np;
+    struct compressor **comp;
+    struct ppp_stats *psp;
+    struct ppp_comp_stats *csp;
+    unsigned char *opt_data;
+    int nxslots, nrslots;
+
+    cp = (comp_state_t *) q->q_ptr;
+    if (cp == 0) {
+       DPRINT("cp == 0 in ppp_comp_wput\n");
+       freemsg(mp);
+       return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+
+    case M_DATA:
+       putq(q, mp);
+       break;
+
+    case M_IOCTL:
+       iop = (struct iocblk *) mp->b_rptr;
+       error = EINVAL;
+       switch (iop->ioc_cmd) {
+
+       case PPPIO_CFLAGS:
+           /* set/get CCP state */
+           if (iop->ioc_count != 2 * sizeof(int))
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit);
+               break;
+           }
+           flags = ((int *) mp->b_cont->b_rptr)[0];
+           mask = ((int *) mp->b_cont->b_rptr)[1];
+           cp->flags = (cp->flags & ~mask) | (flags & mask);
+           if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
+               if (cp->xstate != NULL) {
+                   (*cp->xcomp->comp_free)(cp->xstate);
+                   cp->xstate = NULL;
+               }
+               if (cp->rstate != NULL) {
+                   (*cp->rcomp->decomp_free)(cp->rstate);
+                   cp->rstate = NULL;
+               }
+               cp->flags &= ~CCP_ISUP;
+           }
+           error = 0;
+           iop->ioc_count = sizeof(int);
+           ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
+           mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
+           break;
+
+       case PPPIO_VJINIT:
+           /*
+            * Initialize VJ compressor/decompressor
+            */
+           if (iop->ioc_count != 2)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit);
+               break;
+           }
+           nxslots = mp->b_cont->b_rptr[0] + 1;
+           nrslots = mp->b_cont->b_rptr[1] + 1;
+           if (nxslots > MAX_STATES || nrslots > MAX_STATES)
+               break;
+           vj_compress_init(&cp->vj_comp, nxslots);
+           cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+           error = 0;
+           iop->ioc_count = 0;
+           break;
+
+       case PPPIO_XCOMP:
+       case PPPIO_RCOMP:
+           if (iop->ioc_count <= 0)
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit);
+               break;
+           }
+           opt_data = mp->b_cont->b_rptr;
+           len = mp->b_cont->b_wptr - opt_data;
+           if (len > iop->ioc_count)
+               len = iop->ioc_count;
+           if (opt_data[1] < 2 || opt_data[1] > len)
+               break;
+           for (comp = ppp_compressors; *comp != NULL; ++comp)
+               if ((*comp)->compress_proto == opt_data[0]) {
+                   /* here's the handler! */
+                   error = 0;
+#ifndef __osf__
+                   if (iop->ioc_cmd == PPPIO_XCOMP) {
+                       /* A previous call may have fetched memory for a compressor
+                        * that's now being retired or reset.  Free it using it's
+                        * mechanism for freeing stuff.
+                        */
+                       if (cp->xstate != NULL) {
+                           (*cp->xcomp->comp_free)(cp->xstate);
+                           cp->xstate = NULL;
+                       }
+                       cp->xcomp = *comp;
+                       cp->xstate = (*comp)->comp_alloc(opt_data, len);
+                       if (cp->xstate == NULL)
+                           error = ENOSR;
+                   } else {
+                       if (cp->rstate != NULL) {
+                           (*cp->rcomp->decomp_free)(cp->rstate);
+                           cp->rstate = NULL;
+                       }
+                       cp->rcomp = *comp;
+                       cp->rstate = (*comp)->decomp_alloc(opt_data, len);
+                       if (cp->rstate == NULL)
+                           error = ENOSR;
+                   }
+#else
+                   if ((error = cp->memreq.thread_status) != EAGAIN)
+                   if (iop->ioc_cmd == PPPIO_XCOMP) {
+                       if (cp->xstate) {
+                           (*cp->xcomp->comp_free)(cp->xstate);
+                           cp->xstate = 0;
+                       }
+                       /* sanity check for compressor options
+                        */
+                       if (sizeof (cp->memreq.comp_opts) < len) {
+                           printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+                               opt_data[1]);
+                           cp->memreq.thread_status = ENOSR;
+                           cp->memreq.returned_mem = 0;
+                       }
+                       /* fill in request for the thread and kick it off
+                        */
+                       if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+                           bcopy(opt_data, cp->memreq.comp_opts, len);
+                           cp->memreq.cmd = PPPIO_XCOMP;
+                           cp->xcomp = *comp;
+                           error = cp->memreq.thread_status = EAGAIN;
+                           thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+                       } else {
+                           cp->xstate = cp->memreq.returned_mem;
+                           cp->memreq.returned_mem = 0;
+                           cp->memreq.thread_status = 0;
+                       }
+                   } else {
+                       if (cp->rstate) {
+                           (*cp->rcomp->decomp_free)(cp->rstate);
+                           cp->rstate = NULL;
+                       }
+                       if (sizeof (cp->memreq.comp_opts) < len) {
+                           printf("can't handle options for compressor %d (%d)\n", opt_data[0],
+                               opt_data[1]);
+                           cp->memreq.thread_status = ENOSR;
+                           cp->memreq.returned_mem = 0;
+                       }
+                       if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) {
+                           bcopy(opt_data, cp->memreq.comp_opts, len);
+                           cp->memreq.cmd = PPPIO_RCOMP;
+                           cp->rcomp = *comp;
+                           error = cp->memreq.thread_status = EAGAIN;
+                           thread_wakeup((vm_offset_t)&cp->memreq.thread_status);
+                       } else {
+                           cp->rstate = cp->memreq.returned_mem;
+                           cp->memreq.returned_mem = 0;
+                           cp->memreq.thread_status = 0;
+                       }
+                   }
+#endif
+                   break;
+               }
+           iop->ioc_count = 0;
+           break;
+
+       case PPPIO_GETSTAT:
+           if ((cp->flags & LAST_MOD) == 0) {
+               error = -1;     /* let the ppp_ahdl module handle it */
+               break;
+           }
+           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);
+           iop->ioc_count = sizeof(struct ppp_stats);
+           psp->p = cp->stats;
+           psp->vj = cp->vj_comp.stats;
+           error = 0;
+           break;
+
+       case PPPIO_GETCSTAT:
+           np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI);
+           if (np == 0) {
+               error = ENOSR;
+               break;
+           }
+           if (mp->b_cont != 0)
+               freemsg(mp->b_cont);
+           mp->b_cont = np;
+           csp = (struct ppp_comp_stats *) np->b_wptr;
+           np->b_wptr += sizeof(struct ppp_comp_stats);
+           iop->ioc_count = sizeof(struct ppp_comp_stats);
+           bzero((caddr_t)csp, sizeof(struct ppp_comp_stats));
+           if (cp->xstate != 0)
+               (*cp->xcomp->comp_stat)(cp->xstate, &csp->c);
+           if (cp->rstate != 0)
+               (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d);
+           error = 0;
+           break;
+
+       case PPPIO_DEBUG:
+           if (iop->ioc_count != sizeof(int))
+               break;
+           if (mp->b_cont == 0) {
+               DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit);
+               break;
+           }
+           n = *(int *)mp->b_cont->b_rptr;
+           if (n == PPPDBG_LOG + PPPDBG_COMP) {
+               DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
+               cp->flags |= DBGLOG;
+               error = 0;
+               iop->ioc_count = 0;
+           } else {
+               error = -1;
+           }
+           break;
+
+       case PPPIO_LASTMOD:
+           cp->flags |= LAST_MOD;
+           error = 0;
+           break;
+
+       default:
+           error = -1;
+           break;
+       }
+
+       if (error < 0)
+           putnext(q, mp);
+       else if (error == 0) {
+           mp->b_datap->db_type = M_IOCACK;
+           qreply(q, mp);
+       } else {
+           mp->b_datap->db_type = M_IOCNAK;
+           iop->ioc_error = error;
+           iop->ioc_count = 0;
+           qreply(q, mp);
+       }
+       break;
+
+    case M_CTL:
+       switch (*mp->b_rptr) {
+       case PPPCTL_MTU:
+           cp->mtu = ((unsigned short *)mp->b_rptr)[1];
+           break;
+       case PPPCTL_MRU:
+           cp->mru = ((unsigned short *)mp->b_rptr)[1];
+           break;
+       case PPPCTL_UNIT:
+           cp->unit = mp->b_rptr[1];
+           break;
+       }
+       putnext(q, mp);
+       break;
+
+    default:
+       putnext(q, mp);
+    }
+
+    return 0;
+}
+
+static int
+ppp_comp_wsrv(q)
+    queue_t *q;
+{
+    mblk_t *mp, *cmp = NULL;
+    comp_state_t *cp;
+    int len, proto, type, hlen, code;
+    struct ip *ip;
+    unsigned char *vjhdr, *dp;
+
+    cp = (comp_state_t *) q->q_ptr;
+    if (cp == 0) {
+       DPRINT("cp == 0 in ppp_comp_wsrv\n");
+       return 0;
+    }
+
+    while ((mp = getq(q)) != 0) {
+       /* assert(mp->b_datap->db_type == M_DATA) */
+#ifdef PRIOQ
+        if (!bcanputnext(q,mp->b_band))
+#else
+        if (!canputnext(q))
+#endif PRIOQ
+       {
+           putbq(q, mp);
+           break;
+       }
+
+       /*
+        * First check the packet length and work out what the protocol is.
+        */
+       len = msgdsize(mp);
+       if (len < PPP_HDRLEN) {
+           DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len);
+           freemsg(mp);
+           cp->stats.ppp_oerrors++;
+           putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+           continue;
+       }
+       proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3);
+
+       /*
+        * Make sure we've got enough data in the first mblk
+        * and that we are its only user.
+        */
+       if (proto == PPP_CCP)
+           hlen = len;
+       else if (proto == PPP_IP)
+           hlen = PPP_HDRLEN + MAX_IPHDR;
+       else
+           hlen = PPP_HDRLEN;
+       if (hlen > len)
+           hlen = len;
+       if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) {
+           PULLUP(mp, hlen);
+           if (mp == 0) {
+               DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen);
+               cp->stats.ppp_oerrors++;
+               putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
+               continue;
+           }
+       }
+
+       /*
+        * Do VJ compression if requested.
+        */
+       if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
+           ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
+           if (ip->ip_p == IPPROTO_TCP) {
+               type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp,
+                                      (cp->flags & COMP_VJCCID), &vjhdr);
+               switch (type) {
+               case TYPE_UNCOMPRESSED_TCP:
+                   mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
+                   break;
+               case TYPE_COMPRESSED_TCP:
+                   dp = vjhdr - PPP_HDRLEN;
+                   dp[1] = mp->b_rptr[1]; /* copy control field */
+                   dp[0] = mp->b_rptr[0]; /* copy address field */
+                   dp[2] = 0;             /* set protocol field */
+                   dp[3] = proto = PPP_VJC_COMP;
+                   mp->b_rptr = dp;
+                   break;
+               }
+           }
+       }
+
+       /*
+        * Do packet compression if enabled.
+        */
+       if (proto == PPP_CCP)
+           ppp_comp_ccp(q, mp, 0);
+       else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
+                && cp->xstate != NULL) {
+           len = msgdsize(mp);
+           (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
+                       (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0));
+           if (cmp != NULL) {
+#ifdef PRIOQ
+               cmp->b_band=mp->b_band;
+#endif PRIOQ
+               freemsg(mp);
+               mp = cmp;
+           }
+       }
+
+       /*
+        * Do address/control and protocol compression if enabled.
+        */
+       if ((cp->flags & COMP_AC)
+           && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) {
+           mp->b_rptr += 2;    /* drop the address & ctrl fields */
+           if (proto < 0x100 && (cp->flags & COMP_PROT))
+               ++mp->b_rptr;   /* drop the high protocol byte */
+       } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
+           /* shuffle up the address & ctrl fields */
+           mp->b_rptr[2] = mp->b_rptr[1];
+           mp->b_rptr[1] = mp->b_rptr[0];
+           ++mp->b_rptr;
+       }
+
+       cp->stats.ppp_opackets++;
+       cp->stats.ppp_obytes += msgdsize(mp);
+       putnext(q, mp);
+    }
+
+    return 0;
+}
+
+static int
+ppp_comp_rput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    comp_state_t *cp;
+    struct iocblk *iop;
+    struct ppp_stats *psp;
+
+    cp = (comp_state_t *) q->q_ptr;
+    if (cp == 0) {
+       DPRINT("cp == 0 in ppp_comp_rput\n");
+       freemsg(mp);
+       return 0;
+    }
+
+    switch (mp->b_datap->db_type) {
+
+    case M_DATA:
+       putq(q, mp);
+       break;
+
+    case M_IOCACK:
+       iop = (struct iocblk *) mp->b_rptr;
+       switch (iop->ioc_cmd) {
+       case PPPIO_GETSTAT:
+           /*
+            * Catch this on the way back from the ppp_ahdl module
+            * so we can fill in the VJ stats.
+            */
+           if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats))
+               break;
+           psp = (struct ppp_stats *) mp->b_cont->b_rptr;
+           psp->vj = cp->vj_comp.stats;
+           break;
+       }
+       putnext(q, mp);
+       break;
+
+    case M_CTL:
+       switch (mp->b_rptr[0]) {
+       case PPPCTL_IERROR:
+           ++cp->stats.ppp_ierrors;
+           break;
+       case PPPCTL_OERROR:
+           ++cp->stats.ppp_oerrors;
+           break;
+       }
+       putnext(q, mp);
+       break;
+
+    default:
+       putnext(q, mp);
+    }
+
+    return 0;
+}
+
+static int
+ppp_comp_rsrv(q)
+    queue_t *q;
+{
+    int proto, rv, i;
+    mblk_t *mp, *dmp = NULL, *np;
+    uchar_t *dp, *iphdr;
+    comp_state_t *cp;
+    int len, hlen, vjlen;
+    u_int iphlen;
+
+    cp = (comp_state_t *) q->q_ptr;
+    if (cp == 0) {
+       DPRINT("cp == 0 in ppp_comp_rsrv\n");
+       return 0;
+    }
+
+    while ((mp = getq(q)) != 0) {
+       /* assert(mp->b_datap->db_type == M_DATA) */
+       if (!canputnext(q)) {
+           putbq(q, mp);
+           break;
+       }
+
+       len = msgdsize(mp);
+       cp->stats.ppp_ibytes += len;
+       cp->stats.ppp_ipackets++;
+
+       /*
+        * First work out the protocol and where the PPP header ends.
+        */
+       i = 0;
+       proto = MSG_BYTE(mp, 0);
+       if (proto == PPP_ALLSTATIONS) {
+           i = 2;
+           proto = MSG_BYTE(mp, 2);
+       }
+       if ((proto & 1) == 0) {
+           ++i;
+           proto = (proto << 8) + MSG_BYTE(mp, i);
+       }
+       hlen = i + 1;
+
+       /*
+        * Now reconstruct a complete, contiguous PPP header at the
+        * start of the packet.
+        */
+       if (hlen < ((cp->flags & DECOMP_AC)? 0: 2)
+                  + ((cp->flags & DECOMP_PROT)? 1: 2)) {
+           /* count these? */
+           goto bad;
+       }
+       if (mp->b_rptr + hlen > mp->b_wptr) {
+           adjmsg(mp, hlen);   /* XXX check this call */
+           hlen = 0;
+       }
+       if (hlen != PPP_HDRLEN) {
+           /*
+            * We need to put some bytes on the front of the packet
+            * to make a full-length PPP header.
+            * If we can put them in *mp, we do, otherwise we
+            * tack another mblk on the front.
+            * XXX we really shouldn't need to carry around
+            * the address and control at this stage.
+            */
+           dp = mp->b_rptr + hlen - PPP_HDRLEN;
+           if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) {
+               np = allocb(PPP_HDRLEN, BPRI_MED);
+               if (np == 0)
+                   goto bad;
+               np->b_cont = mp;
+               mp->b_rptr += hlen;
+               mp = np;
+               dp = mp->b_wptr;
+               mp->b_wptr += PPP_HDRLEN;
+           } else
+               mp->b_rptr = dp;
+
+           dp[0] = PPP_ALLSTATIONS;
+           dp[1] = PPP_UI;
+           dp[2] = proto >> 8;
+           dp[3] = proto;
+       }
+
+       /*
+        * Now see if we have a compressed packet to decompress,
+        * or a CCP packet to take notice of.
+        */
+       proto = PPP_PROTOCOL(mp->b_rptr);
+       if (proto == PPP_CCP) {
+           len = msgdsize(mp);
+           if (mp->b_wptr < mp->b_rptr + len) {
+               PULLUP(mp, len);
+               if (mp == 0)
+                   goto bad;
+           }
+           ppp_comp_ccp(q, mp, 1);
+       } else if (proto == PPP_COMP) {
+           if ((cp->flags & CCP_ISUP)
+               && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
+               && (cp->flags & CCP_ERR) == 0) {
+               rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
+               switch (rv) {
+               case DECOMP_OK:
+                   freemsg(mp);
+                   mp = dmp;
+                   if (mp == NULL) {
+                       /* no error, but no packet returned either. */
+                       continue;
+                   }
+                   break;
+               case DECOMP_ERROR:
+                   cp->flags |= CCP_ERROR;
+                   ++cp->stats.ppp_ierrors;
+                   putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+                   break;
+               case DECOMP_FATALERROR:
+                   cp->flags |= CCP_FATALERROR;
+                   ++cp->stats.ppp_ierrors;
+                   putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+                   break;
+               }
+           }
+       } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+           (*cp->rcomp->incomp)(cp->rstate, mp);
+       }
+
+       /*
+        * Now do VJ decompression.
+        */
+       proto = PPP_PROTOCOL(mp->b_rptr);
+       if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
+           len = msgdsize(mp) - PPP_HDRLEN;
+           if ((cp->flags & DECOMP_VJC) == 0 || len <= 0)
+               goto bad;
+
+           /*
+            * Advance past the ppp header.
+            * Here we assume that the whole PPP header is in the first mblk.
+            */
+           np = mp;
+           dp = np->b_rptr + PPP_HDRLEN;
+           if (dp >= mp->b_wptr) {
+               np = np->b_cont;
+               dp = np->b_rptr;
+           }
+
+           /*
+            * Make sure we have sufficient contiguous data at this point.
+            */
+           hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR;
+           if (hlen > len)
+               hlen = len;
+           if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) {
+               PULLUP(mp, hlen + PPP_HDRLEN);
+               if (mp == 0)
+                   goto bad;
+               np = mp;
+               dp = np->b_rptr + PPP_HDRLEN;
+           }
+
+           if (proto == PPP_VJC_COMP) {
+               /*
+                * Decompress VJ-compressed packet.
+                * First reset compressor if an input error has occurred.
+                */
+               if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT1("ppp%d: resetting VJ\n", cp->unit);
+                   vj_uncompress_err(&cp->vj_comp);
+                   cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+               }
+
+               vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
+                                         &cp->vj_comp, &iphdr, &iphlen);
+               if (vjlen < 0) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
+                               cp->unit, len);
+                   ++cp->vj_last_ierrors;  /* so we don't reset next time */
+                   goto bad;
+               }
+
+               /* drop ppp and vj headers off */
+               if (mp != np) {
+                   freeb(mp);
+                   mp = np;
+               }
+               mp->b_rptr = dp + vjlen;
+
+               /* allocate a new mblk for the ppp and ip headers */
+               if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0)
+                   goto bad;
+               dp = np->b_rptr;        /* prepend mblk with TCP/IP hdr */
+               dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
+               dp[1] = PPP_UI;
+               dp[2] = PPP_IP >> 8;
+               dp[3] = PPP_IP;
+               bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen);
+               np->b_wptr = dp + iphlen + PPP_HDRLEN;
+               np->b_cont = mp;
+
+               /* XXX there seems to be a bug which causes panics in strread
+                  if we make an mbuf with only the IP header in it :-( */
+               if (mp->b_wptr - mp->b_rptr > 4) {
+                   bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4);
+                   mp->b_rptr += 4;
+                   np->b_wptr += 4;
+               } else {
+                   bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr,
+                         mp->b_wptr - mp->b_rptr);
+                   np->b_wptr += mp->b_wptr - mp->b_rptr;
+                   np->b_cont = mp->b_cont;
+                   freeb(mp);
+               }
+
+               mp = np;
+
+           } else {
+               /*
+                * "Decompress" a VJ-uncompressed packet.
+                */
+               cp->vj_last_ierrors = cp->stats.ppp_ierrors;
+               if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
+                   if (cp->flags & DBGLOG)
+                       DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
+                               cp->unit, len);
+                   ++cp->vj_last_ierrors;  /* don't need to reset next time */
+                   goto bad;
+               }
+               mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
+           }
+       }
+
+       putnext(q, mp);
+       continue;
+
+    bad:
+       if (mp != 0)
+           freemsg(mp);
+       cp->stats.ppp_ierrors++;
+       putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
+    }
+
+    return 0;
+}
+
+/*
+ * Handle a CCP packet being sent or received.
+ * Here all the data in the packet is in a single mbuf.
+ */
+static void
+ppp_comp_ccp(q, mp, rcvd)
+    queue_t *q;
+    mblk_t *mp;
+    int rcvd;
+{
+    int len, clen;
+    comp_state_t *cp;
+    unsigned char *dp;
+
+    len = msgdsize(mp);
+    if (len < PPP_HDRLEN + CCP_HDRLEN)
+       return;
+
+    cp = (comp_state_t *) q->q_ptr;
+    dp = mp->b_rptr + PPP_HDRLEN;
+    len -= PPP_HDRLEN;
+    clen = CCP_LENGTH(dp);
+    if (clen > len)
+       return;
+
+    switch (CCP_CODE(dp)) {
+    case CCP_CONFREQ:
+    case CCP_TERMREQ:
+    case CCP_TERMACK:
+       cp->flags &= ~CCP_ISUP;
+       break;
+
+    case CCP_CONFACK:
+       if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
+           && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
+           && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
+           if (!rcvd) {
+               if (cp->xstate != NULL
+                   && (*cp->xcomp->comp_init)
+                       (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+                        cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
+                   cp->flags |= CCP_COMP_RUN;
+           } else {
+               if (cp->rstate != NULL
+                   && (*cp->rcomp->decomp_init)
+                       (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
+                        cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
+                   cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
+           }
+       }
+       break;
+
+    case CCP_RESETACK:
+       if (cp->flags & CCP_ISUP) {
+           if (!rcvd) {
+               if (cp->xstate && (cp->flags & CCP_COMP_RUN))
+                   (*cp->xcomp->comp_reset)(cp->xstate);
+           } else {
+               if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
+                   (*cp->rcomp->decomp_reset)(cp->rstate);
+                   cp->flags &= ~CCP_ERROR;
+               }
+           }
+       }
+       break;
+    }
+}
+
+#if 0
+dump_msg(mp)
+    mblk_t *mp;
+{
+    dblk_t *db;
+
+    while (mp != 0) {
+       db = mp->b_datap;
+       DPRINT2("mp=%x cont=%x ", mp, mp->b_cont);
+       DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db);
+       DPRINT2("  base=%x lim=%x", db->db_base, db->db_lim);
+       DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type);
+       mp = mp->b_cont;
+    }
+}
+#endif
+
+static int
+msg_byte(mp, i)
+    mblk_t *mp;
+    unsigned int i;
+{
+    while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
+       mp = mp->b_cont;
+    if (mp == 0)
+       return -1;
+    return mp->b_rptr[i];
+}
diff --git a/solaris/ppp_comp_mod.c b/solaris/ppp_comp_mod.c
new file mode 100644 (file)
index 0000000..1471bc0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * ppp_comp_mod.c - modload support for PPP compression STREAMS module.
+ *
+ * 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_comp_mod.c,v 1.1 2000/04/18 23:51:29 masputra Exp $
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+
+extern struct streamtab ppp_compinfo;
+
+static struct fmodsw fsw = {
+    "ppp_comp",
+    &ppp_compinfo,
+    D_NEW | D_MP | D_MTQPAIR
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod = {
+    &mod_strmodops,
+    "PPP compression module",
+    &fsw
+};
+
+static struct modlinkage modlinkage = {
+    MODREV_1,
+    (void *) &modlstrmod,
+    NULL
+};
+
+/*
+ * Entry points for modloading.
+ */
+int
+_init(void)
+{
+    return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+    return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+    struct modinfo *mip;
+{
+    return mod_info(&modlinkage, mip);
+}
diff --git a/solaris/ppp_mod.c b/solaris/ppp_mod.c
new file mode 100644 (file)
index 0000000..f543e22
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * ppp_mod.c - modload support for PPP pseudo-device driver.
+ *
+ * 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_mod.c,v 1.1 2000/04/18 23:51:29 masputra Exp $
+ */
+
+/*
+ * This file is used under Solaris 2.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/ksynch.h>
+
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+static int ppp_identify __P((dev_info_t *));
+static int ppp_attach __P((dev_info_t *, ddi_attach_cmd_t));
+static int ppp_detach __P((dev_info_t *, ddi_detach_cmd_t));
+static int ppp_devinfo __P((dev_info_t *, ddi_info_cmd_t, void *, void **));
+
+extern struct streamtab pppinfo;
+extern krwlock_t ppp_lower_lock;
+
+static dev_info_t *ppp_dip;
+
+static struct cb_ops cb_ppp_ops = {
+    nulldev, nulldev, nodev, nodev,    /* cb_open, ... */
+    nodev, nodev, nodev, nodev,                /* cb_dump, ... */
+    nodev, nodev, nodev, nochpoll,     /* cb_devmap, ... */
+    ddi_prop_op,                       /* cb_prop_op */
+    &pppinfo,                          /* cb_stream */
+    D_NEW|D_MP|D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL       /* cb_flag */
+};
+
+static struct dev_ops ppp_ops = {
+    DEVO_REV,                          /* devo_rev */
+    0,                                 /* devo_refcnt */
+    ppp_devinfo,                       /* devo_getinfo */
+    ppp_identify,                      /* devo_identify */
+    nulldev,                           /* devo_probe */
+    ppp_attach,                                /* devo_attach */
+    ppp_detach,                                /* devo_detach */
+    nodev,                             /* devo_reset */
+    &cb_ppp_ops,                       /* devo_cb_ops */
+    NULL                               /* devo_bus_ops */
+};
+
+/*
+ * Module linkage information
+ */
+
+static struct modldrv modldrv = {
+    &mod_driverops,                    /* says this is a pseudo driver */
+    "PPP-2.3 multiplexing driver",
+    &ppp_ops                           /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+    MODREV_1,
+    (void *) &modldrv,
+    NULL
+};
+
+int
+_init(void)
+{
+    return mod_install(&modlinkage);
+}
+
+int
+_fini(void)
+{
+    return mod_remove(&modlinkage);
+}
+
+int
+_info(mip)
+    struct modinfo *mip;
+{
+    return mod_info(&modlinkage, mip);
+}
+
+static int
+ppp_identify(dip)
+    dev_info_t *dip;
+{
+    return strcmp(ddi_get_name(dip), "ppp") == 0? DDI_IDENTIFIED:
+       DDI_NOT_IDENTIFIED;
+}
+
+static int
+ppp_attach(dip, cmd)
+    dev_info_t *dip;
+    ddi_attach_cmd_t cmd;
+{
+
+    if (cmd != DDI_ATTACH)
+       return DDI_FAILURE;
+    if (ddi_create_minor_node(dip, "ppp", S_IFCHR, 0, DDI_PSEUDO, CLONE_DEV)
+       == DDI_FAILURE) {
+       ddi_remove_minor_node(dip, NULL);
+       return DDI_FAILURE;
+    }
+    rw_init(&ppp_lower_lock, NULL, RW_DRIVER, NULL);
+    return DDI_SUCCESS;
+}
+
+static int
+ppp_detach(dip, cmd)
+    dev_info_t *dip;
+    ddi_detach_cmd_t cmd;
+{
+    rw_destroy(&ppp_lower_lock);
+    ddi_remove_minor_node(dip, NULL);
+    return DDI_SUCCESS;
+}
+
+static int
+ppp_devinfo(dip, cmd, arg, result)
+    dev_info_t *dip;
+    ddi_info_cmd_t cmd;
+    void *arg;
+    void **result;
+{
+    int error;
+
+    error = DDI_SUCCESS;
+    switch (cmd) {
+    case DDI_INFO_DEVT2DEVINFO:
+       if (ppp_dip == NULL)
+           error = DDI_FAILURE;
+       else
+           *result = (void *) ppp_dip;
+       break;
+    case DDI_INFO_DEVT2INSTANCE:
+       *result = NULL;
+       break;
+    default:
+       error = DDI_FAILURE;
+    }
+    return error;
+}
diff --git a/solaris/ppp_mod.h b/solaris/ppp_mod.h
new file mode 100644 (file)
index 0000000..f0af008
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Miscellaneous definitions for PPP STREAMS modules.
+ */
+
+/*
+ * Macros for allocating and freeing kernel memory.
+ */
+#ifdef SVR4                    /* SVR4, including Solaris 2 */
+#include <sys/kmem.h>
+#define ALLOC_SLEEP(n)         kmem_alloc((n), KM_SLEEP)
+#define ALLOC_NOSLEEP(n)       kmem_alloc((n), KM_NOSLEEP)
+#define FREE(p, n)             kmem_free((p), (n))
+#endif
+
+#ifdef SUNOS4
+#include <sys/kmem_alloc.h>    /* SunOS 4.x */
+#define ALLOC_SLEEP(n)         kmem_alloc((n), KMEM_SLEEP)
+#define ALLOC_NOSLEEP(n)       kmem_alloc((n), KMEM_NOSLEEP)
+#define FREE(p, n)             kmem_free((p), (n))
+#define NOTSUSER()             (suser()? 0: EPERM)
+#define bcanputnext(q, band)   canputnext((q))
+#endif /* SunOS 4 */
+
+#ifdef __osf__
+#include <sys/malloc.h>
+
+/* caution: this mirrors macros in sys/malloc.h, and uses interfaces
+ * which are subject to change.
+ * The problems are that:
+ *     - the official MALLOC macro wants the lhs of the assignment as an argument,
+ *      and it takes care of the assignment itself (yuck.)
+ *     - PPP insists on using "FREE" which conflicts with a macro of the same name.
+ *
+ */
+#ifdef BUCKETINDX /* V2.0 */
+#define ALLOC_SLEEP(n)         (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n)       (void *)malloc((u_long)(n), BUCKETP(n), M_DEVBUF, M_NOWAIT)
+#else
+#define ALLOC_SLEEP(n)         (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_WAITOK)
+#define ALLOC_NOSLEEP(n)       (void *)malloc((u_long)(n), BUCKETINDEX(n), M_DEVBUF, M_NOWAIT)
+#endif
+
+#define bcanputnext(q, band)   canputnext((q))
+
+#ifdef FREE
+#undef FREE
+#endif
+#define FREE(p, n)             free((void *)(p), M_DEVBUF)
+
+#define NO_DLPI 1
+
+#ifndef IFT_PPP
+#define IFT_PPP 0x17
+#endif
+
+#include <sys/proc.h>
+#define NOTSUSER()             (suser(u.u_procp->p_rcred, &u.u_acflag) ? EPERM : 0)
+
+/* #include "ppp_osf.h" */
+
+#endif /* __osf__ */
+
+#ifdef AIX4
+#define ALLOC_SLEEP(n)         xmalloc((n), 0, pinned_heap)    /* AIX V4.x */
+#define ALLOC_NOSLEEP(n)       xmalloc((n), 0, pinned_heap)    /* AIX V4.x */
+#define FREE(p, n)             xmfree((p), pinned_heap)
+#define NOTSUSER()             (suser()? 0: EPERM)
+#endif /* AIX */
+
+/*
+ * Macros for printing debugging stuff.
+ */
+#ifdef DEBUG
+#if defined(SVR4) || defined(__osf__)
+#if defined(SNI)
+#include <sys/strlog.h>
+#define STRLOG_ID              4712
+#define DPRINT(f)              strlog(STRLOG_ID, 0, 0, SL_TRACE, f)
+#define DPRINT1(f, a1)         strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1)
+#define DPRINT2(f, a1, a2)     strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) strlog(STRLOG_ID, 0, 0, SL_TRACE, f, a1, a2, a3)
+#else
+#define DPRINT(f)              cmn_err(CE_CONT, f)
+#define DPRINT1(f, a1)         cmn_err(CE_CONT, f, a1)
+#define DPRINT2(f, a1, a2)     cmn_err(CE_CONT, f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) cmn_err(CE_CONT, f, a1, a2, a3)
+#endif /* SNI */
+#else
+#define DPRINT(f)              printf(f)
+#define DPRINT1(f, a1)         printf(f, a1)
+#define DPRINT2(f, a1, a2)     printf(f, a1, a2)
+#define DPRINT3(f, a1, a2, a3) printf(f, a1, a2, a3)
+#endif /* SVR4 or OSF */
+
+#else
+#define DPRINT(f)              0
+#define DPRINT1(f, a1)         0
+#define DPRINT2(f, a1, a2)     0
+#define DPRINT3(f, a1, a2, a3) 0
+#endif /* DEBUG */
+
+#ifndef SVR4
+typedef unsigned char uchar_t;
+typedef unsigned short ushort_t;
+#ifndef __osf__
+typedef int minor_t;
+#endif
+#endif
+
+/*
+ * If we don't have multithreading support, define substitutes.
+ */
+#ifndef D_MP
+# define qprocson(q)
+# define qprocsoff(q)
+# define put(q, mp)    ((*(q)->q_qinfo->qi_putp)((q), (mp)))
+# define canputnext(q) canput((q)->q_next)
+# define qwriter(q, mp, func, scope)   (func)((q), (mp))
+#endif
+
+#ifdef D_MP
+/* Use msgpullup if we have other multithreading support. */
+#define PULLUP(mp, len)                                \
+    do {                                       \
+       mblk_t *np = msgpullup((mp), (len));    \
+       freemsg((mp));                          \
+       mp = np;                                \
+    } while (0)
+
+#else
+/* Use pullupmsg if we don't have any multithreading support. */
+#define PULLUP(mp, len)                        \
+    do {                               \
+       if (!pullupmsg((mp), (len))) {  \
+           freemsg((mp));              \
+           mp = 0;                     \
+       }                               \
+    } while (0)
+#endif
+
+/*
+ * How to declare the open and close procedures for a module.
+ */
+#ifdef SVR4
+#define MOD_OPEN_DECL(name)    \
+static int name __P((queue_t *, dev_t *, int, int, cred_t *))
+
+#define MOD_CLOSE_DECL(name)   \
+static int name __P((queue_t *, int, cred_t *))
+
+#define MOD_OPEN(name)                         \
+static int name(q, devp, flag, sflag, credp)   \
+    queue_t *q;                                        \
+    dev_t *devp;                               \
+    int flag, sflag;                           \
+    cred_t *credp;
+
+#define MOD_CLOSE(name)                \
+static int name(q, flag, credp)        \
+    queue_t *q;                        \
+    int flag;                  \
+    cred_t *credp;
+
+#define OPEN_ERROR(x)          return (x)
+#define DRV_OPEN_OK(dev)       return 0
+
+#define NOTSUSER()             (drv_priv(credp))
+
+#else  /* not SVR4 */
+#define MOD_OPEN_DECL(name)    \
+static int name __P((queue_t *, int, int, int))
+
+#define MOD_CLOSE_DECL(name)   \
+static int name __P((queue_t *, int))
+
+#define MOD_OPEN(name)         \
+static int name(q, dev, flag, sflag)   \
+    queue_t *q;                                \
+    int dev;                           \
+    int flag, sflag;
+
+#define MOD_CLOSE(name)                \
+static int name(q, flag)       \
+    queue_t *q;                        \
+    int flag;
+
+#define OPEN_ERROR(x)          { u.u_error = (x); return OPENFAIL; }
+#define DRV_OPEN_OK(dev)       return (dev)
+
+#endif /* SVR4 */