ftp.nice.ch/peanuts/GeneralData/Documents/user-groups/rmNUG/dialupip.tar.gz#/dialupip/sys/net/if_du.c

This is if_du.c in view mode; [Download] [Up]

/*
**  Dialup IP driver interface.
**  Based heavily on 4.3 slip driver.
**  Copyright (c) 1991 Bolt Beranek and Newman, Inc.
**  All rights reserved.
**
**  Redistribution and use in source and binary forms are permitted
**  provided that: (1) source distributions retain this entire copyright
**  notice and comment, and (2) distributions including binaries display
**  the following acknowledgement:  ``This product includes software
**  developed by Bolt Beranek and Newman, Inc. and CREN/CSNET'' in the
**  documentation or other materials provided with the distribution and in
**  all advertising materials mentioning features or use of this software.
**  Neither the name of Bolt Beranek and Newman nor CREN/CSNET may be used
**  to endorse or promote products derived from this software without
**  specific prior written permission.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
**  WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "du.h"
#if	NDU > 0

#define DEBUG

#include "../h/param.h"
#include "../h/mbuf.h"
#include "../h/buf.h"
#include "../h/dk.h"
#include "../h/socket.h"
#include "../h/ioctl.h"
#include "../h/file.h"
#include "../h/tty.h"
#include "../h/errno.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#ifdef   vax
#include "../netinet/in_var.h"
#endif	/* vax */
#include "../netinet/ip.h"
#include "../net/if_du.h"
#ifdef	vax
#include "../vax/mtpr.h"
#ifdef	ultrix
#ifndef PRE_ULTRIX_3_0
#include "../rpc/types.h"	/* Needed for Ultrix V3.0 and later	*/
#endif	/* PRE_ULTRIX_3_0 */
#endif	/* ultrix */
#endif	/* vax */



/*
**  Shortcuts for use with duioctl.
*/
#ifdef	vax
#define DATAVAL(p)		(((struct ifreq *)p)->ifr_data)
#define GET_DATA(p)		DATAVAL(p)
#define SET_DATA(p, d)		DATAVAL(p) = (caddr_t)(d)
#else
#define DATAVAL(p)		(*((int *)p))
#define GET_DATA(p)		DATAVAL(p)
#define SET_DATA(p, d)		DATAVAL(p) = (d)
#endif	/* vax */


/*
**  DMTU is now a hard limit on the input packet site.  It must be
**  less than CLBYTES - sizeof(struct ifnet *).
*/
#define DUMTU		1006

/*
**  Timeout parameters and clist usage parameters.
*/
#define DU_HZ			5  		/* five second granularity */
#define DEFAULT_INACTIVITY_TIME	(60 * 5)	/* 5 minutes */
#define DIAL_MONITORTIMEOUT	(4 * DU_HZ)
#define CLISTRESERVE		(ds->ds_mtu - 6)
#define DU_HIWAT		(ds->ds_mtu - 6)


/*
**  Escape sequences for framing
*/
#define FRAME_END	 	0xC0		/* Frame End */
#define FRAME_ESCAPE		0xDB		/* Frame Esc */
#define TRANS_FRAME_END	 	0xDC		/* transposed frame end */
#define TRANS_FRAME_ESCAPE 	0xDD		/* transposed frame esc */

/*
**  Packet types.
*/
#define DUT_IP			03	/* IP packet */
#define DUT_CP			02	/* Control packet */
#define DUT_XX			01	/* Reserved for future use */
#define DUT_EXT			00	/* extended -- leader contains type */

/*
**  Line header.
*/
struct du_hdr {
#ifdef	vax
    u_char
	duh_hopcnt:4,		/* hop count -- ignored */
	duh_handling:2,		/* routing mechanism used -- ignored */
	duh_type:2;		/* type -- used */
#else
    u_char
	duh_type:2,
	duh_handling:2,
	duh_hopcnt:4;
#endif	/* vax */
    u_char duh_dest;
};


struct du_softc		du_softc[NDU];
struct globdialstats	globdialstats = { NDU };
int			du_attached = 0;

#ifdef	vax
#define t_du T_LINEP
#else
#define t_du t_linep
#endif	/* vax */

#ifdef	DEBUG

#define D_ICHAR   0x0001
#define D_TIMER   0x0002
#define D_IPKT    0x0004
#define D_OCHAR   0x0008
#define D_OPKT    0x0010
#define D_OUTPUT  0x0020
#define D_INPUT   0x0040
#define D_IOCTL   0x0080
#define D_TRACE   0x0100
#define D_INFO    0x0200

int		dial_debug =
	/* D_ICHAR  + */
	/* D_TIMER  + */
	/* D_IPKT   + */
	/* D_OCHAR  + */
	/* D_OPKT   + */
	/* D_OUTPUT + */
	/* D_INPUT  + */
	/* D_IOCTL  + */
	/* D_TRACE  + */
	/* D_INFO   + */
	0;
#define DDEBUG(flag, format, a1, a2, a3)	\
	if ((flag) & dial_debug) printf(format, a1, a2, a3); else
