]>
Commit | Line | Data |
---|---|---|
fdfd5971 | 1 | /* |
7af5ce03 | 2 | * Copyright (c) 2003-2012 Apple Inc. All rights reserved. |
fdfd5971 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
b7080c8e A |
29 | /* |
30 | * Copyright (c) 1984, 1993 | |
31 | * The Regents of the University of California. All rights reserved. | |
32 | * | |
33 | * This code is derived from software contributed to Berkeley by | |
34 | * Sun Microsystems, Inc. | |
35 | * | |
36 | * Redistribution and use in source and binary forms, with or without | |
37 | * modification, are permitted provided that the following conditions | |
38 | * are met: | |
39 | * 1. Redistributions of source code must retain the above copyright | |
40 | * notice, this list of conditions and the following disclaimer. | |
41 | * 2. Redistributions in binary form must reproduce the above copyright | |
42 | * notice, this list of conditions and the following disclaimer in the | |
43 | * documentation and/or other materials provided with the distribution. | |
b7080c8e A |
44 | * 4. Neither the name of the University nor the names of its contributors |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | */ | |
60 | ||
9c859447 | 61 | #if 0 |
b7080c8e | 62 | #ifndef lint |
ac2f15b3 | 63 | static char const copyright[] = |
b7080c8e A |
64 | "@(#) Copyright (c) 1984, 1993\n\ |
65 | The Regents of the University of California. All rights reserved.\n"; | |
66 | #endif /* not lint */ | |
9c859447 | 67 | #endif |
b7080c8e A |
68 | |
69 | /* | |
70 | * arp - display, set, and delete arp table entries | |
71 | */ | |
72 | ||
73 | ||
74 | #include <sys/param.h> | |
75 | #include <sys/file.h> | |
76 | #include <sys/socket.h> | |
ac2f15b3 | 77 | #include <sys/sockio.h> |
b7080c8e | 78 | #include <sys/sysctl.h> |
ac2f15b3 A |
79 | #include <sys/ioctl.h> |
80 | #include <sys/time.h> | |
b7080c8e A |
81 | |
82 | #include <net/if.h> | |
83 | #include <net/if_dl.h> | |
84 | #include <net/if_types.h> | |
85 | #include <net/route.h> | |
9c859447 A |
86 | #if 0 |
87 | #include <net/iso88025.h> | |
88 | #endif | |
b7080c8e A |
89 | |
90 | #include <netinet/in.h> | |
91 | #include <netinet/if_ether.h> | |
92 | ||
93 | #include <arpa/inet.h> | |
94 | ||
9c859447 | 95 | #include <ctype.h> |
b7080c8e A |
96 | #include <err.h> |
97 | #include <errno.h> | |
98 | #include <netdb.h> | |
99 | #include <nlist.h> | |
100 | #include <paths.h> | |
101 | #include <stdio.h> | |
102 | #include <stdlib.h> | |
9c859447 | 103 | #include <string.h> |
ac2f15b3 | 104 | #include <strings.h> |
b7080c8e A |
105 | #include <unistd.h> |
106 | ||
9c859447 A |
107 | typedef void (action_fn)(struct sockaddr_dl *sdl, |
108 | struct sockaddr_inarp *s_in, struct rt_msghdr *rtm); | |
fdfd5971 A |
109 | typedef void (action_ext_fn)(struct sockaddr_dl *sdl, |
110 | struct sockaddr_inarp *s_in, struct rt_msghdr_ext *rtm); | |
9c859447 A |
111 | |
112 | static int search(in_addr_t addr, action_fn *action); | |
fdfd5971 | 113 | static int search_ext(in_addr_t addr, action_ext_fn *action); |
9c859447 A |
114 | static action_fn print_entry; |
115 | static action_fn nuke_entry; | |
fdfd5971 | 116 | static action_ext_fn print_entry_ext; |
9c859447 | 117 | |
fdfd5971 | 118 | static char *print_lladdr(struct sockaddr_dl *); |
9c859447 A |
119 | static int delete(char *host, int do_proxy); |
120 | static void usage(void); | |
121 | static int set(int argc, char **argv); | |
122 | static int get(char *host); | |
123 | static int file(char *name); | |
124 | static struct rt_msghdr *rtmsg(int cmd, | |
125 | struct sockaddr_inarp *dst, struct sockaddr_dl *sdl); | |
126 | static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr); | |
127 | static struct sockaddr_inarp *getaddr(char *host); | |
128 | static int valid_type(int type); | |
fdfd5971 | 129 | static char *sec2str(time_t); |
9c859447 | 130 | |
ac2f15b3 | 131 | static int nflag; /* no reverse dns lookups */ |
7af5ce03 | 132 | static int xflag; /* extended link-layer reachability information */ |
9c859447 A |
133 | static char *rifname; |
134 | ||
135 | static int expire_time, flags, doing_proxy, proxy_only; | |
136 | ||
137 | static char *boundif = NULL; | |
138 | static unsigned int ifscope = 0; | |
ac2f15b3 A |
139 | |
140 | /* which function we're supposed to do */ | |
141 | #define F_GET 1 | |
142 | #define F_SET 2 | |
143 | #define F_FILESET 3 | |
144 | #define F_REPLACE 4 | |
145 | #define F_DELETE 5 | |
146 | ||
9c859447 A |
147 | #ifndef SA_SIZE |
148 | #define SA_SIZE(sa) \ | |
149 | ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \ | |
150 | sizeof(uint32_t) : \ | |
151 | 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) ) | |
152 | #endif | |
153 | ||
ac2f15b3 | 154 | #define SETFUNC(f) { if (func) usage(); func = (f); } |
b7080c8e | 155 | |
9c859447 | 156 | |
b7080c8e | 157 | int |
ac2f15b3 | 158 | main(int argc, char *argv[]) |
b7080c8e | 159 | { |
ac2f15b3 A |
160 | int ch, func = 0; |
161 | int rtn = 0; | |
9c859447 | 162 | int aflag = 0; /* do it for all entries */ |
fdfd5971 A |
163 | int lflag = 0; |
164 | uint32_t ifindex = 0; | |
b7080c8e | 165 | |
7af5ce03 | 166 | while ((ch = getopt(argc, argv, "andflsSi:x")) != -1) |
b7080c8e A |
167 | switch((char)ch) { |
168 | case 'a': | |
8052502f A |
169 | aflag = 1; |
170 | break; | |
b7080c8e | 171 | case 'd': |
ac2f15b3 A |
172 | SETFUNC(F_DELETE); |
173 | break; | |
b7080c8e A |
174 | case 'n': |
175 | nflag = 1; | |
8052502f | 176 | break; |
fdfd5971 A |
177 | case 'l': |
178 | lflag = 1; | |
179 | break; | |
ac2f15b3 A |
180 | case 'S': |
181 | SETFUNC(F_REPLACE); | |
182 | break; | |
b7080c8e | 183 | case 's': |
ac2f15b3 A |
184 | SETFUNC(F_SET); |
185 | break; | |
186 | case 'f' : | |
187 | SETFUNC(F_FILESET); | |
188 | break; | |
9c859447 A |
189 | case 'i': |
190 | rifname = optarg; | |
191 | break; | |
7af5ce03 A |
192 | case 'x': |
193 | xflag = 1; | |
194 | lflag = 1; | |
195 | break; | |
b7080c8e A |
196 | case '?': |
197 | default: | |
198 | usage(); | |
199 | } | |
ac2f15b3 A |
200 | argc -= optind; |
201 | argv += optind; | |
202 | ||
ac2f15b3 A |
203 | if (!func) |
204 | func = F_GET; | |
9c859447 A |
205 | if (rifname) { |
206 | if (func != F_GET && !(func == F_DELETE && aflag)) | |
207 | errx(1, "-i not applicable to this operation"); | |
fdfd5971 | 208 | if ((ifindex = if_nametoindex(rifname)) == 0) { |
9c859447 A |
209 | if (errno == ENXIO) |
210 | errx(1, "interface %s does not exist", rifname); | |
211 | else | |
212 | err(1, "if_nametoindex(%s)", rifname); | |
213 | } | |
214 | } | |
ac2f15b3 A |
215 | switch (func) { |
216 | case F_GET: | |
217 | if (aflag) { | |
218 | if (argc != 0) | |
219 | usage(); | |
fdfd5971 A |
220 | if (lflag) { |
221 | printf("%-23s %-17s %-9.9s %-9.9s %8.8s %4s " | |
7af5ce03 A |
222 | "%4s", "Neighbor", |
223 | "Linklayer Address", "Expire(O)", | |
224 | "Expire(I)", "Netif", "Refs", "Prbs"); | |
225 | if (xflag) | |
226 | printf(" %-7.7s %-7.7s %-7.7s", | |
227 | "RSSI", "LQM", "NPM"); | |
228 | printf("\n"); | |
fdfd5971 A |
229 | search_ext(0, print_entry_ext); |
230 | } else { | |
231 | search(0, print_entry); | |
232 | } | |
ac2f15b3 A |
233 | } else { |
234 | if (argc != 1) | |
235 | usage(); | |
9c859447 | 236 | rtn = get(argv[0]); |
ac2f15b3 A |
237 | } |
238 | break; | |
239 | case F_SET: | |
240 | case F_REPLACE: | |
241 | if (argc < 2 || argc > 6) | |
242 | usage(); | |
243 | if (func == F_REPLACE) | |
9c859447 | 244 | (void)delete(argv[0], 0); |
ac2f15b3 A |
245 | rtn = set(argc, argv) ? 1 : 0; |
246 | break; | |
247 | case F_DELETE: | |
248 | if (aflag) { | |
249 | if (argc != 0) | |
250 | usage(); | |
251 | search(0, nuke_entry); | |
252 | } else { | |
9c859447 A |
253 | int do_proxy = 0; |
254 | int i; | |
255 | ||
256 | for (i = 1; i < argc; i++) { | |
257 | if (strncmp(argv[i], "pub", sizeof("pub")) == 0) { | |
258 | do_proxy = SIN_PROXY; | |
259 | } else if (strncmp(argv[i], "ifscope", sizeof("ifscope")) == 0) { | |
260 | if (i + 1 >= argc) { | |
261 | printf("ifscope needs an interface parameter\n"); | |
262 | return (1); | |
263 | } | |
264 | boundif = argv[++i]; | |
265 | if ((ifscope = if_nametoindex(boundif)) == 0) | |
266 | errx(1, "ifscope has bad interface name: %s", boundif); | |
267 | } else { | |
268 | usage(); | |
269 | } | |
270 | } | |
fdfd5971 A |
271 | if (i > argc) |
272 | usage(); | |
9c859447 | 273 | rtn = delete(argv[0], do_proxy); |
ac2f15b3 A |
274 | } |
275 | break; | |
276 | case F_FILESET: | |
277 | if (argc != 1) | |
278 | usage(); | |
279 | rtn = file(argv[0]); | |
280 | break; | |
8052502f | 281 | } |
ac2f15b3 | 282 | |
9c859447 | 283 | return (rtn); |
b7080c8e A |
284 | } |
285 | ||
286 | /* | |
287 | * Process a file to set standard arp entries | |
288 | */ | |
9c859447 | 289 | static int |
ac2f15b3 | 290 | file(char *name) |
b7080c8e A |
291 | { |
292 | FILE *fp; | |
293 | int i, retval; | |
9c859447 | 294 | char line[128], arg[7][50], *args[7], *p; |
b7080c8e | 295 | |
ac2f15b3 | 296 | if ((fp = fopen(name, "r")) == NULL) |
9c859447 | 297 | err(1, "cannot open %s", name); |
b7080c8e A |
298 | args[0] = &arg[0][0]; |
299 | args[1] = &arg[1][0]; | |
300 | args[2] = &arg[2][0]; | |
301 | args[3] = &arg[3][0]; | |
302 | args[4] = &arg[4][0]; | |
9c859447 A |
303 | args[5] = &arg[5][0]; |
304 | args[6] = &arg[6][0]; | |
b7080c8e | 305 | retval = 0; |
9c859447 A |
306 | while(fgets(line, sizeof(line), fp) != NULL) { |
307 | if ((p = strchr(line, '#')) != NULL) | |
308 | *p = '\0'; | |
309 | for (p = line; isblank(*p); p++); | |
310 | if (*p == '\n' || *p == '\0') | |
311 | continue; | |
312 | i = sscanf(p, "%49s %49s %49s %49s %49s %49s %49s", arg[0], arg[1], | |
313 | arg[2], arg[3], arg[4], arg[5], arg[6]); | |
b7080c8e | 314 | if (i < 2) { |
ac2f15b3 | 315 | warnx("bad line: %s", line); |
b7080c8e A |
316 | retval = 1; |
317 | continue; | |
318 | } | |
319 | if (set(i, args)) | |
320 | retval = 1; | |
321 | } | |
322 | fclose(fp); | |
323 | return (retval); | |
324 | } | |
325 | ||
9c859447 A |
326 | /* |
327 | * Given a hostname, fills up a (static) struct sockaddr_inarp with | |
328 | * the address of the host and returns a pointer to the | |
329 | * structure. | |
330 | */ | |
331 | static struct sockaddr_inarp * | |
332 | getaddr(char *host) | |
ac2f15b3 | 333 | { |
9c859447 A |
334 | struct hostent *hp; |
335 | static struct sockaddr_inarp reply; | |
336 | ||
337 | bzero(&reply, sizeof(reply)); | |
338 | reply.sin_len = sizeof(reply); | |
339 | reply.sin_family = AF_INET; | |
340 | reply.sin_addr.s_addr = inet_addr(host); | |
341 | if (reply.sin_addr.s_addr == INADDR_NONE) { | |
342 | if (!(hp = gethostbyname(host))) { | |
343 | warnx("%s: %s", host, hstrerror(h_errno)); | |
344 | return (NULL); | |
345 | } | |
346 | bcopy((char *)hp->h_addr, (char *)&reply.sin_addr, | |
347 | sizeof reply.sin_addr); | |
348 | } | |
349 | return (&reply); | |
350 | } | |
351 | ||
352 | /* | |
353 | * Returns true if the type is a valid one for ARP. | |
354 | */ | |
355 | static int | |
356 | valid_type(int type) | |
357 | { | |
358 | ||
359 | switch (type) { | |
360 | case IFT_ETHER: | |
361 | case IFT_FDDI: | |
362 | case IFT_ISO88023: | |
363 | case IFT_ISO88024: | |
364 | #if 0 | |
365 | case IFT_ISO88025: | |
366 | #endif | |
367 | case IFT_L2VLAN: | |
368 | #ifdef IFT_BRIDGE | |
369 | case IFT_BRIDGE: | |
370 | #endif | |
371 | return (1); | |
372 | default: | |
373 | return (0); | |
b7080c8e A |
374 | } |
375 | } | |
376 | ||
b7080c8e | 377 | /* |
ac2f15b3 | 378 | * Set an individual arp entry |
b7080c8e | 379 | */ |
9c859447 | 380 | static int |
ac2f15b3 | 381 | set(int argc, char **argv) |
b7080c8e | 382 | { |
9c859447 A |
383 | struct sockaddr_inarp *addr; |
384 | struct sockaddr_inarp *dst; /* what are we looking for */ | |
385 | struct sockaddr_dl *sdl; | |
386 | struct rt_msghdr *rtm; | |
ac2f15b3 | 387 | struct ether_addr *ea; |
b7080c8e | 388 | char *host = argv[0], *eaddr = argv[1]; |
9c859447 | 389 | struct sockaddr_dl sdl_m; |
b7080c8e | 390 | |
b7080c8e A |
391 | argc -= 2; |
392 | argv += 2; | |
9c859447 A |
393 | |
394 | bzero(&sdl_m, sizeof(sdl_m)); | |
395 | sdl_m.sdl_len = sizeof(sdl_m); | |
396 | sdl_m.sdl_family = AF_LINK; | |
397 | ||
398 | dst = getaddr(host); | |
399 | if (dst == NULL) | |
400 | return (1); | |
ac2f15b3 | 401 | doing_proxy = flags = proxy_only = expire_time = 0; |
9c859447 A |
402 | boundif = NULL; |
403 | ifscope = 0; | |
b7080c8e | 404 | while (argc-- > 0) { |
9c859447 | 405 | if (strncmp(argv[0], "temp", sizeof("temp")) == 0) { |
ac2f15b3 A |
406 | struct timeval tv; |
407 | gettimeofday(&tv, 0); | |
408 | expire_time = tv.tv_sec + 20 * 60; | |
9c859447 | 409 | } else if (strncmp(argv[0], "pub", sizeof("pub")) == 0) { |
b7080c8e | 410 | flags |= RTF_ANNOUNCE; |
ac2f15b3 | 411 | doing_proxy = 1; |
9c859447 | 412 | if (argc && strncmp(argv[1], "only", sizeof("only")) == 0) { |
ac2f15b3 | 413 | proxy_only = 1; |
9c859447 | 414 | dst->sin_other = SIN_PROXY; |
ac2f15b3 A |
415 | argc--; argv++; |
416 | } | |
9c859447 A |
417 | } else if (strncmp(argv[0], "blackhole", sizeof("blackhole")) == 0) { |
418 | flags |= RTF_BLACKHOLE; | |
419 | } else if (strncmp(argv[0], "reject", sizeof("reject")) == 0) { | |
420 | flags |= RTF_REJECT; | |
421 | } else if (strncmp(argv[0], "trail", sizeof("trail")) == 0) { | |
422 | /* XXX deprecated and undocumented feature */ | |
b7080c8e A |
423 | printf("%s: Sending trailers is no longer supported\n", |
424 | host); | |
9c859447 A |
425 | } else if (strncmp(argv[0], "ifscope", sizeof("ifscope")) == 0) { |
426 | if (argc < 1) { | |
427 | printf("ifscope needs an interface parameter\n"); | |
428 | return (1); | |
429 | } | |
430 | boundif = argv[1]; | |
431 | if ((ifscope = if_nametoindex(boundif)) == 0) | |
432 | errx(1, "ifscope has bad interface name: %s", boundif); | |
433 | argc--; argv++; | |
b7080c8e A |
434 | } |
435 | argv++; | |
436 | } | |
ac2f15b3 A |
437 | ea = (struct ether_addr *)LLADDR(&sdl_m); |
438 | if (doing_proxy && !strcmp(eaddr, "auto")) { | |
9c859447 | 439 | if (!get_ether_addr(dst->sin_addr.s_addr, ea)) { |
ac2f15b3 | 440 | printf("no interface found for %s\n", |
9c859447 | 441 | inet_ntoa(dst->sin_addr)); |
ac2f15b3 A |
442 | return (1); |
443 | } | |
444 | sdl_m.sdl_alen = ETHER_ADDR_LEN; | |
445 | } else { | |
9c859447 A |
446 | struct ether_addr *ea1 = ether_aton(eaddr); |
447 | ||
448 | if (ea1 == NULL) { | |
449 | warnx("invalid Ethernet address '%s'", eaddr); | |
450 | return (1); | |
451 | } else { | |
452 | *ea = *ea1; | |
ac2f15b3 | 453 | sdl_m.sdl_alen = ETHER_ADDR_LEN; |
9c859447 | 454 | } |
ac2f15b3 | 455 | } |
9c859447 A |
456 | for (;;) { /* try at most twice */ |
457 | rtm = rtmsg(RTM_GET, dst, &sdl_m); | |
458 | if (rtm == NULL) { | |
459 | warn("%s", host); | |
460 | return (1); | |
461 | } | |
462 | addr = (struct sockaddr_inarp *)(rtm + 1); | |
463 | sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); | |
464 | if (addr->sin_addr.s_addr != dst->sin_addr.s_addr) | |
465 | break; | |
b7080c8e A |
466 | if (sdl->sdl_family == AF_LINK && |
467 | (rtm->rtm_flags & RTF_LLINFO) && | |
9c859447 A |
468 | !(rtm->rtm_flags & RTF_GATEWAY) && |
469 | valid_type(sdl->sdl_type) ) | |
470 | break; | |
471 | /* | |
472 | * If we asked for a scope entry and did not get one or | |
473 | * did not asked for a scope entry and got one, we can | |
474 | * proceed. | |
475 | */ | |
476 | if ((ifscope != 0) != (rtm->rtm_flags & RTF_IFSCOPE)) | |
477 | break; | |
b7080c8e A |
478 | if (doing_proxy == 0) { |
479 | printf("set: can only proxy for %s\n", host); | |
480 | return (1); | |
481 | } | |
9c859447 | 482 | if (dst->sin_other & SIN_PROXY) { |
b7080c8e | 483 | printf("set: proxy entry exists for non 802 device\n"); |
9c859447 | 484 | return (1); |
b7080c8e | 485 | } |
9c859447 | 486 | dst->sin_other = SIN_PROXY; |
ac2f15b3 | 487 | proxy_only = 1; |
b7080c8e | 488 | } |
9c859447 | 489 | |
b7080c8e A |
490 | if (sdl->sdl_family != AF_LINK) { |
491 | printf("cannot intuit interface index and type for %s\n", host); | |
492 | return (1); | |
493 | } | |
494 | sdl_m.sdl_type = sdl->sdl_type; | |
495 | sdl_m.sdl_index = sdl->sdl_index; | |
9c859447 | 496 | return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL); |
b7080c8e A |
497 | } |
498 | ||
499 | /* | |
500 | * Display an individual arp entry | |
501 | */ | |
9c859447 | 502 | static int |
ac2f15b3 | 503 | get(char *host) |
b7080c8e | 504 | { |
9c859447 A |
505 | struct sockaddr_inarp *addr; |
506 | ||
507 | addr = getaddr(host); | |
508 | if (addr == NULL) | |
509 | return (1); | |
510 | if (0 == search(addr->sin_addr.s_addr, print_entry)) { | |
511 | printf("%s (%s) -- no entry", | |
ac2f15b3 | 512 | host, inet_ntoa(addr->sin_addr)); |
9c859447 A |
513 | if (rifname) |
514 | printf(" on %s", rifname); | |
515 | printf("\n"); | |
516 | return (1); | |
b7080c8e | 517 | } |
9c859447 | 518 | return (0); |
b7080c8e A |
519 | } |
520 | ||
521 | /* | |
ac2f15b3 | 522 | * Delete an arp entry |
b7080c8e | 523 | */ |
9c859447 A |
524 | static int |
525 | delete(char *host, int do_proxy) | |
b7080c8e | 526 | { |
9c859447 A |
527 | struct sockaddr_inarp *addr, *dst; |
528 | struct rt_msghdr *rtm; | |
b7080c8e A |
529 | struct sockaddr_dl *sdl; |
530 | ||
9c859447 A |
531 | dst = getaddr(host); |
532 | if (dst == NULL) | |
533 | return (1); | |
534 | dst->sin_other = do_proxy; | |
535 | for (;;) { /* try twice */ | |
536 | rtm = rtmsg(RTM_GET, dst, NULL); | |
537 | if (rtm == NULL) { | |
538 | warn("%s", host); | |
b7080c8e A |
539 | return (1); |
540 | } | |
9c859447 A |
541 | addr = (struct sockaddr_inarp *)(rtm + 1); |
542 | sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr); | |
543 | if (addr->sin_addr.s_addr == dst->sin_addr.s_addr && | |
544 | sdl->sdl_family == AF_LINK && | |
b7080c8e | 545 | (rtm->rtm_flags & RTF_LLINFO) && |
9c859447 A |
546 | !(rtm->rtm_flags & RTF_GATEWAY) && |
547 | valid_type(sdl->sdl_type) ) | |
548 | break; /* found it */ | |
549 | if (dst->sin_other & SIN_PROXY) { | |
550 | fprintf(stderr, "delete: cannot locate %s\n",host); | |
551 | return (1); | |
b7080c8e | 552 | } |
9c859447 | 553 | dst->sin_other = SIN_PROXY; |
b7080c8e | 554 | } |
9c859447 | 555 | if (rtmsg(RTM_DELETE, dst, NULL) != NULL) { |
ac2f15b3 A |
556 | printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr)); |
557 | return (0); | |
558 | } | |
559 | return (1); | |
b7080c8e A |
560 | } |
561 | ||
562 | /* | |
ac2f15b3 | 563 | * Search the arp table and do some action on matching entries |
b7080c8e | 564 | */ |
9c859447 A |
565 | static int |
566 | search(in_addr_t addr, action_fn *action) | |
b7080c8e A |
567 | { |
568 | int mib[6]; | |
569 | size_t needed; | |
9c859447 | 570 | char *lim, *buf, *newbuf, *next; |
b7080c8e | 571 | struct rt_msghdr *rtm; |
ac2f15b3 | 572 | struct sockaddr_inarp *sin2; |
b7080c8e | 573 | struct sockaddr_dl *sdl; |
9c859447 A |
574 | char ifname[IF_NAMESIZE]; |
575 | int st, found_entry = 0; | |
b7080c8e A |
576 | |
577 | mib[0] = CTL_NET; | |
578 | mib[1] = PF_ROUTE; | |
579 | mib[2] = 0; | |
580 | mib[3] = AF_INET; | |
581 | mib[4] = NET_RT_FLAGS; | |
582 | mib[5] = RTF_LLINFO; | |
583 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) | |
9c859447 A |
584 | err(1, "route-sysctl-estimate"); |
585 | if (needed == 0) /* empty table */ | |
586 | return 0; | |
587 | buf = NULL; | |
588 | for (;;) { | |
589 | newbuf = realloc(buf, needed); | |
590 | if (newbuf == NULL) { | |
591 | if (buf != NULL) | |
592 | free(buf); | |
593 | errx(1, "could not reallocate memory"); | |
594 | } | |
595 | buf = newbuf; | |
596 | st = sysctl(mib, 6, buf, &needed, NULL, 0); | |
597 | if (st == 0 || errno != ENOMEM) | |
598 | break; | |
599 | needed += needed / 8; | |
600 | } | |
601 | if (st == -1) | |
602 | err(1, "actual retrieval of routing table"); | |
b7080c8e A |
603 | lim = buf + needed; |
604 | for (next = buf; next < lim; next += rtm->rtm_msglen) { | |
605 | rtm = (struct rt_msghdr *)next; | |
ac2f15b3 | 606 | sin2 = (struct sockaddr_inarp *)(rtm + 1); |
9c859447 A |
607 | sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); |
608 | if (rifname && if_indextoname(sdl->sdl_index, ifname) && | |
609 | strcmp(ifname, rifname)) | |
610 | continue; | |
b7080c8e | 611 | if (addr) { |
ac2f15b3 | 612 | if (addr != sin2->sin_addr.s_addr) |
b7080c8e A |
613 | continue; |
614 | found_entry = 1; | |
615 | } | |
ac2f15b3 | 616 | (*action)(sdl, sin2, rtm); |
b7080c8e | 617 | } |
ac2f15b3 | 618 | free(buf); |
9c859447 | 619 | return (found_entry); |
b7080c8e A |
620 | } |
621 | ||
ac2f15b3 A |
622 | /* |
623 | * Stolen and adapted from ifconfig | |
624 | */ | |
fdfd5971 | 625 | static char * |
ac2f15b3 A |
626 | print_lladdr(struct sockaddr_dl *sdl) |
627 | { | |
fdfd5971 | 628 | static char buf[256]; |
ac2f15b3 | 629 | char *cp; |
fdfd5971 | 630 | int n, bufsize = sizeof (buf), p = 0; |
ac2f15b3 | 631 | |
fdfd5971 | 632 | bzero(buf, sizeof (buf)); |
ac2f15b3 A |
633 | cp = (char *)LLADDR(sdl); |
634 | if ((n = sdl->sdl_alen) > 0) { | |
635 | while (--n >= 0) | |
fdfd5971 A |
636 | p += snprintf(buf + p, bufsize - p, "%x%s", |
637 | *cp++ & 0xff, n > 0 ? ":" : ""); | |
ac2f15b3 | 638 | } |
fdfd5971 | 639 | return (buf); |
ac2f15b3 A |
640 | } |
641 | ||
ac2f15b3 A |
642 | /* |
643 | * Display an arp entry | |
644 | */ | |
9c859447 | 645 | static void |
ac2f15b3 A |
646 | print_entry(struct sockaddr_dl *sdl, |
647 | struct sockaddr_inarp *addr, struct rt_msghdr *rtm) | |
b7080c8e | 648 | { |
ac2f15b3 A |
649 | const char *host; |
650 | struct hostent *hp; | |
ac2f15b3 | 651 | char ifname[IF_NAMESIZE]; |
9c859447 A |
652 | #if 0 |
653 | struct iso88025_sockaddr_dl_data *trld; | |
654 | int seg; | |
655 | #endif | |
ac2f15b3 A |
656 | |
657 | if (nflag == 0) | |
658 | hp = gethostbyaddr((caddr_t)&(addr->sin_addr), | |
659 | sizeof addr->sin_addr, AF_INET); | |
660 | else | |
661 | hp = 0; | |
662 | if (hp) | |
663 | host = hp->h_name; | |
664 | else { | |
665 | host = "?"; | |
666 | if (h_errno == TRY_AGAIN) | |
667 | nflag = 1; | |
668 | } | |
669 | printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr)); | |
670 | if (sdl->sdl_alen) { | |
9c859447 | 671 | #if 1 |
fdfd5971 | 672 | printf("%s", print_lladdr(sdl)); |
9c859447 A |
673 | #else |
674 | if ((sdl->sdl_type == IFT_ETHER || | |
675 | sdl->sdl_type == IFT_L2VLAN || | |
676 | sdl->sdl_type == IFT_BRIDGE) && | |
677 | sdl->sdl_alen == ETHER_ADDR_LEN) | |
678 | printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl))); | |
679 | else { | |
680 | int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0; | |
681 | ||
682 | printf("%s", link_ntoa(sdl) + n); | |
683 | } | |
684 | #endif | |
685 | } else | |
ac2f15b3 A |
686 | printf("(incomplete)"); |
687 | if (if_indextoname(sdl->sdl_index, ifname) != NULL) | |
688 | printf(" on %s", ifname); | |
9c859447 A |
689 | if ((rtm->rtm_flags & RTF_IFSCOPE)) |
690 | printf(" ifscope"); | |
ac2f15b3 A |
691 | if (rtm->rtm_rmx.rmx_expire == 0) |
692 | printf(" permanent"); | |
693 | if (addr->sin_other & SIN_PROXY) | |
694 | printf(" published (proxy only)"); | |
695 | if (rtm->rtm_addrs & RTA_NETMASK) { | |
696 | addr = (struct sockaddr_inarp *) | |
9c859447 | 697 | (SA_SIZE(sdl) + (char *)sdl); |
ac2f15b3 A |
698 | if (addr->sin_addr.s_addr == 0xffffffff) |
699 | printf(" published"); | |
700 | if (addr->sin_len != 8) | |
701 | printf("(weird)"); | |
702 | } | |
703 | switch(sdl->sdl_type) { | |
9c859447 | 704 | case IFT_ETHER: |
ac2f15b3 A |
705 | printf(" [ethernet]"); |
706 | break; | |
9c859447 A |
707 | #if 0 |
708 | case IFT_ISO88025: | |
ac2f15b3 A |
709 | printf(" [token-ring]"); |
710 | trld = SDL_ISO88025(sdl); | |
711 | if (trld->trld_rcf != 0) { | |
712 | printf(" rt=%x", ntohs(trld->trld_rcf)); | |
713 | for (seg = 0; | |
714 | seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2); | |
715 | seg++) | |
716 | printf(":%x", ntohs(*(trld->trld_route[seg]))); | |
717 | } | |
9c859447 | 718 | break; |
ac2f15b3 | 719 | #endif |
9c859447 A |
720 | case IFT_FDDI: |
721 | printf(" [fddi]"); | |
ac2f15b3 | 722 | break; |
9c859447 A |
723 | case IFT_ATM: |
724 | printf(" [atm]"); | |
725 | break; | |
726 | case IFT_L2VLAN: | |
ac2f15b3 A |
727 | printf(" [vlan]"); |
728 | break; | |
9c859447 A |
729 | case IFT_IEEE1394: |
730 | printf(" [firewire]"); | |
731 | break; | |
732 | #ifdef IFT_BRIDGE | |
733 | case IFT_BRIDGE: | |
734 | printf(" [bridge]"); | |
ac2f15b3 | 735 | break; |
9c859447 A |
736 | #endif |
737 | default: | |
2b484d24 | 738 | break; |
ac2f15b3 A |
739 | } |
740 | ||
741 | printf("\n"); | |
742 | ||
743 | } | |
744 | ||
745 | /* | |
746 | * Nuke an arp entry | |
747 | */ | |
9c859447 A |
748 | static void |
749 | nuke_entry(struct sockaddr_dl *sdl __unused, | |
fdfd5971 | 750 | struct sockaddr_inarp *addr, struct rt_msghdr *rtm) |
ac2f15b3 A |
751 | { |
752 | char ip[20]; | |
753 | ||
754 | snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr)); | |
fdfd5971 A |
755 | /* |
756 | * When deleting all entries, specify the interface scope of each entry | |
757 | */ | |
758 | if ((rtm->rtm_flags & RTF_IFSCOPE)) | |
759 | ifscope = rtm->rtm_index; | |
9c859447 | 760 | (void)delete(ip, 0); |
fdfd5971 | 761 | ifscope = 0; |
b7080c8e A |
762 | } |
763 | ||
9c859447 | 764 | static void |
ac2f15b3 | 765 | usage(void) |
b7080c8e | 766 | { |
ac2f15b3 | 767 | fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", |
9c859447 | 768 | "usage: arp [-n] [-i interface] hostname", |
fdfd5971 | 769 | " arp [-n] [-i interface] [-l] -a", |
9c859447 A |
770 | " arp -d hostname [pub] [ifscope interface]", |
771 | " arp -d [-i interface] -a", | |
772 | " arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", | |
773 | " arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]", | |
ac2f15b3 | 774 | " arp -f filename"); |
b7080c8e A |
775 | exit(1); |
776 | } | |
777 | ||
9c859447 A |
778 | static struct rt_msghdr * |
779 | rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl) | |
b7080c8e A |
780 | { |
781 | static int seq; | |
782 | int rlen; | |
9c859447 A |
783 | int l; |
784 | struct sockaddr_in so_mask, *so_mask_ptr = &so_mask; | |
785 | static int s = -1; | |
786 | static pid_t pid; | |
787 | ||
788 | static struct { | |
789 | struct rt_msghdr m_rtm; | |
790 | char m_space[512]; | |
791 | } m_rtmsg; | |
792 | ||
793 | struct rt_msghdr *rtm = &m_rtmsg.m_rtm; | |
794 | char *cp = m_rtmsg.m_space; | |
795 | ||
796 | if (s < 0) { /* first time: open socket, get pid */ | |
797 | s = socket(PF_ROUTE, SOCK_RAW, 0); | |
798 | if (s < 0) | |
799 | err(1, "socket"); | |
800 | pid = getpid(); | |
801 | } | |
802 | bzero(&so_mask, sizeof(so_mask)); | |
803 | so_mask.sin_len = 8; | |
804 | so_mask.sin_addr.s_addr = 0xffffffff; | |
b7080c8e A |
805 | |
806 | errno = 0; | |
9c859447 A |
807 | /* |
808 | * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer | |
809 | * appropriately (except for the mask set just above). | |
810 | */ | |
b7080c8e A |
811 | if (cmd == RTM_DELETE) |
812 | goto doit; | |
813 | bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); | |
814 | rtm->rtm_flags = flags; | |
815 | rtm->rtm_version = RTM_VERSION; | |
816 | ||
9c859447 A |
817 | /* |
818 | * Note: On RTM_GET the kernel will return a scoped route when both a scoped route and | |
819 | * a unscoped route exist. That means we cannot delete a unscoped route if there is | |
820 | * also a matching scope route | |
821 | */ | |
822 | if (ifscope) { | |
823 | rtm->rtm_index = ifscope; | |
824 | rtm->rtm_flags |= RTF_IFSCOPE; | |
825 | } | |
826 | ||
b7080c8e A |
827 | switch (cmd) { |
828 | default: | |
ac2f15b3 | 829 | errx(1, "internal wrong cmd"); |
b7080c8e A |
830 | case RTM_ADD: |
831 | rtm->rtm_addrs |= RTA_GATEWAY; | |
832 | rtm->rtm_rmx.rmx_expire = expire_time; | |
833 | rtm->rtm_inits = RTV_EXPIRE; | |
834 | rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); | |
9c859447 | 835 | dst->sin_other = 0; |
b7080c8e | 836 | if (doing_proxy) { |
ac2f15b3 | 837 | if (proxy_only) |
9c859447 | 838 | dst->sin_other = SIN_PROXY; |
b7080c8e A |
839 | else { |
840 | rtm->rtm_addrs |= RTA_NETMASK; | |
841 | rtm->rtm_flags &= ~RTF_HOST; | |
842 | } | |
843 | } | |
844 | /* FALLTHROUGH */ | |
845 | case RTM_GET: | |
846 | rtm->rtm_addrs |= RTA_DST; | |
847 | } | |
848 | #define NEXTADDR(w, s) \ | |
9c859447 A |
849 | if ((s) != NULL && rtm->rtm_addrs & (w)) { \ |
850 | bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);} | |
b7080c8e | 851 | |
9c859447 A |
852 | NEXTADDR(RTA_DST, dst); |
853 | NEXTADDR(RTA_GATEWAY, sdl); | |
854 | NEXTADDR(RTA_NETMASK, so_mask_ptr); | |
b7080c8e A |
855 | |
856 | rtm->rtm_msglen = cp - (char *)&m_rtmsg; | |
857 | doit: | |
858 | l = rtm->rtm_msglen; | |
859 | rtm->rtm_seq = ++seq; | |
860 | rtm->rtm_type = cmd; | |
861 | if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { | |
862 | if (errno != ESRCH || cmd != RTM_DELETE) { | |
ac2f15b3 | 863 | warn("writing to routing socket"); |
9c859447 | 864 | return (NULL); |
b7080c8e A |
865 | } |
866 | } | |
867 | do { | |
868 | l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); | |
869 | } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); | |
870 | if (l < 0) | |
ac2f15b3 | 871 | warn("read from routing socket"); |
9c859447 | 872 | return (rtm); |
b7080c8e | 873 | } |
ac2f15b3 A |
874 | |
875 | /* | |
876 | * get_ether_addr - get the hardware address of an interface on the | |
877 | * the same subnet as ipaddr. | |
878 | */ | |
879 | #define MAX_IFS 32 | |
880 | ||
9c859447 A |
881 | static int |
882 | get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr) | |
ac2f15b3 A |
883 | { |
884 | struct ifreq *ifr, *ifend, *ifp; | |
9c859447 | 885 | in_addr_t ina, mask; |
ac2f15b3 A |
886 | struct sockaddr_dl *dla; |
887 | struct ifreq ifreq; | |
888 | struct ifconf ifc; | |
889 | struct ifreq ifs[MAX_IFS]; | |
890 | int sock; | |
9c859447 | 891 | int retval = 0; |
ac2f15b3 A |
892 | |
893 | sock = socket(AF_INET, SOCK_DGRAM, 0); | |
894 | if (sock < 0) | |
895 | err(1, "socket"); | |
896 | ||
897 | ifc.ifc_len = sizeof(ifs); | |
898 | ifc.ifc_req = ifs; | |
899 | if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { | |
900 | warnx("ioctl(SIOCGIFCONF)"); | |
9c859447 | 901 | goto done; |
ac2f15b3 A |
902 | } |
903 | ||
9c859447 A |
904 | #define NEXTIFR(i) \ |
905 | ((struct ifreq *)((char *)&(i)->ifr_addr \ | |
906 | + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) ) | |
907 | ||
ac2f15b3 | 908 | /* |
9c859447 A |
909 | * Scan through looking for an interface with an Internet |
910 | * address on the same subnet as `ipaddr'. | |
911 | */ | |
912 | ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len); | |
913 | for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) { | |
914 | if (ifr->ifr_addr.sa_family != AF_INET) | |
915 | continue; | |
916 | strncpy(ifreq.ifr_name, ifr->ifr_name, | |
917 | sizeof(ifreq.ifr_name)); | |
918 | ifreq.ifr_addr = ifr->ifr_addr; | |
919 | /* | |
920 | * Check that the interface is up, | |
921 | * and not point-to-point or loopback. | |
922 | */ | |
923 | if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0) | |
924 | continue; | |
925 | if ((ifreq.ifr_flags & | |
926 | (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| | |
927 | IFF_LOOPBACK|IFF_NOARP)) | |
928 | != (IFF_UP|IFF_BROADCAST)) | |
929 | continue; | |
930 | /* | |
931 | * Get its netmask and check that it's on | |
932 | * the right subnet. | |
933 | */ | |
934 | if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0) | |
935 | continue; | |
936 | mask = ((struct sockaddr_in *) | |
937 | &ifreq.ifr_addr)->sin_addr.s_addr; | |
938 | ina = ((struct sockaddr_in *) | |
939 | &ifr->ifr_addr)->sin_addr.s_addr; | |
940 | if ((ipaddr & mask) == (ina & mask)) | |
941 | break; /* ok, we got it! */ | |
ac2f15b3 A |
942 | } |
943 | ||
9c859447 A |
944 | if (ifr >= ifend) |
945 | goto done; | |
ac2f15b3 A |
946 | |
947 | /* | |
9c859447 A |
948 | * Now scan through again looking for a link-level address |
949 | * for this interface. | |
950 | */ | |
ac2f15b3 | 951 | ifp = ifr; |
9c859447 A |
952 | for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr)) |
953 | if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 && | |
954 | ifr->ifr_addr.sa_family == AF_LINK) | |
955 | break; | |
956 | if (ifr >= ifend) | |
957 | goto done; | |
958 | /* | |
959 | * Found the link-level address - copy it out | |
960 | */ | |
961 | dla = (struct sockaddr_dl *) &ifr->ifr_addr; | |
962 | memcpy(hwaddr, LLADDR(dla), dla->sdl_alen); | |
963 | printf("using interface %s for proxy with address ", | |
964 | ifp->ifr_name); | |
965 | printf("%s\n", ether_ntoa(hwaddr)); | |
966 | retval = dla->sdl_alen; | |
967 | done: | |
968 | close(sock); | |
969 | return (retval); | |
ac2f15b3 | 970 | } |
fdfd5971 A |
971 | |
972 | static char * | |
973 | sec2str(total) | |
974 | time_t total; | |
975 | { | |
976 | static char result[256]; | |
977 | int days, hours, mins, secs; | |
978 | int first = 1; | |
979 | char *p = result; | |
980 | ||
981 | days = total / 3600 / 24; | |
982 | hours = (total / 3600) % 24; | |
983 | mins = (total / 60) % 60; | |
984 | secs = total % 60; | |
985 | ||
986 | if (days) { | |
987 | first = 0; | |
988 | p += snprintf(p, sizeof(result) - (p - result), "%dd", days); | |
989 | } | |
990 | if (!first || hours) { | |
991 | first = 0; | |
992 | p += snprintf(p, sizeof(result) - (p - result), "%dh", hours); | |
993 | } | |
994 | if (!first || mins) { | |
995 | first = 0; | |
996 | p += snprintf(p, sizeof(result) - (p - result), "%dm", mins); | |
997 | } | |
998 | snprintf(p, sizeof(result) - (p - result), "%ds", secs); | |
999 | ||
1000 | return(result); | |
1001 | } | |
1002 | ||
1003 | static int | |
1004 | search_ext(in_addr_t addr, action_ext_fn *action) | |
1005 | { | |
1006 | int mib[6]; | |
1007 | size_t needed; | |
1008 | char *lim, *buf, *newbuf, *next; | |
1009 | struct rt_msghdr_ext *ertm; | |
1010 | struct sockaddr_inarp *sin2; | |
1011 | struct sockaddr_dl *sdl; | |
1012 | char ifname[IF_NAMESIZE]; | |
1013 | int st, found_entry = 0; | |
1014 | ||
1015 | mib[0] = CTL_NET; | |
1016 | mib[1] = PF_ROUTE; | |
1017 | mib[2] = 0; | |
1018 | mib[3] = AF_INET; | |
1019 | mib[4] = NET_RT_DUMPX_FLAGS; | |
1020 | mib[5] = RTF_LLINFO; | |
1021 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) | |
1022 | err(1, "route-sysctl-estimate"); | |
1023 | if (needed == 0) /* empty table */ | |
1024 | return 0; | |
1025 | buf = NULL; | |
1026 | for (;;) { | |
1027 | newbuf = realloc(buf, needed); | |
1028 | if (newbuf == NULL) { | |
1029 | if (buf != NULL) | |
1030 | free(buf); | |
1031 | errx(1, "could not reallocate memory"); | |
1032 | } | |
1033 | buf = newbuf; | |
1034 | st = sysctl(mib, 6, buf, &needed, NULL, 0); | |
1035 | if (st == 0 || errno != ENOMEM) | |
1036 | break; | |
1037 | needed += needed / 8; | |
1038 | } | |
1039 | if (st == -1) | |
1040 | err(1, "actual retrieval of routing table"); | |
1041 | lim = buf + needed; | |
1042 | for (next = buf; next < lim; next += ertm->rtm_msglen) { | |
1043 | ertm = (struct rt_msghdr_ext *)next; | |
1044 | sin2 = (struct sockaddr_inarp *)(ertm + 1); | |
1045 | sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); | |
1046 | if (rifname && if_indextoname(sdl->sdl_index, ifname) && | |
1047 | strcmp(ifname, rifname)) | |
1048 | continue; | |
1049 | if (addr) { | |
1050 | if (addr != sin2->sin_addr.s_addr) | |
1051 | continue; | |
1052 | found_entry = 1; | |
1053 | } | |
1054 | (*action)(sdl, sin2, ertm); | |
1055 | } | |
1056 | free(buf); | |
1057 | return (found_entry); | |
1058 | } | |
1059 | ||
1060 | static void | |
1061 | print_entry_ext(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, | |
1062 | struct rt_msghdr_ext *ertm) | |
1063 | { | |
1064 | const char *host; | |
1065 | struct hostent *hp; | |
1066 | char ifname[IF_NAMESIZE]; | |
1067 | struct timeval time; | |
1068 | ||
1069 | if (nflag == 0) | |
1070 | hp = gethostbyaddr((caddr_t)&(addr->sin_addr), | |
1071 | sizeof (addr->sin_addr), AF_INET); | |
1072 | else | |
1073 | hp = 0; | |
1074 | ||
1075 | if (hp) | |
1076 | host = hp->h_name; | |
1077 | else | |
1078 | host = inet_ntoa(addr->sin_addr); | |
1079 | ||
1080 | printf("%-23s ", host); | |
1081 | ||
1082 | if (sdl->sdl_alen) | |
1083 | printf("%-17s ", print_lladdr(sdl)); | |
1084 | else | |
1085 | printf("%-17s ", "(incomplete)"); | |
1086 | ||
1087 | gettimeofday(&time, 0); | |
1088 | ||
1089 | if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_snd_expire == 0) | |
1090 | printf("%-9.9s ", "(none)"); | |
1091 | else if (ertm->rtm_ri.ri_snd_expire > time.tv_sec) | |
1092 | printf("%-9.9s ", | |
1093 | sec2str(ertm->rtm_ri.ri_snd_expire - time.tv_sec)); | |
1094 | else | |
1095 | printf("%-9.9s ", "expired"); | |
1096 | ||
1097 | if (ertm->rtm_ri.ri_refcnt == 0 || ertm->rtm_ri.ri_rcv_expire == 0) | |
1098 | printf("%-9.9s", "(none)"); | |
1099 | else if (ertm->rtm_ri.ri_rcv_expire > time.tv_sec) | |
1100 | printf("%-9.9s", | |
1101 | sec2str(ertm->rtm_ri.ri_rcv_expire - time.tv_sec)); | |
1102 | else | |
1103 | printf("%-9.9s", "expired"); | |
1104 | ||
1105 | if (if_indextoname(sdl->sdl_index, ifname) == NULL) | |
1106 | snprintf(ifname, sizeof (ifname), "%s", "?"); | |
1107 | printf(" %8.8s", ifname); | |
1108 | ||
1109 | if (ertm->rtm_ri.ri_refcnt) { | |
1110 | printf(" %4d", ertm->rtm_ri.ri_refcnt); | |
1111 | if (ertm->rtm_ri.ri_probes) | |
1112 | printf(" %4d", ertm->rtm_ri.ri_probes); | |
7af5ce03 A |
1113 | |
1114 | if (xflag) { | |
1115 | if (!ertm->rtm_ri.ri_probes) | |
1116 | printf(" %-4.4s", "none"); | |
1117 | ||
1118 | if (ertm->rtm_ri.ri_rssi != IFNET_RSSI_UNKNOWN) | |
1119 | printf(" %7d", ertm->rtm_ri.ri_rssi); | |
1120 | else | |
1121 | printf(" %-7.7s", "unknown"); | |
1122 | ||
1123 | switch (ertm->rtm_ri.ri_lqm) | |
1124 | { | |
1125 | case IFNET_LQM_THRESH_OFF: | |
1126 | printf(" %-7.7s", "off"); | |
1127 | break; | |
1128 | case IFNET_LQM_THRESH_UNKNOWN: | |
1129 | printf(" %-7.7s", "unknown"); | |
1130 | break; | |
1131 | case IFNET_LQM_THRESH_POOR: | |
1132 | printf(" %-7.7s", "poor"); | |
1133 | break; | |
1134 | case IFNET_LQM_THRESH_GOOD: | |
1135 | printf(" %-7.7s", "good"); | |
1136 | break; | |
1137 | default: | |
1138 | printf(" %7d", ertm->rtm_ri.ri_lqm); | |
1139 | break; | |
1140 | } | |
1141 | ||
1142 | switch (ertm->rtm_ri.ri_npm) | |
1143 | { | |
1144 | case IFNET_NPM_THRESH_UNKNOWN: | |
1145 | printf(" %-7.7s", "unknown"); | |
1146 | break; | |
1147 | case IFNET_NPM_THRESH_NEAR: | |
1148 | printf(" %-7.7s", "near"); | |
1149 | break; | |
1150 | case IFNET_NPM_THRESH_GENERAL: | |
1151 | printf(" %-7.7s", "general"); | |
1152 | break; | |
1153 | case IFNET_NPM_THRESH_FAR: | |
1154 | printf(" %-7.7s", "far"); | |
1155 | break; | |
1156 | default: | |
1157 | printf(" %7d", ertm->rtm_ri.ri_npm); | |
1158 | break; | |
1159 | } | |
1160 | } | |
fdfd5971 A |
1161 | } |
1162 | printf("\n"); | |
1163 | } |