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

⟦9d1aec04e⟧ TextFile

    Length: 18465 (0x4821)
    Types: TextFile
    Names: »gw2.c«

Derivation

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

TextFile

/*
 * AppleTalk / Ethernet Gateway;  part 2.
 *
 * (c) 1986, Stanford Univ. CSLI.
 * May be used but not sold without permission.
 *
 * $Header: gw2.c,v 4.1 88/11/01 19:49:25 sw0l Locked $
 */

#include "gw.h"
#include "gwctl.h"
#include "fp/pbuf.h"
#include "ab.h"
#include "inet.h"
#include "fp/cmdmacro.h"
#ifdef SNMP
#include "snmp.h"
#include "mib.h"
#include "snmp_vars.h"
#endif

#include "glob.h"

/*
 * NBP packet input from appletalk (abreceive).
 */
nbpinput(p)
	struct pbuf *p;
{
	register struct NBP *n = (struct NBP *)(p->p_off + lapSize + ddpSize);
	register op = (n->control & nbpControlMask);
	Entity ent;
	register iaddr;

	/*
	 * if bridge request, put on backround queue
	 * to nbpback, who will send all the LkUp's.
	 */
	if (op == nbpBrRq) {
		K_PENQNP(&nbpq, p);
		return;
	}
	/* else packet is a LkUp or LkUpReply, check for local names */
	nbpgetent(n->tuple.name, &ent);	/* break up tuple name entity */
	/*
	 * if lookup reply for us, complete any pending requests.
	 */
	if (op == nbpLkUpReply) {
		if (ddp.dstNode != ifab.if_dnode
		    || strcmp(ent.type, "IPADDRESS") != 0
		    || (iaddr = atoip(ent.obj, 99)) == 0 )
			goto drop;
		/* pass the address reply to ARP and ipdad modules */
		arpgotreply(&ifab, iaddr, &n->tuple.addr);
		ipdadreply(&ifab, iaddr, &n->tuple.addr);
		goto drop;
	}
	/*
	 * else lookup;  handle requests for our IP related services.
	 */
	if (strcmp(ent.type, "IPGATEWAY") == 0) {
		iaddr = conf.ipaddr;
		goto replyus;
	}
	if (strcmp(ent.type, "IPADDRESS") == 0) {
		if ((iaddr = atoip(ent.obj, 99)) == 0
		    || ipbroadcast(iaddr))
			goto drop;	/* dont reply to bad addresses */
		if (abmatch(iaddr))
			goto replyus;
	}
	if (strcmp(ent.type, "=") == 0 && strcmp(ent.obj, "=") == 0) {
		iaddr = conf.ipaddr;
		strcpy(ent.type, "IPGATEWAY");
		goto reply;
	}
	goto drop;
replyus:
	if (getaroute(n->tuple.addr.net) == 0)
		goto drop;	/* not our responsibility */
reply:
	iptoa(iaddr, ent.obj);
	/* Should always return with "my zone" */
	ddp.length = nbpsetent(n->tuple.name, ent.obj, ent.type, "*")
		+ ddpSize + nbpMinSize;
	ddp.checksum = 0;
	ddp.srcNet = source_if->if_dnet;
	ddp.srcNode = source_if->if_dnode;
	ddp.dstNet = n->tuple.addr.net;
	ddp.dstNode = n->tuple.addr.node;
	ddp.dstSkt = n->tuple.addr.skt;
	n->tuple.addr.net = ddp.srcNet;
	n->tuple.addr.node = ddp.srcNode;
	n->tuple.addr.skt = ddpIPSkt;
	ddp.srcSkt = nbpNIS;
	n->control = nbpLkUpReply + 1;
	p->p_len = ddp.length + lapSize;
	bcopy((caddr_t)&ddp, p->p_off+lapSize, ddpSize);
	routeddp(p, 0, 0);
	return;
drop:
	K_PFREE(p);
}


/*
 * Queue an NBP lookup request.  If net/node is non-zero,
 * does an 'nbpconfirm' operation.
 */