#else
#define DDEBUG(flag, format, a1, a2, a3)	/* NULL */
#endif	/* DEBUG */


/*
**  du_softc Indexing code
**  See also duattach and dutioctl.
*/
#ifdef	SAFE_DU_INDEX
#define DU_Index(ifp)	((((ifp)->if_name[2] - 'a') * 10) + (ifp)->if_unit)
#else
static int
DU_Index(ifp)
    struct ifnet *ifp;
{
    register char *p;
    int i;

    /* Check to see if the input looks real or not */
    p = ifp->if_name;
    if (p[0] != 'd' || p[1] != 'u'
     || p[2] < 'a' || p[2] > 'z' || p[3] != 0
     || ifp->if_unit < 0 || ifp->if_unit > 9) {
	printf("DU_Index: WARNING: struct ifnet ifp is not a du interface.\n");
	printf("DU_Index:          Interface is \"%s\", Unit is %d\n",
	    ifp->if_name, ifp->if_unit);
	return(0);		/* XXX Panic? */
    }

    i = ((p[2] - 'a') * 10) + ifp->if_unit;
    DDEBUG(D_INFO, "DU_Index: if_name = \"%s\", if_unit = %d, index = %d\n",
	ifp->if_name, ifp->if_unit, i);
    return(i);
}
#endif	/* SAFE_DU_INDEX */

int duoutput(), duioctl(), dutimer();



/*
** TTY LINE DISCIPLINE ROUTINES
*/

/*
**  Open line.
*/
/* ARGSUSED */
dutopen(dev, tp)
    dev_t dev;
    register struct tty *tp;
{
    DDEBUG(D_OUTPUT, "dutopen line %d\n", tp->t_dev, 0, 0);
    if (!suser())
	return(EPERM);

    if (tp->t_line == DUDISC)
	return(EBUSY);

    /* Unlike SLIP, we don't attach here. */
    return(0);
}


/*
**  Close line.  Detach the tty from the du.  Mimics part of ttyclose().
*/
dutclose(tp)
    struct tty *tp;
{
    DDEBUG(D_TRACE, "dutclose line %x\n", tp->t_dev, 0, 0);
    ttywflush(tp);
    tp->t_line = 0;
    if (tp->t_du) {
	dutnetclose(tp->t_du);
	tp->t_du = NULL;
    }
}


/*
**  Clean up the net interface when a connection is closed or abandoned.
**  This is separate from dutclose since we may not have a line when we
**  call this.
*/
dutnetclose(ds)
    struct du_softc *ds;
{
    struct mbuf *m;
    int s;

#ifdef	DEBUG
    if (ds)
	DDEBUG(D_TRACE, "dutnetclose ds %x dstty %x\n", ds, ds->ds_ttyp, 0);
    else
	DDEBUG(D_TRACE, "dutnetclose ds %x\n", ds, 0, 0);
#endif	/* DEBUG */

    if (ds) {
	s = splimp();			/* paranoid; splnet probably ok */
	/* inactive line */
	ds->ds_flags &= ~(DS_LWAITING|DS_LACTIVE|DS_LDOWN|DS_FAILCALL);
	if (ds->ds_ttyp)
	    ds->ds_ttyp->t_du = NULL;

	if (ds->ds_buf) {
#ifdef	vax
#ifdef	ultrix
#ifndef	PRE_ULTRIX_3_0
            kmem_free((caddr_t)ds->ds_buf, KM_DEVBUF);
#else
            km_free((caddr_t)ds->ds_buf, ds->ds_mtu);
#endif	/* PRE_ULTRIX_3_0 */
#else
	    /* BSD */
	    MCLFREE((struct mbuf *)ds->ds_buf);
#endif	/* ultrix */
#else	/* sun */
	    kmem_free((caddr_t)ds->ds_buf, ds->ds_mtu);
#endif	/* vax */
	    ds->ds_buf = NULL;
	}

	/* Clear queues. */
	while (ds->ds_if.if_snd.ifq_len > 0) {
	    IF_DEQUEUE(&ds->ds_if.if_snd, m);
	    IF_DROP(&ds->ds_if.if_snd);
	    m_freem(m);
	}
	splx(s);

	if (ds->ds_ttyp) {
	    /* tell application to go away */
	    DDEBUG(D_TRACE, "netclose: sending signal %d\n",
		ds->ds_ttyp->t_pgrp, 0, 0);
	    gsignal(ds->ds_ttyp->t_pgrp, SIGHUP);
	    gsignal(ds->ds_ttyp->t_pgrp, SIGCONT);
	    ds->ds_ttyp = NULL;
	}
    }
}


