]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1984, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Sun Microsystems, Inc. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
b7080c8e A |
16 | * 4. Neither the name of the University nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software | |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
30 | * SUCH DAMAGE. | |
31 | */ | |
32 | ||
9c859447 | 33 | #if 0 |
b7080c8e | 34 | #ifndef lint |
ac2f15b3 | 35 | static char const copyright[] = |
b7080c8e A |
36 | "@(#) Copyright (c) 1984, 1993\n\ |
37 | The Regents of the University of California. All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
ac2f15b3 | 41 | static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94"; |
b7080c8e | 42 | #endif /* not lint */ |
9c859447 A |
43 | #include <sys/cdefs.h> |
44 | __FBSDID("$FreeBSD: src/usr.sbin/arp/arp.c,v 1.65.2.1 2008/04/25 16:38:14 sam Exp $"); | |
45 | #endif | |
b7080c8e A |
46 | |
47 | /* | |
48 | * arp - display, set, and delete arp table entries | |
49 | */ | |
50 | ||
51 | ||
52 | #include <sys/param.h> | |
53 | #include <sys/file.h> | |
54 | #include <sys/socket.h> | |
ac2f15b3 | 55 | #include <sys/sockio.h> |
b7080c8e | 56 | #include <sys/sysctl.h> |
ac2f15b3 A |
57 | #include <sys/ioctl.h> |
58 | #include <sys/time.h> | |
b7080c8e A |
59 | |
60 | #include <net/if.h> | |
61 | #include <net/if_dl.h> | |
62 | #include <net/if_types.h> | |
63 | #include <net/route.h> | |
9c859447 A |
64 | #if 0 |
65 | #include <net/iso88025.h> | |
66 | #endif | |
b7080c8e A |
67 | |
68 | #include <netinet/in.h> | |
69 | #include <netinet/if_ether.h> | |
70 | ||
71 | #include <arpa/inet.h> | |
72 | ||
9c859447 | 73 | #include <ctype.h> |
b7080c8e A |
74 | #include <err.h> |
75 | #include <errno.h> | |
76 | #include <netdb.h> | |
77 | #include <nlist.h> | |
78 | #include <paths.h> | |
79 | #include <stdio.h> | |
80 | #include <stdlib.h> | |
9c859447 | 81 | #include <string.h> |
ac2f15b3 | 82 | #include <strings.h> |
b7080c8e A |
83 | #include <unistd.h> |
84 | ||
9c859447 A |
85 | typedef void (action_fn)(struct sockaddr_dl *sdl, |
86 | struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); | |
87 | ||
88 | static int search(in_addr_t addr, action_fn *action); | |
89 | static action_fn print_entry; | |
90 | static action_fn nuke_entry; | |
91 | ||
92 | static int delete(char *host, int do_proxy); | |
93 | static void usage(void); | |
94 | static int set(int argc, char **argv); | |
95 | static int get(char *host); | |
96 | static int file(char *name); | |
97 | static struct rt_msghdr *rtmsg(int cmd, | |
98 | struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); | |
99 | static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); | |
100 | static struct sockaddr_inarp *getaddr(char *host); | |
101 | static int valid_type(int type); | |
102 | ||
ac2f15b3 | 103 | static int nflag; /* no reverse dns lookups */ |
9c859447 A |
104 | static char *rifname; |
105 | ||
106 | static int expire_time, flags, doing_proxy, proxy_only; | |
107 | ||
108 | static char *boundif = NULL; | |
109 | static unsigned int ifscope = 0; | |
ac2f15b3 A |
110 | |
111 | /* which function we're supposed to do */ | |
112 | #define F_GET 1 | |
113 | #define F_SET 2 | |
114 | #define F_FILESET 3 | |
115 | #define F_REPLACE 4 | |
116 | #define F_DELETE 5 | |
117 | ||
9c859447 A |
118 | #ifndef SA_SIZE |
119 | #define SA_SIZE(sa) \ | |
120 | ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ | |
121 | sizeof(uint32_t) : \ | |
122 | 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) | |
123 | #endif | |
124 | ||
ac2f15b3 | 125 | #define SETFUNC(f) { if (func) usage(); func = (f); } |
b7080c8e | 126 | |
9c859447 | 127 | |
b7080c8e | 128 | int |
ac2f15b3 | 129 | main(int argc, char *argv[]) |
b7080c8e | 130 | { |
ac2f15b3 A |
131 | int ch, func = 0; |
132 | int rtn = 0; | |
9c859447 | 133 | int aflag = 0; /* do it for all entries */ |
b7080c8e | 134 | |
9c859447 | 135 | while ((ch = getopt(argc, argv, "andfsSi:")) != -1) |
b7080c8e A |
136 | switch((char)ch) { |
137 | case 'a': | |
8052502f A |
138 | aflag = 1; |
139 | break; | |
b7080c8e | 140 | case 'd': |
ac2f15b3 A |
141 | SETFUNC(F_DELETE); |
142 | break; | |
b7080c8e A |
143 | case 'n': |
144 | nflag = 1; | |
8052502f | 145 | break; |
ac2f15b3 A |
146 | case 'S': |
147 | SETFUNC(F_REPLACE); | |
148 | break; | |
b7080c8e | 149 | case 's': |
ac2f15b3 A |
150 | SETFUNC(F_SET); |
151 | break; | |
152 | case 'f' : | |
153 | SETFUNC(F_FILESET); | |
154 | break; | |
9c859447 A |
155 | case 'i': |
156 | rifname = optarg; | |
157 | break; | |
b7080c8e A |
158 | case '?': |
159 | default: | |
160 | usage(); | |
161 | } | |
ac2f15b3 A |
162 | argc -= optind; |
163 | argv += optind; | |
164 | ||
ac2f15b3 A |
165 | if (!func) |
166 | func = F_GET; | |
9c859447 A |
167 | if (rifname) { |
168 | if (func != F_GET && !(func == F_DELETE && aflag)) | |
169 | errx(1, "-i not applicable to this operation"); | |
170 | if (if_nametoindex(rifname) == 0) { | |
171 | if (errno == ENXIO) | |
172 | errx(1, "interface %s does not exist", rifname); | |
173 | else | |
174 | err(1, "if_nametoindex(%s)", rifname); | |
175 | } | |
176 | } | |
ac2f15b3 A |
177 | switch (func) { |
178 | case F_GET: | |
179 | if (aflag) { | |
180 | if (argc != 0) | |
181 | usage(); | |
182 | search(0, print_entry); | |
183 | } else { | |
184 | if (argc != 1) | |
185 | usage(); | |
9c859447 | 186 | rtn = get(argv[0]); |
ac2f15b3 A |
187 | } |
188 | break; | |
189 | case F_SET: | |
190 | case F_REPLACE: | |
191 | if (argc < 2 || argc > 6) | |
192 | usage(); | |
193 | if (func == F_REPLACE) | |
9c859447 | 194 | (void)delete(argv[0], 0); |
ac2f15b3 A |
195 | rtn = set(argc, argv) ? 1 : 0; |
196 | break; | |
197 | case F_DELETE: | |
198 | if (aflag) { | |
199 | if (argc != 0) | |
200 | usage(); | |
201 | search(0, nuke_entry); | |
202 | } else { | |
9c859447 A |
203 | int do_proxy = 0; |
204 | int i; | |
205 | ||
206 | for (i = 1; i < argc; i++) { | |
207 | if (strncmp(argv[i], "pub", sizeof("pub")) == 0) { | |
208 | do_proxy = SIN_PROXY; | |
209 | } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) { | |
210 | if (i + 1 >= argc) { | |
211 | printf("ifscope needs an interface parameter\n"); | |
212 | return (1); | |
213 | } | |
214 | boundif = argv[++i]; | |
215 | if ((ifscope = if_nametoindex(boundif)) == 0) | |
216 | errx(1, "ifscope has bad interface name: %s", boundif); | |
217 | } else { | |
218 | usage(); | |
219 | } | |
220 | } | |
221 | rtn = delete(argv[0], do_proxy); | |
ac2f15b3 A |
222 | } |
223 | break; | |
224 | case F_FILESET: | |
225 | if (argc != 1) | |
226 | usage(); | |
227 | rtn = file(argv[0]); | |
228 | break; | |
8052502f | 229 | } |
ac2f15b3 | 230 | |
9c859447 | 231 | return (rtn); |
b7080c8e A |
232 | } |
233 | ||
234 | /* | |
235 | * Process a file to set standard arp entries | |
236 | */ | |
9c859447 | 237 | static int |
ac2f15b3 | 238 | file(char *name) |
b7080c8e A |
239 | { |
240 | FILE *fp; | |
241 | int i, retval; | |
9c859447 | 242 | char line[128], arg[7][50], *args[7], *p; |
b7080c8e | 243 | |
ac2f15b3 | 244 | if ((fp = fopen(name, "r")) == NULL) |
9c859447 | 245 | err(1, "cannot open %s", name); |
b7080c8e A |
246 | args[0] = &arg[0][0]; |
247 | args[1] = &arg[1][0]; | |
248 | args[2] = &arg[2][0]; | |
249 | args[3] = &arg[3][0]; | |
250 | args[4] = &arg[4][0]; | |
9c859447 A |
251 | args[5] = &arg[5][0]; |
252 | args[6] = &arg[6][0]; | |
b7080c8e | 253 | retval = 0; |
9c859447 A |
254 | while(fgets(line, sizeof(line), fp) != NULL) { |
255 | if ((p = strchr(line, '#')) != NULL) | |
256 | *p = '\0'; | |
257 | for (p = line; isblank(*p); p++); | |
258 | if (*p == '\n' || *p == '\0') | |
259 | continue; | |
260 | i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1], | |
261 | arg[2], arg[3], arg[4], arg[5], arg[6]); | |
b7080c8e | 262 | if (i < 2) { |
ac2f15b3 | 263 | warnx("bad line: %s", line); |
b7080c8e A |
264 | retval = 1; |
265 | continue; | |
266 | } | |
267 | if (set(i, args)) | |
268 | retval = 1; | |
269 | } | |
270 | fclose(fp); | |
271 | return (retval); | |
272 | } | |
273 | ||
9c859447 A |
274 | /* |
275 | * Given a hostname, fills up a (static) struct sockaddr_inarp with | |
276 | * the address of the host and returns a pointer to the | |
277 | * structure. | |
278 | */ | |
279 | static struct sockaddr_inarp * | |
280 | getaddr(char *host) | |
ac2f15b3 | 281 | { |
9c859447 A |
282 | struct hostent *hp; |
283 | static struct sockaddr_inarp reply; | |
284 | ||
285 | bzero(&reply, sizeof(reply)); | |
286 | reply.sin_len = sizeof(reply); | |
287 | reply.sin_family = AF_INET; | |
288 | reply.sin_addr.s_addr = inet_addr(host); | |
289 | if (reply.sin_addr.s_addr == INADDR_NONE) { | |
290 | if (!(hp = gethostbyname(host))) { | |
291 | warnx("%s: %s", host, hstrerror(h_errno)); | |
292 | return (NULL); | |
293 | } | |
294 | bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, | |
295 | sizeof reply.sin_addr); | |
296 | } | |
297 | return (&reply); | |
298 | } | |
299 | ||
300 | /* | |
301 | * Returns true if the type is a valid one for ARP. | |
302 | */ | |
303 | static int | |
304 | valid_type(int type) | |
305 | { | |
306 | ||
307 | switch (type) { | |
308 | case IFT_ETHER: | |
309 | case IFT_FDDI: | |
310 | case IFT_ISO88023: | |
311 | case IFT_ISO88024: | |
312 | #if 0 | |
313 | case IFT_ISO88025: | |
314 | #endif | |
315 | case IFT_L2VLAN: | |
316 | #ifdef IFT_BRIDGE | |
317 | case IFT_BRIDGE: | |
318 | #endif | |
319 | return (1); | |
320 | default: | |
321 | return (0); | |
b7080c8e A |
322 | } |
323 | } | |
324 | ||
b7080c8e | 325 | /* |
ac2f15b3 | 326 | * Set an individual arp entry |
b7080c8e | 327 | */ |
9c859447 | 328 | static int |
ac2f15b3 | 329 | set(int argc, char **argv) |
b7080c8e | 330 | { |
9c859447 A |
331 | struct sockaddr_inarp *addr; |
332 | struct sockaddr_inarp *dst; /* what are we looking for */ | |
333 | struct sockaddr_dl *sdl; | |
334 | struct rt_msghdr *rtm; | |
ac2f15b3 | 335 | struct ether_addr *ea; |
b7080c8e | 336 | char *host = argv[0], *eaddr = argv[1]; |
9c859447 | 337 | struct sockaddr_dl sdl_m; |
b7080c8e | 338 | |
b7080c8e A |
339 | argc -= 2; |
340 | argv += 2; | |
9c859447 A |
341 | |
342 | bzero(&sdl_m, sizeof(sdl_m)); | |
343 | sdl_m.sdl_len = sizeof(sdl_m); | |
344 | sdl_m.sdl_family = AF_LINK; | |
345 | ||
346 | dst = getaddr(host); | |
347 | if (dst == NULL) | |
348 | return (1); | |
ac2f15b3 | 349 | doing_proxy = flags = proxy_only = expire_time = 0; |
9c859447 A |
350 | boundif = NULL; |
351 | ifscope = 0; | |
b7080c8e | 352 | while (argc-- > 0) { |
9c859447 | 353 | if (strncmp(argv[0], "temp", sizeof("temp")) == 0) { |
ac2f15b3 A |
354 | struct timeval tv; |
355 | gettimeofday(&tv, 0); | |
356 | expire_time = tv.tv_sec + 20 * 60; | |
9c859447 | 357 | } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) { |
b7080c8e | 358 | flags |= RTF_ANNOUNCE; |
ac2f15b3 | 359 | doing_proxy = 1; |
9c859447 | 360 | if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) { |
ac2f15b3 | 361 | proxy_only = 1; |
9c859447 | 362 | dst->sin_other = SIN_PROXY; |
ac2f15b3 A |
363 | argc--; argv++; |
364 | } | |
9c859447 A |
365 | } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) { |
366 | flags |= RTF_BLACKHOLE; | |
367 | } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) { | |
368 | flags |= RTF_REJECT; | |
369 | } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) { | |
370 | /* XXX deprecated and undocumented feature */ | |
b7080c8e A |
371 | printf("%s: Sending trailers is no longer supported\n", |
372 | host); | |
9c859447 A |
373 | } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) { |
374 | if (argc < 1) { | |
375 | printf("ifscope needs an interface parameter\n"); | |
376 | return (1); | |
377 | } | |
378 | boundif = argv[1]; | |
379 | if ((ifscope = if_nametoindex(boundif)) == 0) | |
380 | errx(1, "ifscope has bad interface name: %s", boundif); | |
381 | argc--; argv++; | |
b7080c8e A |
382 | } |
383 | argv++; | |
384 | } | |
ac2f15b3 A |
385 | ea = (struct ether_addr *)LLADDR(&sdl_m); |
386 | if (doing_proxy && !strcmp(eaddr, "auto")) { | |
9c859447 | 387 | if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { |
ac2f15b3 | 388 | printf("no interface found for %s\n", |
9c859447 | 389 | inet_ntoa(dst->sin_addr)); |
ac2f15b3 A |
390 | return (1); |
391 | } | |
392 | sdl_m.sdl_alen = ETHER_ADDR_LEN; | |
393 | } else { | |
9c859447 A |
394 | struct ether_addr *ea1 = ether_aton(eaddr); |
395 | ||
396 | if (ea1 == NULL) { | |
397 | warnx("invalid Ethernet address '%s'", eaddr); | |
398 | return (1); | |
399 | } else { | |
400 | *ea = *ea1; | |
ac2f15b3 | 401 | sdl_m.sdl_alen = ETHER_ADDR_LEN; |
9c859447 | 402 | } |
ac2f15b3 | 403 | } |
9c859447 A |
404 | for (;;) { /* try at most twice */ |
405 | rtm = rtmsg(RTM_GET, dst, &sdl_m); | |
406 | if (rtm == NULL) { | |
407 | warn("%s", host); | |
408 | return (1); | |
409 | } | |
410 | addr = (struct sockaddr_inarp *)(rtm + 1); | |
411 | sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); | |
412 | if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) | |
413 | break; | |
b7080c8e A |
414 | if (sdl->sdl_family == AF_LINK && |
415 | (rtm->rtm_flags & RTF_LLINFO) && | |
9c859447 A |
416 | !(rtm->rtm_flags & RTF_GATEWAY) && |
417 | valid_type(sdl->sdl_type) ) | |
418 | break; | |
419 | /* | |
420 | * If we asked for a scope entry and did not get one or | |
421 | * did not asked for a scope entry and got one, we can | |
422 | * proceed. | |
423 | */ | |
424 | if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE)) | |
425 | break; | |
b7080c8e A |
426 | if (doing_proxy == 0) { |
427 | printf("set: can only proxy for %s\n", host); | |
428 | return (1); | |
429 | } | |
9c859447 | 430 | if (dst->sin_other & SIN_PROXY) { |
b7080c8e | 431 | printf("set: proxy entry exists for non 802 device\n"); |
9c859447 | 432 | return (1); |
b7080c8e | 433 | } |
9c859447 | 434 | dst->sin_other = SIN_PROXY; |
ac2f15b3 | 435 | proxy_only = 1; |
b7080c8e | 436 | } |
9c859447 | 437 | |
b7080c8e A |
438 | if (sdl->sdl_family != AF_LINK) { |
439 | printf("cannot intuit interface index and type for %s\n", host); | |
440 | return (1); | |
441 | } | |
442 | sdl_m.sdl_type = sdl->sdl_type; | |
443 | sdl_m.sdl_index = sdl->sdl_index; | |
9c859447 | 444 | return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); |
b7080c8e A |
445 | } |
446 | ||
447 | /* | |
448 | * Display an individual arp entry | |
449 | */ | |
9c859447 | 450 | static int |
ac2f15b3 | 451 | get(char *host) |
b7080c8e | 452 | { |
9c859447 A |
453 | struct sockaddr_inarp *addr; |
454 | ||
455 | addr = getaddr(host); | |
456 | if (addr == NULL) | |
457 | return (1); | |
458 | if (0 == search(addr->sin_addr.s_addr, print_entry)) { | |
459 | printf("%s (%s) -- no entry", | |
ac2f15b3 | 460 | host, inet_ntoa(addr->sin_addr)); |
9c859447 A |
461 | if (rifname) |
462 | printf(" on %s", rifname); | |
463 | printf("\n"); | |
464 | return (1); | |
b7080c8e | 465 | } |
9c859447 | 466 | return (0); |
b7080c8e A |
467 | } |
468 | ||
469 | /* | |
ac2f15b3 | 470 | * Delete an arp entry |
b7080c8e | 471 | */ |
9c859447 A |
472 | static int |
473 | delete(char *host, int do_proxy) | |
b7080c8e | 474 | { |
9c859447 A |
475 | struct sockaddr_inarp *addr, *dst; |
476 | struct rt_msghdr *rtm; | |
b7080c8e A |
477 | struct sockaddr_dl *sdl; |
478 | ||
9c859447 A |
479 | dst = getaddr(host); |
480 | if (dst == NULL) | |
481 | return (1); | |
482 | dst->sin_other = do_proxy; | |
483 | for (;;) { /* try twice */ | |
484 | rtm = rtmsg(RTM_GET, dst, NULL); | |
485 | if (rtm == NULL) { | |
486 | warn("%s", host); | |
b7080c8e A |
487 | return (1); |
488 | } | |
9c859447 A |
489 | addr = (struct sockaddr_inarp *)(rtm + 1); |
490 | sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); | |
491 | if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && | |
492 | sdl->sdl_family == AF_LINK && | |
b7080c8e | 493 | (rtm->rtm_flags & RTF_LLINFO) && |
9c859447 A |
494 | !(rtm->rtm_flags & RTF_GATEWAY) && |
495 | valid_type(sdl->sdl_type) ) | |
496 | break; /* found it */ | |
497 | if (dst->sin_other & SIN_PROXY) { | |
498 | fprintf(stderr, "delete: cannot locate %s\n",host); | |
499 | return (1); | |
b7080c8e | 500 | } |
9c859447 | 501 | dst->sin_other = SIN_PROXY; |
b7080c8e | 502 | } |
9c859447 | 503 | if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { |
ac2f15b3 A |
504 | printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); |
505 | return (0); | |
506 | } | |
507 | return (1); | |
b7080c8e A |
508 | } |
509 | ||
510 | /* | |
ac2f15b3 | 511 | * Search the arp table and do some action on matching entries |
b7080c8e | 512 | */ |
9c859447 A |
513 | static int |
514 | search(in_addr_t addr, action_fn *action) | |
b7080c8e A |
515 | { |
516 | int mib[6]; | |
517 | size_t needed; | |
9c859447 | 518 | char *lim, *buf, *newbuf, *next; |
b7080c8e | 519 | struct rt_msghdr *rtm; |
ac2f15b3 | 520 | struct sockaddr_inarp *sin2; |
b7080c8e | 521 | struct sockaddr_dl *sdl; |
9c859447 A |
522 | char ifname[IF_NAMESIZE]; |
523 | int st, found_entry = 0; | |
b7080c8e A |
524 | |
525 | mib[0] = CTL_NET; | |
526 | mib[1] = PF_ROUTE; | |
527 | mib[2] = 0; | |
528 | mib[3] = AF_INET; | |
529 | mib[4] = NET_RT_FLAGS; | |
530 | mib[5] = RTF_LLINFO; | |
531 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) | |
9c859447 A |
532 | err(1, "route-sysctl-estimate"); |
533 | if (needed == 0) /* empty table */ | |
534 | return 0; | |
535 | buf = NULL; | |
536 | for (;;) { | |
537 | newbuf = realloc(buf, needed); | |
538 | if (newbuf == NULL) { | |
539 | if (buf != NULL) | |
540 | free(buf); | |
541 | errx(1, "could not reallocate memory"); | |
542 | } | |
543 | buf = newbuf; | |
544 | st = sysctl(mib, 6, buf, &needed, NULL, 0); | |
545 | if (st == 0 || errno != ENOMEM) | |
546 | break; | |
547 | needed += needed / 8; | |
548 | } | |
549 | if (st == -1) | |
550 | err(1, "actual retrieval of routing table"); | |
b7080c8e A |
551 | lim = buf + needed; |
552 | for (next = buf; next < lim; next += rtm->rtm_msglen) { | |
553 | rtm = (struct rt_msghdr *)next; | |
ac2f15b3 | 554 | sin2 = (struct sockaddr_inarp *)(rtm + 1); |
9c859447 A |
555 | sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); |
556 | if (rifname && if_indextoname(sdl->sdl_index, ifname) && | |
557 | strcmp(ifname, rifname)) | |
558 | continue; | |
b7080c8e | 559 | if (addr) { |
ac2f15b3 | 560 | if (addr != sin2->sin_addr.s_addr) |
b7080c8e A |
561 | continue; |
562 | found_entry = 1; | |
563 | } | |
ac2f15b3 | 564 | (*action)(sdl, sin2, rtm); |
b7080c8e | 565 | } |
ac2f15b3 | 566 | free(buf); |
9c859447 | 567 | return (found_entry); |
b7080c8e A |
568 | } |
569 | ||
ac2f15b3 A |
570 | /* |
571 | * Stolen and adapted from ifconfig | |
572 | */ | |
573 | static void | |
574 | print_lladdr(struct sockaddr_dl *sdl) | |
575 | { | |
576 | char *cp; | |
577 | int n; | |
578 | ||
579 | cp = (char *)LLADDR(sdl); | |
580 | if ((n = sdl->sdl_alen) > 0) { | |
581 | while (--n >= 0) | |
582 | printf("%x%s",*cp++ & 0xff, n>0? ":" : ""); | |
583 | } | |
584 | } | |
585 | ||
586 | ||
587 | /* | |
588 | * Display an arp entry | |
589 | */ | |
9c859447 | 590 | static void |
ac2f15b3 A |
591 | print_entry(struct sockaddr_dl *sdl, |
592 | struct sockaddr_inarp *addr, struct rt_msghdr *rtm) | |
b7080c8e | 593 | { |
ac2f15b3 A |
594 | const char *host; |
595 | struct hostent *hp; | |
ac2f15b3 | 596 | char ifname[IF_NAMESIZE]; |
9c859447 A |
597 | #if 0 |
598 | struct iso88025_sockaddr_dl_data *trld; | |
599 | int seg; | |
600 | #endif | |
ac2f15b3 A |
601 | |
602 | if (nflag == 0) | |
603 | hp = gethostbyaddr((caddr_t)&(addr->sin_addr), | |
604 | sizeof addr->sin_addr, AF_INET); | |
605 | else | |
606 | hp = 0; | |
607 | if (hp) | |
608 | host = hp->h_name; | |
609 | else { | |
610 | host = "?"; | |
611 | if (h_errno == TRY_AGAIN) | |
612 | nflag = 1; | |
613 | } | |
614 | printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); | |
615 | if (sdl->sdl_alen) { | |
9c859447 | 616 | #if 1 |
ac2f15b3 | 617 | print_lladdr(sdl); |
9c859447 A |
618 | #else |
619 | if ((sdl->sdl_type == IFT_ETHER || | |
620 | sdl->sdl_type == IFT_L2VLAN || | |
621 | sdl->sdl_type == IFT_BRIDGE) && | |
622 | sdl->sdl_alen == ETHER_ADDR_LEN) | |
623 | printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); | |
624 | else { | |
625 | int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; | |
626 | ||
627 | printf("%s", link_ntoa(sdl) + n); | |
628 | } | |
629 | #endif | |
630 | } else | |
ac2f15b3 A |
631 | printf("(incomplete)"); |
632 | if (if_indextoname(sdl->sdl_index, ifname) != NULL) | |
633 | printf(" on %s", ifname); | |
9c859447 A |
634 | if ((rtm->rtm_flags & RTF_IFSCOPE)) |
635 | printf(" ifscope"); | |
ac2f15b3 A |
636 | if (rtm->rtm_rmx.rmx_expire == 0) |
637 | printf(" permanent"); | |
638 | if (addr->sin_other & SIN_PROXY) | |
639 | printf(" published (proxy only)"); | |
640 | if (rtm->rtm_addrs & RTA_NETMASK) { | |
641 | addr = (struct sockaddr_inarp *) | |
9c859447 | 642 | (SA_SIZE(sdl) + (char *)sdl); |
ac2f15b3 A |
643 | if (addr->sin_addr.s_addr == 0xffffffff) |
644 | printf(" published"); | |
645 | if (addr->sin_len != 8) | |
646 | printf("(weird)"); | |
647 | } | |
648 | switch(sdl->sdl_type) { | |
9c859447 | 649 | case IFT_ETHER: |
ac2f15b3 A |
650 | printf(" [ethernet]"); |
651 | break; | |
9c859447 A |
652 | #if 0 |
653 | case IFT_ISO88025: | |
ac2f15b3 A |
654 | printf(" [token-ring]"); |
655 | trld = SDL_ISO88025(sdl); | |
656 | if (trld->trld_rcf != 0) { | |
657 | printf(" rt=%x", ntohs(trld->trld_rcf)); | |
658 | for (seg = 0; | |
659 | seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); | |
660 | seg++) | |
661 | printf(":%x", ntohs(*(trld->trld_route[seg]))); | |
662 | } | |
9c859447 | 663 | break; |
ac2f15b3 | 664 | #endif |
9c859447 A |
665 | case IFT_FDDI: |
666 | printf(" [fddi]"); | |
ac2f15b3 | 667 | break; |
9c859447 A |
668 | case IFT_ATM: |
669 | printf(" [atm]"); | |
670 | break; | |
671 | case IFT_L2VLAN: | |
ac2f15b3 A |
672 | printf(" [vlan]"); |
673 | break; | |
9c859447 A |
674 | case IFT_IEEE1394: |
675 | printf(" [firewire]"); | |
676 | break; | |
677 | #ifdef IFT_BRIDGE | |
678 | case IFT_BRIDGE: | |
679 | printf(" [bridge]"); | |
ac2f15b3 | 680 | break; |
9c859447 A |
681 | #endif |
682 | default: | |
2b484d24 | 683 | break; |
ac2f15b3 A |
684 | } |
685 | ||
686 | printf("\n"); | |
687 | ||
688 | } | |
689 | ||
690 | /* | |
691 | * Nuke an arp entry | |
692 | */ | |
9c859447 A |
693 | static void |
694 | nuke_entry(struct sockaddr_dl *sdl __unused, | |
695 | struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused) | |
ac2f15b3 A |
696 | { |
697 | char ip[20]; | |
698 | ||
699 | snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); | |
9c859447 | 700 | (void)delete(ip, 0); |
b7080c8e A |
701 | } |
702 | ||
9c859447 | 703 | static void |
ac2f15b3 | 704 | usage(void) |
b7080c8e | 705 | { |
ac2f15b3 | 706 | fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", |
9c859447 A |
707 | "usage: arp [-n] [-i interface] hostname", |
708 | " arp [-n] [-i interface] -a", | |
709 | " arp -d hostname [pub] [ifscope interface]", | |
710 | " arp -d [-i interface] -a", | |
711 | " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", | |
712 | " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", | |
ac2f15b3 | 713 | " arp -f filename"); |
b7080c8e A |
714 | exit(1); |
715 | } | |
716 | ||
9c859447 A |
717 | static struct rt_msghdr * |
718 | rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) | |
b7080c8e A |
719 | { |
720 | static int seq; | |
721 | int rlen; | |
9c859447 A |
722 | int l; |
723 | struct sockaddr_in so_mask, *so_mask_ptr = &so_mask; | |
724 | static int s = -1; | |
725 | static pid_t pid; | |
726 | ||
727 | static struct { | |
728 | struct rt_msghdr m_rtm; | |
729 | char m_space[512]; | |
730 | } m_rtmsg; | |
731 | ||
732 | struct rt_msghdr *rtm = &m_rtmsg.m_rtm; | |
733 | char *cp = m_rtmsg.m_space; | |
734 | ||
735 | if (s < 0) { /* first time: open socket, get pid */ | |
736 | s = socket(PF_ROUTE, SOCK_RAW, 0); | |
737 | if (s < 0) | |
738 | err(1, "socket"); | |
739 | pid = getpid(); | |
740 | } | |
741 | bzero(&so_mask, sizeof(so_mask)); | |
742 | so_mask.sin_len = 8; | |
743 | so_mask.sin_addr.s_addr = 0xffffffff; | |
b7080c8e A |
744 | |
745 | errno = 0; | |
9c859447 A |
746 | /* |
747 | * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer | |
748 | * appropriately (except for the mask set just above). | |
749 | */ | |
b7080c8e A |
750 | if (cmd == RTM_DELETE) |
751 | goto doit; | |
752 | bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); | |
753 | rtm->rtm_flags = flags; | |
754 | rtm->rtm_version = RTM_VERSION; | |
755 | ||
9c859447 A |
756 | /* |
757 | * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and | |
758 | * a unscoped route exist. That means we cannot delete a unscoped route if there is | |
759 | * also a matching scope route | |
760 | */ | |
761 | if (ifscope) { | |
762 | rtm->rtm_index = ifscope; | |
763 | rtm->rtm_flags |= RTF_IFSCOPE; | |
764 | } | |
765 | ||
b7080c8e A |
766 | switch (cmd) { |
767 | default: | |
ac2f15b3 | 768 | errx(1, "internal wrong cmd"); |
b7080c8e A |
769 | case RTM_ADD: |
770 | rtm->rtm_addrs |= RTA_GATEWAY; | |
771 | rtm->rtm_rmx.rmx_expire = expire_time; | |
772 | rtm->rtm_inits = RTV_EXPIRE; | |
773 | rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); | |
9c859447 | 774 | dst->sin_other = 0; |
b7080c8e | 775 | if (doing_proxy) { |
ac2f15b3 | 776 | if (proxy_only) |
9c859447 | 777 | dst->sin_other = SIN_PROXY; |
b7080c8e A |
778 | else { |
779 | rtm->rtm_addrs |= RTA_NETMASK; | |
780 | rtm->rtm_flags &= ~RTF_HOST; | |
781 | } | |
782 | } | |
783 | /* FALLTHROUGH */ | |
784 | case RTM_GET: | |
785 | rtm->rtm_addrs |= RTA_DST; | |
786 | } | |
787 | #define NEXTADDR(w, s) \ | |
9c859447 A |
788 | if ((s) != NULL && rtm->rtm_addrs & (w)) { \ |
789 | bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} | |
b7080c8e | 790 | |
9c859447 A |
791 | NEXTADDR(RTA_DST, dst); |
792 | NEXTADDR(RTA_GATEWAY, sdl); | |
793 | NEXTADDR(RTA_NETMASK, so_mask_ptr); | |
b7080c8e A |
794 | |
795 | rtm->rtm_msglen = cp - (char *)&m_rtmsg; | |
796 | doit: | |
797 | l = rtm->rtm_msglen; | |
798 | rtm->rtm_seq = ++seq; | |
799 | rtm->rtm_type = cmd; | |
800 | if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { | |
801 | if (errno != ESRCH || cmd != RTM_DELETE) { | |
ac2f15b3 | 802 | warn("writing to routing socket"); |
9c859447 | 803 | return (NULL); |
b7080c8e A |
804 | } |
805 | } | |
806 | do { | |
807 | l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); | |
808 | } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); | |
809 | if (l < 0) | |
ac2f15b3 | 810 | warn("read from routing socket"); |
9c859447 | 811 | return (rtm); |
b7080c8e | 812 | } |
ac2f15b3 A |
813 | |
814 | /* | |
815 | * get_ether_addr - get the hardware address of an interface on the | |
816 | * the same subnet as ipaddr. | |
817 | */ | |
818 | #define MAX_IFS 32 | |
819 | ||
9c859447 A |
820 | static int |
821 | get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) | |
ac2f15b3 A |
822 | { |
823 | struct ifreq *ifr, *ifend, *ifp; | |
9c859447 | 824 | in_addr_t ina, mask; |
ac2f15b3 A |
825 | struct sockaddr_dl *dla; |
826 | struct ifreq ifreq; | |
827 | struct ifconf ifc; | |
828 | struct ifreq ifs[MAX_IFS]; | |
829 | int sock; | |
9c859447 | 830 | int retval = 0; |
ac2f15b3 A |
831 | |
832 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
833 | if (sock < 0) | |
834 | err(1, "socket"); | |
835 | ||
836 | ifc.ifc_len = sizeof(ifs); | |
837 | ifc.ifc_req = ifs; | |
838 | if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { | |
839 | warnx("ioctl(SIOCGIFCONF)"); | |
9c859447 | 840 | goto done; |
ac2f15b3 A |
841 | } |
842 | ||
9c859447 A |
843 | #define NEXTIFR(i) \ |
844 | ((struct ifreq *)((char *)&(i)->ifr_addr \ | |
845 | + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) | |
846 | ||
ac2f15b3 | 847 | /* |
9c859447 A |
848 | * Scan through looking for an interface with an Internet |
849 | * address on the same subnet as `ipaddr'. | |
850 | */ | |
851 | ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); | |
852 | for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { | |
853 | if (ifr->ifr_addr.sa_family != AF_INET) | |
854 | continue; | |
855 | strncpy(ifreq.ifr_name, ifr->ifr_name, | |
856 | sizeof(ifreq.ifr_name)); | |
857 | ifreq.ifr_addr = ifr->ifr_addr; | |
858 | /* | |
859 | * Check that the interface is up, | |
860 | * and not point-to-point or loopback. | |
861 | */ | |
862 | if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) | |
863 | continue; | |
864 | if ((ifreq.ifr_flags & | |
865 | (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| | |
866 | IFF_LOOPBACK|IFF_NOARP)) | |
867 | != (IFF_UP|IFF_BROADCAST)) | |
868 | continue; | |
869 | /* | |
870 | * Get its netmask and check that it's on | |
871 | * the right subnet. | |
872 | */ | |
873 | if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) | |
874 | continue; | |
875 | mask = ((struct sockaddr_in *) | |
876 | &ifreq.ifr_addr)->sin_addr.s_addr; | |
877 | ina = ((struct sockaddr_in *) | |
878 | &ifr->ifr_addr)->sin_addr.s_addr; | |
879 | if ((ipaddr & mask) == (ina & mask)) | |
880 | break; /* ok, we got it! */ | |
ac2f15b3 A |
881 | } |
882 | ||
9c859447 A |
883 | if (ifr >= ifend) |
884 | goto done; | |
ac2f15b3 A |
885 | |
886 | /* | |
9c859447 A |
887 | * Now scan through again looking for a link-level address |
888 | * for this interface. | |
889 | */ | |
ac2f15b3 | 890 | ifp = ifr; |
9c859447 A |
891 | for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) |
892 | if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && | |
893 | ifr->ifr_addr.sa_family == AF_LINK) | |
894 | break; | |
895 | if (ifr >= ifend) | |
896 | goto done; | |
897 | /* | |
898 | * Found the link-level address - copy it out | |
899 | */ | |
900 | dla = (struct sockaddr_dl *) &ifr->ifr_addr; | |
901 | memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); | |
902 | printf("using interface %s for proxy with address ", | |
903 | ifp->ifr_name); | |
904 | printf("%s\n", ether_ntoa(hwaddr)); | |
905 | retval = dla->sdl_alen; | |
906 | done: | |
907 | close(sock); | |
908 | return (retval); | |
ac2f15b3 | 909 | } |