nbplookup(ent, net, node)
	Entity *ent;
{
	register struct NBP *n;
	register struct pbuf *p;
	struct DDP d;

	K_PGET(PT_DATA, p);
	if (p == 0)
		return;
	n = (struct NBP *)(p->p_off + lapSize + ddpSize);
	n->control = nbpLkUp + 1;
	n->id = 0;
	n->tuple.enume = 0;
	n->tuple.addr.net = d.srcNet = ifab.if_dnet;
	n->tuple.addr.node = d.srcNode = ifab.if_dnode;
	n->tuple.addr.skt = d.srcSkt = d.dstSkt = nbpNIS;
	d.checksum = 0;
	d.type = ddpNBP;
	d.length = nbpsetent(n->tuple.name, ent->obj, ent->type, ent->zone)
		+ ddpSize + nbpMinSize;
	p->p_off[2] = lapDDP;
	p->p_len = d.length + lapSize;
	if (net) {	/* wants an nbpconfirm */
		d.dstNet = net;
		d.dstNode = node;
	}
	bcopy((caddr_t)&d, p->p_off + lapSize, ddpSize);
	if (net) {
		bcopy((caddr_t)&d, (caddr_t)&ddp, ddpSize);
		routeddp(p, 0, 0);
	} else {
		K_PENQNP(&nbpq, p);
	}
}


/* 'static' */
struct aroute *nback_arfrom;
struct aroute *nback_arnext;
short nback_zoneindex;

/*
 * NBP 'backround'.  Check nbpq for BrRq's to be sent out as LkUp's.
 * Sends to one net per call/loop.
 */
nbpback()
{
	register struct aroute *ar, *arx;
	register struct pbuf *pq, *p;
	struct NBP *n;
	register int zi;
	u_char i;
	u_char *zone;

	if ((pq = nbpq.pq_head) == 0)
		return;		/* if nothing to do */
	if (sendq->pq_head)
		return;		/* delay if ether sendq exists */

	if ((ar = nback_arnext) == 0) {	/* first time for this packet */
		ar = nback_arnext = &aroute[0];
		bcopy(pq->p_off + lapSize, (caddr_t)&ddp, ddpSize);
		ddp.checksum = 0;
		ddp.dstNode = 0xFF;	/* dstNet set below */
		n = (struct NBP *)(pq->p_off + lapSize + ddpSize);
		n->control = nbpLkUp + 1;
		zi = n->tuple.name[0] + 1; /* skip name */
		/* should check to see if we are off end of pkt */
		if (zi > 33)
		  goto drop;
		if (n->tuple.name[zi] > 32)
		  goto drop;
		zi += n->tuple.name[zi] + 1; /* skip type */
		zone = &n->tuple.name[zi]; /* zone length + zone */
		if (zone[0] > 32)
		  goto drop;
		/* find route */
		for (arx = &aroute[0]; arx < &aroute[NAROUTE]; ++arx)
		  if (arx->net == ddp.srcNet)
		    break;
		/* forget it if we can't figure out where it came from */
		if (arx >= &aroute[NAROUTE])
		  goto drop;
		/* Translate '*' to my zone */
		if (zone[0] == 1  && zone[1] == '*') {
		  /* Choose the zone for the interface message came from */
		  if (arx->zone == 0)
		    goto drop;
		  i = azone[arx->zone][0]+1;
		  bcopy(azone[arx->zone], zone, i);
		  i -= 2;	/* -2 for '\01*' */
		  pq->p_len += i;
		  ddp.length += i;
		}
		nback_arfrom = arx;
		/* copy deferred from above so we can reset ddp length */
		bcopy((caddr_t)&ddp, pq->p_off + lapSize, ddpSize);
		if ((nback_zoneindex = zipfind(zone, 1)) == -1)
			goto drop;
	}
	n = (struct NBP *)(pq->p_off + lapSize + ddpSize);
	/*
	 * for each net, route a packet there 
	 */
	for ( ; ar < &aroute[NAROUTE] ; ++ar) {
		if (ar->net == 0)
			continue;
		/* continue if wrong zone and the zone is not all zones */
		/* make sure allzones is set before using */
		if (ar->zone != nback_zoneindex &&
		    !(allzones && ar->zone == allzones))
		  continue;
		K_PGET(PT_DATA, p);
		if (p == 0)
			return;	/* try later */
		/* copy efficiently in 4 byte (long) chunks */
		bcopy(pq->p_off, p->p_off, ((pq->p_len + 3) & 0xFFFC));
		p->p_len = pq->p_len;
		/*
		 * Set the net number directly into the ddp header,
		 * without copying.  Setup a few ddp.xxx globals for
		 * the convenience of 'routeddp'.
		 */
		p->p_off[7] = (ar->net >> 8);
		p->p_off[8] = ar->net;
		ddp.srcNet = nback_arfrom->net;
		ddp.dstNet = ar->net;
		ddp.dstNode = 0xFF;
		ddp.dstSkt = ddp.srcSkt = nbpNIS;
		routeddp(p, porttoif[nback_arfrom->port], 0);
		nback_arnext = ar + 1;
		return;
	}
drop:
	/* done with this BrRq, dequeue it */
	K_PDEQNP(&nbpq, pq);
	K_PFREE(pq);
	nback_arnext = 0;
}


