DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T g

⟦013be15a0⟧ TextFile

    Length: 25425 (0x6351)
    Types: TextFile
    Names: »gw.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦3b20aab50⟧ »EurOpenD3/network/snmp/kip-snmp.91.tar.Z« 
        └─⟦b503a39fe⟧ 
            └─⟦this⟧ »kip/gw.c« 

TextFile

/*
 *  AppleTalk / Ethernet Gateway.
 *
 *  (c) 1986, Stanford Univ. SUMEX project.
 *  May be used but not sold without permission.
 *
 *  (c) 1986, Kinetics, Inc.
 *  May be used but not sold without permission.
 */

char rcsident[] =
"$Header: gw.c,v 4.1 88/11/01 19:49:12 sw0l Locked $";

#include "gw.h"
#include "gwctl.h"
#include "fp/pbuf.h"
#include "ab.h"
#include "ether.h"
#include "inet.h"
#include "fp/cmdmacro.h"

#include "glob.h"
#include "conf.h"

#ifndef KFPSVERSION
# include "kfpsversion.h"
#endif

#ifdef SNMP
#include "mib.h"
#include "snmp_vars.h"
#endif

/*
 * Main event loop.
 */
main()
{
	register struct pbuf *p;
	short pri;
	struct fp_mem mem;
	extern char begin;	/* first address in RAM */
	extern char edata;	/* last address in initialized data */
	extern char end;	/* last address in this downloaded program */

	K_PROMRAM(&pvars); /* get addresses of relevant prom variables */
	data_init(); /* initialize data normally done at compile time */
	pq    = pvars.fpr_bufs->fpb_pq; /* shorthand for queue headers */
	sendq = pvars.fpr_bufs->fpb_sendq;
	bcopy(pvars.fpr_state->fps_unused, (caddr_t)&conf, sizeof conf);
	bcopy(conf.etheraddr, etheraddr, 6);
	asminit();		/* set up ethernet interrupt vector */

	/* initialize buffer pool */
	K_INIPROM();
	mem.fpm_count = 0;
	K_GETMEM(&mem);		/* gets address of the top of available mem */
	topram = (int)mem.fpm_memp;
	/* use all the rest of available ram for buffers */
	bufs = (struct pbuf *)((int)(&end) + 2);
	mem.fpm_count = topram - ((int)(bufs)) - 2;
	mem.fpm_memp = (char *)bufs;
	K_CLRMEM(&mem);		/* initialize entire area to all zeroes */
	/* set free list and queue headers to empty */
	*(pvars.fpr_bufs->fpb_pfree) = 0;
	pq->pq_head = 0;
	pq->pq_tail = 0;
	pq->pq_len = 0;
	sendq->pq_head = 0;
	sendq->pq_tail = 0;
	sendq->pq_len = 0;
	K_BUFINIT(&mem);	/* divide the area into buffers */

	/* initialize interface structures */
	ifnet = &ifie; ifie.if_next = &ifab; 
	ifab.if_next = &ifet; ifet.if_next = 0;

#ifdef notdef
	{ short nodenum;
	  K_ATINIT(&nodenum);	/* initialize AppleTalk network interface */
	  atnode = nodenum;
	}
#else
	atnode = pvars.fpr_state->fps_node;
#endif
	ie_init();	/* initialize Ethernet interface... */
	if (conf.ipaddr == 0 || etheraddr[2] != 0x89)
		panic("bad conf");

	ifie.if_addr = conf.ipaddr; /* initialize internet data structures */
	ipnet = ifie.if_addrnet = ipnetpart(ifie.if_addr);
	ifie.if_dnode = (conf.ipaddr & 0xFF);
	ifab.if_dnode = ifab.if_haddr[0] = atnode;
	ifet.if_dnode = 0;	/* no Ethertalk address known yet */
#ifdef notdef
	checksum(&begin, &edata); /* set up RAM memory checksum protection */
#endif
	if (conf.ready == confReady)
		confready(0);	/* finish configuration */
	K_WAIT(20);
	sendf("KFPS(IP) version %s node #%d lives!! - %d buffers", 
		KFPSVERSION, atnode,
	      mem.fpm_count/sizeof (struct pbuf));

	K_LEDOFF();
	K_SPL0(&pri); /* allow interrupts */

	for (;;) {
		if (pq->pq_head == 0) { /* no packets on main queue */
			back();		/* perform background processing */
			continue;
		}
		K_PDEQ(SPLIMP,pq,p);	/* remove buffer from queue */
		if (p->p_len < 0) {	/* if error packet */
			if (p->p_len != -1)
				goto pan;
			K_PFREE(p); /* put the buffer back on the free list */
			continue;
		}
		switch (p->p_type) {	/* switch on packet type */
		case PT_ABUS:
#ifdef SNMP
			/*
			 * Check the ddp source address to see that
			 * this wasn't a broadcast ddp from this node.
			 */
			if (p->p_off[1] != ifab.if_dnode)
			    mib_ifEntry[1].ifInOctets += p->p_len;
#endif
#ifdef notdef
			p_if(p) = &ifab; /* should be in prom code? */
#endif
			source_if = &ifab;
 			abreceive(p);	/* receive AppleTalk packet */
			break;
		case PT_ENET:
#ifdef SNMP
			mib_ifEntry[0].ifInOctets += p->p_len;
#endif SNMP
			source_if = &ifie;
			iereceive(p);	/* receive ethernet packet */
			break;
		default:
pan:
#ifdef	STATS
			K_SPLIMP(&pri);	/* prevent interruptions */
			p_print("main",p);
			dmpbf();
			ieprintstats();
			abprintstats();
#endif	STATS
			panic("main: bad pkt");
		}
	}
}


