Commit | Line | Data |
---|---|---|
b746b290 | 1 | /* if_ether.c 6.5 84/07/08 */ |
2b0e2bab SL |
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" | |
755d8841 | 13 | #include "../h/errno.h" |
07847ffa | 14 | #include "../h/ioctl.h" |
2b0e2bab SL |
15 | |
16 | #include "../net/if.h" | |
17 | #include "../netinet/in.h" | |
07847ffa MK |
18 | #include "../netinet/in_systm.h" |
19 | #include "../netinet/ip.h" | |
2b0e2bab SL |
20 | #include "../netinet/if_ether.h" |
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 | ||
07847ffa | 47 | struct ether_addr etherbroadcastaddr = {{ 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)); |
07847ffa | 108 | eh->ether_dhost = etherbroadcastaddr; |
2b0e2bab SL |
109 | eh->ether_type = ETHERPUP_ARPTYPE; /* if_output will swap */ |
110 | ea->arp_hrd = htons(ARPHRD_ETHER); | |
111 | ea->arp_pro = htons(ETHERPUP_IPTYPE); | |
07847ffa MK |
112 | ea->arp_hln = sizeof arp_sha(ea); /* hardware address length */ |
113 | ea->arp_pln = sizeof arp_spa(ea); /* protocol address length */ | |
2b0e2bab | 114 | ea->arp_op = htons(ARPOP_REQUEST); |
07847ffa MK |
115 | arp_sha(ea) = ac->ac_enaddr; |
116 | arp_spa(ea) = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr; | |
117 | arp_tpa(ea) = *addr; | |
2b0e2bab | 118 | sa.sa_family = AF_UNSPEC; |
07847ffa | 119 | return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa)); |
2b0e2bab SL |
120 | } |
121 | ||
122 | /* | |
123 | * Resolve an IP address into an ethernet address. If success, | |
124 | * desten is filled in and 1 is returned. If there is no entry | |
125 | * in arptab, set one up and broadcast a request | |
126 | * for the IP address; return 0. Hold onto this mbuf and | |
127 | * resend it once the address is finally resolved. | |
128 | * | |
129 | * We do some (conservative) locking here at splimp, since | |
130 | * arptab is also altered from input interrupt service (ecintr/ilintr | |
131 | * calls arpinput when ETHERPUP_ARPTYPE packets come in). | |
132 | */ | |
133 | arpresolve(ac, m, destip, desten) | |
134 | register struct arpcom *ac; | |
135 | struct mbuf *m; | |
136 | register struct in_addr *destip; | |
07847ffa | 137 | register struct ether_addr *desten; |
2b0e2bab SL |
138 | { |
139 | register struct arptab *at; | |
1a259c14 | 140 | register struct ifnet *ifp; |
07847ffa | 141 | register int i; |
2b0e2bab SL |
142 | struct sockaddr_in sin; |
143 | int s, lna; | |
144 | ||
145 | lna = in_lnaof(*destip); | |
146 | if (lna == INADDR_ANY) { /* broadcast address */ | |
07847ffa | 147 | *desten = etherbroadcastaddr; |
2b0e2bab SL |
148 | return (1); |
149 | } | |
1a259c14 SL |
150 | ifp = &ac->ac_if; |
151 | /* if for us, then use software loopback driver */ | |
152 | if (destip->s_addr == | |
07847ffa MK |
153 | ((struct sockaddr_in *)&ifp->if_addr)-> sin_addr.s_addr && |
154 | (loif.if_flags & IFF_UP)) { | |
2b0e2bab SL |
155 | sin.sin_family = AF_INET; |
156 | sin.sin_addr = *destip; | |
b746b290 SL |
157 | (void) looutput(&loif, m, (struct sockaddr *)&sin); |
158 | /* | |
159 | * We really don't want to indicate failure, | |
160 | * but the packet has already been sent and freed. | |
161 | */ | |
162 | return (0); | |
2b0e2bab | 163 | } |
2b0e2bab SL |
164 | s = splimp(); |
165 | ARPTAB_LOOK(at, destip->s_addr); | |
166 | if (at == 0) { /* not found */ | |
07847ffa MK |
167 | if ((ifp->if_flags & IFF_NOARP) || lna >= oldmap) { |
168 | *desten = ac->ac_enaddr; | |
169 | desten->ether_addr_octet[3] = (lna >> 16) & 0x7f; | |
170 | desten->ether_addr_octet[4] = (lna >> 8) & 0xff; | |
171 | desten->ether_addr_octet[5] = lna & 0xff; | |
172 | splx(s); | |
173 | return (1); | |
174 | } else { | |
175 | at = arptnew(destip); | |
176 | at->at_hold = m; | |
177 | arpwhohas(ac, destip); | |
178 | splx(s); | |
179 | return (0); | |
180 | } | |
2b0e2bab SL |
181 | } |
182 | at->at_timer = 0; /* restart the timer */ | |
183 | if (at->at_flags & ATF_COM) { /* entry IS complete */ | |
07847ffa | 184 | *desten = at->at_enaddr; |
2b0e2bab SL |
185 | splx(s); |
186 | return (1); | |
187 | } | |
188 | /* | |
189 | * There is an arptab entry, but no ethernet address | |
190 | * response yet. Replace the held mbuf with this | |
191 | * latest one. | |
192 | */ | |
193 | if (at->at_hold) | |
194 | m_freem(at->at_hold); | |
195 | at->at_hold = m; | |
196 | arpwhohas(ac, destip); /* ask again */ | |
197 | splx(s); | |
198 | return (0); | |
199 | } | |
200 | ||
2b0e2bab SL |
201 | /* |
202 | * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP | |
07847ffa | 203 | * is received. Algorithm is that given in RFC 826. |
2b0e2bab SL |
204 | * In addition, a sanity check is performed on the sender |
205 | * protocol address, to catch impersonators. | |
206 | */ | |
207 | arpinput(ac, m) | |
208 | register struct arpcom *ac; | |
209 | struct mbuf *m; | |
210 | { | |
211 | register struct ether_arp *ea; | |
212 | struct ether_header *eh; | |
213 | register struct arptab *at = 0; /* same as "merge" flag */ | |
214 | struct sockaddr_in sin; | |
215 | struct sockaddr sa; | |
216 | struct mbuf *mhold; | |
217 | struct in_addr isaddr,itaddr,myaddr; | |
218 | ||
219 | if (m->m_len < sizeof *ea) | |
220 | goto out; | |
07847ffa MK |
221 | if (ac->ac_if.if_flags & IFF_NOARP) |
222 | goto out; | |
2b0e2bab SL |
223 | myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr; |
224 | ea = mtod(m, struct ether_arp *); | |
225 | if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE) | |
226 | goto out; | |
07847ffa MK |
227 | isaddr = arp_spa(ea); |
228 | itaddr = arp_tpa(ea); | |
229 | if (!bcmp((caddr_t)&arp_sha(ea), (caddr_t)&ac->ac_enaddr, | |
755d8841 | 230 | sizeof (ac->ac_enaddr))) |
2b0e2bab SL |
231 | goto out; /* it's from me, ignore it. */ |
232 | if (isaddr.s_addr == myaddr.s_addr) { | |
233 | printf("duplicate IP address!! sent from ethernet address: "); | |
07847ffa MK |
234 | printf("%x %x %x %x %x %x\n", ea->arp_xsha[0], ea->arp_xsha[1], |
235 | ea->arp_xsha[2], ea->arp_xsha[3], | |
236 | ea->arp_xsha[4], ea->arp_xsha[5]); | |
237 | itaddr = myaddr; | |
2b0e2bab SL |
238 | if (ntohs(ea->arp_op) == ARPOP_REQUEST) |
239 | goto reply; | |
240 | goto out; | |
241 | } | |
242 | ARPTAB_LOOK(at, isaddr.s_addr); | |
07847ffa MK |
243 | if (at) { /* XXX ? - can overwrite ATF_PERM */ |
244 | at->at_enaddr = arp_sha(ea); | |
2b0e2bab SL |
245 | at->at_flags |= ATF_COM; |
246 | if (at->at_hold) { | |
247 | mhold = at->at_hold; | |
248 | at->at_hold = 0; | |
249 | sin.sin_family = AF_INET; | |
250 | sin.sin_addr = isaddr; | |
251 | (*ac->ac_if.if_output)(&ac->ac_if, | |
252 | mhold, (struct sockaddr *)&sin); | |
253 | } | |
07847ffa MK |
254 | } else if (itaddr.s_addr == myaddr.s_addr) { |
255 | /* ensure we have a table entry */ | |
2b0e2bab | 256 | at = arptnew(&isaddr); |
07847ffa | 257 | at->at_enaddr = arp_sha(ea); |
2b0e2bab SL |
258 | at->at_flags |= ATF_COM; |
259 | } | |
260 | if (ntohs(ea->arp_op) != ARPOP_REQUEST) | |
261 | goto out; | |
07847ffa MK |
262 | ARPTAB_LOOK(at, itaddr.s_addr); |
263 | if (at == NULL) { | |
264 | if (itaddr.s_addr != myaddr.s_addr) | |
265 | goto out; /* if I am not the target */ | |
266 | at = arptnew(&myaddr); | |
267 | at->at_enaddr = ac->ac_enaddr; | |
268 | at->at_flags |= ATF_COM; | |
269 | } | |
270 | if (itaddr.s_addr != myaddr.s_addr && (at->at_flags & ATF_PUBL) == 0) | |
271 | goto out; | |
272 | ||
2b0e2bab | 273 | reply: |
07847ffa MK |
274 | arp_tha(ea) = arp_sha(ea); |
275 | arp_tpa(ea) = arp_spa(ea); | |
276 | arp_sha(ea) = at->at_enaddr; | |
277 | arp_spa(ea) = itaddr; | |
2b0e2bab SL |
278 | ea->arp_op = htons(ARPOP_REPLY); |
279 | eh = (struct ether_header *)sa.sa_data; | |
07847ffa | 280 | eh->ether_dhost = arp_tha(ea); |
2b0e2bab SL |
281 | eh->ether_type = ETHERPUP_ARPTYPE; |
282 | sa.sa_family = AF_UNSPEC; | |
283 | (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); | |
284 | return; | |
285 | out: | |
286 | m_freem(m); | |
287 | return; | |
288 | } | |
289 | ||
290 | /* | |
291 | * Free an arptab entry. | |
292 | */ | |
293 | arptfree(at) | |
294 | register struct arptab *at; | |
295 | { | |
296 | int s = splimp(); | |
297 | ||
298 | if (at->at_hold) | |
299 | m_freem(at->at_hold); | |
300 | at->at_hold = 0; | |
301 | at->at_timer = at->at_flags = 0; | |
302 | at->at_iaddr.s_addr = 0; | |
303 | splx(s); | |
304 | } | |
305 | ||
306 | /* | |
307 | * Enter a new address in arptab, pushing out the oldest entry | |
308 | * from the bucket if there is no room. | |
07847ffa MK |
309 | * This always succeeds since no bucket can be completely filled |
310 | * with permanent entries (except from arpioctl when testing whether | |
311 | * another permanent entry). | |
2b0e2bab SL |
312 | */ |
313 | struct arptab * | |
314 | arptnew(addr) | |
315 | struct in_addr *addr; | |
316 | { | |
317 | register n; | |
318 | int oldest = 0; | |
07847ffa MK |
319 | register struct arptab *at, *ato = NULL; |
320 | static int first = 1; | |
2b0e2bab | 321 | |
07847ffa MK |
322 | if (first) { |
323 | first = 0; | |
324 | timeout(arptimer, (caddr_t)0, hz); | |
325 | } | |
326 | at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; | |
2b0e2bab SL |
327 | for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) { |
328 | if (at->at_flags == 0) | |
329 | goto out; /* found an empty entry */ | |
07847ffa MK |
330 | if (at->at_flags & ATF_PERM) |
331 | continue; | |
2b0e2bab SL |
332 | if (at->at_timer > oldest) { |
333 | oldest = at->at_timer; | |
334 | ato = at; | |
335 | } | |
336 | } | |
07847ffa MK |
337 | if (ato == NULL) |
338 | return(NULL); | |
2b0e2bab SL |
339 | at = ato; |
340 | arptfree(at); | |
341 | out: | |
342 | at->at_iaddr = *addr; | |
343 | at->at_flags = ATF_INUSE; | |
344 | return (at); | |
345 | } | |
07847ffa MK |
346 | |
347 | arpioctl(cmd, data) | |
348 | int cmd; | |
349 | caddr_t data; | |
350 | { | |
351 | register struct arpreq *ar = (struct arpreq *)data; | |
352 | register struct arptab *at; | |
353 | register struct sockaddr_in *sin; | |
354 | int s; | |
355 | ||
356 | if (ar->arp_pa.sa_family != AF_INET || | |
357 | ar->arp_ha.sa_family != AF_UNSPEC) | |
358 | return (EAFNOSUPPORT); | |
359 | sin = (struct sockaddr_in *)&ar->arp_pa; | |
360 | s = splimp(); | |
361 | ARPTAB_LOOK(at, sin->sin_addr.s_addr); | |
362 | if (at == NULL) { /* not found */ | |
363 | if (cmd != SIOCSARP) { | |
364 | splx(s); | |
365 | return (ENXIO); | |
366 | } | |
367 | if (if_ifwithnet(&ar->arp_pa) == NULL) { | |
368 | splx(s); | |
369 | return (ENETUNREACH); | |
370 | } | |
371 | } | |
372 | switch (cmd) { | |
373 | ||
374 | case SIOCSARP: /* set entry */ | |
375 | if (at == NULL) { | |
376 | at = arptnew(&sin->sin_addr); | |
377 | if (ar->arp_flags & ATF_PERM) { | |
378 | /* never make all entries in a bucket permanent */ | |
379 | register struct arptab *tat; | |
380 | ||
381 | /* try to re-allocate */ | |
382 | tat = arptnew(&sin->sin_addr); | |
383 | if (tat == NULL) { | |
384 | arptfree(at); | |
385 | splx(s); | |
386 | return (EADDRNOTAVAIL); | |
387 | } | |
388 | arptfree(tat); | |
389 | } | |
390 | } | |
391 | at->at_enaddr = *(struct ether_addr *)ar->arp_ha.sa_data; | |
392 | at->at_flags = ATF_COM | ATF_INUSE | | |
393 | (ar->arp_flags & (ATF_PERM|ATF_PUBL)); | |
394 | at->at_timer = 0; | |
395 | break; | |
396 | ||
397 | case SIOCDARP: /* delete entry */ | |
398 | arptfree(at); | |
399 | break; | |
400 | ||
401 | case SIOCGARP: /* get entry */ | |
402 | *(struct ether_addr *)ar->arp_ha.sa_data = at->at_enaddr; | |
403 | ar->arp_flags = at->at_flags; | |
404 | break; | |
405 | } | |
406 | splx(s); | |
407 | return (0); | |
408 | } |