/*
 * Ask "who has" IP address addr, using NBP on my locally
 * connected segments.
 */
nbpwhohasip(addr)
	iaddr_t addr;
{
	Entity ent;

	iptoa(addr, ent.obj);
	strcpy(ent.zone, "*");
	strcpy(ent.type, "IPADDRESS");
	nbplookup(&ent, 0, 0);
}


/*
 * Unpack an nbp entity starting at cp.
 * Returns total size of entity (for skipping to next tuple).
 */
nbpgetent(cp, ent)
	register char *cp;
	register Entity *ent;
{
	char *oldcp;
	register i;

	oldcp = cp;
	i = (*cp & 0x1F); bcopy(cp+1, ent->obj, i); ent->obj[i] = 0; cp += i+1;
	i = (*cp & 0x1F); bcopy(cp+1, ent->type, i); ent->type[i] = 0; cp += i+1;
	i = (*cp & 0x1F); bcopy(cp+1, ent->zone, i); ent->zone[i] = 0; cp += i+1;
	return (cp - oldcp);
}


/*
 * Setup an nbp entity field with object, type, and zone strings.
 * Returns length of this tuple.
 */
nbpsetent(cp, obj, type, zone)
	register char *cp;
	char *obj, *type, *zone;
{
	register char *oldcp = cp;
	register i;

	*cp = i = strlen(obj);  bcopy(obj, cp+1, i);  cp += i+1;
	*cp = i = strlen(type);  bcopy(type, cp+1, i);  cp += i+1;
	*cp = i = strlen(zone);  bcopy(zone, cp+1, i);  cp += i+1;
	return (cp - oldcp);
}


/*
 * Filter out certain NBP lookup replies to provide (some)
 * security for LaserWriter and other servers outside the local zone.
 */
nbpfilter(p)
	struct pbuf *p;
{
	register struct NBP *n = (struct NBP *)(p->p_off + lapSize + ddpSize);
	register u_char *cp;
	register i,j;
	register struct aroute *ar;
	char filter = 0;

	if ((n->control & nbpControlMask) != nbpLkUpReply)
		return (0);
	cp = n->tuple.name;
	if (conf.flags & conf_tildefilter) {
		i = *cp;
		if (cp[i] == '~')
			filter++;
	}
	if(conf.flags & conf_laserfilter) {
		cp += *cp + 1;
		if (*cp == 11 && strncmpci(cp+1, "LaserWriter", 11) == 0)
			filter++;
	}
	if (filter == 0)
		return (0);
	/*
	 * if srcNet is in my zone but dstNet is not,
	 * drop it.
	 */
	i = j = 0;
	for (ar = &aroute[0] ; ar < &aroute[NAROUTE] ; ++ar) {
		if (ar->net == ddp.srcNet)
			i = ar->zone;
		if (ar->net == ddp.dstNet)
			j = ar->zone;
		if (i && j)
			break;
	}
	if (i != aroute[0].zone)
		return (0);
	if (j == i)
		return (0);
	return (1);		/* drop it */
}


/*
 * Process an IP packet directed to the gateway's own IP address.
 * These are usually control packets such as admin configure/
 * net table, debug packets, ICMP packets.
 */
ip4me(p)
	struct pbuf *p;
{
	register struct ip *ip = (struct ip *)p->p_off;
	int hlen = ip->ip_hl << 2;
	struct udp *u;
	register struct gwdb *g;
	register struct aaconf *m;
	struct icmp *ic;