/*
 * Complete configuration process.  Called when configuration 'ready'
 * via preset struct conf or aaconf packet received.
 */
confready(aa)
	register struct aaconf *aa;
{
	if (aa) {	/* configure packet received */
		bcopy(aa->stuff, (caddr_t)(&conf) + confPrefix,
			sizeof conf - confPrefix);
		/* will go away after KIP 06/88 */
		/* prevents us from doing bad things */
		if (conf.startddpWKSUnix == 0)
		  conf.startddpWKSUnix = defddpWKSUnix;
		conf.ready = confReady;
	}
	if (conf.ipdynamic > NIPDAD)
		conf.ipdynamic = NIPDAD;
	ifab.if_dnet = conf.atneta;
	ifab.if_unit = 0;
	aroute[0].net = conf.atneta;	/* hops=0, node=0, flags=0 */
	aroute[0].port = 0;
	porttoif[0] = &ifab;
	ifie.if_dnet = conf.atnete;
	ifie.if_unit = 1;
	aroute[1].net = conf.atnete;
	aroute[1].node = (conf.ipaddr & ~0xFF);	/* hops=0*/
	aroute[1].flags = arouteNet;
	aroute[1].port = 1;
	porttoif[1] = &ifie;
	if (conf.anetet) {
	  ifet.if_dnet = aroute[2].net = conf.anetet;
	  ifet.if_unit = 2;
	  aroute[2].port = 2; /* node=0, hops=0,flags=0 */
	  porttoif[2] = &ifet;
	  conf.anetet = 0;	/* some clients expect this to be 0 */
	}
}


short	confdelay;	/* sigh, should be 'static', but checksum complains */

/*
 * Configuration clock routine;  get config from appletalk admin.
 */
conftimer()
{
	if (confdelay && ++confdelay < 10)
		return;
	confdelay = 1;
	confrequest(aaCONF);
}


/*
 * Request configuration (or net table) from appletalk administrator.
 */
confrequest(op)
{
	register struct aaconf *m;
	register struct udp *u;
	register struct pbuf *p;

	K_PGET(PT_DATA, p);
	if (p == 0)
		return;
	p->p_len = sizeof (struct ip) + sizeof *u + aaconfMinSize;
	setiphdr(p, conf.ipadmin);
	u = (struct udp *)(p->p_off + sizeof (struct ip));
	u->src = aaPort;
	u->dst = aaPort;
	u->length = p->p_len - sizeof (struct ip);
	u->checksum = 0;
#ifdef SNMP
	mib_udp.udpOutDatagrams++;
#endif
	m = (struct aaconf *)(u + 1);
	m->magic = aaMagic;
	m->type = op;
	m->ipaddr = conf.ipaddr;
	m->count = 0;
	source_if = 0;
	routeip(p, 0, 0);
}


int	backclock;	/* 'static' */
/*
 * Backround processing.  Call once per second timeout routines.
 * Call 'backround' routines.
 */
