xref: /csrg-svn/sys/netinet/if_ether.c (revision 11570)
1*11570Ssam /*	if_ether.c	4.1	83/03/15	*/
2*11570Ssam 
3*11570Ssam /*
4*11570Ssam  * Ethernet address resolution protocol.
5*11570Ssam  */
6*11570Ssam 
7*11570Ssam #include "../h/param.h"
8*11570Ssam #include "../h/systm.h"
9*11570Ssam #include "../h/mbuf.h"
10*11570Ssam #include "../h/socket.h"
11*11570Ssam #include "../h/time.h"
12*11570Ssam #include "../h/kernel.h"
13*11570Ssam 
14*11570Ssam #include "../net/if.h"
15*11570Ssam #include "../netinet/in.h"
16*11570Ssam #include "../netinet/if_ether.h"
17*11570Ssam 
18*11570Ssam 
19*11570Ssam /*
20*11570Ssam  * Internet to ethernet address resolution table.
21*11570Ssam  */
22*11570Ssam struct	arptab {
23*11570Ssam 	struct	in_addr at_iaddr;	/* internet address */
24*11570Ssam 	u_char	at_enaddr[6];		/* ethernet address */
25*11570Ssam 	struct	mbuf *at_hold;		/* last packet until resolved/timeout */
26*11570Ssam 	u_char	at_timer;		/* minutes since last reference */
27*11570Ssam 	u_char	at_flags;		/* flags */
28*11570Ssam };
29*11570Ssam /* at_flags field values */
30*11570Ssam #define	ATF_INUSE	1		/* entry in use */
31*11570Ssam #define ATF_COM		2		/* completed entry (enaddr valid) */
32*11570Ssam 
33*11570Ssam #define	ARPTAB_BSIZ	5		/* bucket size */
34*11570Ssam #define	ARPTAB_NB	19		/* number of buckets */
35*11570Ssam #define	ARPTAB_SIZE	(ARPTAB_BSIZ * ARPTAB_NB)
36*11570Ssam struct	arptab arptab[ARPTAB_SIZE];
37*11570Ssam 
38*11570Ssam #define	ARPTAB_HASH(a) \
39*11570Ssam 	((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB)
40*11570Ssam 
41*11570Ssam #define	ARPTAB_LOOK(at,addr) { \
42*11570Ssam 	register n; \
43*11570Ssam 	at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \
44*11570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \
45*11570Ssam 		if (at->at_iaddr.s_addr == addr) \
46*11570Ssam 			break; \
47*11570Ssam 	if (n >= ARPTAB_BSIZ) \
48*11570Ssam 		at = 0; }
49*11570Ssam 
50*11570Ssam struct	arpcom *arpcom;		/* chain of active ether interfaces */
51*11570Ssam int	arpt_age;		/* aging timer */
52*11570Ssam 
53*11570Ssam /* timer values */
54*11570Ssam #define	ARPT_AGE	(60*1)	/* aging timer, 1 min. */
55*11570Ssam #define	ARPT_KILLC	20	/* kill completed entry in 20 mins. */
56*11570Ssam #define	ARPT_KILLI	3	/* kill incomplete entry in 3 minutes */
57*11570Ssam 
58*11570Ssam u_char	etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
59*11570Ssam extern struct ifnet loif;
60*11570Ssam 
61*11570Ssam #define	OLDMAP	1		/* if LNA > 1023 use old 3COM mapping */
62*11570Ssam 
63*11570Ssam /*
64*11570Ssam  * Attach an ethernet interface to the list "arpcom" where
65*11570Ssam  * arptimer() can find it.  If first time
66*11570Ssam  * initialization, start arptimer().
67*11570Ssam  */
68*11570Ssam arpattach(ac)
69*11570Ssam 	register struct arpcom *ac;
70*11570Ssam {
71*11570Ssam 	register struct arpcom *acp;
72*11570Ssam 
73*11570Ssam 	for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac)
74*11570Ssam 		if (acp == ac)		/* if already on list */
75*11570Ssam 			return;
76*11570Ssam 	ac->ac_ac = arpcom;
77*11570Ssam 	arpcom = ac;
78*11570Ssam 	if (arpcom->ac_ac == 0)		/* very first time */
79*11570Ssam 		arptimer();
80*11570Ssam }
81*11570Ssam 
82*11570Ssam /*
83*11570Ssam  * Timeout routine.  Age arp_tab entries once a minute.
84*11570Ssam  */
85*11570Ssam arptimer()
86*11570Ssam {
87*11570Ssam 	register struct arpcom *ac;
88*11570Ssam 	register struct arptab *at;
89*11570Ssam 	register i;
90*11570Ssam 
91*11570Ssam 	timeout(arptimer, 0, hz);
92*11570Ssam #ifdef notdef
93*11570Ssam 	if (++arpt_sanity > ARPT_SANITY) {
94*11570Ssam 		/*
95*11570Ssam 		 * Randomize sanity timer based on my host address.
96*11570Ssam 		 * Ask who has my own address;  if someone else replies,
97*11570Ssam 		 * then they are impersonating me.
98*11570Ssam 		 */
99*11570Ssam 		arpt_sanity = arpcom->ac_enaddr[5] & 0x3f;
100*11570Ssam 		for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac)
101*11570Ssam 			arpwhohas(ac, &((struct sockaddr_in *)
102*11570Ssam 			    &ac->ac_if.if_addr)->sin_addr);
103*11570Ssam 	}
104*11570Ssam #endif
105*11570Ssam 	if (++arpt_age > ARPT_AGE) {
106*11570Ssam 		arpt_age = 0;
107*11570Ssam 		at = &arptab[0];
108*11570Ssam 		for (i = 0; i < ARPTAB_SIZE; i++, at++) {
109*11570Ssam 			if (at->at_flags == 0)
110*11570Ssam 				continue;
111*11570Ssam 			if (++at->at_timer < ((at->at_flags&ATF_COM) ?
112*11570Ssam 			    ARPT_KILLC : ARPT_KILLI))
113*11570Ssam 				continue;
114*11570Ssam 			/* timer has expired, clear entry */
115*11570Ssam 			arptfree(at);
116*11570Ssam 		}
117*11570Ssam 	}
118*11570Ssam }
119*11570Ssam 
120*11570Ssam /*
121*11570Ssam  * Broadcast an ARP packet, asking who has addr on interface ac.
122*11570Ssam  */
123*11570Ssam arpwhohas(ac, addr)
124*11570Ssam 	register struct arpcom *ac;
125*11570Ssam 	struct in_addr *addr;
126*11570Ssam {
127*11570Ssam 	register struct mbuf *m;
128*11570Ssam 	register struct ether_header *eh;
129*11570Ssam 	register struct ether_arp *ea;
130*11570Ssam 	struct sockaddr sa;
131*11570Ssam 
132*11570Ssam 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL)
133*11570Ssam 		return (1);
134*11570Ssam 	m->m_len = sizeof *ea + sizeof *eh;
135*11570Ssam 	m->m_off = MMAXOFF - m->m_len;
136*11570Ssam 	ea = mtod(m, struct ether_arp *);
137*11570Ssam 	eh = (struct ether_header *)sa.sa_data;
138*11570Ssam 	bzero((caddr_t)ea, sizeof *ea);
139*11570Ssam 	bcopy(etherbroadcastaddr, eh->ether_dhost, sizeof etherbroadcastaddr);
140*11570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;	/* if_output will swap */
141*11570Ssam 	ea->arp_hrd = htons(ARPHRD_ETHER);
142*11570Ssam 	ea->arp_pro = htons(ETHERPUP_IPTYPE);
143*11570Ssam 	ea->arp_hln = sizeof ea->arp_sha;	/* hardware address length */
144*11570Ssam 	ea->arp_pln = sizeof ea->arp_spa;	/* protocol address length */
145*11570Ssam 	ea->arp_op = htons(ARPOP_REQUEST);
146*11570Ssam 	bcopy(ac->ac_enaddr, ea->arp_sha, sizeof ea->arp_sha);
147*11570Ssam 	bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr,
148*11570Ssam 	    ea->arp_spa, sizeof ea->arp_spa);
149*11570Ssam 	bcopy((caddr_t)addr, ea->arp_tpa, sizeof ea->arp_tpa);
150*11570Ssam 	sa.sa_family = AF_UNSPEC;
151*11570Ssam 	return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa));
152*11570Ssam }
153*11570Ssam 
154*11570Ssam /*
155*11570Ssam  * Resolve an IP address into an ethernet address.  If success,
156*11570Ssam  * desten is filled in and 1 is returned.  If there is no entry
157*11570Ssam  * in arptab, set one up and broadcast a request
158*11570Ssam  * for the IP address;  return 0.  Hold onto this mbuf and
159*11570Ssam  * resend it once the address is finally resolved.
160*11570Ssam  *
161*11570Ssam  * We do some (conservative) locking here at splimp, since
162*11570Ssam  * arptab is also altered from input interrupt service (ecintr/ilintr
163*11570Ssam  * calls arpinput when ETHERPUP_ARPTYPE packets come in).
164*11570Ssam  */
165*11570Ssam arpresolve(ac, m, destip, desten)
166*11570Ssam 	register struct arpcom *ac;
167*11570Ssam 	struct mbuf *m;
168*11570Ssam 	register struct in_addr *destip;
169*11570Ssam 	register u_char *desten;
170*11570Ssam {
171*11570Ssam 	register struct arptab *at;
172*11570Ssam 	register i;
173*11570Ssam 	struct sockaddr_in sin;
174*11570Ssam 	int s, lna;
175*11570Ssam 
176*11570Ssam 	lna = in_lnaof(*destip);
177*11570Ssam 	if (lna == INADDR_ANY) {	/* broadcast address */
178*11570Ssam 		bcopy(etherbroadcastaddr, desten, sizeof etherbroadcastaddr);
179*11570Ssam 		return (1);
180*11570Ssam 	}
181*11570Ssam 	if (destip->s_addr == ((struct sockaddr_in *)&ac->ac_if.if_addr)->
182*11570Ssam 	    sin_addr.s_addr) {			/* if for us, use lo driver */
183*11570Ssam 		sin.sin_family = AF_INET;
184*11570Ssam 		sin.sin_addr = *destip;
185*11570Ssam 		looutput(&loif, m, &sin);
186*11570Ssam 		return (0);
187*11570Ssam 	}
188*11570Ssam #ifdef OLDMAP
189*11570Ssam 	if (lna >= 1024) {
190*11570Ssam 		bcopy(ac->ac_enaddr, desten, 3);
191*11570Ssam 		desten[3] = (lna >> 16) & 0x7f;
192*11570Ssam 		desten[4] = (lna >> 8) & 0xff;
193*11570Ssam 		desten[5] = lna & 0xff;
194*11570Ssam 		return (1);
195*11570Ssam 	}
196*11570Ssam #endif OLDMAP
197*11570Ssam 	s = splimp();
198*11570Ssam 	ARPTAB_LOOK(at, destip->s_addr);
199*11570Ssam 	if (at == 0) {			/* not found */
200*11570Ssam 		at = arptnew(destip);
201*11570Ssam 		at->at_hold = m;
202*11570Ssam 		arpwhohas(ac, destip);
203*11570Ssam 		splx(s);
204*11570Ssam 		return (0);
205*11570Ssam 	}
206*11570Ssam 	at->at_timer = 0;		/* restart the timer */
207*11570Ssam 	if (at->at_flags & ATF_COM) {	/* entry IS complete */
208*11570Ssam 		bcopy(at->at_enaddr, desten, 6);
209*11570Ssam 		splx(s);
210*11570Ssam 		return (1);
211*11570Ssam 	}
212*11570Ssam 	/*
213*11570Ssam 	 * There is an arptab entry, but no ethernet address
214*11570Ssam 	 * response yet.  Replace the held mbuf with this
215*11570Ssam 	 * latest one.
216*11570Ssam 	 */
217*11570Ssam 	if (at->at_hold)
218*11570Ssam 		m_freem(at->at_hold);
219*11570Ssam 	at->at_hold = m;
220*11570Ssam 	arpwhohas(ac, destip);		/* ask again */
221*11570Ssam 	splx(s);
222*11570Ssam 	return (0);
223*11570Ssam }
224*11570Ssam 
225*11570Ssam /*
226*11570Ssam  * Find my own IP address.  It will either be waiting for us in
227*11570Ssam  * monitor RAM, or can be obtained via broadcast to the file/boot
228*11570Ssam  * server (not necessarily using the ARP packet format).
229*11570Ssam  *
230*11570Ssam  * Unimplemented at present, return 0 and assume that the host
231*11570Ssam  * will set his own IP address via the SIOCSIFADDR ioctl.
232*11570Ssam  */
233*11570Ssam struct in_addr
234*11570Ssam arpmyaddr(ac)
235*11570Ssam 	register struct arpcom *ac;
236*11570Ssam {
237*11570Ssam 	static struct in_addr addr;
238*11570Ssam 
239*11570Ssam 	addr.s_addr = 0;
240*11570Ssam 	return (addr);
241*11570Ssam }
242*11570Ssam 
243*11570Ssam /*
244*11570Ssam  * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP
245*11570Ssam  * is received.  Algorithm is exactly that given in RFC 826.
246*11570Ssam  * In addition, a sanity check is performed on the sender
247*11570Ssam  * protocol address, to catch impersonators.
248*11570Ssam  */
249*11570Ssam arpinput(ac, m)
250*11570Ssam 	register struct arpcom *ac;
251*11570Ssam 	struct mbuf *m;
252*11570Ssam {
253*11570Ssam 	register struct ether_arp *ea;
254*11570Ssam 	struct ether_header *eh;
255*11570Ssam 	register struct arptab *at = 0;  /* same as "merge" flag */
256*11570Ssam 	struct sockaddr_in sin;
257*11570Ssam 	struct sockaddr sa;
258*11570Ssam 	struct mbuf *mhold;
259*11570Ssam 	struct in_addr isaddr,itaddr,myaddr;
260*11570Ssam 
261*11570Ssam 	if (m->m_len < sizeof *ea)
262*11570Ssam 		goto out;
263*11570Ssam 	myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr;
264*11570Ssam 	ea = mtod(m, struct ether_arp *);
265*11570Ssam 	if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE)
266*11570Ssam 		goto out;
267*11570Ssam 	isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr;
268*11570Ssam 	itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr;
269*11570Ssam 	if (!bcmp(ea->arp_sha, ac->ac_enaddr, sizeof ac->ac_enaddr))
270*11570Ssam 		goto out;	/* it's from me, ignore it. */
271*11570Ssam 	if (isaddr.s_addr == myaddr.s_addr) {
272*11570Ssam 		printf("duplicate IP address!! sent from ethernet address: ");
273*11570Ssam 		printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1],
274*11570Ssam 		    ea->arp_sha[2], ea->arp_sha[3],
275*11570Ssam 		    ea->arp_sha[4], ea->arp_sha[5]);
276*11570Ssam 		if (ntohs(ea->arp_op) == ARPOP_REQUEST)
277*11570Ssam 			goto reply;
278*11570Ssam 		goto out;
279*11570Ssam 	}
280*11570Ssam 	ARPTAB_LOOK(at, isaddr.s_addr);
281*11570Ssam 	if (at) {
282*11570Ssam 		bcopy(ea->arp_sha, at->at_enaddr, sizeof ea->arp_sha);
283*11570Ssam 		at->at_flags |= ATF_COM;
284*11570Ssam 		if (at->at_hold) {
285*11570Ssam 			mhold = at->at_hold;
286*11570Ssam 			at->at_hold = 0;
287*11570Ssam 			sin.sin_family = AF_INET;
288*11570Ssam 			sin.sin_addr = isaddr;
289*11570Ssam 			(*ac->ac_if.if_output)(&ac->ac_if,
290*11570Ssam 			    mhold, (struct sockaddr *)&sin);
291*11570Ssam 		}
292*11570Ssam 	}
293*11570Ssam 	if (itaddr.s_addr != myaddr.s_addr)
294*11570Ssam 		goto out;	/* if I am not the target */
295*11570Ssam 	if (at == 0) {		/* ensure we have a table entry */
296*11570Ssam 		at = arptnew(&isaddr);
297*11570Ssam 		bcopy(ea->arp_sha, at->at_enaddr, sizeof ea->arp_sha);
298*11570Ssam 		at->at_flags |= ATF_COM;
299*11570Ssam 	}
300*11570Ssam 	if (ntohs(ea->arp_op) != ARPOP_REQUEST)
301*11570Ssam 		goto out;
302*11570Ssam reply:
303*11570Ssam 	bcopy(ea->arp_sha, ea->arp_tha, sizeof ea->arp_sha);
304*11570Ssam 	bcopy(ea->arp_spa, ea->arp_tpa, sizeof ea->arp_spa);
305*11570Ssam 	bcopy(ac->ac_enaddr, ea->arp_sha, sizeof ea->arp_sha);
306*11570Ssam 	bcopy((caddr_t)&myaddr, ea->arp_spa, sizeof ea->arp_spa);
307*11570Ssam 	ea->arp_op = htons(ARPOP_REPLY);
308*11570Ssam 	eh = (struct ether_header *)sa.sa_data;
309*11570Ssam 	bcopy(ea->arp_tha, eh->ether_dhost, sizeof eh->ether_dhost);
310*11570Ssam 	eh->ether_type = ETHERPUP_ARPTYPE;
311*11570Ssam 	sa.sa_family = AF_UNSPEC;
312*11570Ssam 	(*ac->ac_if.if_output)(&ac->ac_if, m, &sa);
313*11570Ssam 	return;
314*11570Ssam out:
315*11570Ssam 	m_freem(m);
316*11570Ssam 	return;
317*11570Ssam }
318*11570Ssam 
319*11570Ssam /*
320*11570Ssam  * Free an arptab entry.
321*11570Ssam  */
322*11570Ssam arptfree(at)
323*11570Ssam 	register struct arptab *at;
324*11570Ssam {
325*11570Ssam 	int s = splimp();
326*11570Ssam 
327*11570Ssam 	if (at->at_hold)
328*11570Ssam 		m_freem(at->at_hold);
329*11570Ssam 	at->at_hold = 0;
330*11570Ssam 	at->at_timer = at->at_flags = 0;
331*11570Ssam 	at->at_iaddr.s_addr = 0;
332*11570Ssam 	splx(s);
333*11570Ssam }
334*11570Ssam 
335*11570Ssam /*
336*11570Ssam  * Enter a new address in arptab, pushing out the oldest entry
337*11570Ssam  * from the bucket if there is no room.
338*11570Ssam  */
339*11570Ssam struct arptab *
340*11570Ssam arptnew(addr)
341*11570Ssam 	struct in_addr *addr;
342*11570Ssam {
343*11570Ssam 	register n;
344*11570Ssam 	int oldest = 0;
345*11570Ssam 	register struct arptab *at, *ato;
346*11570Ssam 
347*11570Ssam 	ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ];
348*11570Ssam 	for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) {
349*11570Ssam 		if (at->at_flags == 0)
350*11570Ssam 			goto out;	 /* found an empty entry */
351*11570Ssam 		if (at->at_timer > oldest) {
352*11570Ssam 			oldest = at->at_timer;
353*11570Ssam 			ato = at;
354*11570Ssam 		}
355*11570Ssam 	}
356*11570Ssam 	at = ato;
357*11570Ssam 	arptfree(at);
358*11570Ssam out:
359*11570Ssam 	at->at_iaddr = *addr;
360*11570Ssam 	at->at_flags = ATF_INUSE;
361*11570Ssam 	return (at);
362*11570Ssam }
363