/*
**  Handle modem state changes; basically line drops.
*/
dutmodem(tp, flag)
    register struct tty *tp;
    int flag;
{
    DDEBUG(D_TRACE, "dutmodem (Did we lose carrier?)\n", 0, 0, 0);
    if (!flag) {
	/* Lost carrier. */
	tp->t_state &= ~TS_CARR_ON;
	if (tp->t_state & TS_ISOPEN && !(tp->t_flags & NOHANG)) {
	    DDEBUG(D_TRACE, "dutmodem kill %d\n", tp->t_pgrp, 0, 0);
	    gsignal(tp->t_pgrp, SIGHUP);
	    gsignal(tp->t_pgrp, SIGCONT);
	    return(0);
	}
    }
    else {
	/* Carrier now on. */
	tp->t_state |= TS_CARR_ON;
    }
    return(1);
}

/*
**  IOCTL.  Attach to TTY, get unit number.
*/
/* ARGSUSED */
dutioctl(tp, cmd, data, flag)
    struct tty *tp;
    int cmd;
    caddr_t data;
    int flag;
{
    struct ifreq *ifr;
    struct du_softc *ds;
    register char *p;
    int metric;

    DDEBUG(D_TRACE, "dutioctl: line %d cmd 0x%x\n", tp->t_dev, cmd, 0);
    if (tp == NULL)
	return(ENOTTY);

    if (!du_attached) {
	printf("dutioctl: Used du device before attach\n");
	return (EINVAL);
    }

    switch (cmd) {
    case SIOCSIFADDR:
	ifr = (struct ifreq *)data;
	p = ifr->ifr_name;
	/* Does this have the correct form? */
	if (p[0] != 'd' || p[1] != 'u'
	 || p[2] < 'a' || p[2] > 'z' || p[3] < '0' || p[3] > '9'
	 || p[4] != '\0') {
	    DDEBUG(D_IOCTL, "dutioctl: Bad du device \"%s\"\n", p, 0, 0);
	    return(EINVAL);
	}

	/* Extract the index and verify. */
	DDEBUG(D_IOCTL, "dutioctl: du device name = \"%s\"\n", p, 0, 0);
	metric = ((p[2] - 'a') * 10) + (p[3] - '0');
	DDEBUG(D_IOCTL, "dutioctl: name %s metric %d\n", p, metric, 0);
	if (metric < 0 || metric >= NDU)
	    return(EINVAL);
	ds = &du_softc[metric];

	/* already busy */
	if (ds->ds_flags & DS_LACTIVE)
	    return(EIO);
	if (duinit(ds) == 0)
	    return(ENOBUFS);

	ds->ds_ilen = 0;
	tp->t_du = (caddr_t)ds;
	/* flush out the line */
	ttyflush(tp, FREAD|FWRITE);

	ds->ds_flags |= DS_LACTIVE;
	ds->ds_flags &= ~DS_LWAITING;
	ds->ds_timer = ds->ds_atimo;
	ds->ds_if.if_timer = DU_HZ;
	/* and try to start I/O on device */
	ds->ds_ttyp = tp;
	ds->ds_if.if_flags |= IFF_UP;

	dutstart(ds->ds_ttyp);
	return(0);

    /* slip uses this to get device number, do we care? */
    case TIOCGETD:
	*(int *)data = DU_Index(&((struct du_softc *)tp->t_du)->ds_if);
	return(0);
    }
    return(-1);
}


/*
**  Copy data buffer to mbuf chain leave space for interface pointer.
*/
struct mbuf *
du_btom(ds, len)
    struct du_softc *ds;
    register int len;
{
    register caddr_t cp;
    register struct mbuf *m, **mp;
    register unsigned int count;
    int first;
    struct mbuf *top;

    first = 1;
    top = NULL;
    for (cp = ds->ds_buf, mp = &top; len > 0; mp = &m->m_next, len -= count) {
	MGET(m, M_DONTWAIT, MT_DATA);
	if ((*mp = m) == NULL) {
	    m_freem(top);
	    return(NULL);
	}
#ifdef	BSD
	if (first) {
	    m->m_off += sizeof(struct ifnet *);
	    count = MIN(len, MLEN - sizeof(struct ifnet *));
	    first = 0;
	}
	else
#endif	/* BSD */
	    count = MIN(len, MLEN);
	bcopy(cp, mtod(m, caddr_t), count);
	m->m_len = count;
	cp += count;
    }

    return(top);
}


