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