	if (ip->ip_p == IPPROTO_ICMP)
		goto icmp;
	if (ip->ip_p == IPPROTO_UDP)
		goto udp;
#ifdef SNMP
	mib_ip.ipInUnknownProtos++;
#endif
drop:
	K_PFREE(p);
	return;
udp:
#ifdef SNMP
	mib_ip.ipInDelivers++;
#endif
	u = (struct udp *)(p->p_off + hlen);
	switch (u->dst) {
	case aaPort:
#ifdef SNMP
		mib_udp.udpInDatagrams++;
#endif
		m = (struct aaconf *)(u+1);
		/* this may be too inflexible, but so be it */
		if (m->magic != aaMagic || ipRus(ip->ip_src) == 0)
			goto drop;	/* not from our friends */
		switch (m->type) {
		case aaCONF:
			confready(m);
			break;

		case aaROUTEI:
			azoneinit = 0; /* need to fetch zone info */
		case aaROUTE:
		case aaROUTEQ:
			artinput(m, ip->ip_src);
			break;

		case aaRESTART:
			K_EXECUTE();
			break;
#ifdef notdef
		case aaZONE:
			zipinit(m);
			break;
#endif
		case aaZONEQ:
			zipinput(m->stuff,(int)m->count, ip->ip_src);
			break;
		}
		goto drop;

	case gwdbPort:
#ifdef SNMP
		mib_udp.udpInDatagrams++;
#endif
		goto gwdb;

	case rebPort:
#ifdef SNMP
		mib_udp.udpInDatagrams++;
#endif
		if (ip->ip_dst != conf.ipaddr)
			goto drop;
		bcopy((caddr_t)(u+1) + lapSize, &ddp, ddpSize);
		p->p_off += hlen + sizeof *u;
		p->p_len -= hlen + sizeof *u;
		routeddp(p, 0, 0);
		return;
#ifdef SNMP
	case SNMP_PORT:
		mib_udp.udpInDatagrams++;
		snmp_input(p);
		goto drop;
	default:
		mib_udp.udpNoPorts++;
#endif SNMP
	}
	goto drop;
gwdb:
	g = (struct gwdb *)(u+1);
	if (ip->ip_src != conf.ipdebug || g->magic != gwdbMagic)
		goto drop;
	switch (g->op) {
	case gwdbRead:
		bcopy(g->address, g->data, g->count);
		break;

	case gwdbWrite:
		bcopy(g->data, g->address, g->count);
		break;

	default:
		g->op = 0;	/* error reply code */
	}
	/*
	 * Assume the packet length does not change.
	 */
	u->checksum = 0;
#ifdef SNMP
	mib_udp.udpOutDatagrams++;
#endif
flip:
	ip->ip_dst = ip->ip_src;
	ip->ip_src = conf.ipaddr;
	ip->ip_sum = 0;
	ip->ip_sum = in_cksum((caddr_t)ip, hlen);
	routeip(p, 0, 0);
	return;
icmp:
#ifdef SNMP
	mib_ip.ipInDelivers++;
	mib_icmpInMsgs++;
#endif
	ic = (struct icmp *)(p->p_off + hlen);
#ifdef SNMP
	mib_icmpInCount[ic->icmp_type]++;
#endif
	if (ic->icmp_type == ICMP_ECHO) {
		ic->icmp_type = ICMP_ECHOREPLY;
		ic->icmp_cksum = 0;
		ic->icmp_cksum = in_cksum((caddr_t)ic, ip->ip_len - hlen);
#ifdef SNMP
		mib_icmpOutCount[ic->icmp_type]++;
		mib_icmpOutMsgs++;
#endif
		goto flip;
	}
	goto drop;
}


/*
 * IP are us.  Returns true if this IP address is 'one of us':
 * the administrator or one of the configured gateways.
 */
ipRus(ia)
	iaddr_t ia;
{
	register struct aroute *ar;
	register f;

	f = (arouteKbox|arouteAA);
	if (ia == conf.ipadmin || ia == conf.ipdebug)
		return (1);
	for (ar = &aroute[0] ; ar < &aroute[NAROUTE] ; ++ar) {
		if (ar->net == 0)
			continue;
		if ((ar->flags & f) != f)
			continue;
		if (ar->node == ia)
			return (1);
	}
	return (0);
}

/*
 * Process a DDP packet directed to the gateway's own address.
 * Called from routeddp with the ddp header already unpacked
 * in global struct 'ddp'.
 */
