xref: /csrg-svn/sys/netinet/if_ether.c (revision 14867)
1*14867Ssam /*	if_ether.c	6.2	83/08/28	*/
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 
62*14867Ssam /*
63*14867Ssam  * Local addresses in the range oldmap to infinity are
64*14867Ssam  * mapped according to the old mapping scheme.  That is,
65*14867Ssam  * mapping of Internet to Ethernet addresses is performed
66*14867Ssam  * by taking the high three bytes of the network interface's
67*14867Ssam  * address and the low three bytes of the local address part.
68*14867Ssam  * This only allows boards from the same manufacturer to
69*14867Ssam  * communicate unless the on-board address is overridden
70*14867Ssam  * (not possible in many manufacture's hardware).
71*14867Ssam  *
72*14867Ssam  * NB: setting oldmap to zero completely disables ARP
73*14867Ssam  *     (i.e. identical to setting IFF_NOARP with an ioctl).
74*14867Ssam  */
75*14867Ssam int	oldmap = 1024;
7611570Ssam 
7711570Ssam /*
7811570Ssam  * Attach an ethernet interface to the list "arpcom" where
7911570Ssam  * arptimer() can find it.  If first time
8011570Ssam  * initialization, start arptimer().
8111570Ssam  */
8211570Ssam arpattach(ac)
8311570Ssam 	register struct arpcom *ac;
8411570Ssam {
8511570Ssam 	register struct arpcom *acp;
8611570Ssam 
8711570Ssam 	for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac)
8811570Ssam 		if (acp == ac)		/* if already on list */
8911570Ssam 			return;
9011570Ssam 	ac->ac_ac = arpcom;
9111570Ssam 	arpcom = ac;
9211570Ssam 	if (arpcom->ac_ac == 0)		/* very first time */
9311570Ssam 		arptimer();
9411570Ssam }
9511570Ssam 
9611570Ssam /*
9711570Ssam  * Timeout routine.  Age arp_tab entries once a minute.
9811570Ssam  */
9911570Ssam arptimer()
10011570Ssam {
10111570Ssam 	register struct arptab *at;
10211570Ssam 	register i;
10311570Ssam 
10412763Ssam 	timeout(arptimer, (caddr_t)0, hz);
10511570Ssam #ifdef notdef
10611570Ssam 	if (++arpt_sanity > ARPT_SANITY) {
10712763Ssam 		register struct arpcom *ac;
10812763Ssam 
10911570Ssam 		/*
11011570Ssam 		 * Randomize sanity timer based on my host address.
11111570Ssam 		 * Ask who has my own address;  if someone else replies,
11211570Ssam 		 * then they are impersonating me.
11311570Ssam 		 */
11411570Ssam 		arpt_sanity = arpcom->ac_enaddr[5] & 0x3f;
11511570Ssam 		for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac)
11611570Ssam 			arpwhohas(ac, &((struct sockaddr_in *)
11711570Ssam 			    &ac->ac_if.if_addr)->sin_addr);
11811570Ssam 	}
11911570Ssam #endif
12011570Ssam 	if (++arpt_age > ARPT_AGE) {
12111570Ssam 		arpt_age = 0;
12211570Ssam 		at = &arptab[0];
12311570Ssam 		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
12411570Ssam 			if (at->at_flags == 0)
12511570Ssam 				continue;
12611570Ssam 			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
12711570Ssam 			    ARPT_KILLC : ARPT_KILLI))
12811570Ssam 				continue;
12911570Ssam 			/* timer has expired, clear entry */
13011570Ssam 			arptfree(at);
13111570Ssam 		}
13211570Ssam 	}
13311570Ssam }
13411570Ssam 
13511570Ssam /*
13611570Ssam  * Broadcast an ARP packet, asking who has addr on interface ac.
13711570Ssam  */
13811570Ssam arpwhohas(ac, addr)
13911570Ssam 	register struct arpcom *ac;
14011570Ssam 	struct in_addr *addr;
14111570Ssam {
14211570Ssam 	register struct mbuf *m;
14311570Ssam 	register struct ether_header *eh;
14411570Ssam 	register struct ether_arp *ea;
14511570Ssam 	struct sockaddr sa;
14611570Ssam 
14711570Ssam 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL)
14813095Ssam 		return;
14911570Ssam 	m->m_len = sizeof *ea + sizeof *eh;
15011570Ssam 	m->m_off = MMAXOFF - m->m_len;
15111570Ssam 	ea = mtod(m, struct ether_arp *);
15211570Ssam 	eh = (struct ether_header *)sa.sa_data;
15312763Ssam 	bzero((caddr_t)ea, sizeof (*ea));
15412763Ssam 	bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost,
15512763Ssam 	   sizeof (etherbroadcastaddr));
15611570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;	/* if_output will swap */
15711570Ssam 	ea->arp_hrd = htons(ARPHRD_ETHER);
15811570Ssam 	ea->arp_pro = htons(ETHERPUP_IPTYPE);
15911570Ssam 	ea->arp_hln = sizeof ea->arp_sha;	/* hardware address length */
16011570Ssam 	ea->arp_pln = sizeof ea->arp_spa;	/* protocol address length */
16111570Ssam 	ea->arp_op = htons(ARPOP_REQUEST);
16212763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
16312763Ssam 	   sizeof (ea->arp_sha));
16411570Ssam 	bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr,
16512763Ssam 	   (caddr_t)ea->arp_spa, sizeof (ea->arp_spa));
16612763Ssam 	bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa));
16711570Ssam 	sa.sa_family = AF_UNSPEC;
16813095Ssam 	(void) (*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
16911570Ssam }
17011570Ssam 
17111570Ssam /*
17211570Ssam  * Resolve an IP address into an ethernet address.  If success,
17311570Ssam  * desten is filled in and 1 is returned.  If there is no entry
17411570Ssam  * in arptab, set one up and broadcast a request
17511570Ssam  * for the IP address;  return 0.  Hold onto this mbuf and
17611570Ssam  * resend it once the address is finally resolved.
17711570Ssam  *
17811570Ssam  * We do some (conservative) locking here at splimp, since
17911570Ssam  * arptab is also altered from input interrupt service (ecintr/ilintr
18011570Ssam  * calls arpinput when ETHERPUP_ARPTYPE packets come in).
18111570Ssam  */
18211570Ssam arpresolve(ac, m, destip, desten)
18311570Ssam 	register struct arpcom *ac;
18411570Ssam 	struct mbuf *m;
18511570Ssam 	register struct in_addr *destip;
18611570Ssam 	register u_char *desten;
18711570Ssam {
18811570Ssam 	register struct arptab *at;
189*14867Ssam 	register struct ifnet *ifp;
19011570Ssam 	struct sockaddr_in sin;
19111570Ssam 	int s, lna;
19211570Ssam 
19311570Ssam 	lna = in_lnaof(*destip);
19411570Ssam 	if (lna == INADDR_ANY) {	/* broadcast address */
19512763Ssam 		bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten,
19612763Ssam 		   sizeof (etherbroadcastaddr));
19711570Ssam 		return (1);
19811570Ssam 	}
199*14867Ssam 	ifp = &ac->ac_if;
200*14867Ssam 	/* if for us, then use software loopback driver */
201*14867Ssam 	if (destip->s_addr ==
202*14867Ssam 	    ((struct sockaddr_in *)&ifp->if_addr)-> sin_addr.s_addr) {
20311570Ssam 		sin.sin_family = AF_INET;
20411570Ssam 		sin.sin_addr = *destip;
20512763Ssam 		return (looutput(&loif, m, (struct sockaddr *)&sin));
20611570Ssam 	}
207*14867Ssam 	if ((ifp->if_flags & IFF_NOARP) || lna >= oldmap) {
20812763Ssam 		bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3);
20911570Ssam 		desten[3] = (lna >> 16) & 0x7f;
21011570Ssam 		desten[4] = (lna >> 8) & 0xff;
21111570Ssam 		desten[5] = lna & 0xff;
21211570Ssam 		return (1);
21311570Ssam 	}
21411570Ssam 	s = splimp();
21511570Ssam 	ARPTAB_LOOK(at, destip->s_addr);
21611570Ssam 	if (at == 0) {			/* not found */
21711570Ssam 		at = arptnew(destip);
21811570Ssam 		at->at_hold = m;
21911570Ssam 		arpwhohas(ac, destip);
22011570Ssam 		splx(s);
22111570Ssam 		return (0);
22211570Ssam 	}
22311570Ssam 	at->at_timer = 0;		/* restart the timer */
22411570Ssam 	if (at->at_flags & ATF_COM) {	/* entry IS complete */
22512763Ssam 		bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, 6);
22611570Ssam 		splx(s);
22711570Ssam 		return (1);
22811570Ssam 	}
22911570Ssam 	/*
23011570Ssam 	 * There is an arptab entry, but no ethernet address
23111570Ssam 	 * response yet.  Replace the held mbuf with this
23211570Ssam 	 * latest one.
23311570Ssam 	 */
23411570Ssam 	if (at->at_hold)
23511570Ssam 		m_freem(at->at_hold);
23611570Ssam 	at->at_hold = m;
23711570Ssam 	arpwhohas(ac, destip);		/* ask again */
23811570Ssam 	splx(s);
23911570Ssam 	return (0);
24011570Ssam }
24111570Ssam 
24211570Ssam /*
24311570Ssam  * Find my own IP address.  It will either be waiting for us in
24411570Ssam  * monitor RAM, or can be obtained via broadcast to the file/boot
24511570Ssam  * server (not necessarily using the ARP packet format).
24611570Ssam  *
24711570Ssam  * Unimplemented at present, return 0 and assume that the host
24811570Ssam  * will set his own IP address via the SIOCSIFADDR ioctl.
24911570Ssam  */
25012763Ssam /*ARGSUSED*/
25111570Ssam struct in_addr
25211570Ssam arpmyaddr(ac)
25311570Ssam 	register struct arpcom *ac;
25411570Ssam {
25511570Ssam 	static struct in_addr addr;
25611570Ssam 
25713095Ssam #ifdef lint
25813095Ssam 	ac = ac;
25913095Ssam #endif
26011570Ssam 	addr.s_addr = 0;
26111570Ssam 	return (addr);
26211570Ssam }
26311570Ssam 
26411570Ssam /*
26511570Ssam  * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP
26611570Ssam  * is received.  Algorithm is exactly that given in RFC 826.
26711570Ssam  * In addition, a sanity check is performed on the sender
26811570Ssam  * protocol address, to catch impersonators.
26911570Ssam  */
27011570Ssam arpinput(ac, m)
27111570Ssam 	register struct arpcom *ac;
27211570Ssam 	struct mbuf *m;
27311570Ssam {
27411570Ssam 	register struct ether_arp *ea;
27511570Ssam 	struct ether_header *eh;
27611570Ssam 	register struct arptab *at = 0;  /* same as "merge" flag */
27711570Ssam 	struct sockaddr_in sin;
27811570Ssam 	struct sockaddr sa;
27911570Ssam 	struct mbuf *mhold;
28011570Ssam 	struct in_addr isaddr,itaddr,myaddr;
28111570Ssam 
28211570Ssam 	if (m->m_len < sizeof *ea)
28311570Ssam 		goto out;
28411570Ssam 	myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr;
28511570Ssam 	ea = mtod(m, struct ether_arp *);
28611570Ssam 	if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE)
28711570Ssam 		goto out;
28811570Ssam 	isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr;
28911570Ssam 	itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr;
29012763Ssam 	if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
29112763Ssam 	  sizeof (ac->ac_enaddr)))
29211570Ssam 		goto out;	/* it's from me, ignore it. */
29311570Ssam 	if (isaddr.s_addr == myaddr.s_addr) {
29411570Ssam 		printf("duplicate IP address!! sent from ethernet address: ");
29511570Ssam 		printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1],
29611570Ssam 		    ea->arp_sha[2], ea->arp_sha[3],
29711570Ssam 		    ea->arp_sha[4], ea->arp_sha[5]);
29811570Ssam 		if (ntohs(ea->arp_op) == ARPOP_REQUEST)
29911570Ssam 			goto reply;
30011570Ssam 		goto out;
30111570Ssam 	}
30211570Ssam 	ARPTAB_LOOK(at, isaddr.s_addr);
30311570Ssam 	if (at) {
30412763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
30512763Ssam 		   sizeof (ea->arp_sha));
30611570Ssam 		at->at_flags |= ATF_COM;
30711570Ssam 		if (at->at_hold) {
30811570Ssam 			mhold = at->at_hold;
30911570Ssam 			at->at_hold = 0;
31011570Ssam 			sin.sin_family = AF_INET;
31111570Ssam 			sin.sin_addr = isaddr;
31211570Ssam 			(*ac->ac_if.if_output)(&ac->ac_if,
31311570Ssam 			    mhold, (struct sockaddr *)&sin);
31411570Ssam 		}
31511570Ssam 	}
31611570Ssam 	if (itaddr.s_addr != myaddr.s_addr)
31711570Ssam 		goto out;	/* if I am not the target */
31811570Ssam 	if (at == 0) {		/* ensure we have a table entry */
31911570Ssam 		at = arptnew(&isaddr);
32012763Ssam 		bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr,
32112763Ssam 		   sizeof (ea->arp_sha));
32211570Ssam 		at->at_flags |= ATF_COM;
32311570Ssam 	}
32411570Ssam 	if (ntohs(ea->arp_op) != ARPOP_REQUEST)
32511570Ssam 		goto out;
32611570Ssam reply:
32712763Ssam 	bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha,
32812763Ssam 	   sizeof (ea->arp_sha));
32912763Ssam 	bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa,
33012763Ssam 	   sizeof (ea->arp_spa));
33112763Ssam 	bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha,
33212763Ssam 	   sizeof (ea->arp_sha));
33312763Ssam 	bcopy((caddr_t)&myaddr, (caddr_t)ea->arp_spa,
33412763Ssam 	   sizeof (ea->arp_spa));
33511570Ssam 	ea->arp_op = htons(ARPOP_REPLY);
33611570Ssam 	eh = (struct ether_header *)sa.sa_data;
33712763Ssam 	bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost,
33812763Ssam 	   sizeof (eh->ether_dhost));
33911570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;
34011570Ssam 	sa.sa_family = AF_UNSPEC;
34111570Ssam 	(*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
34211570Ssam 	return;
34311570Ssam out:
34411570Ssam 	m_freem(m);
34511570Ssam 	return;
34611570Ssam }
34711570Ssam 
34811570Ssam /*
34911570Ssam  * Free an arptab entry.
35011570Ssam  */
35111570Ssam arptfree(at)
35211570Ssam 	register struct arptab *at;
35311570Ssam {
35411570Ssam 	int s = splimp();
35511570Ssam 
35611570Ssam 	if (at->at_hold)
35711570Ssam 		m_freem(at->at_hold);
35811570Ssam 	at->at_hold = 0;
35911570Ssam 	at->at_timer = at->at_flags = 0;
36011570Ssam 	at->at_iaddr.s_addr = 0;
36111570Ssam 	splx(s);
36211570Ssam }
36311570Ssam 
36411570Ssam /*
36511570Ssam  * Enter a new address in arptab, pushing out the oldest entry
36611570Ssam  * from the bucket if there is no room.
36711570Ssam  */
36811570Ssam struct arptab *
36911570Ssam arptnew(addr)
37011570Ssam 	struct in_addr *addr;
37111570Ssam {
37211570Ssam 	register n;
37311570Ssam 	int oldest = 0;
37411570Ssam 	register struct arptab *at, *ato;
37511570Ssam 
37611570Ssam 	ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ];
37711570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
37811570Ssam 		if (at->at_flags == 0)
37911570Ssam 			goto out;	 /* found an empty entry */
38011570Ssam 		if (at->at_timer > oldest) {
38111570Ssam 			oldest = at->at_timer;
38211570Ssam 			ato = at;
38311570Ssam 		}
38411570Ssam 	}
38511570Ssam 	at = ato;
38611570Ssam 	arptfree(at);
38711570Ssam out:
38811570Ssam 	at->at_iaddr = *addr;
38911570Ssam 	at->at_flags = ATF_INUSE;
39011570Ssam 	return (at);
39111570Ssam }
392