/* The following used for Ethertalk startup */
iaddr_t et_probe;		/* Current address we are trying */
short et_probe_count;		/* number of times we've PROBEd it */
#define ETALK_NPROBE 10		/* Number of times to probe */
/*
 * The Ethertalk spec requires some ungodly number of probes at an
 * unreasonably short interval. In order to protect the ethernet I'm
 * going to try to get away with a smaller number
 */
back()
{
	register i;

	nbpback();	/* NBP backround (sends 1 LkUp per loop) */
#ifdef 	KINETICS
	msclock++;
	if ((i = msclock - backclock) < 0)
		i = -i;
	if (i > 12000)		/* ~one second */
#else
	if ((i = msclock - backclock) < 0)
		i = -i;
	if (i > 1000)		/*  ~one second */
#endif
	  {
	    backclock = msclock;
	    arptimer();	/* timeout arp cache */
	    if (conf.ready != confReady) {
	      conftimer();
	      return;
	    }

	    /* Ethertalk - keep probing if address not chosen yet */
	    if (ifet.if_dnet && ifet.if_dnode == 0) {
	      /* Still probing for ethertalk address */
	      if (arpresolve(&ifet, (struct pbuf *) 0, &et_probe, (char *)0)) {
		/* This address is known */
		et_probe_count = 0;
		++((char *)&et_probe)[3];
	      } else ++et_probe_count;

	      if (et_probe_count > ETALK_NPROBE) {
		/* use this node address */
		ifet.if_addr = et_probe;
		ifet.if_dnode = ((char *)&et_probe)[3];
	      }
	    } /* end test if ETalk address set */
	      /* Don't send out RTMP packets until Etalk is set */
	    else {
	      rtmptimer();	/* routing table maint protocol timer */
	    }
	    arttimer();		/* arouteTuple timer */
	    ipdadtimer();	/* IP dynamic address assignment timer */
	    ziptimer();	/* zone info timer */
	  }
      }


/*
 * The functions of the "abreceive" and "ilreceive" routines below might 
 * normally belong in the input interrupt sections of their respective
 * drivers.  But since they do some protocol translation as well, we've
 * stuck them here.  (Another consideration is that the LAP input interrupt
 * code is very time critical).
 */

/*
 * Receive next packet from AppleTalk.
 * Forward the packet onto the ethernet leg, possibly encapsulating
 * in a UDP packet first.
 */
