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 a

⟦9c3fb1881⟧ TextFile

    Length: 9692 (0x25dc)
    Types: TextFile
    Names: »arp.c«

Derivation

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

TextFile

/*
 *  (c) 1986, Stanford Univ. CSLI.
 *  May be used but not sold without permission.
 *
 *  (c) 1986, Kinetics, Inc.
 *  May be used but not sold without permission.
 *
 *  $Header: arp.c,v 4.1 88/11/01 19:47:44 sw0l Exp $
 */

/*
 * Address resolution protocol.
 */

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

#include "glob.h"

/*
 * See RFC 826 (see "arp.rfc") for protocol description.
 * Field names used correspond to RFC 826.
 */

#define	ARPHLNMAX	6	/* largest arp_hln value needed */
#define	ARPPLN		4	/* length of protocol address (IP) */

struct	arp {
	u_short	arp_hrd;	/* format of hardware address */
	u_short	arp_pro;	/* format of proto. address  */
	u_char	arp_hln;	/* length of hardware address  */
	u_char	arp_pln;	/* length of protocol address  */
	u_short	arp_op;
#define	ARPOP_REQUEST	1	/* request to resolve address */
#define	ARPOP_REPLY	2	/* response to previous request */
#define ARPOP_PROBE     3	/* Apple 'Address PROBE' opcode */
	u_char	arp_d[ARPHLNMAX*2+ARPPLN*2];
				/* contains 4 packed fields:
				   sender hardware/proto addresses,
				   target hardware/proto addresses */
};
#define	arp_sha(ea)	(&(ea)->arp_d[0])
#define	arp_spa(ea)	(&(ea)->arp_d[hln])
#define	arp_tha(ea)	(&(ea)->arp_d[hln+ARPPLN])
#define	arp_tpa(ea)	(&(ea)->arp_d[hln+hln+ARPPLN])
#define	sizeof_arp	(sizeof(struct arp) - ARPHLNMAX*2  + hln + hln)


/*
 * Internet to hardware address resolution table.
 */
struct	arptab {
	iaddr_t at_iaddr;		/* internet address */
	u_char	at_haddr[ARPHLNMAX];	/* hardware address */
	u_char	at_timer;		/* minutes since last reference */
	u_char	at_flags;		/* flags */
	struct	pbuf *at_hold;		/* last packet until resolved/timeout */
};
/* at_flags field values */
#define	ATF_INUSE	1		/* entry in use */
#define ATF_COM		2		/* completed entry (haddr valid) */

#define	ARPTAB_BSIZ	3		/* bucket size */
#define	ARPTAB_NB	8		/* number of buckets */
#define	ARPTAB_SIZE	(ARPTAB_BSIZ * ARPTAB_NB)

struct	arptab *arptnew();

struct	arptab arptab[ARPTAB_SIZE];

#define	ARPTAB_HASH(a) \
	((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB)

#define	ARPTAB_LOOK(at,addr) { \
	register n; \
	at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \
	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \
		if (at->at_iaddr == addr) \
			break; \
	if (n >= ARPTAB_BSIZ) \
		at = 0; }

int	arpt_age;		/* aging timer */

/* timer values */
#define	ARPT_AGE	(60*1)	/* aging timer, 1 min. */
#define	ARPT_KILLC	20	/* kill completed entry in 20 mins. */
#define	ARPT_KILLI	3	/* kill incomplete entry in 3 minutes */


u_char	broadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

/*
 * Timeout routine.  Age arptab entries once a minute.
 * Entered once per second.
 */
arptimer()
{
	register struct arptab *at;
	register i;

	if (++arpt_age > ARPT_AGE) {
		arpt_age = 0;
		at = &arptab[0];
		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
			if (at->at_flags == 0)
				continue;
			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
			    ARPT_KILLC : ARPT_KILLI))
				continue;
			/* timer has expired, clear entry */
			arptfree(at);
		}
	}
}

/*
 * Broadcast an ARP packet, asking who has addr on interface ifp.
 */
