xref: /csrg-svn/sys/netinet/if_ether.c (revision 13095)
1*13095Ssam /*	if_ether.c	4.3	83/06/13	*/
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"
1312763Ssam #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 
9112763Ssam 	timeout(arptimer, (caddr_t)0, hz);
9211570Ssam #ifdef notdef
9311570Ssam 	if (++arpt_sanity > ARPT_SANITY) {
9412763Ssam 		register struct arpcom *ac;
9512763Ssam 
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*13095Ssam 		return;
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;
14012763Ssam 	bzero((caddr_t)ea, sizeof (*ea));
14112763Ssam 	bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
14212763Ssam 	   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);
14912763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
15012763Ssam 	   sizeof (ea->arp_sha));
15111570Ssam 	bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr,
15212763Ssam 	   (caddr_t)ea->arp_spa, sizeof (ea->arp_spa));
15312763Ssam 	bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa));
15411570Ssam 	sa.sa_family = AF_UNSPEC;
155*13095Ssam 	(void) (*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 */
18112763Ssam 		bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
18212763Ssam 		   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;
18912763Ssam 		return (looutput(&loif, m, (struct sockaddr *)&sin));
19011570Ssam 	}
19111570Ssam #ifdef OLDMAP
19211570Ssam 	if (lna >= 1024) {
19312763Ssam 		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 */
21112763Ssam 		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  */
23612763Ssam /*ARGSUSED*/
23711570Ssam struct in_addr
23811570Ssam arpmyaddr(ac)
23911570Ssam 	register struct arpcom *ac;
24011570Ssam {
24111570Ssam 	static struct in_addr addr;
24211570Ssam 
243*13095Ssam #ifdef lint
244*13095Ssam 	ac = ac;
245*13095Ssam #endif
24611570Ssam 	addr.s_addr = 0;
24711570Ssam 	return (addr);
24811570Ssam }
24911570Ssam 
25011570Ssam /*
25111570Ssam  * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP
25211570Ssam  * is received.  Algorithm is exactly that given in RFC 826.
25311570Ssam  * In addition, a sanity check is performed on the sender
25411570Ssam  * protocol address, to catch impersonators.
25511570Ssam  */
25611570Ssam arpinput(ac, m)
25711570Ssam 	register struct arpcom *ac;
25811570Ssam 	struct mbuf *m;
25911570Ssam {
26011570Ssam 	register struct ether_arp *ea;
26111570Ssam 	struct ether_header *eh;
26211570Ssam 	register struct arptab *at = 0;  /* same as "merge" flag */
26311570Ssam 	struct sockaddr_in sin;
26411570Ssam 	struct sockaddr sa;
26511570Ssam 	struct mbuf *mhold;
26611570Ssam 	struct in_addr isaddr,itaddr,myaddr;
26711570Ssam 
26811570Ssam 	if (m->m_len < sizeof *ea)
26911570Ssam 		goto out;
27011570Ssam 	myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr;
27111570Ssam 	ea = mtod(m, struct ether_arp *);
27211570Ssam 	if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE)
27311570Ssam 		goto out;
27411570Ssam 	isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr;
27511570Ssam 	itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr;
27612763Ssam 	if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
27712763Ssam 	  sizeof (ac->ac_enaddr)))
27811570Ssam 		goto out;	/* it's from me, ignore it. */
27911570Ssam 	if (isaddr.s_addr == myaddr.s_addr) {
28011570Ssam 		printf("duplicate IP address!! sent from ethernet address: ");
28111570Ssam 		printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1],
28211570Ssam 		    ea->arp_sha[2], ea->arp_sha[3],
28311570Ssam 		    ea->arp_sha[4], ea->arp_sha[5]);
28411570Ssam 		if (ntohs(ea->arp_op) == ARPOP_REQUEST)
28511570Ssam 			goto reply;
28611570Ssam 		goto out;
28711570Ssam 	}
28811570Ssam 	ARPTAB_LOOK(at, isaddr.s_addr);
28911570Ssam 	if (at) {
29012763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
29112763Ssam 		   sizeof (ea->arp_sha));
29211570Ssam 		at->at_flags |= ATF_COM;
29311570Ssam 		if (at->at_hold) {
29411570Ssam 			mhold = at->at_hold;
29511570Ssam 			at->at_hold = 0;
29611570Ssam 			sin.sin_family = AF_INET;
29711570Ssam 			sin.sin_addr = isaddr;
29811570Ssam 			(*ac->ac_if.if_output)(&ac->ac_if,
29911570Ssam 			    mhold, (struct sockaddr *)&sin);
30011570Ssam 		}
30111570Ssam 	}
30211570Ssam 	if (itaddr.s_addr != myaddr.s_addr)
30311570Ssam 		goto out;	/* if I am not the target */
30411570Ssam 	if (at == 0) {		/* ensure we have a table entry */
30511570Ssam 		at = arptnew(&isaddr);
30612763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
30712763Ssam 		   sizeof (ea->arp_sha));
30811570Ssam 		at->at_flags |= ATF_COM;
30911570Ssam 	}
31011570Ssam 	if (ntohs(ea->arp_op) != ARPOP_REQUEST)
31111570Ssam 		goto out;
31211570Ssam reply:
31312763Ssam 	bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
31412763Ssam 	   sizeof (ea->arp_sha));
31512763Ssam 	bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa,
31612763Ssam 	   sizeof (ea->arp_spa));
31712763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
31812763Ssam 	   sizeof (ea->arp_sha));
31912763Ssam 	bcopy((caddr_t)&myaddr, (caddr_t)ea->arp_spa,
32012763Ssam 	   sizeof (ea->arp_spa));
32111570Ssam 	ea->arp_op = htons(ARPOP_REPLY);
32211570Ssam 	eh = (struct ether_header *)sa.sa_data;
32312763Ssam 	bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost,
32412763Ssam 	   sizeof (eh->ether_dhost));
32511570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;
32611570Ssam 	sa.sa_family = AF_UNSPEC;
32711570Ssam 	(*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
32811570Ssam 	return;
32911570Ssam out:
33011570Ssam 	m_freem(m);
33111570Ssam 	return;
33211570Ssam }
33311570Ssam 
33411570Ssam /*
33511570Ssam  * Free an arptab entry.
33611570Ssam  */
33711570Ssam arptfree(at)
33811570Ssam 	register struct arptab *at;
33911570Ssam {
34011570Ssam 	int s = splimp();
34111570Ssam 
34211570Ssam 	if (at->at_hold)
34311570Ssam 		m_freem(at->at_hold);
34411570Ssam 	at->at_hold = 0;
34511570Ssam 	at->at_timer = at->at_flags = 0;
34611570Ssam 	at->at_iaddr.s_addr = 0;
34711570Ssam 	splx(s);
34811570Ssam }
34911570Ssam 
35011570Ssam /*
35111570Ssam  * Enter a new address in arptab, pushing out the oldest entry
35211570Ssam  * from the bucket if there is no room.
35311570Ssam  */
35411570Ssam struct arptab *
35511570Ssam arptnew(addr)
35611570Ssam 	struct in_addr *addr;
35711570Ssam {
35811570Ssam 	register n;
35911570Ssam 	int oldest = 0;
36011570Ssam 	register struct arptab *at, *ato;
36111570Ssam 
36211570Ssam 	ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ];
36311570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
36411570Ssam 		if (at->at_flags == 0)
36511570Ssam 			goto out;	 /* found an empty entry */
36611570Ssam 		if (at->at_timer > oldest) {
36711570Ssam 			oldest = at->at_timer;
36811570Ssam 			ato = at;
36911570Ssam 		}
37011570Ssam 	}
37111570Ssam 	at = ato;
37211570Ssam 	arptfree(at);
37311570Ssam out:
37411570Ssam 	at->at_iaddr = *addr;
37511570Ssam 	at->at_flags = ATF_INUSE;
37611570Ssam 	return (at);
37711570Ssam }
378