abreceive(p)
	register struct pbuf *p;
{
	register wasbroad;
	int temp;

	bcopy(p->p_off, (caddr_t)&lap, lapSize);
	wasbroad = (lap.dst == 0xFF);
#ifdef SNMP
	/*
	 *  Check the source_if to make sure that this isn't an ethertalk packet
	 * off of the ethernet.  Also check the ddp source address to see that
	 * this wasn't a broadcast ddp from this node.
	 */
	if (source_if == &ifab && p->p_off[1] != ifab.if_dnode)
	    if (wasbroad)
		mib_ifEntry[1].ifInNUcastPkts++;
	    else
		mib_ifEntry[1].ifInUcastPkts++;
#endif SNMP
	p->p_off += lapSize;
	p->p_len -= lapSize;
	wasddp = 0;	/* assume not a ddp */
	/*
	 * Below we copy the DDP header into a global structure to
	 * get word alignment.  Since lapSize and ddpSize are both odd,
	 * after handling the headers the packet is again on an even
	 * boundary (odd+odd=even).
	 */
	switch (lap.type) {
	case lapDDPS:
		if ((p->p_len -= ddpSSize) < 0)
			goto drop;
		bcopy(p->p_off, (caddr_t)&ddps, ddpSSize);
		p->p_off += ddpSSize;
		/* reset length in case ethertalk */
		temp = ddps.length - ddpSSize;
		ddp.dstSkt = ddps.dstSkt;
		ddp.type = ddps.type;
		break;

	case lapDDP:
		if ((p->p_len -= ddpSize) < 0)
			goto drop;
		bcopy(p->p_off, (caddr_t)&ddp, ddpSize);
		/* 0 network means this_network */
		if (ddp.srcNet == 0)
		  ddp.srcNet = source_if->if_dnet;
		if (ddp.dstNet == 0)
		  ddp.dstNet = source_if->if_dnet;
		temp = (ddp.length & ddpLengthMask) - ddpSize;
		p->p_off += ddpSize;
		wasddp = 1;
		break;

	case 'K':		/* Kinetics protocol type */
		K_KLAP(p);
		K_PFREE(p);
		return;

	default:
#ifdef SNMP
		/*
		 *  Check the source_if to make sure that this isn't an ethertalk packet
		 * off of the ethernet.  Also check the ddp source address to see that
		 * this wasn't a broadcast ddp from this node.
		 */
		if (source_if == &ifab && p->p_off[1] != ifab.if_dnode)
		    mib_ifEntry[1].ifInUnknownProtos++;
#endif
		goto drop;
	}

	/* MUST BE ddp at this point */
	/* reset packet length in case ethertalk */
	if (temp < p->p_len)	/* make sure we don't overun buffer */
	  p->p_len = temp;

	if (conf.ready != confReady)
		goto drop;

#ifdef notdef
/* Looks like EtherTalk has made it necessary to assume that */
/* incoming RTMP or ZIP may be LONG DDP */
	/*
	 * here we switch out immediately to short-DDP-only
	 * handlers, to avoid having to convert them to long DDPs.
	 */
	switch (ddp.dstSkt) {
	case 0:
		goto drop;

	case rtmpSkt:
		if (lap.src == source_if->if_dnode)
			goto drop;	/* from ourself */
		rtmpinput(p);
		return;

	case zipSkt:
		if (lap.src == source_if->if_dnode)
			goto drop;	/* from ourself */
		zipinput(p);
		return;
	}
#endif
	if (ddp.type == ddpIP) {
#ifdef SNMP
		mib_ip.ipInReceives++;
#endif
		routeip(p, source_if, wasbroad);
		return;
	}
	/* not magic type, just route as a ddp */
	if (wasddp == 0)
		ddps2ddp(p);
route:
	p->p_off -= lapSize + ddpSize;
	p->p_len += lapSize + ddpSize;
	routeddp(p, source_if, wasbroad);
	return;

drop:
	/* put the buffer back on the free list */
	K_PFREE(p);
	stats.dropabin++;
	return;
}


/*
 * Convert packet from short DDP form to long DDP form.
 */
ddps2ddp(p)
	register struct pbuf *p;
{
	if (wasddp)
		return;
	ddp.length = ddps.length - ddpSSize + ddpSize;
	ddp.checksum = 0;
	ddp.dstNet = ddp.srcNet = source_if->if_dnet;
	ddp.srcNode = lap.src;
	ddp.dstNode = lap.dst;
	ddp.srcSkt = ddps.srcSkt;
	ddp.dstSkt = ddps.dstSkt;
	ddp.type = ddps.type;
	bcopy((caddr_t)&ddp, p->p_off - ddpSize, ddpSize);
	wasddp = 1;
}


/*
 * Receive next packet from intel ethernet.
 */