/*
**  Receiver interrupt.
*/
dutinput(c, tp)
    register int c;
    register struct tty *tp;
{
    register struct du_softc *ds;
    register struct mbuf *m;
    struct du_hdr ldr;
    int s;

    DDEBUG(D_ICHAR, "dutinput\n", 0, 0, 0);
    tk_nin++;

    /* Handle spurious interrupts before the SIOCSIFADDR is done. */
    ds = (struct du_softc *)tp->t_du;
    if (ds < &du_softc[0] || ds > &du_softc[NDU])
	return;

    DDEBUG(D_INPUT,"dutinput tp = x%x\n", tp, 0, 0);
    if (ds == NULL || !(ds->ds_flags & DS_LACTIVE))
	return;

    ds->ds_cchr++;

    c &= 0xFF;
    DDEBUG(D_ICHAR, "dutinput flags %x len %d c= x%x\n",
	ds->ds_flags, ds->ds_ilen, c);
    if (ds->ds_flags & DS_ESCAPED) {
	ds->ds_flags &= ~DS_ESCAPED;
	switch (c) {
	default:
	    ds->ds_if.if_ierrors++;
	    ds->ds_mp = ds->ds_buf;
	    ds->ds_ilen = 0;
	    return;
	case TRANS_FRAME_ESCAPE:
	    ds->ds_resc++;
	    c = FRAME_ESCAPE;
	    break;
	case TRANS_FRAME_END:
	    ds->ds_resc++;
	    c = FRAME_END;
	    break;
	}
    }
    else {
	switch (c) {
	case FRAME_END:
	    if (ds->ds_ilen == 0)
		return;
	    m = du_btom(ds, ds->ds_ilen);
	    if (m == NULL) {
		ds->ds_if.if_ierrors++;
		return;
	    }
	    ds->ds_mp = ds->ds_buf;
	    ds->ds_ilen = 0;
	    ds->ds_if.if_ipackets++;
	    globdialstats.gds_ipln++;
#ifdef	BSD
	    *mtod(m, struct ifnet**) = &ds->ds_if;
#endif	/* BSD */
	    DDEBUG(D_INPUT,"dutinput: IP PKT\n", 0, 0, 0);
	    globdialstats.gds_opup++;
	    ds->ds_cpsip++;
	    s = splimp();
	    if (IF_QFULL(&ipintrq)) {
		IF_DROP(&ipintrq);
		ds->ds_if.if_ierrors++;
		m_freem(m);
	    }
	    else {
		IF_ENQUEUE(&ipintrq, m);
		schednetisr(NETISR_IP);
	    }
	    ds->ds_timer = ds->ds_atimo;
	    splx(s);
	    return;
	case FRAME_ESCAPE:
	    ds->ds_flags |= DS_ESCAPED;
	    return;
	}
    }

    if (++ds->ds_ilen > ds->ds_mtu) {
	ds->ds_if.if_ierrors++;
	ds->ds_mp = ds->ds_buf;
	ds->ds_ilen = 0;
	return;
    }

    *ds->ds_mp++ = c;
}



