]> git.ozlabs.org Git - ppp.git/commitdiff
moved here from sunos4 directory
authorPaul Mackerras <paulus@samba.org>
Mon, 1 Jul 1996 01:01:30 +0000 (01:01 +0000)
committerPaul Mackerras <paulus@samba.org>
Mon, 1 Jul 1996 01:01:30 +0000 (01:01 +0000)
modules/if_ppp.c [new file with mode: 0644]

diff --git a/modules/if_ppp.c b/modules/if_ppp.c
new file mode 100644 (file)
index 0000000..c0b3ef5
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * if_ppp.c - a network interface connected to a 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: if_ppp.c,v 1.1 1996/07/01 01:01:30 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/stream.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>
+#include "ppp_mod.h"
+#else
+#include <sys/sockio.h>
+#include <modules/ppp_mod.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
+
+#define PPP_MINMTU     64
+#define PPP_MAXMTU     65536
+
+static int if_ppp_open __P((queue_t *, int, int, int));
+static int if_ppp_close __P((queue_t *, int));
+static int if_ppp_wput __P((queue_t *, mblk_t *));
+static int if_ppp_rput __P((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 __P((struct ifnet *, struct mbuf *,
+                             struct sockaddr *));
+static int if_ppp_ioctl __P((struct ifnet *, u_int, caddr_t));
+static struct mbuf *make_mbufs __P((mblk_t *, int));
+static mblk_t *make_message __P((struct mbuf *, int));
+
+#ifndef __osf__
+static void ppp_if_detach __P((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]);
+    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 *));
+               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_MRU;
+               ifp->if_flags = IFF_POINTOPOINT | IFF_RUNNING;
+#ifdef IFF_MULTICAST
+               ifp->if_flags |= IFF_MULTICAST;
+#endif
+               ifp->if_output = if_ppp_output;
+#ifdef __osf__
+               ifp->if_version = "Point-to-Point Protocol, version 2.3";
+               ifp->if_mediamtu = 1500;
+               ifp->if_type = IFT_PPP;
+               ifp->if_hdrlen = PPP_HDRLEN;
+               ifp->if_addrlen = 0;
+#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_MRU;
+               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;
+       }
+
+/* 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;
+       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;
+
+    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;
+       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.  Taking the i/f down from pppd will take
+ * care of most of the stuff that this code intends to do
+ * anyhow
+ */
+#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__ */