iereceive(p)
register struct pbuf *p;
{
	struct ether_header *ehp;
	register struct ip *ip;
	register struct udp *up;
	register int l;
	int wasbroad;

#ifdef	EDATA
	K_WAIT(20);
	p_print("ierecv", p);
#endif	EDATA
	ehp = (struct ether_header *)p->p_off;
	wasbroad = (ehp->ether_dhost.ether_addr_octet[0] & 0x1);
#ifdef SNMP
	if (wasbroad)
	    mib_ifEntry[0].ifInNUcastPkts++;
	else
	    mib_ifEntry[0].ifInUcastPkts++;
#endif SNMP
	p->p_off += sizeof *ehp;
	p->p_len -= sizeof *ehp;
	switch (ntohs(ehp->ether_type)) {
	    case ETHERTYPE_ETHERTALK:
	        if (ifet.if_dnet == 0){
		  goto drop; /* ignore unless enabled */
		}
	        source_if = &ifet; /* switch virtual interfaces */
	        abreceive(p);
		return;

	    case ETHERTYPE_AARPTYPE:
		source_if = &ifet;
		/* fall through */
	    case ETHERTYPE_ARPTYPE:
		arpinput(p);	/* pass it to ARP */
		return;
	    case ETHERTYPE_IPTYPE:
#ifdef SNMP
		mib_ip.ipInReceives++;
#endif
		break;
	    default:
#ifdef SNMP
		mib_ifEntry[0].ifInUnknownProtos++;
#endif
		goto drop;		/* can't handle it */
	}

	/*
	 *  If the proto type is IP/UDP and the port
	 *  number is in the magic range, remove the
	 *  encapsulation from the DDP datagram.
	 */
	ip = (struct ip *)p->p_off;
	/* Checksum the IP header ? */
	if (p->p_len < sizeof (*ip)){
#ifdef SNMP
		mib_ip.ipInHdrErrors++;
#endif
		goto drop;
	}
	if (conf.ready != confReady) {
		/* if conf not ready, only process control packets */
		if (ip->ip_dst != conf.ipaddr)
			goto drop;
		ip4me(p);
		return;
	}
	if (ip->ip_p != IPPROTO_UDP || ip->ip_dst != conf.ipaddr)
		goto toip;
	l = (ip->ip_hl << 2);
	up = (struct udp *)(p->p_off + l);
	if (p->p_len < (l + sizeof(*up))){
#ifdef SNMP
		mib_udp.udpInErrors++;
#endif
		goto drop;
	}
	if ((up->dst >= ddpNWKSUnix + 128 && up->dst < ddpNWKSUnix + 256)
	    || (up->dst >= ddpWKSUnix && up->dst < ddpWKSUnix + 128)) {
		/* decapsulate */
#ifdef SNMP
		mib_udp.udpInDatagrams++;
#endif
		p->p_off += (l + sizeof(*up));
		p->p_len -= (l + sizeof(*up));
		/* route as ddp */
		if (p->p_len < ddpSize + lapSize)
			goto drop;
		bcopy(p->p_off + lapSize, (caddr_t)&ddp, ddpSize);
		l = (ddp.length & ddpLengthMask);
		if (l < p->p_len - lapSize)
			p->p_len = l + lapSize; /* zap padding */
		wasddp = 1;
		routeddp(p, source_if, wasbroad);
		return;
	}
toip:
	routeip(p, source_if, wasbroad);
	return;
drop:
/*	sendf("Got bad IP packet"); */
	stats.dropiein++;
	/* put the buffer back on the free list */
	K_PFREE(p);
	return;
}

/*
 * These match routines return 0 if no match and 1 (true) if a match occurs.
 * They are called from arpinput/nbpinput to determine if an IP
 * address is in our range.
 *
 * A 'true' return value causes the arpinput code to return a 'fake ARP'
 * to the requester:  i.e. the requester will think that the real destination
 * host responded to the ARP, whereas it was really this gateway 'faking'
 * a response.
 */

/*
 * Match an AppleTalk ARP.  If the target is not in my own
 * range, return true (match).  This has the effect of forwarding all
 * traffic for 'unknown' nets, to the backbone ethernet.
 */
abmatch(target)
	iaddr_t target;
{
	register i;

	if (target == conf.ipaddr)
		return (1);
	if (ipnetpart(target) == 0x7F000000)
		return (0);
	if (conf.ready != confReady)
		return (0);
	i = target - conf.ipaddr;
	if (i < 0)
		return (1);
	if (i >= conf.ipstatic + conf.ipdynamic + 1)
		return (1);
	return (0);
}

/*
 * Match an ethernet ARP.
 */
iematch(target)
	iaddr_t target;
{
	register i;

	i = target - conf.ipaddr;
	if (i == 0)
		return (1);
	if (conf.ready != confReady)
		return (0);
	if (i >= 0 && i <= conf.ipstatic + conf.ipdynamic)
		return (1);
	return (0);
}


/*
 * Route an IP packet.  This code currently assumes only one AppleTalk and
 * ether interface per gateway;  instead it should scan a list of interfaces.
 * If a routing table existed within the gateway, it would determine
 * which interface pointer (ifp) and destination address (dst below)
 * are passed to ifp->if_output.
 */
routeip(p, srcif, br)
     register struct pbuf *p;
     struct ifnet *srcif;	/* interface packet came in on */
{
	register struct ifnet *ifp = &ifie;	/* default output to ether */
	register struct ip *ip = (struct ip *)p->p_off;
	int dst = ip->ip_dst;
	register iaddr_t dstnet = ipnetpart(dst);

