Commit | Line | Data |
---|---|---|
36e09a72 | 1 | /* if_ether.c 6.9 85/04/24 */ |
2b0e2bab SL |
2 | |
3 | /* | |
4 | * Ethernet address resolution protocol. | |
5 | */ | |
6 | ||
20666ad3 JB |
7 | #include "param.h" |
8 | #include "systm.h" | |
9 | #include "mbuf.h" | |
10 | #include "socket.h" | |
11 | #include "time.h" | |
12 | #include "kernel.h" | |
13 | #include "errno.h" | |
14 | #include "ioctl.h" | |
2b0e2bab SL |
15 | |
16 | #include "../net/if.h" | |
20666ad3 JB |
17 | #include "in.h" |
18 | #include "in_systm.h" | |
19 | #include "ip.h" | |
20 | #include "if_ether.h" | |
2b0e2bab | 21 | |
2b0e2bab SL |
22 | #define ARPTAB_BSIZ 5 /* bucket size */ |
23 | #define ARPTAB_NB 19 /* number of buckets */ | |
24 | #define ARPTAB_SIZE (ARPTAB_BSIZ * ARPTAB_NB) | |
25 | struct arptab arptab[ARPTAB_SIZE]; | |
07847ffa | 26 | int arptab_size = ARPTAB_SIZE; /* for arp command */ |
2b0e2bab SL |
27 | |
28 | #define ARPTAB_HASH(a) \ | |
29 | ((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB) | |
30 | ||
31 | #define ARPTAB_LOOK(at,addr) { \ | |
32 | register n; \ | |
33 | at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \ | |
34 | for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \ | |
35 | if (at->at_iaddr.s_addr == addr) \ | |
36 | break; \ | |
37 | if (n >= ARPTAB_BSIZ) \ | |
38 | at = 0; } | |
39 | ||
2b0e2bab SL |
40 | int arpt_age; /* aging timer */ |
41 | ||
42 | /* timer values */ | |
43 | #define ARPT_AGE (60*1) /* aging timer, 1 min. */ | |
44 | #define ARPT_KILLC 20 /* kill completed entry in 20 mins. */ | |
45 | #define ARPT_KILLI 3 /* kill incomplete entry in 3 minutes */ | |
46 | ||
9350cacc | 47 | u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
2b0e2bab SL |
48 | extern struct ifnet loif; |
49 | ||
1a259c14 SL |
50 | /* |
51 | * Local addresses in the range oldmap to infinity are | |
52 | * mapped according to the old mapping scheme. That is, | |
53 | * mapping of Internet to Ethernet addresses is performed | |
54 | * by taking the high three bytes of the network interface's | |
55 | * address and the low three bytes of the local address part. | |
56 | * This only allows boards from the same manufacturer to | |
57 | * communicate unless the on-board address is overridden | |
58 | * (not possible in many manufacture's hardware). | |
59 | * | |
60 | * NB: setting oldmap to zero completely disables ARP | |
61 | * (i.e. identical to setting IFF_NOARP with an ioctl). | |
62 | */ | |
63 | int oldmap = 1024; | |
2b0e2bab | 64 | |
2b0e2bab SL |
65 | /* |
66 | * Timeout routine. Age arp_tab entries once a minute. | |
67 | */ | |
68 | arptimer() | |
69 | { | |
2b0e2bab SL |
70 | register struct arptab *at; |
71 | register i; | |
72 | ||
755d8841 | 73 | timeout(arptimer, (caddr_t)0, hz); |
2b0e2bab SL |
74 | if (++arpt_age > ARPT_AGE) { |
75 | arpt_age = 0; | |
76 | at = &arptab[0]; | |
77 | for (i = 0; i < ARPTAB_SIZE; i++, at++) { | |
07847ffa | 78 | if (at->at_flags == 0 || (at->at_flags & ATF_PERM)) |
2b0e2bab SL |
79 | continue; |
80 | if (++at->at_timer < ((at->at_flags&ATF_COM) ? | |
81 | ARPT_KILLC : ARPT_KILLI)) | |
82 | continue; | |
83 | /* timer has expired, clear entry */ | |
84 | arptfree(at); | |
85 | } | |
86 | } | |
87 | } | |
88 | ||
89 | /* | |
90 | * Broadcast an ARP packet, asking who has addr on interface ac. | |
91 | */ | |
92 | arpwhohas(ac, addr) | |
93 | register struct arpcom *ac; | |
94 | struct in_addr *addr; | |
95 | { | |
96 | register struct mbuf *m; | |
97 | register struct ether_header *eh; | |
98 | register struct ether_arp *ea; | |
99 | struct sockaddr sa; | |
100 | ||
101 | if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) | |
07847ffa | 102 | return (1); |
b5bc9bc1 | 103 | m->m_len = sizeof *ea; |
2b0e2bab SL |
104 | m->m_off = MMAXOFF - m->m_len; |
105 | ea = mtod(m, struct ether_arp *); | |
106 | eh = (struct ether_header *)sa.sa_data; | |
755d8841 | 107 | bzero((caddr_t)ea, sizeof (*ea)); |
9350cacc MK |
108 | bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, |
109 | sizeof(eh->ether_dhost)); | |
935a6d59 | 110 | eh->ether_type = ETHERTYPE_ARP; /* if_output will swap */ |
2b0e2bab | 111 | ea->arp_hrd = htons(ARPHRD_ETHER); |
935a6d59 | 112 | ea->arp_pro = htons(ETHERTYPE_IP); |
9350cacc MK |
113 | ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */ |
114 | ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */ | |
2b0e2bab | 115 | ea->arp_op = htons(ARPOP_REQUEST); |
9350cacc MK |
116 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, |
117 | sizeof(ea->arp_sha)); | |
118 | bcopy((caddr_t)&ac->ac_ipaddr, (caddr_t)ea->arp_spa, | |
119 | sizeof(ea->arp_spa)); | |
120 | bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa)); | |
2b0e2bab | 121 | sa.sa_family = AF_UNSPEC; |
07847ffa | 122 | return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa)); |
2b0e2bab SL |
123 | } |
124 | ||
125 | /* | |
126 | * Resolve an IP address into an ethernet address. If success, | |
127 | * desten is filled in and 1 is returned. If there is no entry | |
128 | * in arptab, set one up and broadcast a request | |
129 | * for the IP address; return 0. Hold onto this mbuf and | |
130 | * resend it once the address is finally resolved. | |
131 | * | |
132 | * We do some (conservative) locking here at splimp, since | |
133 | * arptab is also altered from input interrupt service (ecintr/ilintr | |
935a6d59 | 134 | * calls arpinput when ETHERTYPE_ARP packets come in). |
2b0e2bab SL |
135 | */ |
136 | arpresolve(ac, m, destip, desten) | |
137 | register struct arpcom *ac; | |
138 | struct mbuf *m; | |
139 | register struct in_addr *destip; | |
9350cacc | 140 | register u_char *desten; |
2b0e2bab SL |
141 | { |
142 | register struct arptab *at; | |
1a259c14 | 143 | register struct ifnet *ifp; |
07847ffa | 144 | register int i; |
2b0e2bab SL |
145 | struct sockaddr_in sin; |
146 | int s, lna; | |
147 | ||
935a6d59 | 148 | if (in_broadcast(*destip)) { /* broadcast address */ |
9350cacc MK |
149 | bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten, |
150 | sizeof(etherbroadcastaddr)); | |
2b0e2bab SL |
151 | return (1); |
152 | } | |
935a6d59 | 153 | lna = in_lnaof(*destip); |
1a259c14 SL |
154 | ifp = &ac->ac_if; |
155 | /* if for us, then use software loopback driver */ | |
935a6d59 | 156 | if (destip->s_addr == ac->ac_ipaddr.s_addr && |
07847ffa | 157 | (loif.if_flags & IFF_UP)) { |
2b0e2bab SL |
158 | sin.sin_family = AF_INET; |
159 | sin.sin_addr = *destip; | |
b746b290 SL |
160 | (void) looutput(&loif, m, (struct sockaddr *)&sin); |
161 | /* | |
162 | * We really don't want to indicate failure, | |
163 | * but the packet has already been sent and freed. | |
164 | */ | |
165 | return (0); | |
2b0e2bab | 166 | } |
2b0e2bab SL |
167 | s = splimp(); |
168 | ARPTAB_LOOK(at, destip->s_addr); | |
169 | if (at == 0) { /* not found */ | |
07847ffa | 170 | if ((ifp->if_flags & IFF_NOARP) || lna >= oldmap) { |
9350cacc MK |
171 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3); |
172 | desten[3] = (lna >> 16) & 0x7f; | |
173 | desten[4] = (lna >> 8) & 0xff; | |
174 | desten[5] = lna & 0xff; | |
07847ffa MK |
175 | splx(s); |
176 | return (1); | |
177 | } else { | |
178 | at = arptnew(destip); | |
179 | at->at_hold = m; | |
180 | arpwhohas(ac, destip); | |
181 | splx(s); | |
182 | return (0); | |
183 | } | |
2b0e2bab SL |
184 | } |
185 | at->at_timer = 0; /* restart the timer */ | |
186 | if (at->at_flags & ATF_COM) { /* entry IS complete */ | |
9350cacc MK |
187 | bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, |
188 | sizeof(at->at_enaddr)); | |
2b0e2bab SL |
189 | splx(s); |
190 | return (1); | |
191 | } | |
192 | /* | |
193 | * There is an arptab entry, but no ethernet address | |
194 | * response yet. Replace the held mbuf with this | |
195 | * latest one. | |
196 | */ | |
197 | if (at->at_hold) | |
198 | m_freem(at->at_hold); | |
199 | at->at_hold = m; | |
200 | arpwhohas(ac, destip); /* ask again */ | |
201 | splx(s); | |
202 | return (0); | |
203 | } | |
204 | ||
2b0e2bab | 205 | /* |
935a6d59 MK |
206 | * Called from 10 Mb/s Ethernet interrupt handlers |
207 | * when ether packet type ETHERTYPE_ARP | |
07847ffa | 208 | * is received. Algorithm is that given in RFC 826. |
2b0e2bab SL |
209 | * In addition, a sanity check is performed on the sender |
210 | * protocol address, to catch impersonators. | |
211 | */ | |
212 | arpinput(ac, m) | |
213 | register struct arpcom *ac; | |
214 | struct mbuf *m; | |
215 | { | |
216 | register struct ether_arp *ea; | |
217 | struct ether_header *eh; | |
218 | register struct arptab *at = 0; /* same as "merge" flag */ | |
219 | struct sockaddr_in sin; | |
220 | struct sockaddr sa; | |
2b0e2bab SL |
221 | struct in_addr isaddr,itaddr,myaddr; |
222 | ||
223 | if (m->m_len < sizeof *ea) | |
224 | goto out; | |
07847ffa MK |
225 | if (ac->ac_if.if_flags & IFF_NOARP) |
226 | goto out; | |
935a6d59 | 227 | myaddr = ac->ac_ipaddr; |
2b0e2bab | 228 | ea = mtod(m, struct ether_arp *); |
935a6d59 | 229 | if (ntohs(ea->arp_pro) != ETHERTYPE_IP) |
2b0e2bab | 230 | goto out; |
9350cacc MK |
231 | isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr; |
232 | itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr; | |
233 | if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr, | |
234 | sizeof (ea->arp_sha))) | |
2b0e2bab SL |
235 | goto out; /* it's from me, ignore it. */ |
236 | if (isaddr.s_addr == myaddr.s_addr) { | |
237 | printf("duplicate IP address!! sent from ethernet address: "); | |
9350cacc MK |
238 | printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1], |
239 | ea->arp_sha[2], ea->arp_sha[3], | |
240 | ea->arp_sha[4], ea->arp_sha[5]); | |
07847ffa | 241 | itaddr = myaddr; |
9350cacc MK |
242 | if (ntohs(ea->arp_op) == ARPOP_REQUEST) { |
243 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, | |
244 | sizeof(ea->arp_sha)); | |
2b0e2bab | 245 | goto reply; |
9350cacc | 246 | } |
2b0e2bab SL |
247 | goto out; |
248 | } | |
249 | ARPTAB_LOOK(at, isaddr.s_addr); | |
36e09a72 | 250 | if (at && (at->at_flags & ATF_COM) == 0) { |
9350cacc MK |
251 | bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, |
252 | sizeof(ea->arp_sha)); | |
2b0e2bab SL |
253 | at->at_flags |= ATF_COM; |
254 | if (at->at_hold) { | |
2b0e2bab SL |
255 | sin.sin_family = AF_INET; |
256 | sin.sin_addr = isaddr; | |
257 | (*ac->ac_if.if_output)(&ac->ac_if, | |
36e09a72 MK |
258 | at->at_hold, (struct sockaddr *)&sin); |
259 | at->at_hold = 0; | |
2b0e2bab | 260 | } |
36e09a72 MK |
261 | } |
262 | if (at == 0 && itaddr.s_addr == myaddr.s_addr) { | |
07847ffa | 263 | /* ensure we have a table entry */ |
2b0e2bab | 264 | at = arptnew(&isaddr); |
9350cacc MK |
265 | bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, |
266 | sizeof(ea->arp_sha)); | |
2b0e2bab SL |
267 | at->at_flags |= ATF_COM; |
268 | } | |
269 | if (ntohs(ea->arp_op) != ARPOP_REQUEST) | |
270 | goto out; | |
07847ffa MK |
271 | ARPTAB_LOOK(at, itaddr.s_addr); |
272 | if (at == NULL) { | |
273 | if (itaddr.s_addr != myaddr.s_addr) | |
274 | goto out; /* if I am not the target */ | |
275 | at = arptnew(&myaddr); | |
9350cacc MK |
276 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)at->at_enaddr, |
277 | sizeof(at->at_enaddr)); | |
07847ffa MK |
278 | at->at_flags |= ATF_COM; |
279 | } | |
280 | if (itaddr.s_addr != myaddr.s_addr && (at->at_flags & ATF_PUBL) == 0) | |
281 | goto out; | |
282 | ||
2b0e2bab | 283 | reply: |
9350cacc MK |
284 | bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha, |
285 | sizeof(ea->arp_sha)); | |
286 | bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, | |
287 | sizeof(ea->arp_spa)); | |
36e09a72 MK |
288 | if (at) /* done above if at == 0 */ |
289 | bcopy((caddr_t)at->at_enaddr, (caddr_t)ea->arp_sha, | |
290 | sizeof(ea->arp_sha)); | |
9350cacc MK |
291 | bcopy((caddr_t)&itaddr, (caddr_t)ea->arp_spa, |
292 | sizeof(ea->arp_spa)); | |
2b0e2bab SL |
293 | ea->arp_op = htons(ARPOP_REPLY); |
294 | eh = (struct ether_header *)sa.sa_data; | |
9350cacc MK |
295 | bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost, |
296 | sizeof(eh->ether_dhost)); | |
935a6d59 | 297 | eh->ether_type = ETHERTYPE_ARP; |
2b0e2bab SL |
298 | sa.sa_family = AF_UNSPEC; |
299 | (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); | |
300 | return; | |
301 | out: | |
302 | m_freem(m); | |
303 | return; | |
304 | } | |
305 | ||
306 | /* | |
307 | * Free an arptab entry. | |
308 | */ | |
309 | arptfree(at) | |
310 | register struct arptab *at; | |
311 | { | |
312 | int s = splimp(); | |
313 | ||
314 | if (at->at_hold) | |
315 | m_freem(at->at_hold); | |
316 | at->at_hold = 0; | |
317 | at->at_timer = at->at_flags = 0; | |
318 | at->at_iaddr.s_addr = 0; | |
319 | splx(s); | |
320 | } | |
321 | ||
322 | /* | |
323 | * Enter a new address in arptab, pushing out the oldest entry | |
324 | * from the bucket if there is no room. | |
07847ffa MK |
325 | * This always succeeds since no bucket can be completely filled |
326 | * with permanent entries (except from arpioctl when testing whether | |
9350cacc | 327 | * another permanent entry will fit). |
2b0e2bab SL |
328 | */ |
329 | struct arptab * | |
330 | arptnew(addr) | |
331 | struct in_addr *addr; | |
332 | { | |
333 | register n; | |
334 | int oldest = 0; | |
07847ffa MK |
335 | register struct arptab *at, *ato = NULL; |
336 | static int first = 1; | |
2b0e2bab | 337 | |
07847ffa MK |
338 | if (first) { |
339 | first = 0; | |
340 | timeout(arptimer, (caddr_t)0, hz); | |
341 | } | |
342 | at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; | |
9350cacc | 343 | for (n = 0; n < ARPTAB_BSIZ; n++,at++) { |
2b0e2bab SL |
344 | if (at->at_flags == 0) |
345 | goto out; /* found an empty entry */ | |
07847ffa MK |
346 | if (at->at_flags & ATF_PERM) |
347 | continue; | |
2b0e2bab SL |
348 | if (at->at_timer > oldest) { |
349 | oldest = at->at_timer; | |
350 | ato = at; | |
351 | } | |
352 | } | |
07847ffa | 353 | if (ato == NULL) |
9350cacc | 354 | return (NULL); |
2b0e2bab SL |
355 | at = ato; |
356 | arptfree(at); | |
357 | out: | |
358 | at->at_iaddr = *addr; | |
359 | at->at_flags = ATF_INUSE; | |
360 | return (at); | |
361 | } | |
07847ffa MK |
362 | |
363 | arpioctl(cmd, data) | |
364 | int cmd; | |
365 | caddr_t data; | |
366 | { | |
367 | register struct arpreq *ar = (struct arpreq *)data; | |
368 | register struct arptab *at; | |
369 | register struct sockaddr_in *sin; | |
370 | int s; | |
371 | ||
372 | if (ar->arp_pa.sa_family != AF_INET || | |
373 | ar->arp_ha.sa_family != AF_UNSPEC) | |
374 | return (EAFNOSUPPORT); | |
375 | sin = (struct sockaddr_in *)&ar->arp_pa; | |
376 | s = splimp(); | |
377 | ARPTAB_LOOK(at, sin->sin_addr.s_addr); | |
378 | if (at == NULL) { /* not found */ | |
379 | if (cmd != SIOCSARP) { | |
380 | splx(s); | |
381 | return (ENXIO); | |
382 | } | |
935a6d59 | 383 | if (ifa_ifwithnet(&ar->arp_pa) == NULL) { |
07847ffa MK |
384 | splx(s); |
385 | return (ENETUNREACH); | |
386 | } | |
387 | } | |
388 | switch (cmd) { | |
389 | ||
390 | case SIOCSARP: /* set entry */ | |
391 | if (at == NULL) { | |
392 | at = arptnew(&sin->sin_addr); | |
393 | if (ar->arp_flags & ATF_PERM) { | |
394 | /* never make all entries in a bucket permanent */ | |
395 | register struct arptab *tat; | |
396 | ||
397 | /* try to re-allocate */ | |
398 | tat = arptnew(&sin->sin_addr); | |
399 | if (tat == NULL) { | |
400 | arptfree(at); | |
401 | splx(s); | |
402 | return (EADDRNOTAVAIL); | |
403 | } | |
404 | arptfree(tat); | |
405 | } | |
406 | } | |
9350cacc MK |
407 | bcopy((caddr_t)ar->arp_ha.sa_data, (caddr_t)at->at_enaddr, |
408 | sizeof(at->at_enaddr)); | |
07847ffa MK |
409 | at->at_flags = ATF_COM | ATF_INUSE | |
410 | (ar->arp_flags & (ATF_PERM|ATF_PUBL)); | |
411 | at->at_timer = 0; | |
412 | break; | |
413 | ||
414 | case SIOCDARP: /* delete entry */ | |
415 | arptfree(at); | |
416 | break; | |
417 | ||
418 | case SIOCGARP: /* get entry */ | |
9350cacc MK |
419 | bcopy((caddr_t)at->at_enaddr, (caddr_t)ar->arp_ha.sa_data, |
420 | sizeof(at->at_enaddr)); | |
07847ffa MK |
421 | ar->arp_flags = at->at_flags; |
422 | break; | |
423 | } | |
424 | splx(s); | |
425 | return (0); | |
426 | } |