Include file changes. No more ../h
[unix-history] / usr / src / sys / netinet / if_ether.c
CommitLineData
20666ad3 1/* if_ether.c 6.6 84/08/29 */
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)
25struct arptab arptab[ARPTAB_SIZE];
07847ffa 26int 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
40int 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 47struct ether_addr etherbroadcastaddr = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }};
2b0e2bab
SL
48extern 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 */
63int oldmap = 1024;
2b0e2bab 64
2b0e2bab
SL
65/*
66 * Timeout routine. Age arp_tab entries once a minute.
67 */
68arptimer()
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 */
92arpwhohas(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 */
133arpresolve(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 */
207arpinput(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 273reply:
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;
285out:
286 m_freem(m);
287 return;
288}
289
290/*
291 * Free an arptab entry.
292 */
293arptfree(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 */
313struct arptab *
314arptnew(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);
341out:
342 at->at_iaddr = *addr;
343 at->at_flags = ATF_INUSE;
344 return (at);
345}
07847ffa
MK
346
347arpioctl(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}