	/* omit any bytes past the ip data */
	if (p->p_len > ip->ip_len) {
		p->p_len -= (p->p_len - ip->ip_len);
	}
	if (dst == conf.ipaddr) {
		ip4me(p);
		return;
	}
#ifdef SNMP
	/* if not from me, I must be forwarding this */
	if (ip->ip_src == conf.ipaddr)
	    mib_ip.ipOutRequests++;
	else
	    mib_ip.ipForwDatagrams++;
#endif
	if (iematch(dst))
		ifp = &ifab;
	else if (dstnet != ifie.if_addrnet
	    || (ipbroadcast(dst) && dst != conf.ipbroad))
		dst = conf.iproutedef;	/* needs to be forwarded */
	if (ifp != srcif) /* do not reflect packets to same net */
		return((*ifp->if_output)(ifp, p, AF_IP, &dst));
drop:
	K_PFREE(p);
	stats.droprouteip++;
	return;
}


/* IP directed broadcast types, used by 'routeddp' */
iaddr_t ipbroadtypes[] = { 0, 0xFF, 0xFFFF, 0xFFFFFF };


/*
 * Route a DDP packet.  Assumes ddp has already been unpacked into
 * global struct ddp.  If 'si' (source interface) is 0, packet
 * originates inside the gateway;  otherwise this is a forward.
 * 'br' is true if the packet was received as a broadcast.
 */
routeddp(p, si, br)
	register struct pbuf *p;
	struct ifnet *si;
	int br;
{
	register struct udp *u;
	register struct aroute *ar;
	iaddr_t idst;
	struct ifnet *ifp;
	int wasbroadcast;
	register i, port;
	u_char dst;

	if (ddp.dstNet == 0 || ddp.dstNode == 0)
		goto drop;
	if (p->p_len < (ddpSize + lapSize))
		goto drop;
	wasbroadcast = br && ddp.dstNode == 0xff; /* really broadcast? */
	if (si && ddp.dstNet == si->if_dnet &&
	    (ddp.dstNode == si->if_dnode || wasbroadcast)) {
	  ddp4me(p);
	  return;
	}
	for (ar = &aroute[0] ; ar < &aroute[NAROUTE] ; ar++) {
		if (ar->net == 0)	/* empty slot */
			continue;
		if (ar->net == ddp.dstNet && ar->port < MAX_PORT) {
		  if ((ifp = porttoif[ar->port]) == 0) /* nothing defined */
		    continue;
		  goto found;
		}
	}
	goto drop;	/* didnt find it */
found:
	/*
	 * if this packet originates externally and is addressed
	 * to us (or broadcast), hand it to 'ddp4me'.
	 */
	if (ar->hops)	/* if not one of our interfaces */
		goto fwd;

	/* net already matched, just check node */
	if (wasbroadcast || ifp->if_dnode == ddp.dstNode) {
	  ddp4me(p);
	  return;
	}
fwd:
	if (((ddp.length >> ddpHopShift) & 0xF) == 0xF)
		goto drop;
	if (si)			/* if not internally sent */
	  p->p_off[lapSize] += 4; /* increment hop count in pbuf */
fwd2:
	if (br)
		goto drop;	/* dont forward broadcasts */
	if (ddp.type == ddpNBP && ddp.dstSkt != nbpNIS) /* NBP reply filter */
		if (nbpfilter(p))
			goto drop;

	if (ifp->if_flags & IF_APPLETYPE) {
	  /* your basic "LAP" or "ELAP" output routine */
	  i = AF_DDP;		/* assume long ddp */
	  /* should collapse to short ddp if we can here (internal only) */

	  /* short ddp allowed on interface? (and internal?) */
	  if (!si && (ifp->if_flags & IF_ALAP))
	    if (ar->hops == 0 && ddp.srcNet == ddp.dstNet) {
	      /* primary interface and input/output net the same */

	      /* move ahead to ddp short boundary */
	      p->p_off += (ddpSize - ddpSSize);
	      p->p_len -= (ddpSize - ddpSSize);
	      ddps.srcSkt = ddp.srcSkt;
	      ddps.dstSkt = ddp.dstSkt;
	      ddps.type = ddp.type;
	      ddps.length = ddp.length - (ddpSize - ddpSSize);
	      bcopy((caddr_t)&ddps, p->p_off+lapSize, ddpSSize);
	      i = AF_SDDP;
	    }
	  dst = (ar->hops ? ar->node : ddp.dstNode);
	  (*ifp->if_output)(ifp, p, i, &dst);
	  return;
	}
/*
  paranoia 
	if ((ifp->if_flags & IF_IPUDP) == 0)
	  goto drop;
*/
	/*
	 * Else destination is IP address, encapsulate.
	 * Choose the appropriate IP host or directed broadcast address.
	 */
	port = ddp2ipskt(ddp.dstSkt);
	switch ((i=ar->flags & arouteTYPE)) {
	default:
	  goto drop;
	case arouteKbox:
	  idst = ar->node;
	  break;
	case arouteNet:
	case arouteHost:
	  if (ddp.dstNode != 0xFF) { /* not broadcast? */
	    idst = (ar->node & ~0xFF) + ddp.dstNode; /* nope */
	    break;
	  }
	  if (i == arouteNet) {
	    /* problematic sometimes : also conf.ipbroad should be set on */
	    /* ifp */
	    idst = (ddp.dstNet == ifp->if_dnet) ? conf.ipbroad :
	      ar->node + ipbroadtypes[ar->flags&arouteBMask];
	  } else {
	    idst = ar->node;
	    port = rebPort;
	  }
	  break;
	}
	lap.dst = 0xFA;
	lap.src = 0xCE;
	lap.type = lapDDP;
	bcopy((caddr_t)&lap, p->p_off, lapSize);
	p->p_off -= sizeof (struct ip) + sizeof *u;
	p->p_len += sizeof (struct ip) + sizeof *u;
	setiphdr(p, idst);
	u = (struct udp *)(p->p_off + sizeof (struct ip));
	u->src = ddp2ipskt(ddp.srcSkt);
	u->dst = port;
	u->length = p->p_len - sizeof (struct ip);
	u->checksum = 0;
#ifdef SNMP
	mib_udp.udpOutDatagrams++;
#endif
	routeip(p, 0, 0);
	return;
drop:
	/*
	K_WAIT(20);
	sendf("routeddp: dropping packet");
	*/
	stats.droprouteddp++;
	/* put the buffer back on the free list */
	K_PFREE(p);
}