/*
**  Start output on interface.  Get another datagram to send from the
**  interface queue and map it to the interface before starting output.
*/
dutstart(tp)
    register struct tty *tp;
{
    register struct du_softc *ds;
    register struct mbuf *m;
    register int len;
    register u_char *cp;
    int flush, nd, np, n, s;
    struct mbuf *m2;
    extern int cfreecount;

    if (tp == NULL)
	return(ENOTTY);

    ds = (struct du_softc *)tp->t_du;
    DDEBUG(D_TRACE, "dutstart for \"%s%d\" nc = %d\n",
	ds->ds_if.if_name, ds->ds_if.if_unit, tp->t_outq.c_cc);

    for ( ; ; ) {
	/* If there is more in the output queue, just send it now.  We are
	 * being called in lieu of ttstart and must do what it would. */
	if (tp->t_outq.c_cc > 0)
	    ttstart(tp);
	if (tp->t_outq.c_cc > DU_HIWAT)
	    return;
	/* This happens briefly when the line shuts down. */
	if (ds == NULL) {
	    DDEBUG(D_TRACE, "dutstart null ds (not an error)\n", 0, 0, 0);
	    return;
	}

	/* If system is getting low on clists and we have something
	 * running already, stop here. */
	if (cfreecount < CLISTRESERVE + ds->ds_mtu) {
	    DDEBUG(D_TRACE, "clists = %d\n", cfreecount, 0, 0);
	    ds->ds_flags &= ~DS_OACTIVE;
	    /* If the free clist is empty, try to bring things back up to
	     * the low water mark.  Do our fair share.  */
	    if (cfreecount == 0) {
		DDEBUG(D_TRACE, "No clists, dumping some packets\n", 0, 0, 0);
		s = splimp();
		/* dequeue the pkts and hand back the mbufs */
		while (ds->ds_if.if_snd.ifq_len
		 && cfreecount < CLISTRESERVE + ds->ds_mtu) {
		    IF_DEQUEUE(&ds->ds_if.if_snd, m);
		    IF_DROP(&ds->ds_if.if_snd);
		    if (m)
			m_freem(m);
		}
		ds->ds_flags |= DS_OACTIVE;
		splx(s);
	    }
	    return;
	}

	/* Get a packet and send it to the interface. */
	DDEBUG(D_OUTPUT, "Getting a packet\n", 0, 0, 0);
	s = splimp();
	IF_DEQUEUE(&ds->ds_if.if_snd, m);
	if (m == NULL) {
	    if (tp->t_outq.c_cc == 0) {
		ds->ds_flags &= ~DS_OACTIVE;
		ds->ds_timer = ds->ds_atimo;
		ds->ds_if.if_timer = DU_HZ;
	    }
	    splx(s);
	    DDEBUG(D_OUTPUT, "null mbuf\n", 0, 0, 0);
	    return;
	}
	flush = !(ds->ds_flags & DS_OACTIVE);
	ds->ds_flags |= DS_OACTIVE;
	ds->ds_timer = -1;
	ds->ds_if.if_timer = 0;
	splx(s);
	/* The extra FRAME_END will start up a new packet, and thus
	 * will flush any accumulated garbage.  We do this whenever
	 * the line may have been idle for some time. */
	if (flush) {
	    DDEBUG(D_OCHAR,"dutstart: Frame End\n", 0, 0, 0);
	    (void) putc(FRAME_END, &tp->t_outq);
	    ds->ds_cchs++;	/* one for frame END */
	}

	while (m) {
	    DDEBUG(D_OUTPUT, "M\n", 0, 0, 0);
	    cp = mtod(m, u_char *);
	    for (len = m->m_len, ds->ds_cchs += len; len > 0; ) {
		/* Find out how many bytes in the string we can
		 * handle without doing something special. */
		nd = locc(FRAME_ESCAPE, len, cp);
		np = locc(FRAME_END, len, cp);
		n = len - MAX(nd, np);
		if (n) {
		    /* Put n characters at once into the tty output queue. */
		    if (b_to_q((char *)cp, n, &tp->t_outq))
			break;
		    len -= n;
		    cp += n;
		}
		/* If there are characters left in the mbuf, the first
		 * one must be special -- put it out in a different form. */
		if (len) {
		    DDEBUG(D_OCHAR,"dutstart: FRAME_ESC\n", 0, 0, 0);
		    if (putc(FRAME_ESCAPE, &tp->t_outq))
			break;
		    DDEBUG(D_OCHAR, "dutstart: *cp = x%02x\n", *cp, 0, 0);
		    ds->ds_sesc++;
		    if (putc(*cp == FRAME_ESCAPE ? TRANS_FRAME_ESCAPE :
				TRANS_FRAME_END, &tp->t_outq)) {
			(void)unputc(&tp->t_outq);
			break;
		    }
		    ds->ds_cchs += 2;  /* one for ESC one for char */
		    cp++;
		    len--;
		}
	    }
	    MFREE(m, m2);
	    m = m2;
	}
	DDEBUG(D_OPKT|D_OUTPUT, "ENDM\n", 0, 0, 0);
	if (putc(FRAME_END, &tp->t_outq)) {
	    /* No room.  Remove a char to make room and end the packet
	     * normally.  If you get many collisions (more than one or two
	     * a day) you probably do not have enough clists. */
	    (void)unputc(&tp->t_outq);
	    (void)putc(FRAME_END, &tp->t_outq);
	    ds->ds_if.if_collisions++;
	}
	else {
	    ds->ds_if.if_opackets++;
	    globdialstats.gds_opln++;
	    ds->ds_cchs++;	/* one for FRAME_END */
	}
    }
}



/*
**  DU INTERFACE ROUTINES
*/
duattach(net)
    int net;
{
    static char du_names[(NDU / 10) + 1][4];
    register struct du_softc *ds;
    register int i;

    DDEBUG(D_TRACE, "duattach entered\n", 0, 0, 0);
    if (du_attached)
	return;
    du_attached = 1;

    /* Initialize the names (hard coded into dutioctl() also). */
    for (i = 0; i <= NDU / 10; i++) {
        du_names[i][0] = 'd';
	du_names[i][1] = 'u';
	du_names[i][2] = 'a' + i;
	du_names[i][3] = '\0';
    }

    /* Initialize the slots. */
    for (i = 0; i < NDU; i++) {
	ds = &du_softc[i];
	/* set up timer information -- wait for connection to time out. */
	ds->ds_atimo = DEFAULT_INACTIVITY_TIME/DU_HZ;
	ds->ds_wtimo = 12 * DU_HZ;
	ds->ds_timer = -1;

	/* Fill in if structure. */
	ds->ds_if.if_name = du_names[i / 10];
	ds->ds_if.if_unit = i % 10;
	ds->ds_if.if_mtu = ds->ds_mtu = DUMTU;
	ds->ds_if.if_flags = IFF_POINTOPOINT;
	ds->ds_if.if_ioctl = duioctl;
	ds->ds_if.if_output = duoutput;
	/* We need a larger queue because of the startup delay. */
	ds->ds_if.if_snd.ifq_maxlen = 2 * IFQ_MAXLEN;
	ds->ds_if.if_watchdog = dutimer;
	ds->ds_if.if_timer = 0;
	if_attach(&ds->ds_if);
	DDEBUG(D_TRACE, "duattach adding unit %s%d at 0x%x\n",
	    ds->ds_if.if_name, ds->ds_if.if_unit, &ds->ds_if);
    }
}


/*
**  Queue a packet.  Start transmission if not active.
*/
duoutput(ifp, m, dst)
    register struct ifnet *ifp;
    register struct mbuf *m;
    struct sockaddr *dst;
{
    register struct du_softc *ds;
    register struct mbuf *tmpm;
    struct du_hdr leader;
    int s;