ddp4me(p)
	register struct pbuf *p;
{
	register struct ATP *a;
	register struct IPGP *ig;

	switch (ddp.type) {
	case ddpNBP:
	  if (ddp.dstSkt != nbpNIS)
	    goto drop;
	  nbpinput(p);
	  return;
	case ddpRTMP:
	case ddpRTMPR:
	  if (ddp.dstSkt != rtmpSkt)
	    goto drop;
	  rtmpinput(p);
	  return;
	case ddpECHO:
	  if (ddp.dstSkt != echoSkt)
	    goto drop;
	  if (p->p_off[lapSize+ddpSize] != echoRequest) 
	    goto drop;
	  p->p_off[lapSize+ddpSize] = echoReply;
	  ddpreply(p, echoSkt);
	  return;
	case ddpZIP:
	  if (ddp.dstSkt == zipSkt)
	    zipinput(p->p_off+lapSize+ddpSize, p->p_len-lapSize-ddpSize,0L);
	  goto drop;
	case ddpATP:
	  if (ddp.dstSkt == zipSkt) {
	    zipatp(p);
	    return;
	  }
	  if (ddp.dstSkt != ddpIPSkt)
	    goto drop;
	  break;
	default:
	  goto drop;
	}
	a = (struct ATP *)(p->p_off + lapSize + ddpSize);
	if ((ddp.length & ddpLengthMask) < (ddpSize + sizeof *a + 4))
		goto drop;
	if ((a->control & (~atpFlagMask)) != atpReqCode)
		goto drop;
	ig = (struct IPGP *)(a + 1);
	switch (ig->op) {
	case ipgpAssign:
	case ipgpServer:
		ipgassign(ig, ig->op);
		break;

	/* case ipgpName:
		ipgname(ig);
		goto drop;*/

	default:
		strcpy(ig->string, "bad op");
		ig->op = -1;
		break;
	}
	a->control = atpRspCode + atpEOM;
	a->bitmap = 0;
	/* +1 is to account for null */
	p->p_len=lapSize+ddpSize+sizeof(*a)+ipgpMinSize+strlen(ig->string)+1;
	ddpreply(p, ddpIPSkt);
	return;
drop:
	K_PFREE(p);
}


/*
 * IPGATEWAY request to assign address.
 */
ipgassign(ig, op)
	register struct IPGP *ig;
{
	register struct ipdad *d;
	struct ipdad *od;
	int dmax, otimer;
	register i;

	if (op == ipgpServer)
		goto server;	/* skip most of this */
	dmax = conf.ipdynamic;
	otimer = 0;
	for (d = &ipdad[0], i = 0 ; i < dmax ; d++,i++ ) {
		if (d->timer == 0) {
			otimer = ipdadTimerMax;
			od = d;
		} else if (d->timer > otimer) {
			otimer = d->timer;
			od = d;
		}
		if (d->net == ddp.srcNet && d->node == ddp.srcNode)
			goto assign;
	}
	if (otimer <= ipdadTimerMin) {
		ig->op = -1;
		strcpy(ig->string, "no free address");
		return;
	}
	d = od;
assign:
	d->net = ddp.srcNet;
	d->node = ddp.srcNode;
	d->timer = 1;
	ig->ipaddress = (d - &ipdad[0]) + conf.ipaddr + conf.ipstatic + 1;
	if (i == dmax)	/* if this address may have been reassigned */
		arpdelete(ig->ipaddress);
server:
	ig->string[0] = 0;
	ig->ipname = conf.ipname;
	ig->ipbroad = conf.ipbroad;
	ig->ipfile = conf.ipfile;
	bcopy((caddr_t)conf.ipother, (caddr_t)ig->ipother, sizeof ig->ipother);
}


short	ipd_index, ipd_timer, ipd_first;	/* 'static' */

/*
 * Timeout unused ipdad table entries.  Called once per second, sends
 * one 'tickle' per minute to each 'active' IP address client.
 * Sends at most one NBP per second.
 */
ipdadtimer()
{
	Entity ent;
	register struct ipdad *d;

	if (conf.ipdynamic == 0)
		return;
	/*
	 * This one-time code runs between seconds 25 to 30 after the
	 * gateway is booted.  It does an nbplookup request for
	 * all IPADDRESSes so we can initially fill in our table.
	 */
	if (ipd_first < 30) {
		ipd_first++;
		if (ipd_first < 25)	/* wait for RTMPs to establish */
			return;
		strcpy(ent.obj, "=");
		strcpy(ent.type, "IPADDRESS");
		strcpy(ent.zone, "*");
		nbplookup(&ent, 0, 0);
		return;
	}
	/*
	 * This code runs every second after startup.  Step thru
	 * the table pinging active entries.
	 */
	ipd_timer++;
	d = &ipdad[ipd_index];
	for ( ; ; d++, ipd_index++) {
		if (ipd_index >= conf.ipdynamic) {
			if (ipd_timer < 60)
				return;
			ipd_timer = ipd_index = 0;
			d = &ipdad[0];
		}
		/* dont ping empty or really old entries */
		if (d->timer == 0 ||  d->timer == ipdadTimerMax)
			continue;
		if (++d->timer > ipdadTimerMin)
			continue;
		iptoa(conf.ipaddr + conf.ipstatic + 1 + ipd_index, ent.obj);
		strcpy(ent.type, "IPADDRESS");
		strcpy(ent.zone, "*");
		nbplookup(&ent, d->net, d->node);
		ipd_index++;
		return;
	}
}