/*
 * AppleTalk output routine.
 * Encapsulate a packet of type af for the local net.
 * 
 * Look at a normal net output routine (such as ethernet output, iloutput),
 * for the more general case of an output routine which can handle 
 * multiple units (using the ifp->if_unit field) with interrupt driven
 * output (and output queues).
 *
 * Also, normally an output routine prepends only the link 
 * level header (e.g. LAP).  However this routine is a bit unusual:
 *
 *   Since the LAP and DDP header sizes are both odd, DDP packets are
 *   usually passed about with both headers on the front, so that the
 *   data will always be on an even boundary.
 *
 *   Since IPs are inside DDPs, we prefix a LAP/DDP header onto them.
 */
aboutput(ifp, p, af, dst)
	register struct ifnet *ifp;
	register struct pbuf *p;
	caddr_t dst;
{
	iaddr_t idst;
	AddrBlock tdst;
	struct LAP l;
	struct DDP d;
	WDS wds[2];		/* write data structure for abuswrite */

	l.src = ifp->if_dnode;	/* (also done as part of atwrite) */
	switch (af) {

	case AF_DDP:	/* DDP: already has LAP header */
		l.dst = *dst;
		l.type = lapDDP;
		bcopy((caddr_t)&l, p->p_off, lapSize);
		break;
		
	case AF_SDDP:	/* ShortDDP: already has LAP header */
		l.dst = *dst;
		l.type = lapDDPS;
		bcopy((caddr_t)&l, p->p_off, lapSize);
		break;

	case AF_IP:
		idst = *(iaddr_t *)dst;
		if (!arpresolve(ifp, p, &idst, &tdst))
			return (0);	/* if not yet resolved */
	lapddp:
		l.type = lapDDP;
		d.length = p->p_len + ddpSize;
		d.checksum = 0;
		d.srcNet = ifp->if_dnet;
		d.dstNet = tdst.net;
		d.srcNode = ifp->if_dnode;
		d.dstNode = tdst.node;
		d.srcSkt = d.dstSkt = ddpIPSkt;
		d.type = ddpIP;
		if (tdst.net == ifp->if_dnet)
			l.dst = tdst.node;
		else if ((l.dst = getaroute(tdst.net)) == 0)
			goto drop;
		p->p_off -= (lapSize + ddpSize);
		p->p_len += (lapSize + ddpSize);
		bcopy((caddr_t)&l, p->p_off, lapSize);
		bcopy((caddr_t)&d, p->p_off+lapSize, ddpSize);
		break;

	default:
#ifdef SNMP
		/* in case panic calls snmp_trap() */
		mib_ifEntry[0].ifOutErrors++;
#endif
		panic("ab%d: can't handle af%d\n", ifp->if_unit, af);
	}

	wds[1].size = 0;	/* terminate list */
	wds[0].size = p->p_len;
	wds[0].ptr = p->p_off;
#ifdef SNMP
	mib_ifEntry[1].ifOutOctets += p->p_len;
#endif
	K_ATWRITE(&wds[0]);	/* appletalk write */
	if (l.dst == 0xFF) {	/* if was broadcast, give myself a copy */
#ifdef SNMP
		mib_ifEntry[1].ifOutNUcastPkts++;
#endif
		p->p_type = PT_ABUS;
		/* oops, atwrite smashes src during xmit(?) */
		p->p_off[1] = ifp->if_dnode;
		K_PENQ(SPLIMP, pq, p);
		return;
#ifdef SNMP
	} else {
	    mib_ifEntry[1].ifOutUcastPkts++;
#endif
	}
drop:
	/* put the buffer back on the free list */
	K_PFREE(p);
	return;
}