    DDEBUG(D_TRACE, "duoutput for \"%s%d\"\n", ifp->if_name, ifp->if_unit, 0);
    globdialstats.gds_ipup++;

    /* Check address family. */
    if (dst->sa_family != AF_INET) {
	printf("duoutput: \"%s%d\": AF %d not supported\n",
	    ifp->if_name, ifp->if_unit, dst->sa_family);
	m_freem(m);
	return(EAFNOSUPPORT);
    }

    /* check family and build our leader. */
    bzero((caddr_t)&leader, sizeof leader);
    ds = &du_softc[DU_Index(ifp)];
    ds->ds_cprip++;
    leader.duh_type = DUT_IP;
    DDEBUG(D_OUTPUT, "dutoutput flags %x\n",ds->ds_flags, 0, 0);

    if (!(ds->ds_if.if_flags & IFF_UP)) {
	m_freem(m);
	return(EHOSTUNREACH);
    }
    if ((ds->ds_flags & DS_FAILCALL)) {
	m_freem(m);
	return(ENETDOWN);
    }

    if (!(ds->ds_flags & (DS_LWAITING|DS_LACTIVE|DS_LDOWN|DS_FAILCALL))) {
	if (!(ds->ds_flags & DS_ENABLECALL)) {
	    m_freem(m);
	    return(ENETDOWN);
	}
	/* Not waiting and not active -- eventually make unit
	 * correspond to family. */
	if ((tmpm = m_pullup(m, sizeof(struct ip))) == NULL) {
	    printf("m_pullup failed in duoutput\n");
	    return(0);
	}

	/* get address of other end of p-to-p link */
	m = tmpm;
	if (dialupreq(mtod(m, struct ip *),
#ifdef	vax
		(struct sockaddr_in*)&ifp->if_addrlist->ifa_dstaddr,
#else
		(struct sockaddr_in*)&ifp->if_dstaddr,
#endif	/* vax */
		ifp->if_name, ifp->if_unit, 1)) {
	    m_freem(m);
	    return(EHOSTUNREACH);
	}

	ds->ds_flags |= DS_LWAITING;
	ds->ds_timer = ds->ds_wtimo;
	ds->ds_if.if_timer = DU_HZ;
    }

    /* now queue */
    s = splimp();
    if (IF_QFULL(&ifp->if_snd)) {
	IF_DROP(&ifp->if_snd);
	splx(s);
	m_freem(m);
	ds->ds_if.if_oerrors++;
	return(ENOBUFS);
    }
    IF_ENQUEUE(&ifp->if_snd, m);
    splx(s);

    /* If line is up and no activity, start something */
    if ((ds->ds_flags & DS_LACTIVE) && !(ds->ds_flags & DS_OACTIVE)) {
	if (ds->ds_ttyp == NULL)
	    panic("duoutput no tty");

	if (!(ds->ds_ttyp->t_state & TS_CARR_ON)) {
	    DDEBUG(D_TRACE, "duoutput Carrier Loss.\n", 0, 0, 0);
	    dutnetclose(ds);
	    return(EHOSTUNREACH);
	}
	dutstart(ds->ds_ttyp);
    }
    return(0);
}


duinit(ds)
    register struct du_softc *ds;
{
#ifdef	vax
#ifdef	ultrix
#ifdef	PRE_ULTRIX_3_0
    caddr_t km_alloc();
#endif	/* PRE_ULTRIX_3_0 */
    caddr_t p;
#else
    struct mbuf *p;
#endif	/* ultrix */
#else
    caddr_t kmem_alloc();
    caddr_t p;
#endif	/* vax */

    DDEBUG(D_TRACE, "duinit: ds %x\n", ds, 0, 0);
    if (ds->ds_buf == NULL) {
#ifdef	vax
#ifdef	ultrix
#ifdef	PRE_ULTRIX_3_0
	p = km_alloc(ds->ds_mtu);
#else
	kmem_alloc(p, caddr_t, ds->ds_mtu, KM_DEVBUF);
#endif	/* PRE_ULTRIX_3_0 */
#else
	MCLALLOC(p, 1);
#endif	/* ultrix */
#else
	p = kmem_alloc(ds->ds_mtu);
#endif	/* vax */

	if (p == NULL) {
	    printf("du%d: can't allocate buffer\n", ds - du_softc);
	    ds->ds_if.if_flags &= ~IFF_UP;
	    return(0);
	}
	ds->ds_buf = (char *)p;
	ds->ds_mp = ds->ds_buf;
    }
    return(1);
}



/*
**  Monitor routine that records CPU state.
*/
dialmonitor()
{
    register struct du_softc *ds;

    for (ds = du_softc; ds < &du_softc[NDU]; ds++) {
	if (!(ds->ds_flags & DS_MONITORON) || ds->ds_ttyp == NULL)
	    continue;
	if (ds->ds_ttyp->t_state & TS_BUSY)
	    ds->ds_ctpbusy++;
	else
	    ds->ds_ctpidle++;
    }
    timeout(dialmonitor, (caddr_t)NULL, DIAL_MONITORTIMEOUT);
}


