xref: /csrg-svn/sys/netinet/if_ether.c (revision 12763)
1*12763Ssam /*	if_ether.c	4.2	83/05/27	*/
211570Ssam 
311570Ssam /*
411570Ssam  * Ethernet address resolution protocol.
511570Ssam  */
611570Ssam 
711570Ssam #include "../h/param.h"
811570Ssam #include "../h/systm.h"
911570Ssam #include "../h/mbuf.h"
1011570Ssam #include "../h/socket.h"
1111570Ssam #include "../h/time.h"
1211570Ssam #include "../h/kernel.h"
13*12763Ssam #include "../h/errno.h"
1411570Ssam 
1511570Ssam #include "../net/if.h"
1611570Ssam #include "../netinet/in.h"
1711570Ssam #include "../netinet/if_ether.h"
1811570Ssam 
1911570Ssam 
2011570Ssam /*
2111570Ssam  * Internet to ethernet address resolution table.
2211570Ssam  */
2311570Ssam struct	arptab {
2411570Ssam 	struct	in_addr at_iaddr;	/* internet address */
2511570Ssam 	u_char	at_enaddr[6];		/* ethernet address */
2611570Ssam 	struct	mbuf *at_hold;		/* last packet until resolved/timeout */
2711570Ssam 	u_char	at_timer;		/* minutes since last reference */
2811570Ssam 	u_char	at_flags;		/* flags */
2911570Ssam };
3011570Ssam /* at_flags field values */
3111570Ssam #define	ATF_INUSE	1		/* entry in use */
3211570Ssam #define ATF_COM		2		/* completed entry (enaddr valid) */
3311570Ssam 
3411570Ssam #define	ARPTAB_BSIZ	5		/* bucket size */
3511570Ssam #define	ARPTAB_NB	19		/* number of buckets */
3611570Ssam #define	ARPTAB_SIZE	(ARPTAB_BSIZ * ARPTAB_NB)
3711570Ssam struct	arptab arptab[ARPTAB_SIZE];
3811570Ssam 
3911570Ssam #define	ARPTAB_HASH(a) \
4011570Ssam 	((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB)
4111570Ssam 
4211570Ssam #define	ARPTAB_LOOK(at,addr) { \
4311570Ssam 	register n; \
4411570Ssam 	at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \
4511570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \
4611570Ssam 		if (at->at_iaddr.s_addr == addr) \
4711570Ssam 			break; \
4811570Ssam 	if (n >= ARPTAB_BSIZ) \
4911570Ssam 		at = 0; }
5011570Ssam 
5111570Ssam struct	arpcom *arpcom;		/* chain of active ether interfaces */
5211570Ssam int	arpt_age;		/* aging timer */
5311570Ssam 
5411570Ssam /* timer values */
5511570Ssam #define	ARPT_AGE	(60*1)	/* aging timer, 1 min. */
5611570Ssam #define	ARPT_KILLC	20	/* kill completed entry in 20 mins. */
5711570Ssam #define	ARPT_KILLI	3	/* kill incomplete entry in 3 minutes */
5811570Ssam 
5911570Ssam u_char	etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
6011570Ssam extern struct ifnet loif;
6111570Ssam 
6211570Ssam #define	OLDMAP	1		/* if LNA > 1023 use old 3COM mapping */
6311570Ssam 
6411570Ssam /*
6511570Ssam  * Attach an ethernet interface to the list "arpcom" where
6611570Ssam  * arptimer() can find it.  If first time
6711570Ssam  * initialization, start arptimer().
6811570Ssam  */
6911570Ssam arpattach(ac)
7011570Ssam 	register struct arpcom *ac;
7111570Ssam {
7211570Ssam 	register struct arpcom *acp;
7311570Ssam 
7411570Ssam 	for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac)
7511570Ssam 		if (acp == ac)		/* if already on list */
7611570Ssam 			return;
7711570Ssam 	ac->ac_ac = arpcom;
7811570Ssam 	arpcom = ac;
7911570Ssam 	if (arpcom->ac_ac == 0)		/* very first time */
8011570Ssam 		arptimer();
8111570Ssam }
8211570Ssam 
8311570Ssam /*
8411570Ssam  * Timeout routine.  Age arp_tab entries once a minute.
8511570Ssam  */
8611570Ssam arptimer()
8711570Ssam {
8811570Ssam 	register struct arptab *at;
8911570Ssam 	register i;
9011570Ssam 
91*12763Ssam 	timeout(arptimer, (caddr_t)0, hz);
9211570Ssam #ifdef notdef
9311570Ssam 	if (++arpt_sanity > ARPT_SANITY) {
94*12763Ssam 		register struct arpcom *ac;
95*12763Ssam 
9611570Ssam 		/*
9711570Ssam 		 * Randomize sanity timer based on my host address.
9811570Ssam 		 * Ask who has my own address;  if someone else replies,
9911570Ssam 		 * then they are impersonating me.
10011570Ssam 		 */
10111570Ssam 		arpt_sanity = arpcom->ac_enaddr[5] & 0x3f;
10211570Ssam 		for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac)
10311570Ssam 			arpwhohas(ac, &((struct sockaddr_in *)
10411570Ssam 			    &ac->ac_if.if_addr)->sin_addr);
10511570Ssam 	}
10611570Ssam #endif
10711570Ssam 	if (++arpt_age > ARPT_AGE) {
10811570Ssam 		arpt_age = 0;
10911570Ssam 		at = &arptab[0];
11011570Ssam 		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
11111570Ssam 			if (at->at_flags == 0)
11211570Ssam 				continue;
11311570Ssam 			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
11411570Ssam 			    ARPT_KILLC : ARPT_KILLI))
11511570Ssam 				continue;
11611570Ssam 			/* timer has expired, clear entry */
11711570Ssam 			arptfree(at);
11811570Ssam 		}
11911570Ssam 	}
12011570Ssam }
12111570Ssam 
12211570Ssam /*
12311570Ssam  * Broadcast an ARP packet, asking who has addr on interface ac.
12411570Ssam  */
12511570Ssam arpwhohas(ac, addr)
12611570Ssam 	register struct arpcom *ac;
12711570Ssam 	struct in_addr *addr;
12811570Ssam {
12911570Ssam 	register struct mbuf *m;
13011570Ssam 	register struct ether_header *eh;
13111570Ssam 	register struct ether_arp *ea;
13211570Ssam 	struct sockaddr sa;
13311570Ssam 
13411570Ssam 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL)
135*12763Ssam 		return (ENOBUFS);
13611570Ssam 	m->m_len = sizeof *ea + sizeof *eh;
13711570Ssam 	m->m_off = MMAXOFF - m->m_len;
13811570Ssam 	ea = mtod(m, struct ether_arp *);
13911570Ssam 	eh = (struct ether_header *)sa.sa_data;
140*12763Ssam 	bzero((caddr_t)ea, sizeof (*ea));
141*12763Ssam 	bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
142*12763Ssam 	   sizeof (etherbroadcastaddr));
14311570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;	/* if_output will swap */
14411570Ssam 	ea->arp_hrd = htons(ARPHRD_ETHER);
14511570Ssam 	ea->arp_pro = htons(ETHERPUP_IPTYPE);
14611570Ssam 	ea->arp_hln = sizeof ea->arp_sha;	/* hardware address length */
14711570Ssam 	ea->arp_pln = sizeof ea->arp_spa;	/* protocol address length */
14811570Ssam 	ea->arp_op = htons(ARPOP_REQUEST);
149*12763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
150*12763Ssam 	   sizeof (ea->arp_sha));
15111570Ssam 	bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr,
152*12763Ssam 	   (caddr_t)ea->arp_spa, sizeof (ea->arp_spa));
153*12763Ssam 	bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa));
15411570Ssam 	sa.sa_family = AF_UNSPEC;
15511570Ssam 	return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa));
15611570Ssam }
15711570Ssam 
15811570Ssam /*
15911570Ssam  * Resolve an IP address into an ethernet address.  If success,
16011570Ssam  * desten is filled in and 1 is returned.  If there is no entry
16111570Ssam  * in arptab, set one up and broadcast a request
16211570Ssam  * for the IP address;  return 0.  Hold onto this mbuf and
16311570Ssam  * resend it once the address is finally resolved.
16411570Ssam  *
16511570Ssam  * We do some (conservative) locking here at splimp, since
16611570Ssam  * arptab is also altered from input interrupt service (ecintr/ilintr
16711570Ssam  * calls arpinput when ETHERPUP_ARPTYPE packets come in).
16811570Ssam  */
16911570Ssam arpresolve(ac, m, destip, desten)
17011570Ssam 	register struct arpcom *ac;
17111570Ssam 	struct mbuf *m;
17211570Ssam 	register struct in_addr *destip;
17311570Ssam 	register u_char *desten;
17411570Ssam {
17511570Ssam 	register struct arptab *at;
17611570Ssam 	struct sockaddr_in sin;
17711570Ssam 	int s, lna;
17811570Ssam 
17911570Ssam 	lna = in_lnaof(*destip);
18011570Ssam 	if (lna == INADDR_ANY) {	/* broadcast address */
181*12763Ssam 		bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
182*12763Ssam 		   sizeof (etherbroadcastaddr));
18311570Ssam 		return (1);
18411570Ssam 	}
18511570Ssam 	if (destip->s_addr == ((struct sockaddr_in *)&ac->ac_if.if_addr)->
18611570Ssam 	    sin_addr.s_addr) {			/* if for us, use lo driver */
18711570Ssam 		sin.sin_family = AF_INET;
18811570Ssam 		sin.sin_addr = *destip;
189*12763Ssam 		return (looutput(&loif, m, (struct sockaddr *)&sin));
19011570Ssam 	}
19111570Ssam #ifdef OLDMAP
19211570Ssam 	if (lna >= 1024) {
193*12763Ssam 		bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3);
19411570Ssam 		desten[3] = (lna >> 16) & 0x7f;
19511570Ssam 		desten[4] = (lna >> 8) & 0xff;
19611570Ssam 		desten[5] = lna & 0xff;
19711570Ssam 		return (1);
19811570Ssam 	}
19911570Ssam #endif OLDMAP
20011570Ssam 	s = splimp();
20111570Ssam 	ARPTAB_LOOK(at, destip->s_addr);
20211570Ssam 	if (at == 0) {			/* not found */
20311570Ssam 		at = arptnew(destip);
20411570Ssam 		at->at_hold = m;
20511570Ssam 		arpwhohas(ac, destip);
20611570Ssam 		splx(s);
20711570Ssam 		return (0);
20811570Ssam 	}
20911570Ssam 	at->at_timer = 0;		/* restart the timer */
21011570Ssam 	if (at->at_flags & ATF_COM) {	/* entry IS complete */
211*12763Ssam 		bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, 6);
21211570Ssam 		splx(s);
21311570Ssam 		return (1);
21411570Ssam 	}
21511570Ssam 	/*
21611570Ssam 	 * There is an arptab entry, but no ethernet address
21711570Ssam 	 * response yet.  Replace the held mbuf with this
21811570Ssam 	 * latest one.
21911570Ssam 	 */
22011570Ssam 	if (at->at_hold)
22111570Ssam 		m_freem(at->at_hold);
22211570Ssam 	at->at_hold = m;
22311570Ssam 	arpwhohas(ac, destip);		/* ask again */
22411570Ssam 	splx(s);
22511570Ssam 	return (0);
22611570Ssam }
22711570Ssam 
22811570Ssam /*
22911570Ssam  * Find my own IP address.  It will either be waiting for us in
23011570Ssam  * monitor RAM, or can be obtained via broadcast to the file/boot
23111570Ssam  * server (not necessarily using the ARP packet format).
23211570Ssam  *
23311570Ssam  * Unimplemented at present, return 0 and assume that the host
23411570Ssam  * will set his own IP address via the SIOCSIFADDR ioctl.
23511570Ssam  */
236*12763Ssam /*ARGSUSED*/
23711570Ssam struct in_addr
23811570Ssam arpmyaddr(ac)
23911570Ssam 	register struct arpcom *ac;
24011570Ssam {
24111570Ssam 	static struct in_addr addr;
24211570Ssam 
24311570Ssam 	addr.s_addr = 0;
24411570Ssam 	return (addr);
24511570Ssam }
24611570Ssam 
24711570Ssam /*
24811570Ssam  * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP
24911570Ssam  * is received.  Algorithm is exactly that given in RFC 826.
25011570Ssam  * In addition, a sanity check is performed on the sender
25111570Ssam  * protocol address, to catch impersonators.
25211570Ssam  */
25311570Ssam arpinput(ac, m)
25411570Ssam 	register struct arpcom *ac;
25511570Ssam 	struct mbuf *m;
25611570Ssam {
25711570Ssam 	register struct ether_arp *ea;
25811570Ssam 	struct ether_header *eh;
25911570Ssam 	register struct arptab *at = 0;  /* same as "merge" flag */
26011570Ssam 	struct sockaddr_in sin;
26111570Ssam 	struct sockaddr sa;
26211570Ssam 	struct mbuf *mhold;
26311570Ssam 	struct in_addr isaddr,itaddr,myaddr;
26411570Ssam 
26511570Ssam 	if (m->m_len < sizeof *ea)
26611570Ssam 		goto out;
26711570Ssam 	myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr;
26811570Ssam 	ea = mtod(m, struct ether_arp *);
26911570Ssam 	if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE)
27011570Ssam 		goto out;
27111570Ssam 	isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr;
27211570Ssam 	itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr;
273*12763Ssam 	if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
274*12763Ssam 	  sizeof (ac->ac_enaddr)))
27511570Ssam 		goto out;	/* it's from me, ignore it. */
27611570Ssam 	if (isaddr.s_addr == myaddr.s_addr) {
27711570Ssam 		printf("duplicate IP address!! sent from ethernet address: ");
27811570Ssam 		printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1],
27911570Ssam 		    ea->arp_sha[2], ea->arp_sha[3],
28011570Ssam 		    ea->arp_sha[4], ea->arp_sha[5]);
28111570Ssam 		if (ntohs(ea->arp_op) == ARPOP_REQUEST)
28211570Ssam 			goto reply;
28311570Ssam 		goto out;
28411570Ssam 	}
28511570Ssam 	ARPTAB_LOOK(at, isaddr.s_addr);
28611570Ssam 	if (at) {
287*12763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
288*12763Ssam 		   sizeof (ea->arp_sha));
28911570Ssam 		at->at_flags |= ATF_COM;
29011570Ssam 		if (at->at_hold) {
29111570Ssam 			mhold = at->at_hold;
29211570Ssam 			at->at_hold = 0;
29311570Ssam 			sin.sin_family = AF_INET;
29411570Ssam 			sin.sin_addr = isaddr;
29511570Ssam 			(*ac->ac_if.if_output)(&ac->ac_if,
29611570Ssam 			    mhold, (struct sockaddr *)&sin);
29711570Ssam 		}
29811570Ssam 	}
29911570Ssam 	if (itaddr.s_addr != myaddr.s_addr)
30011570Ssam 		goto out;	/* if I am not the target */
30111570Ssam 	if (at == 0) {		/* ensure we have a table entry */
30211570Ssam 		at = arptnew(&isaddr);
303*12763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
304*12763Ssam 		   sizeof (ea->arp_sha));
30511570Ssam 		at->at_flags |= ATF_COM;
30611570Ssam 	}
30711570Ssam 	if (ntohs(ea->arp_op) != ARPOP_REQUEST)
30811570Ssam 		goto out;
30911570Ssam reply:
310*12763Ssam 	bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
311*12763Ssam 	   sizeof (ea->arp_sha));
312*12763Ssam 	bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa,
313*12763Ssam 	   sizeof (ea->arp_spa));
314*12763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
315*12763Ssam 	   sizeof (ea->arp_sha));
316*12763Ssam 	bcopy((caddr_t)&myaddr, (caddr_t)ea->arp_spa,
317*12763Ssam 	   sizeof (ea->arp_spa));
31811570Ssam 	ea->arp_op = htons(ARPOP_REPLY);
31911570Ssam 	eh = (struct ether_header *)sa.sa_data;
320*12763Ssam 	bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost,
321*12763Ssam 	   sizeof (eh->ether_dhost));
32211570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;
32311570Ssam 	sa.sa_family = AF_UNSPEC;
32411570Ssam 	(*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
32511570Ssam 	return;
32611570Ssam out:
32711570Ssam 	m_freem(m);
32811570Ssam 	return;
32911570Ssam }
33011570Ssam 
33111570Ssam /*
33211570Ssam  * Free an arptab entry.
33311570Ssam  */
33411570Ssam arptfree(at)
33511570Ssam 	register struct arptab *at;
33611570Ssam {
33711570Ssam 	int s = splimp();
33811570Ssam 
33911570Ssam 	if (at->at_hold)
34011570Ssam 		m_freem(at->at_hold);
34111570Ssam 	at->at_hold = 0;
34211570Ssam 	at->at_timer = at->at_flags = 0;
34311570Ssam 	at->at_iaddr.s_addr = 0;
34411570Ssam 	splx(s);
34511570Ssam }
34611570Ssam 
34711570Ssam /*
34811570Ssam  * Enter a new address in arptab, pushing out the oldest entry
34911570Ssam  * from the bucket if there is no room.
35011570Ssam  */
35111570Ssam struct arptab *
35211570Ssam arptnew(addr)
35311570Ssam 	struct in_addr *addr;
35411570Ssam {
35511570Ssam 	register n;
35611570Ssam 	int oldest = 0;
35711570Ssam 	register struct arptab *at, *ato;
35811570Ssam 
35911570Ssam 	ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ];
36011570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
36111570Ssam 		if (at->at_flags == 0)
36211570Ssam 			goto out;	 /* found an empty entry */
36311570Ssam 		if (at->at_timer > oldest) {
36411570Ssam 			oldest = at->at_timer;
36511570Ssam 			ato = at;
36611570Ssam 		}
36711570Ssam 	}
36811570Ssam 	at = ato;
36911570Ssam 	arptfree(at);
37011570Ssam out:
37111570Ssam 	at->at_iaddr = *addr;
37211570Ssam 	at->at_flags = ATF_INUSE;
37311570Ssam 	return (at);
37411570Ssam }
375