arpwhohas(ifp, addr)
	register struct ifnet *ifp;
	iaddr_t *addr;
{
	register struct pbuf *p;
	register struct arp *ea;
	register hln;
	
	if (ifp == &ifab) {
		nbpwhohasip(*addr);
		return;
	}
	hln = ifp->if_haddrlen;
	K_PGET(PT_ARP,p); /* get a buffer from the free list */
 	if (p == 0)
		return;
	p->p_len = sizeof_arp;
	ea = (struct arp *)p->p_off;
	bzero((caddr_t)ea, sizeof_arp);
	ea->arp_hrd = htons(ifp->if_haddrform);
	ea->arp_pro = htons(ifp->if_addrform);
	ea->arp_hln = hln;	/* hardware address length */
	ea->arp_pln = ARPPLN;	/* protocol address length */

	/* Check whether this is the Ethertalk interface, and
         * the address hasn't been set yet. If so we must still
         * be sending PROBE's
         */
	if (ifp->if_dnode == 0) {
	  /* This is probably the ethertalk interface and */
	  /* we must still be probing for an address */
	  ea->arp_op = htons(ARPOP_PROBE);
	} else 	  
	  ea->arp_op = htons(ARPOP_REQUEST);
	bcopy(ifp->if_haddr, arp_sha(ea), hln);
	bcopy((caddr_t)&ifp->if_addr, arp_spa(ea), ARPPLN);
	bcopy((caddr_t)addr, arp_tpa(ea), ARPPLN);
	(*ifp->if_output)(ifp, p,  AF_ARP, broadcastaddr);
}

/*
 * Resolve an IP address into a hardware address.  If success, 
 * destha is filled in and 1 is returned.  If there is no entry
 * in arptab, set one up and broadcast a request 
 * for the IP address;  return 0.  Hold onto this pbuf and 
 * resend it once the address is finally resolved.
 */
arpresolve(ifp, p, destip, destha)
	register struct ifnet *ifp;
	struct pbuf *p;
	register iaddr_t *destip;
	register u_char *destha;
{
	register struct arptab *at;
	register hln = ifp->if_haddrlen;
	int lna = ntohl(*(long *)destip) & 0xFF;

	if (lna == 0xFF || lna == 0x0) { /* broadcast address */
		bcopy(broadcastaddr, destha, hln);
		return (1);
	}
	/*
	 * We used to do some (conservative) locking here at splimp, since
	 * arpinput was called  from input interrupt service.
	 *	
	 *	s = splimp();
	 */
	ARPTAB_LOOK(at, *destip);
	if (at == 0) {			/* not found */
		at = arptnew(destip);
		at->at_hold = p;
		arpwhohas(ifp, destip);
		/* splx(s); */
		return (0);
	}
	at->at_timer = 0;		/* restart the timer */
	if (at->at_flags & ATF_COM) {	/* entry IS complete */
		if (destha) bcopy(at->at_haddr, destha, hln);
		/* splx(s); */
		return (1);
	}
	/*
	 * There is an arptab entry, but no hardware address
	 * response yet.  Replace the held pbuf with this
	 * latest one.
	 */
 	if (at->at_hold) {
 		/* put buffer back on the free list */
		K_PFREE(at->at_hold);
 	}
	if (p) at->at_hold = p;
	arpwhohas(ifp, destip);		/* ask again */
	/* splx(s); */
	return (0);
}


/*
 * Called when packet containing ARP is received.
 * Algorithm is that given in RFC 826.
 * In addition, a sanity check is performed on the sender
 * protocol address, to catch impersonators.
 */
arpinput(p)
	struct pbuf *p;
{
#ifdef notdef
	register struct ifnet *ifp = p_if(p);
#else
	register struct ifnet *ifp = source_if;
#endif	
	register struct arp *ea;
	register struct arptab *at = 0;  /* same as "merge" flag */
	register hln = ifp->if_haddrlen;
	struct pbuf *phold;
	iaddr_t isaddr,itaddr,myaddr;

	if (p->p_len < sizeof_arp)
		goto out;
	myaddr = ifp->if_addr;
	ea = (struct arp *) p->p_off;
	if (ntohs(ea->arp_pro) != ifp->if_addrform)
		goto out;
	bcopy(arp_spa(ea), (caddr_t)&isaddr, ARPPLN);
	bcopy(arp_tpa(ea), (caddr_t)&itaddr, ARPPLN);
	if (!bcmp(arp_sha(ea), ifp->if_haddr, hln))
		goto out;	/* it's from me, ignore it. */