/*
**  IOCTL
*/
duioctl(ifp, cmd, data, flags)
register struct ifnet *ifp;
int cmd;
caddr_t data;
int flags;
{
    register struct sockaddr_in *sin;
    int s;
    int error;
    struct du_softc *ds;

#ifdef	vax
    sin = (struct sockaddr_in *)&((struct ifaddr *)data)->ifa_addr;
#else  /* vax */
    sin = (struct sockaddr_in *)data;
#endif	/* vax */

    DDEBUG(D_TRACE, "duioctl: \"%s%d\"  cmd = 0x%x\n",
	    ifp->if_name, ifp->if_unit, cmd);
    DDEBUG(D_TRACE, "duioctl: \"%s%d\" data = 0x%x\n",
	    ifp->if_name, ifp->if_unit, data);

    error = 0;
    ds = &du_softc[DU_Index(ifp)];
    switch (cmd) {

    default:
	error = EINVAL;

    case SIOCSIFADDR:
	DDEBUG(D_IOCTL, "duioctl SIOCSIFADDR addr (0x%x)\n",
	    sin->sin_addr.s_addr, 0, 0);
#ifdef	vax
	DDEBUG(D_IOCTL, "duioctl name %s addr (0x%x) dstaddr (0x%x)\n",
	ifp->if_name,
	((struct sockaddr_in *)&ifp->if_addrlist->ifa_addr)->sin_addr.s_addr,
	((struct sockaddr_in*)&ifp->if_addrlist->ifa_dstaddr)->sin_addr.s_addr);
#endif	/* vax */

	if (sin->sin_family != AF_INET) {
	    error = EAFNOSUPPORT;
	    break;
	}
#ifdef	vax
	ifp->if_flags |= IFF_UP;
#else
	/* clear current route, set new address  */
	if_rtinit(ifp, -1);
	ifp->if_addr = *(struct sockaddr *)data;
	ifp->if_net = in_netof(sin->sin_addr);
	ifp->if_flags |= IFF_UP | IFF_RUNNING;

	/* Set up routing entry. */
	if (!(ifp->if_flags & IFF_ROUTE)) {
	    rtinit(&ifp->if_dstaddr, &ifp->if_addr,
		RTF_HOST|RTF_GATEWAY|RTF_UP);
	    ifp->if_flags |= IFF_ROUTE;
	}
#endif	/* vax */
	ds->ds_flags |= DS_MONITORON | DS_ENABLECALL;
	timeout(dialmonitor, (caddr_t)NULL, DIAL_MONITORTIMEOUT);
	break;

    case SIOCSIFDSTADDR:
	if (sin->sin_family != AF_INET)
	    error = EAFNOSUPPORT;
	break;

    case SIOCFAILCALL:
	s = splimp();
	/* Flush any waiting packets. */
	if (ds->ds_flags & DS_LWAITING) {
	    while (ds->ds_if.if_snd.ifq_len) {
		register struct mbuf *m;

		IF_DEQUEUE(&ds->ds_if.if_snd,m);
		IF_DROP(&ds->ds_if.if_snd);
		if (m)
		    m_freem(m);
	    }
	    ds->ds_flags &= ~(DS_LWAITING|DS_LACTIVE|DS_LDOWN);
	    ds->ds_flags |= DS_FAILCALL;
	    ds->ds_timer = ds->ds_atimo;
	    ds->ds_if.if_timer = DU_HZ;
	}
	splx(s);
	break;

    case SIOCSATIMEO:
	ds->ds_atimo = (u_long)GET_DATA(data) / DU_HZ;
	break;

    case SIOCGATIMEO:
	SET_DATA(data, (ds->ds_atimo * DU_HZ));
	break;

    case SIOCSWTIMEO:
	ds->ds_wtimo = (u_long)GET_DATA(data) / DU_HZ;
	break;

    case SIOCGWTIMEO:
	SET_DATA(data, (ds->ds_wtimo * DU_HZ));
	break;

    case SIOCSSOFTTIMER:
	s = splimp();
	ds->ds_timer = (u_long)GET_DATA(data);
	splx(s);
	break;

    case SIOCSSOFTFLAGS:
	s = splimp();
	ds->ds_flags = (int)GET_DATA(data);
	splx(s);
	break;	

    case SIOCGSOFTFLAGS:
	s = splimp();
	SET_DATA(data, ds->ds_flags);
	splx(s);
	break;

    case SIOCGIPKTS:
	s = splimp();
	SET_DATA(data, ifp->if_ipackets);
	splx(s);
	break;

    case SIOCGOPKTS:
	s = splimp();
	SET_DATA(data, ifp->if_opackets);
	splx(s);
	break;

    case SIOCCLEARQ:
	s = splimp();
	/* dequeue the pkts and hand back the mbufs */
	while (ds->ds_if.if_snd.ifq_len) {
	    struct mbuf *m;

	    IF_DEQUEUE(&ds->ds_if.if_snd,m);
	    IF_DROP(&ds->ds_if.if_snd);
	    if (m)
		m_freem(m);
	}
	splx(s);
	break;	

    case SIOCGIFMTU:
	SET_DATA(data, ds->ds_mtu);
	break;

    case SIOCSIFMTU:
	error = EBUSY;		/* XXX */
	break;

    case SIOCBRINGUP:
	{
	    struct ip ipfill;
	    struct sockaddr_in sinfill;

	    if (dialupreq(&ipfill, &sinfill, ifp->if_name, ifp->if_unit, 0)) {
		error = EHOSTUNREACH;
		break;
	    }
	    ds->ds_flags |= DS_LWAITING;
	    ds->ds_timer = ds->ds_wtimo;
	    ds->ds_if.if_timer = DU_HZ;
	    /* If line is up and no activity, start something */
	    if ((ds->ds_flags & DS_LACTIVE) && !(ds->ds_flags & DS_OACTIVE)) {
		if (ds->ds_ttyp == NULL)
		    panic("duioctl no tty");

		if (!(ds->ds_ttyp->t_state & TS_CARR_ON)) {
		    DDEBUG(D_TRACE, "duioctl Carrier Loss.\n", 0, 0, 0);
		    dutnetclose(ds);
		    error = EHOSTUNREACH;
		}
		dutstart(ds->ds_ttyp);
	    }
	}
    }
    DDEBUG(D_IOCTL, "duioctl return(%d)\n", error, 0, 0);
    return(error);
}


