/*
 * 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.17 1999/12/23 01:48:45 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 __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));

#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 __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]);
    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__ */