	/* Check for Ethertalk PROBE - Someone doing address aquisition */
	if (ntohs(ea->arp_op) == ARPOP_PROBE) {
	  if (itaddr == myaddr)  goto reply;
	  else goto out;	/* ignore probes not meant for me */
	}

	if (isaddr == myaddr) {
	  int i;
	  u_char *cp = arp_sha(ea);
	  sendf("duplicate IP address %x!! sent from hardware address: ");
	  for (i = 0 ; i < hln ; i++)
	    sendf("%x ", *cp++);
	  if (ntohs(ea->arp_op) == ARPOP_REQUEST) {
	    itaddr = myaddr;
	    goto reply;
	  }
	  goto out;
	}

	ARPTAB_LOOK(at, isaddr);
	if (at) {
		bcopy(arp_sha(ea), at->at_haddr, hln);
		at->at_flags |= ATF_COM;
		if (at->at_hold) {
			phold = at->at_hold;
			at->at_hold = 0;
			(*ifp->if_output)(ifp, phold, AF_IP, &isaddr);
		}
	}

	if (itaddr != myaddr && 
	    (ifp->if_matchus == 0 || (*ifp->if_matchus)(itaddr) == 0))
		goto out;	/* if I am not the target */

	if (at == 0) {		/* ensure we have a table entry */
		at = arptnew(&isaddr);
		bcopy(arp_sha(ea), at->at_haddr, hln);
		at->at_flags |= ATF_COM;
	}

	if (ntohs(ea->arp_op) != ARPOP_REQUEST)
		goto out;

reply:
	bcopy(arp_sha(ea), arp_tha(ea), hln);
	bcopy(arp_spa(ea), arp_tpa(ea), ARPPLN);
	bcopy(ifp->if_haddr, arp_sha(ea), hln);
	bcopy((caddr_t)&itaddr, arp_spa(ea), ARPPLN);
	ea->arp_op = htons(ARPOP_REPLY);
	(*ifp->if_output)(ifp, p, AF_ARP, arp_tha(ea));
	return;

out:
 	/* put buffer back on the free list */
	K_PFREE(p);
	return;
}

/*
 * Got an arp reply (via nbpinput).  If it completes one of our arptab
 * entries, send off the held buffer.  This subroutine is modeled after
 * the inner section of arpinput.
 */
arpgotreply(ifp, iaddr, haddr)
	register struct ifnet *ifp;
	iaddr_t iaddr;
	caddr_t haddr;
{
	register struct arptab *at;
	struct pbuf *p;

	ARPTAB_LOOK(at, iaddr);
	if (at == 0)	/* if no matching entry, nothing to do */
		return;
	bcopy(haddr, at->at_haddr, 4);
	at->at_flags |= ATF_COM;
	if (at->at_hold) {
		p = at->at_hold;
		at->at_hold = 0;
		(*ifp->if_output)(ifp, p, AF_IP, &iaddr);
	}
}

/*
 * Delete an arp cache entry.  Called from ipgassign when
 * new assignment made.
 */
arpdelete(iaddr)
	iaddr_t iaddr;
{
	register struct arptab *at;
	ARPTAB_LOOK(at, iaddr);
	if (at)
		arptfree(at);
}

/*
 * Free an arptab entry.
 */
arptfree(at)
	register struct arptab *at;
{
 	short pri;
 	
 	K_SPLIMP(&pri);
 	if (at->at_hold) {
 		/* put buffer back on the free list */
		K_PFREE(at->at_hold);
 	}

	at->at_hold = 0;
	at->at_timer = at->at_flags = 0;
	at->at_iaddr = 0;
	K_SPLX(&pri);
}

/*
 * Enter a new address in arptab, pushing out the oldest entry 
 * from the bucket if there is no room.
 */
struct arptab *
arptnew(addr)
	iaddr_t *addr;
{
	register n;
	int oldest = 0;
	register struct arptab *at, *ato;

	ato = at = &arptab[ARPTAB_HASH(*addr) * ARPTAB_BSIZ];
	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
		if (at->at_flags == 0)
			goto out;	 /* found an empty entry */
		if (at->at_timer > oldest) {
			oldest = at->at_timer;
			ato = at;
		}
	}
	at = ato;
	arptfree(at);
out:
	at->at_iaddr = *addr;
	at->at_flags = ATF_INUSE;
	return (at);
}