/*
 * Panic - fatal error
 */
/*VARARGS*/
panic(s,a,b,c)
{
	short pri;

	K_SPLIMP(&pri);
	sendf("PANIC!");
	sendf(s,a,b,c);
#ifdef	STATS
	ieprintstats();
	abprintstats();
#endif	STATS
	K_ERR();
	K_INIPROM();
	K_RESET();
}

#ifdef	STATS
/*
 * Send AppleTalk statistics
 */
abprintstats()
{
	register struct fp_abstats *ss;

	ss = pvars.fpr_abstats;
	K_WAIT(20);
	sendf("appletalk stats: ints %d, in %d, out %d, crc %d, ovr %d",
		ss->fpa_interrupts, ss->fpa_ipackets, ss->fpa_opackets,
		ss->fpa_crc, ss->fpa_ovr);
	K_WAIT(20);
	sendf("iund %d, bad %d, coll %d", ss->fpa_iund, ss->fpa_bad,
		ss->fpa_coll);
	K_WAIT(20);
	sendf("defer %d, idleto %d, nodata %d, ound %d, badddp %d, spur %d",
		ss->fpa_defer, ss->fpa_idleto, ss->fpa_nodata, ss->fpa_ound,
		ss->fpa_badddp, ss->fpa_spur);
}

dmpbf()
{
	struct pbuf *qq;
	int flag;
	register struct fp_bufinfo *ss;

	ss = pvars.fpr_bufs;
	sendf("dump pbufs - pbndrops = %d",ss->fpb_pbndrops);
	sendf("pfree: 0x%x", *(ss->fpb_pfree));
	sendf("   pq: head = 0x%x, tail= 0x%x, len = %d", pq->pq_head,
						pq->pq_tail, pq->pq_len);
	sendf("sendq: head = 0x%x, tail= 0x%x, len = %d",sendq->pq_head,
						sendq->pq_tail, sendq->pq_len);
	for (flag = 0, qq = bufs;
			qq < (struct pbuf *)(topram - ss->fpb_bsize + 1);
			qq++, flag++) {
		sendf("%d=0x%x: %d 0x%x",flag,qq,qq->p_type,qq->p_next);
	}
}

p_print(s, p)
char *s;
register struct pbuf *p;
{
	register u_char *cp = p->p_off;
	register ii,jj;
	char ll[256];
	char *lp;
	extern char tohex[];

	sendf("(%s) addr 0x%x p_type %d, p_len %d, p_off 0x%x\n",
		s, p, p->p_type, p->p_len, p->p_off);
	ii = p->p_len > 64 ? 64 : p->p_len;
	lp = ll;
	for (jj = 0 ; jj < ii ; jj++) {
		*lp++ = tohex[(*cp >> 4) & 0xF];
		*lp++ = tohex[*cp++ & 0xF];
		*lp++ = ' ';
	}
	*lp = '\0';
	sendf(ll);
}
#endif	STATS