Commit | Line | Data |
---|---|---|
44eb2da3 | 1 | /* if_ether.c 4.3 83/06/13 */ |
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" |
2b0e2bab SL |
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 | { | |
2b0e2bab SL |
88 | register struct arptab *at; |
89 | register i; | |
90 | ||
755d8841 | 91 | timeout(arptimer, (caddr_t)0, hz); |
2b0e2bab SL |
92 | #ifdef notdef |
93 | if (++arpt_sanity > ARPT_SANITY) { | |
755d8841 SL |
94 | register struct arpcom *ac; |
95 | ||
2b0e2bab SL |
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) | |
44eb2da3 | 135 | return; |
2b0e2bab SL |
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; | |
755d8841 SL |
140 | bzero((caddr_t)ea, sizeof (*ea)); |
141 | bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, | |
142 | sizeof (etherbroadcastaddr)); | |
2b0e2bab SL |
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); | |
755d8841 SL |
149 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, |
150 | sizeof (ea->arp_sha)); | |
2b0e2bab | 151 | bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr, |
755d8841 SL |
152 | (caddr_t)ea->arp_spa, sizeof (ea->arp_spa)); |
153 | bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa)); | |
2b0e2bab | 154 | sa.sa_family = AF_UNSPEC; |
44eb2da3 | 155 | (void) (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); |
2b0e2bab SL |
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; | |
2b0e2bab SL |
176 | struct sockaddr_in sin; |
177 | int s, lna; | |
178 | ||
179 | lna = in_lnaof(*destip); | |
180 | if (lna == INADDR_ANY) { /* broadcast address */ | |
755d8841 SL |
181 | bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten, |
182 | sizeof (etherbroadcastaddr)); | |
2b0e2bab SL |
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; | |
755d8841 | 189 | return (looutput(&loif, m, (struct sockaddr *)&sin)); |
2b0e2bab SL |
190 | } |
191 | #ifdef OLDMAP | |
192 | if (lna >= 1024) { | |
755d8841 | 193 | bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3); |
2b0e2bab SL |
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 */ | |
755d8841 | 211 | bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, 6); |
2b0e2bab SL |
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 | */ | |
755d8841 | 236 | /*ARGSUSED*/ |
2b0e2bab SL |
237 | struct in_addr |
238 | arpmyaddr(ac) | |
239 | register struct arpcom *ac; | |
240 | { | |
241 | static struct in_addr addr; | |
242 | ||
44eb2da3 SL |
243 | #ifdef lint |
244 | ac = ac; | |
245 | #endif | |
2b0e2bab SL |
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; | |
755d8841 SL |
276 | if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr, |
277 | sizeof (ac->ac_enaddr))) | |
2b0e2bab SL |
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) { | |
755d8841 SL |
290 | bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, |
291 | sizeof (ea->arp_sha)); | |
2b0e2bab SL |
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); | |
755d8841 SL |
306 | bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, |
307 | sizeof (ea->arp_sha)); | |
2b0e2bab SL |
308 | at->at_flags |= ATF_COM; |
309 | } | |
310 | if (ntohs(ea->arp_op) != ARPOP_REQUEST) | |
311 | goto out; | |
312 | reply: | |
755d8841 SL |
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)); | |
2b0e2bab SL |
321 | ea->arp_op = htons(ARPOP_REPLY); |
322 | eh = (struct ether_header *)sa.sa_data; | |
755d8841 SL |
323 | bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost, |
324 | sizeof (eh->ether_dhost)); | |
2b0e2bab SL |
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 | } |