/*
**  Watchdog timer.
**  Must figure out which interface timed out, since we only get a
**  unit number.
*/
dutimer(unit)
    int unit;
{
    struct du_softc *ds;
    register int i;
    register int s;

    DDEBUG(D_TRACE, "dutimer: unit = %d\n", unit, 0, 0);

    if (unit < 0 || unit > 9)
	return;
    for (i = unit; i < NDU; i += 10) {
	if (i >= NDU)
	    return;

	/* Test to see if this is the correct interface. */
	ds = &du_softc[i];

	DDEBUG(D_TIMER, "dutimer: unit %d, flags %x timer %d",
	    i, (int)ds->ds_flags, (int)ds->ds_timer);
	DDEBUG(D_TIMER, "timer %d, if_timer %d, qlen %d\n",
	    (int)ds->ds_if.if_timer, ds->ds_if.if_snd.ifq_len, 0);

	/* Is this the interface we want? */
	if (ds->ds_if.if_timer || ds->ds_timer <= 0)
	    continue;

	/* just being cautious by setting up the interrupt priority */
	s = splimp();
	if (ds->ds_timer > 0 && --(ds->ds_timer) != 0) {
	    /* Reset the interface timer */
	    ds->ds_if.if_timer = DU_HZ;
	    splx(s);
	    continue;
	}

	/* If the call failed, allow new calls to be made. */
	if (ds->ds_flags & DS_FAILCALL)
	    ds->ds_flags &= ~(DS_LWAITING|DS_LACTIVE|DS_LDOWN|DS_FAILCALL);

	/* Trying to get a line - timeout. */
	if (ds->ds_flags & DS_LWAITING) {
	    /* we never had a line, just tidy the net */
	    DDEBUG(D_TRACE, "dutimer: unit %d - we never had a line.\n",
		i, 0, 0);
	    dutnetclose(ds);
	    ds->ds_flags &= ~(DS_LWAITING|DS_LACTIVE|DS_LDOWN|DS_FAILCALL);
	}

	/* Is the line to be shutdown? */
	if (ds->ds_flags & DS_LDOWN) {
	    DDEBUG(D_TRACE, "dutimer: unit %d - shutdown net and line.\n",
		    i, 0, 0);
	    dutnetclose(ds);
	    if (ds->ds_ttyp)
		/* just being careful */
		dutclose(ds->ds_ttyp);
	    ds->ds_flags &= ~(DS_LWAITING|DS_LACTIVE|DS_LDOWN|DS_FAILCALL);
	}

	/* inactivity timer just went off - shutdown the line */
	if (ds->ds_flags & DS_LACTIVE) {
	    /* mistaken? */
	    if (ds->ds_flags & DS_OACTIVE)
		ds->ds_timer = -1;
	    else {
		DDEBUG(D_TRACE, "dutimer: unit %d - bringing down line.\n",
			i, 0, 0);
		dutnetclose(ds);
		if (ds->ds_ttyp)
		    dutclose(ds->ds_ttyp);
	    }
	}
	splx(s);
    }
}


#ifndef	vax
/*
**  locc appears to only be in 4.3 for vaxen
*/
int
locc(mask, size, cp)
    register u_char mask;
    u_int size;
    register u_char *cp;
{
    register u_char *end;

    for (end = &cp[size]; cp < end && *cp != mask; cp++)
	;
    return(end - cp);
}
#endif	/* vax*/

#endif /* NDU > 0 */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.