[Feature][ZXW-265]merge P56U03 version

Only Configure: No
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: Yes
Doc Update: No

Change-Id: I873f6df64e2605a77b8b8bfec35b21e7f33c5444
diff --git a/ap/app/pppd/ppp-2.5.0/modules/if_ppp.c b/ap/app/pppd/ppp-2.5.0/modules/if_ppp.c
new file mode 100755
index 0000000..e150923
--- /dev/null
+++ b/ap/app/pppd/ppp-2.5.0/modules/if_ppp.c
@@ -0,0 +1,873 @@
+/*
+ * if_ppp.c - a network interface connected to a STREAMS module.
+ *
+ * Copyright (c) 1994 Paul Mackerras. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: if_ppp.c,v 1.18 2002/12/06 09:49:15 paulus Exp $
+ */
+
+/*
+ * This file is used under SunOS 4 and Digital UNIX.
+ *
+ * This file provides the glue between PPP and IP.
+ */
+
+#define INET	1
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/ppp_defs.h>
+#include <net/pppio.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#ifdef __osf__
+#include <sys/ioctl.h>
+#include <net/if_types.h>
+#else
+#include <sys/sockio.h>
+#endif
+#include "ppp_mod.h"
+
+#include <sys/stream.h>
+
+#ifdef SNIT_SUPPORT
+#include <sys/time.h>
+#include <net/nit_if.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef __osf__
+#define SIOCSIFMTU SIOCSIPMTU
+#define SIOCGIFMTU SIOCRIPMTU
+#define IFA_ADDR(ifa)   (*(ifa)->ifa_addr)
+#else
+#define IFA_ADDR(ifa)   ((ifa)->ifa_addr)
+#endif
+
+#define ifr_mtu		ifr_metric
+
+static int if_ppp_open(queue_t *, int, int, int);
+static int if_ppp_close(queue_t *, int);
+static int if_ppp_wput(queue_t *, mblk_t *);
+static int if_ppp_rput(queue_t *, mblk_t *);
+
+#define PPP_IF_ID 0x8021
+static struct module_info minfo = {
+    PPP_IF_ID, "if_ppp", 0, INFPSZ, 4096, 128
+};
+
+static struct qinit rinit = {
+    if_ppp_rput, NULL, if_ppp_open, if_ppp_close, NULL, &minfo, NULL
+};
+
+static struct qinit winit = {
+    if_ppp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
+};
+
+struct streamtab if_pppinfo = {
+    &rinit, &winit, NULL, NULL
+};
+
+typedef struct if_ppp_state {
+    int unit;
+    queue_t *q;
+    int flags;
+} if_ppp_t;
+
+/* Values for flags */
+#define DBGLOG		1
+
+static int if_ppp_count;	/* Number of currently-active streams */
+
+static int ppp_nalloc;		/* Number of elements of ifs and states */
+static struct ifnet **ifs;	/* Array of pointers to interface structs */
+static if_ppp_t **states;	/* Array of pointers to state structs */
+
+static int if_ppp_output(struct ifnet *, struct mbuf *,
+			 struct sockaddr *);
+static int if_ppp_ioctl(struct ifnet *, u_int, caddr_t);
+static struct mbuf *make_mbufs(mblk_t *, int);
+static mblk_t *make_message(struct mbuf *, int);
+
+#ifdef SNIT_SUPPORT
+/* Fake ether header for SNIT */
+static struct ether_header snit_ehdr = {{0}, {0}, ETHERTYPE_IP};
+#endif
+
+#ifndef __osf__
+static void ppp_if_detach(struct ifnet *);
+
+/*
+ * Detach all the interfaces before unloading.
+ * Not sure this works.
+ */
+int
+if_ppp_unload()
+{
+    int i;
+
+    if (if_ppp_count > 0)
+	return EBUSY;
+    for (i = 0; i < ppp_nalloc; ++i)
+	if (ifs[i] != 0)
+	    ppp_if_detach(ifs[i]);
+    if (ifs) {
+	FREE(ifs, ppp_nalloc * sizeof (struct ifnet *));
+	FREE(states, ppp_nalloc * sizeof (struct if_ppp_t *));
+    }
+    ppp_nalloc = 0;
+    return 0;
+}
+#endif /* __osf__ */
+
+/*
+ * STREAMS module entry points.
+ */
+static int
+if_ppp_open(q, dev, flag, sflag)
+    queue_t *q;
+    int dev;
+    int flag, sflag;
+{
+    if_ppp_t *sp;
+
+    if (q->q_ptr == 0) {
+	sp = (if_ppp_t *) ALLOC_SLEEP(sizeof (if_ppp_t));
+	if (sp == 0)
+	    return OPENFAIL;
+	bzero(sp, sizeof (if_ppp_t));
+	q->q_ptr = (caddr_t) sp;
+	WR(q)->q_ptr = (caddr_t) sp;
+	sp->unit = -1;		/* no interface unit attached at present */
+	sp->q = WR(q);
+	sp->flags = 0;
+	++if_ppp_count;
+    }
+    return 0;
+}
+
+static int
+if_ppp_close(q, flag)
+    queue_t *q;
+    int flag;
+{
+    if_ppp_t *sp;
+    struct ifnet *ifp;
+
+    sp = (if_ppp_t *) q->q_ptr;
+    if (sp != 0) {
+	if (sp->flags & DBGLOG)
+	    printf("if_ppp closed, q=%x sp=%x\n", q, sp);
+	if (sp->unit >= 0) {
+	    if (sp->unit < ppp_nalloc) {
+		states[sp->unit] = 0;
+		ifp = ifs[sp->unit];
+		if (ifp != 0)
+		    ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
+#ifdef DEBUG
+	    } else {
+		printf("if_ppp: unit %d nonexistent!\n", sp->unit);
+#endif
+	    }
+	}
+	FREE(sp, sizeof (if_ppp_t));
+	--if_ppp_count;
+    }
+    return 0;
+}
+
+static int
+if_ppp_wput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    if_ppp_t *sp;
+    struct iocblk *iop;
+    int error, unit;
+    struct ifnet *ifp;
+
+    sp = (if_ppp_t *) q->q_ptr;
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+	/*
+	 * Now why would we be getting data coming in here??
+	 */
+	if (sp->flags & DBGLOG)
+	    printf("if_ppp: got M_DATA len=%d\n", msgdsize(mp));
+	freemsg(mp);
+	break;
+
+    case M_IOCTL:
+	iop = (struct iocblk *) mp->b_rptr;
+	error = EINVAL;
+
+	if (sp->flags & DBGLOG)
+	    printf("if_ppp: got ioctl cmd=%x count=%d\n",
+		   iop->ioc_cmd, iop->ioc_count);
+
+	switch (iop->ioc_cmd) {
+	case PPPIO_NEWPPA:		/* well almost */
+	    if (iop->ioc_count != sizeof(int) || sp->unit >= 0)
+		break;
+	    if ((error = NOTSUSER()) != 0)
+		break;
+	    unit = *(int *)mp->b_cont->b_rptr;
+
+	    /* Check that this unit isn't already in use */
+	    if (unit < ppp_nalloc && states[unit] != 0) {
+		error = EADDRINUSE;
+		break;
+	    }
+
+	    /* Extend ifs and states arrays if necessary. */
+	    error = ENOSR;
+	    if (unit >= ppp_nalloc) {
+		int newn;
+		struct ifnet **newifs;
+		if_ppp_t **newstates;
+
+		newn = unit + 4;
+		if (sp->flags & DBGLOG)
+		    printf("if_ppp: extending ifs to %d\n", newn);
+		newifs = (struct ifnet **)
+		    ALLOC_NOSLEEP(newn * sizeof (struct ifnet *));
+		if (newifs == 0)
+		    break;
+		bzero(newifs, newn * sizeof (struct ifnet *));
+		newstates = (if_ppp_t **)
+		    ALLOC_NOSLEEP(newn * sizeof (struct if_ppp_t *));
+		if (newstates == 0) {
+		    FREE(newifs, newn * sizeof (struct ifnet *));
+		    break;
+		}
+		bzero(newstates, newn * sizeof (struct if_ppp_t *));
+		bcopy(ifs, newifs, ppp_nalloc * sizeof(struct ifnet *));
+		bcopy(states, newstates, ppp_nalloc * sizeof(if_ppp_t *));
+		if (ifs) {
+		    FREE(ifs, ppp_nalloc * sizeof(struct ifnet *));
+		    FREE(states, ppp_nalloc * sizeof(if_ppp_t *));
+		}
+		ifs = newifs;
+		states = newstates;
+		ppp_nalloc = newn;
+	    }
+
+	    /* Allocate a new ifnet struct if necessary. */
+	    ifp = ifs[unit];
+	    if (ifp == 0) {
+		ifp = (struct ifnet *) ALLOC_NOSLEEP(sizeof (struct ifnet));
+		if (ifp == 0)
+		    break;
+		bzero(ifp, sizeof (struct ifnet));
+		ifs[unit] = ifp;
+		ifp->if_name = "ppp";
+		ifp->if_unit = unit;
+		ifp->if_mtu = PPP_MTU;
+		ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
+#ifndef __osf__
+#ifdef IFF_MULTICAST
+		ifp->if_flags |= IFF_MULTICAST;
+#endif
+#endif /* __osf__ */
+		ifp->if_output = if_ppp_output;
+#ifdef __osf__
+		ifp->if_version = "Point-to-Point Protocol, version 2.3.11";
+		ifp->if_mediamtu = PPP_MTU;
+		ifp->if_type = IFT_PPP;
+		ifp->if_hdrlen = PPP_HDRLEN;
+		ifp->if_addrlen = 0;
+		ifp->if_flags |= IFF_NOARP | IFF_SIMPLEX | IFF_NOTRAILERS;
+#ifdef IFF_VAR_MTU
+		ifp->if_flags |= IFF_VAR_MTU;
+#endif
+#ifdef NETMASTERCPU
+		ifp->if_affinity = NETMASTERCPU;
+#endif
+#endif
+		ifp->if_ioctl = if_ppp_ioctl;
+		ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+		if_attach(ifp);
+		if (sp->flags & DBGLOG)
+		    printf("if_ppp: created unit %d\n", unit);
+	    } else {
+		ifp->if_mtu = PPP_MTU;
+		ifp->if_flags |= IFF_RUNNING;
+	    }
+
+	    states[unit] = sp;
+	    sp->unit = unit;
+
+	    error = 0;
+	    iop->ioc_count = 0;
+	    if (sp->flags & DBGLOG)
+		printf("if_ppp: attached unit %d, sp=%x q=%x\n", unit,
+		       sp, sp->q);
+	    break;
+
+	case PPPIO_DEBUG:
+	    error = -1;
+	    if (iop->ioc_count == sizeof(int)) {
+		if (*(int *)mp->b_cont->b_rptr == PPPDBG_LOG + PPPDBG_IF) {
+		    printf("if_ppp: debug log enabled, q=%x sp=%x\n", q, sp);
+		    sp->flags |= DBGLOG;
+		    error = 0;
+		    iop->ioc_count = 0;
+		}
+	    }
+	    break;
+
+	default:
+	    error = -1;
+	    break;
+	}
+
+	if (sp->flags & DBGLOG)
+	    printf("if_ppp: ioctl result %d\n", error);
+	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;
+
+    default:
+	putnext(q, mp);
+    }
+    return 0;
+}
+
+static int
+if_ppp_rput(q, mp)
+    queue_t *q;
+    mblk_t *mp;
+{
+    if_ppp_t *sp;
+    int proto, s;
+    struct mbuf *mb;
+    struct ifqueue *inq;
+    struct ifnet *ifp;
+    int len;
+
+    sp = (if_ppp_t *) q->q_ptr;
+    switch (mp->b_datap->db_type) {
+    case M_DATA:
+	/*
+	 * Convert the message into an mbuf chain
+	 * and inject it into the network code.
+	 */
+	if (sp->flags & DBGLOG)
+	    printf("if_ppp: rput pkt len %d data %x %x %x %x %x %x %x %x\n",
+		   msgdsize(mp), mp->b_rptr[0], mp->b_rptr[1], mp->b_rptr[2],
+		   mp->b_rptr[3], mp->b_rptr[4], mp->b_rptr[5], mp->b_rptr[6],
+		   mp->b_rptr[7]);
+
+	if (sp->unit < 0) {
+	    freemsg(mp);
+	    break;
+	}
+	if (sp->unit >= ppp_nalloc || (ifp = ifs[sp->unit]) == 0) {
+#ifdef DEBUG
+	    printf("if_ppp: no unit %d!\n", sp->unit);
+#endif
+	    freemsg(mp);
+	    break;
+	}
+
+	if ((ifp->if_flags & IFF_UP) == 0) {
+	    freemsg(mp);
+	    break;
+	}
+	++ifp->if_ipackets;
+
+	proto = PPP_PROTOCOL(mp->b_rptr);
+	adjmsg(mp, PPP_HDRLEN);
+	len = msgdsize(mp);
+	mb = make_mbufs(mp, sizeof(struct ifnet *));
+	freemsg(mp);
+	if (mb == NULL) {
+	    if (sp->flags & DBGLOG)
+		printf("if_ppp%d: make_mbufs failed\n", ifp->if_unit);
+	    ++ifp->if_ierrors;
+	    break;
+	}
+
+#ifdef SNIT_SUPPORT
+	if (proto == PPP_IP && (ifp->if_flags & IFF_PROMISC)) {
+	    struct nit_if nif;
+
+	    nif.nif_header = (caddr_t) &snit_ehdr;
+	    nif.nif_hdrlen = sizeof(snit_ehdr);
+	    nif.nif_bodylen = len;
+	    nif.nif_promisc = 0;
+	    snit_intr(ifp, mb, &nif);
+	}
+#endif
+
+/*
+ * For Digital UNIX, there's space set aside in the header mbuf
+ * for the interface info.
+ *
+ * For Sun it's smuggled around via a pointer at the front of the mbuf.
+ */
+#ifdef __osf__
+        mb->m_pkthdr.rcvif = ifp;
+        mb->m_pkthdr.len = len;
+#else
+	mb->m_off -= sizeof(struct ifnet *);
+	mb->m_len += sizeof(struct ifnet *);
+	*mtod(mb, struct ifnet **) = ifp;
+#endif
+
+	inq = 0;
+	switch (proto) {
+	case PPP_IP:
+	    inq = &ipintrq;
+	    schednetisr(NETISR_IP);
+	}
+
+	if (inq != 0) {
+	    s = splhigh();
+	    if (IF_QFULL(inq)) {
+		IF_DROP(inq);
+		++ifp->if_ierrors;
+		if (sp->flags & DBGLOG)
+		    printf("if_ppp: inq full, proto=%x\n", proto);
+		m_freem(mb);
+	    } else {
+		IF_ENQUEUE(inq, mb);
+	    }
+	    splx(s);
+	} else {
+	    if (sp->flags & DBGLOG)
+		printf("if_ppp%d: proto=%x?\n", ifp->if_unit, proto);
+	    ++ifp->if_ierrors;
+	    m_freem(mb);
+	}
+	break;
+
+    default:
+	putnext(q, mp);
+    }
+    return 0;
+}
+
+/*
+ * Network code wants to output a packet.
+ * Turn it into a STREAMS message and send it down.
+ */
+static int
+if_ppp_output(ifp, m0, dst)
+    struct ifnet *ifp;
+    struct mbuf *m0;
+    struct sockaddr *dst;
+{
+    mblk_t *mp;
+    int proto, s;
+    if_ppp_t *sp;
+    u_char *p;
+
+    if ((ifp->if_flags & IFF_UP) == 0) {
+	m_freem(m0);
+	return ENETDOWN;
+    }
+
+    if ((unsigned)ifp->if_unit >= ppp_nalloc) {
+#ifdef DEBUG
+	printf("if_ppp_output: unit %d?\n", ifp->if_unit);
+#endif
+	m_freem(m0);
+	return EINVAL;
+    }
+    sp = states[ifp->if_unit];
+    if (sp == 0) {
+#ifdef DEBUG
+	printf("if_ppp_output: no queue?\n");
+#endif
+	m_freem(m0);
+	return ENETDOWN;
+    }
+
+    if (sp->flags & DBGLOG) {
+	p = mtod(m0, u_char *);
+	printf("if_ppp_output%d: af=%d data=%x %x %x %x %x %x %x %x q=%x\n",
+	       ifp->if_unit, dst->sa_family, p[0], p[1], p[2], p[3], p[4],
+	       p[5], p[6], p[7], sp->q);
+    }
+
+    switch (dst->sa_family) {
+    case AF_INET:
+	proto = PPP_IP;
+#ifdef SNIT_SUPPORT
+	if (ifp->if_flags & IFF_PROMISC) {
+	    struct nit_if nif;
+	    struct mbuf *m;
+	    int len;
+
+	    for (len = 0, m = m0; m != NULL; m = m->m_next)
+		len += m->m_len;
+	    nif.nif_header = (caddr_t) &snit_ehdr;
+	    nif.nif_hdrlen = sizeof(snit_ehdr);
+	    nif.nif_bodylen = len;
+	    nif.nif_promisc = 0;
+	    snit_intr(ifp, m0, &nif);
+	}
+#endif
+	break;
+
+    default:
+	m_freem(m0);
+	return EAFNOSUPPORT;
+    }
+
+    ++ifp->if_opackets;
+    mp = make_message(m0, PPP_HDRLEN);
+    m_freem(m0);
+    if (mp == 0) {
+	++ifp->if_oerrors;
+	return ENOBUFS;
+    }
+    mp->b_rptr -= PPP_HDRLEN;
+    mp->b_rptr[0] = PPP_ALLSTATIONS;
+    mp->b_rptr[1] = PPP_UI;
+    mp->b_rptr[2] = proto >> 8;
+    mp->b_rptr[3] = proto;
+
+    s = splstr();
+    if (sp->flags & DBGLOG)
+	printf("if_ppp: putnext(%x, %x), r=%x w=%x p=%x\n",
+	       sp->q, mp, mp->b_rptr, mp->b_wptr, proto);
+    putnext(sp->q, mp);
+    splx(s);
+
+    return 0;
+}
+
+/*
+ * Socket ioctl routine for ppp interfaces.
+ */
+static int
+if_ppp_ioctl(ifp, cmd, data)
+    struct ifnet *ifp;
+    u_int cmd;
+    caddr_t data;
+{
+    int s, error;
+    struct ifreq *ifr = (struct ifreq *) data;
+    struct ifaddr *ifa = (struct ifaddr *) data;
+    u_short mtu;
+
+    error = 0;
+    s = splimp();
+    switch (cmd) {
+    case SIOCSIFFLAGS:
+	if ((ifp->if_flags & IFF_RUNNING) == 0)
+	    ifp->if_flags &= ~IFF_UP;
+	break;
+
+    case SIOCSIFADDR:
+	if (IFA_ADDR(ifa).sa_family != AF_INET)
+	    error = EAFNOSUPPORT;
+	break;
+
+    case SIOCSIFDSTADDR:
+	if (IFA_ADDR(ifa).sa_family != AF_INET)
+	    error = EAFNOSUPPORT;
+	break;
+
+    case SIOCSIFMTU:
+	if ((error = NOTSUSER()) != 0)
+	    break;
+#ifdef __osf__
+	/* this hack is necessary because ifioctl checks ifr_data
+	 * in 4.0 and 5.0, but ifr_data and ifr_metric overlay each 
+	 * other in the definition of struct ifreq so pppd can't set both.
+	 */
+        bcopy(ifr->ifr_data, &mtu, sizeof (u_short));
+        ifr->ifr_mtu = mtu;
+#endif
+
+	if (ifr->ifr_mtu < PPP_MINMTU || ifr->ifr_mtu > PPP_MAXMTU) {
+	    error = EINVAL;
+	    break;
+	}
+	ifp->if_mtu = ifr->ifr_mtu;
+	break;
+
+    case SIOCGIFMTU:
+	ifr->ifr_mtu = ifp->if_mtu;
+	break;
+
+    case SIOCADDMULTI:
+    case SIOCDELMULTI:
+	switch(ifr->ifr_addr.sa_family) {
+	case AF_INET:
+	    break;
+	default:
+	    error = EAFNOSUPPORT;
+	    break;
+	}
+	break;
+
+    default:
+	error = EINVAL;
+    }
+    splx(s);
+    return (error);
+}
+
+/*
+ * Turn a STREAMS message into an mbuf chain.
+ */
+static struct mbuf *
+make_mbufs(mp, off)
+    mblk_t *mp;
+    int off;
+{
+    struct mbuf *head, **prevp, *m;
+    int len, space, n;
+    unsigned char *cp, *dp;
+
+    len = msgdsize(mp);
+    if (len == 0)
+	return 0;
+    prevp = &head;
+    space = 0;
+    cp = mp->b_rptr;
+#ifdef __osf__
+    MGETHDR(m, M_DONTWAIT, MT_DATA);
+    m->m_len = 0;
+    space = MHLEN;
+    *prevp = m;
+    prevp = &m->m_next;
+    dp = mtod(m, unsigned char *);
+    len -= space;
+    off = 0;
+#endif
+    for (;;) {
+	while (cp >= mp->b_wptr) {
+	    mp = mp->b_cont;
+	    if (mp == 0) {
+		*prevp = 0;
+		return head;
+	    }
+	    cp = mp->b_rptr;
+	}
+	n = mp->b_wptr - cp;
+	if (space == 0) {
+	    MGET(m, M_DONTWAIT, MT_DATA);
+	    *prevp = m;
+	    if (m == 0) {
+		if (head != 0)
+		    m_freem(head);
+		return 0;
+	    }
+	    if (len + off > 2 * MLEN) {
+#ifdef __osf__
+		MCLGET(m, M_DONTWAIT);
+#else
+		MCLGET(m);
+#endif
+	    }
+#ifdef __osf__
+	    space = ((m->m_flags & M_EXT) ? MCLBYTES : MLEN);
+#else
+	    space = (m->m_off > MMAXOFF? MCLBYTES: MLEN) - off;
+	    m->m_off += off;
+#endif
+	    m->m_len = 0;
+	    len -= space;
+	    dp = mtod(m, unsigned char *);
+	    off = 0;
+	    prevp = &m->m_next;
+	}
+	if (n > space)
+	    n = space;
+	bcopy(cp, dp, n);
+	cp += n;
+	dp += n;
+	space -= n;
+	m->m_len += n;
+    }
+}
+
+/*
+ * Turn an mbuf chain into a STREAMS message.
+ */
+#define ALLOCB_MAX	4096
+
+static mblk_t *
+make_message(m, off)
+    struct mbuf *m;
+    int off;
+{
+    mblk_t *head, **prevp, *mp;
+    int len, space, n, nb;
+    unsigned char *cp, *dp;
+    struct mbuf *nm;
+
+    len = 0;
+    for (nm = m; nm != 0; nm = nm->m_next)
+	len += nm->m_len;
+    prevp = &head;
+    space = 0;
+    cp = mtod(m, unsigned char *);
+    nb = m->m_len;
+    for (;;) {
+	while (nb <= 0) {
+	    m = m->m_next;
+	    if (m == 0) {
+		*prevp = 0;
+		return head;
+	    }
+	    cp = mtod(m, unsigned char *);
+	    nb = m->m_len;
+	}
+	if (space == 0) {
+	    space = len + off;
+	    if (space > ALLOCB_MAX)
+		space = ALLOCB_MAX;
+	    mp = allocb(space, BPRI_LO);
+	    *prevp = mp;
+	    if (mp == 0) {
+		if (head != 0)
+		    freemsg(head);
+		return 0;
+	    }
+	    dp = mp->b_rptr += off;
+	    space -= off;
+	    len -= space;
+	    off = 0;
+	    prevp = &mp->b_cont;
+	}
+	n = nb < space? nb: space;
+	bcopy(cp, dp, n);
+	cp += n;
+	dp += n;
+	nb -= n;
+	space -= n;
+	mp->b_wptr = dp;
+    }
+}
+
+/*
+ * Digital UNIX doesn't allow for removing ifnet structures
+ * from the list.  But then we're not using this as a loadable
+ * module anyway, so that's OK.
+ *
+ * Under SunOS, this should allow the module to be unloaded.
+ * Unfortunately, it doesn't seem to detach all the references,
+ * so your system may well crash after you unload this module :-(
+ */
+#ifndef __osf__
+
+/*
+ * Remove an interface from the system.
+ * This routine contains magic.
+ */
+#include <net/route.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+static void
+ppp_if_detach(ifp)
+    struct ifnet *ifp;
+{
+    int s;
+    struct inpcb *pcb;
+    struct ifaddr *ifa;
+    struct in_ifaddr **inap;
+    struct ifnet **ifpp;
+
+    s = splhigh();
+
+    /*
+     * Clear the interface from any routes currently cached in
+     * TCP or UDP protocol control blocks.
+     */
+    for (pcb = tcb.inp_next; pcb != &tcb; pcb = pcb->inp_next)
+	if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
+	    in_losing(pcb);
+    for (pcb = udb.inp_next; pcb != &udb; pcb = pcb->inp_next)
+	if (pcb->inp_route.ro_rt && pcb->inp_route.ro_rt->rt_ifp == ifp)
+	    in_losing(pcb);
+
+    /*
+     * Delete routes through all addresses of the interface.
+     */
+    for (ifa = ifp->if_addrlist; ifa != 0; ifa = ifa->ifa_next) {
+	rtinit(ifa, ifa, SIOCDELRT, RTF_HOST);
+	rtinit(ifa, ifa, SIOCDELRT, 0);
+    }
+
+    /*
+     * Unlink the interface's address(es) from the in_ifaddr list.
+     */
+    for (inap = &in_ifaddr; *inap != 0; ) {
+	if ((*inap)->ia_ifa.ifa_ifp == ifp)
+	    *inap = (*inap)->ia_next;
+	else
+	    inap = &(*inap)->ia_next;
+    }
+
+    /*
+     * Delete the interface from the ifnet list.
+     */
+    for (ifpp = &ifnet; (*ifpp) != 0; ) {
+	if (*ifpp == ifp)
+	    break;
+	ifpp = &(*ifpp)->if_next;
+    }
+    if (*ifpp == 0)
+	printf("couldn't find interface ppp%d in ifnet list\n", ifp->if_unit);
+    else
+	*ifpp = ifp->if_next;
+
+    splx(s);
+}
+
+#endif /* __osf__ */