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