/*
 * Reply to ipdadtimer NBP 'tickle' received; reset timer.
 */
ipdadreply(ifp, iaddr, daddr)
	struct ifnet *ifp;
	iaddr_t iaddr;
	register AddrBlock *daddr;
{
	register struct ipdad *d;
	register i;

	i = iaddr - (conf.ipaddr + conf.ipstatic + 1);
	if (i < 0 || i >= conf.ipdynamic)
		return;
	d = &ipdad[i];
	d->timer = 1;
	d->net = daddr->net;
	d->node = daddr->node;
}


/*
 * Convert ASCII dot notation string 's' to long ip address.
 * 'n' is optional byte count.
 * Returns address, or 0 if error.
 */
atoip(s, n)
	register char *s;
{
	register addr, dots, num;

	dots = addr = num = 0;
	for ( ; ; s++, n--) {
		if (n == 0 || *s == '.' || *s == 0) {
			dots++;
			if (num > 255)
				return (0);
			addr = (addr << 8) + num;
			num = 0;
			if (n && *s == '.')
				continue;
			if (dots != 4)
				return (0);
			else
				return (addr);
		}
		if (*s >= '0' && *s <= '9')
			num = (num*10) + (*s - '0');
		else
			return (0);
	}
}


/*
 * Convert IP address to ASCII.  Converts 'n' to string at 's'.
 * Returns number of bytes in s (minus null).
 */
iptoa(n, s)
	register n;
	register char *s;
{
	char buf[20];
	register char *cp;
	int i;
	register b;

	cp = buf;
	for (i = 0 ; i < 4 ; i++) {
		b = (n & 0xFF);
		n >>= 8;
		if (b) {
			while (b) {
				*cp++ = (b % 10) + '0';
				b /= 10;
			}
		} else {
			*cp++ = '0';
		}
		*cp++ = '.';
	}
	cp -= 2;	/* skip last dot */
	i = 0;
	while (cp >= buf) {
		*s++ = *cp--;
		i++;
	}
	*s = 0;
	return (i);
}


/*
 * Setup the standard IP header fields for a destination
 */
setiphdr(p, dst)
	struct pbuf *p; 
	iaddr_t dst;
{
	register struct ip *ip = (struct ip *)p->p_off;

	ip->ip_v = IPVERSION;
	ip->ip_hl = sizeof(*ip) >> 2;
	ip->ip_tos = 0;
	ip->ip_len = p->p_len;
	ip->ip_id = ipid++;
	ip->ip_off = 0;
	ip->ip_ttl = IPFRAGTTL;
	ip->ip_p = IPPROTO_UDP;
	ip->ip_src = conf.ipaddr;
	ip->ip_dst = dst;
	ip->ip_sum = 0;
	ip->ip_sum = in_cksum((caddr_t)ip, sizeof(*ip));
}


/*
 * assumes ddp.type already set and that the reply goes to the node specified
 * by: (ddp.srcNet, ddp.srcNode, ddp.srcSkt)
 *
 * p should be aligned so offset points to start of lap data with room 
 * for lap + long ddp.  p->p_len should record the correct length
 *
 * skt is outgoing socket
 *
*/
ddpreply(p, skt)
register struct pbuf *p;
int skt;
{
  u_char dst;
  register struct DDP *dp = &ddp;

  dp->length = p->p_len - lapSize;
  dp->checksum = 0;
  dp->dstNet = dp->srcNet;
  dp->dstNode = dp->srcNode;
  dp->dstSkt = dp->srcSkt;
  dp->srcNet = source_if->if_dnet;
  dp->srcNode = source_if->if_dnode;
  dp->srcSkt = skt;
  bcopy((caddr_t)dp, p->p_off+lapSize, ddpSize);
  routeddp(p, 0, 0);
}