File: | src/usr.sbin/ndp/ndp.c |
Warning: | line 347, column 7 1st function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ndp.c,v 1.103 2021/03/02 05:34:20 jsg Exp $ */ | |||
2 | /* $KAME: ndp.c,v 1.101 2002/07/17 08:46:33 itojun Exp $ */ | |||
3 | ||||
4 | /* | |||
5 | * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. | |||
6 | * All rights reserved. | |||
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. | |||
16 | * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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 | /* | |||
33 | * Copyright (c) 1984, 1993 | |||
34 | * The Regents of the University of California. All rights reserved. | |||
35 | * | |||
36 | * This code is derived from software contributed to Berkeley by | |||
37 | * Sun Microsystems, Inc. | |||
38 | * | |||
39 | * Redistribution and use in source and binary forms, with or without | |||
40 | * modification, are permitted provided that the following conditions | |||
41 | * are met: | |||
42 | * 1. Redistributions of source code must retain the above copyright | |||
43 | * notice, this list of conditions and the following disclaimer. | |||
44 | * 2. Redistributions in binary form must reproduce the above copyright | |||
45 | * notice, this list of conditions and the following disclaimer in the | |||
46 | * documentation and/or other materials provided with the distribution. | |||
47 | * 3. Neither the name of the University nor the names of its contributors | |||
48 | * may be used to endorse or promote products derived from this software | |||
49 | * without specific prior written permission. | |||
50 | * | |||
51 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||
52 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
53 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
54 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||
55 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
56 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
57 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
59 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
60 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
61 | * SUCH DAMAGE. | |||
62 | */ | |||
63 | ||||
64 | /* | |||
65 | * Based on: | |||
66 | * "@(#) Copyright (c) 1984, 1993\n\ | |||
67 | * The Regents of the University of California. All rights reserved.\n"; | |||
68 | * | |||
69 | * "@(#)arp.c 8.2 (Berkeley) 1/2/94"; | |||
70 | */ | |||
71 | ||||
72 | /* | |||
73 | * ndp - display, set, delete and flush neighbor cache | |||
74 | */ | |||
75 | ||||
76 | ||||
77 | #include <sys/ioctl.h> | |||
78 | #include <sys/socket.h> | |||
79 | #include <sys/sysctl.h> | |||
80 | #include <sys/time.h> | |||
81 | #include <sys/queue.h> | |||
82 | ||||
83 | #include <net/if.h> | |||
84 | #include <net/if_dl.h> | |||
85 | #include <net/if_types.h> | |||
86 | #include <net/route.h> | |||
87 | ||||
88 | #include <netinet/in.h> | |||
89 | ||||
90 | #include <netinet/icmp6.h> | |||
91 | #include <netinet6/in6_var.h> | |||
92 | #include <netinet6/nd6.h> | |||
93 | ||||
94 | #include <arpa/inet.h> | |||
95 | ||||
96 | #include <stdio.h> | |||
97 | #include <errno(*__errno()).h> | |||
98 | #include <fcntl.h> | |||
99 | #include <netdb.h> | |||
100 | #include <stdlib.h> | |||
101 | #include <string.h> | |||
102 | #include <unistd.h> | |||
103 | #include <limits.h> | |||
104 | #include <err.h> | |||
105 | ||||
106 | /* packing rule for routing socket */ | |||
107 | #define ROUNDUP(a)((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof (long)) \ | |||
108 | ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) | |||
109 | #define ADVANCE(x, n)(x += (((n)->sa_len) > 0 ? (1 + ((((n)->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))) (x += ROUNDUP((n)->sa_len)(((n)->sa_len) > 0 ? (1 + ((((n)->sa_len) - 1) | (sizeof (long) - 1))) : sizeof(long))) | |||
110 | ||||
111 | static pid_t pid; | |||
112 | static int nflag; | |||
113 | static int tflag; | |||
114 | static int rtsock = -1; | |||
115 | static int repeat = 0; | |||
116 | ||||
117 | char host_buf[NI_MAXHOST256]; /* getnameinfo() */ | |||
118 | char ifix_buf[IFNAMSIZ16]; /* if_indextoname() */ | |||
119 | ||||
120 | int file(char *); | |||
121 | void getsocket(void); | |||
122 | int parse_host(const char *, struct sockaddr_in6 *); | |||
123 | int set(int, char **); | |||
124 | void get(const char *); | |||
125 | int delete(const char *); | |||
126 | void dump(struct sockaddr_in6 *, int); | |||
127 | static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int); | |||
128 | static char *ether_str(struct sockaddr_dl *); | |||
129 | int ndp_ether_aton(const char *, u_char *); | |||
130 | void usage(void); | |||
131 | int rtmsg(int); | |||
132 | int rtget(struct sockaddr_in6 **, struct sockaddr_dl **); | |||
133 | void ifinfo(const char *); | |||
134 | static char *sec2str(time_t); | |||
135 | static int rdomain; | |||
136 | ||||
137 | int | |||
138 | main(int argc, char *argv[]) | |||
139 | { | |||
140 | int ch; | |||
141 | int mode = 0; | |||
142 | char *arg = NULL((void *)0); | |||
143 | const char *errstr; | |||
144 | ||||
145 | pid = getpid(); | |||
146 | rdomain = getrtable(); | |||
147 | while ((ch = getopt(argc, argv, "acd:f:i:nstA:V:")) != -1) { | |||
| ||||
148 | switch (ch) { | |||
149 | case 'a': | |||
150 | case 'c': | |||
151 | case 'p': | |||
152 | case 'r': | |||
153 | case 'P': | |||
154 | case 's': | |||
155 | if (mode) { | |||
156 | usage(); | |||
157 | } | |||
158 | mode = ch; | |||
159 | arg = NULL((void *)0); | |||
160 | break; | |||
161 | case 'd': | |||
162 | case 'f': | |||
163 | case 'i' : | |||
164 | if (mode
| |||
165 | usage(); | |||
166 | } | |||
167 | mode = ch; | |||
168 | arg = optarg; | |||
169 | break; | |||
170 | case 'n': | |||
171 | nflag = 1; | |||
172 | break; | |||
173 | case 't': | |||
174 | tflag = 1; | |||
175 | break; | |||
176 | case 'A': | |||
177 | if (mode) { | |||
178 | usage(); | |||
179 | } | |||
180 | mode = 'a'; | |||
181 | repeat = strtonum(optarg, 1, INT_MAX2147483647, &errstr); | |||
182 | if (errstr) { | |||
183 | usage(); | |||
184 | } | |||
185 | break; | |||
186 | case 'V': | |||
187 | rdomain = strtonum(optarg, 0, RT_TABLEID_MAX255, &errstr); | |||
188 | if (errstr != NULL((void *)0)) { | |||
189 | warn("bad rdomain: %s", errstr); | |||
190 | usage(); | |||
191 | } | |||
192 | break; | |||
193 | default: | |||
194 | usage(); | |||
195 | } | |||
196 | } | |||
197 | argc -= optind; | |||
198 | argv += optind; | |||
199 | ||||
200 | switch (mode) { | |||
201 | case 'a': | |||
202 | case 'c': | |||
203 | if (argc != 0) { | |||
204 | usage(); | |||
205 | } | |||
206 | dump(NULL((void *)0), mode == 'c'); | |||
207 | break; | |||
208 | case 'd': | |||
209 | if (argc != 0) { | |||
210 | usage(); | |||
211 | } | |||
212 | delete(arg); | |||
213 | break; | |||
214 | case 'f': | |||
215 | if (argc != 0) | |||
216 | usage(); | |||
217 | file(arg); | |||
218 | break; | |||
219 | case 'i': | |||
220 | if (argc != 0) | |||
221 | usage(); | |||
222 | ifinfo(arg); | |||
223 | break; | |||
224 | case 's': | |||
225 | if (argc < 2 || argc > 4) | |||
226 | usage(); | |||
227 | exit(set(argc, argv) ? 1 : 0); | |||
228 | case 0: | |||
229 | if (argc != 1) { | |||
230 | usage(); | |||
231 | } | |||
232 | get(argv[0]); | |||
233 | break; | |||
234 | } | |||
235 | exit(0); | |||
236 | } | |||
237 | ||||
238 | /* | |||
239 | * Process a file to set standard ndp entries | |||
240 | */ | |||
241 | int | |||
242 | file(char *name) | |||
243 | { | |||
244 | FILE *fp; | |||
245 | int i, retval; | |||
246 | char line[100], arg[5][50], *args[5]; | |||
247 | ||||
248 | if ((fp = fopen(name, "r")) == NULL((void *)0)) { | |||
249 | err(1, "cannot open %s", name); | |||
250 | } | |||
251 | args[0] = &arg[0][0]; | |||
252 | args[1] = &arg[1][0]; | |||
253 | args[2] = &arg[2][0]; | |||
254 | args[3] = &arg[3][0]; | |||
255 | args[4] = &arg[4][0]; | |||
256 | retval = 0; | |||
257 | while (fgets(line, sizeof(line), fp) != NULL((void *)0)) { | |||
258 | i = sscanf(line, "%49s %49s %49s %49s %49s", | |||
259 | arg[0], arg[1], arg[2], arg[3], arg[4]); | |||
260 | if (i < 2) { | |||
261 | warnx("bad line: %s", line); | |||
262 | retval = 1; | |||
263 | continue; | |||
264 | } | |||
265 | if (set(i, args)) | |||
266 | retval = 1; | |||
267 | } | |||
268 | fclose(fp); | |||
269 | return (retval); | |||
270 | } | |||
271 | ||||
272 | void | |||
273 | getsocket(void) | |||
274 | { | |||
275 | socklen_t len = sizeof(rdomain); | |||
276 | ||||
277 | if (rtsock >= 0) | |||
278 | return; | |||
279 | rtsock = socket(AF_ROUTE17, SOCK_RAW3, 0); | |||
280 | if (rtsock == -1) | |||
281 | err(1, "routing socket"); | |||
282 | if (setsockopt(rtsock, AF_ROUTE17, ROUTE_TABLEFILTER2, &rdomain, len) == -1) | |||
283 | err(1, "ROUTE_TABLEFILTER"); | |||
284 | ||||
285 | if (pledge("stdio dns", NULL((void *)0)) == -1) | |||
286 | err(1, "pledge"); | |||
287 | } | |||
288 | ||||
289 | int | |||
290 | parse_host(const char *host, struct sockaddr_in6 *sin6) | |||
291 | { | |||
292 | struct addrinfo hints, *res; | |||
293 | int gai_error; | |||
294 | ||||
295 | bzero(&hints, sizeof(hints)); | |||
296 | hints.ai_family = AF_INET624; | |||
297 | if (nflag) | |||
298 | hints.ai_flags = AI_NUMERICHOST4; | |||
299 | ||||
300 | gai_error = getaddrinfo(host, NULL((void *)0), &hints, &res); | |||
301 | if (gai_error) { | |||
302 | warnx("%s: %s", host, gai_strerror(gai_error)); | |||
303 | return 1; | |||
304 | } | |||
305 | *sin6 = *(struct sockaddr_in6 *)res->ai_addr; | |||
306 | freeaddrinfo(res); | |||
307 | return 0; | |||
308 | } | |||
309 | ||||
310 | struct sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET624 }; | |||
311 | struct sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET624 }, sin_m; | |||
312 | struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK18 }, sdl_m; | |||
313 | struct sockaddr_dl ifp_m = { sizeof(ifp_m), AF_LINK18 }; | |||
314 | time_t expire_time; | |||
315 | int flags, found_entry; | |||
316 | struct { | |||
317 | struct rt_msghdr m_rtm; | |||
318 | char m_space[512]; | |||
319 | } m_rtmsg; | |||
320 | ||||
321 | /* | |||
322 | * Set an individual neighbor cache entry | |||
323 | */ | |||
324 | int | |||
325 | set(int argc, char *argv[]) | |||
326 | { | |||
327 | struct sockaddr_in6 *sin = &sin_m; | |||
328 | struct sockaddr_dl *sdl; | |||
329 | struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); | |||
330 | u_char *ea; | |||
331 | const char *host = argv[0], *eaddr = argv[1]; | |||
332 | ||||
333 | getsocket(); | |||
334 | argc -= 2; | |||
335 | argv += 2; | |||
336 | sdl_m = blank_sdl; | |||
337 | sin_m = blank_sin; | |||
338 | ||||
339 | if (parse_host(host, sin)) | |||
340 | return 1; | |||
341 | ea = (u_char *)LLADDR(&sdl_m)((caddr_t)((&sdl_m)->sdl_data + (&sdl_m)->sdl_nlen )); | |||
342 | if (ndp_ether_aton(eaddr, ea) == 0) | |||
343 | sdl_m.sdl_alen = 6; | |||
344 | expire_time = 0; | |||
345 | flags = 0; | |||
346 | while (argc-- > 0) { | |||
347 | if (strncmp(argv[0], "temp", 4) == 0) { | |||
| ||||
348 | struct timeval now; | |||
349 | ||||
350 | gettimeofday(&now, 0); | |||
351 | expire_time = now.tv_sec + 20 * 60; | |||
352 | } else if (strncmp(argv[0], "proxy", 5) == 0) | |||
353 | flags |= RTF_ANNOUNCE0x4000; | |||
354 | argv++; | |||
355 | } | |||
356 | ||||
357 | if (rtget(&sin, &sdl)) { | |||
358 | errx(1, "RTM_GET(%s) failed", host); | |||
359 | } | |||
360 | ||||
361 | if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)(memcmp(&(&sin->sin6_addr)->__u6_addr.__u6_addr8 [0], &(&sin_m.sin6_addr)->__u6_addr.__u6_addr8[0], sizeof(struct in6_addr)) == 0) && | |||
362 | sin->sin6_scope_id == sin_m.sin6_scope_id) { | |||
363 | if (sdl->sdl_family == AF_LINK18 && | |||
364 | (rtm->rtm_flags & RTF_LLINFO0x400) && | |||
365 | !(rtm->rtm_flags & RTF_GATEWAY0x2)) { | |||
366 | switch (sdl->sdl_type) { | |||
367 | case IFT_ETHER0x06: case IFT_FDDI0x0f: case IFT_ISO880230x07: | |||
368 | case IFT_ISO880240x08: case IFT_ISO880250x09: | |||
369 | goto overwrite; | |||
370 | } | |||
371 | } | |||
372 | /* | |||
373 | * IPv4 arp command retries with sin_other = SIN_PROXY here. | |||
374 | */ | |||
375 | warnx("set: cannot configure a new entry"); | |||
376 | return 1; | |||
377 | } | |||
378 | ||||
379 | overwrite: | |||
380 | if (sdl->sdl_family != AF_LINK18) { | |||
381 | printf("cannot intuit interface index and type for %s\n", host); | |||
382 | return (1); | |||
383 | } | |||
384 | sdl_m.sdl_type = sdl->sdl_type; | |||
385 | sdl_m.sdl_index = sdl->sdl_index; | |||
386 | return (rtmsg(RTM_ADD0x1)); | |||
387 | } | |||
388 | ||||
389 | /* | |||
390 | * Display an individual neighbor cache entry | |||
391 | */ | |||
392 | void | |||
393 | get(const char *host) | |||
394 | { | |||
395 | struct sockaddr_in6 *sin = &sin_m; | |||
396 | ||||
397 | sin_m = blank_sin; | |||
398 | if (parse_host(host, sin)) | |||
399 | return; | |||
400 | ||||
401 | dump(sin, 0); | |||
402 | if (found_entry == 0) { | |||
403 | getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, | |||
404 | sizeof(host_buf), NULL((void *)0) ,0, | |||
405 | (nflag ? NI_NUMERICHOST1 : 0)); | |||
406 | printf("%s (%s) -- no entry\n", host, host_buf); | |||
407 | exit(1); | |||
408 | } | |||
409 | } | |||
410 | ||||
411 | /* | |||
412 | * Delete a neighbor cache entry | |||
413 | */ | |||
414 | int | |||
415 | delete(const char *host) | |||
416 | { | |||
417 | struct sockaddr_in6 *sin = &sin_m; | |||
418 | struct rt_msghdr *rtm = &m_rtmsg.m_rtm; | |||
419 | struct sockaddr_dl *sdl; | |||
420 | ||||
421 | getsocket(); | |||
422 | sin_m = blank_sin; | |||
423 | if (parse_host(host, sin)) | |||
424 | return 1; | |||
425 | if (rtget(&sin, &sdl)) { | |||
426 | errx(1, "RTM_GET(%s) failed", host); | |||
427 | } | |||
428 | ||||
429 | if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)(memcmp(&(&sin->sin6_addr)->__u6_addr.__u6_addr8 [0], &(&sin_m.sin6_addr)->__u6_addr.__u6_addr8[0], sizeof(struct in6_addr)) == 0) && | |||
430 | sin->sin6_scope_id == sin_m.sin6_scope_id) { | |||
431 | if (sdl->sdl_family == AF_LINK18 && rtm->rtm_flags & RTF_LLINFO0x400) { | |||
432 | if (rtm->rtm_flags & RTF_LOCAL0x200000) | |||
433 | return (0); | |||
434 | if (!(rtm->rtm_flags & RTF_GATEWAY0x2)) | |||
435 | goto delete; | |||
436 | } | |||
437 | /* | |||
438 | * IPv4 arp command retries with sin_other = SIN_PROXY here. | |||
439 | */ | |||
440 | warnx("delete: cannot delete non-NDP entry"); | |||
441 | return 1; | |||
442 | } | |||
443 | ||||
444 | delete: | |||
445 | if (sdl->sdl_family != AF_LINK18) { | |||
446 | printf("cannot locate %s\n", host); | |||
447 | return (1); | |||
448 | } | |||
449 | if (rtmsg(RTM_DELETE0x2) == 0) { | |||
450 | getnameinfo((struct sockaddr *)sin, | |||
451 | sin->sin6_len, host_buf, | |||
452 | sizeof(host_buf), NULL((void *)0), 0, | |||
453 | (nflag ? NI_NUMERICHOST1 : 0)); | |||
454 | printf("%s (%s) deleted\n", host, host_buf); | |||
455 | } | |||
456 | ||||
457 | return 0; | |||
458 | } | |||
459 | ||||
460 | #define W_ADDR36 36 | |||
461 | #define W_LL17 17 | |||
462 | #define W_IF7 7 | |||
463 | ||||
464 | /* | |||
465 | * Dump the entire neighbor cache | |||
466 | */ | |||
467 | void | |||
468 | dump(struct sockaddr_in6 *addr, int cflag) | |||
469 | { | |||
470 | int mib[7]; | |||
471 | size_t needed; | |||
472 | char *lim, *buf = NULL((void *)0), *next; | |||
473 | struct rt_msghdr *rtm; | |||
474 | struct sockaddr_in6 *sin; | |||
475 | struct sockaddr_dl *sdl; | |||
476 | struct in6_nbrinfo *nbi; | |||
477 | struct timeval now; | |||
478 | int addrwidth; | |||
479 | int llwidth; | |||
480 | int ifwidth; | |||
481 | char *ifname; | |||
482 | ||||
483 | /* Print header */ | |||
484 | if (!tflag && !cflag) | |||
485 | printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n", | |||
486 | W_ADDR36, W_ADDR36, "Neighbor", W_LL17, W_LL17, "Linklayer Address", | |||
487 | W_IF7, W_IF7, "Netif", "Expire", "S", "Flags"); | |||
488 | ||||
489 | again:; | |||
490 | lim = NULL((void *)0); | |||
491 | mib[0] = CTL_NET4; | |||
492 | mib[1] = PF_ROUTE17; | |||
493 | mib[2] = 0; | |||
494 | mib[3] = AF_INET624; | |||
495 | mib[4] = NET_RT_FLAGS2; | |||
496 | mib[5] = RTF_LLINFO0x400; | |||
497 | mib[6] = rdomain; | |||
498 | while (1) { | |||
499 | if (sysctl(mib, 7, NULL((void *)0), &needed, NULL((void *)0), 0) == -1) | |||
500 | err(1, "sysctl(PF_ROUTE estimate)"); | |||
501 | if (needed == 0) | |||
502 | break; | |||
503 | if ((buf = realloc(buf, needed)) == NULL((void *)0)) | |||
504 | err(1, "realloc"); | |||
505 | if (sysctl(mib, 7, buf, &needed, NULL((void *)0), 0) == -1) { | |||
506 | if (errno(*__errno()) == ENOMEM12) | |||
507 | continue; | |||
508 | err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)"); | |||
509 | } | |||
510 | lim = buf + needed; | |||
511 | break; | |||
512 | } | |||
513 | ||||
514 | for (next = buf; next && lim && next < lim; next += rtm->rtm_msglen) { | |||
515 | int isrouter = 0, prbs = 0; | |||
516 | ||||
517 | rtm = (struct rt_msghdr *)next; | |||
518 | if (rtm->rtm_version != RTM_VERSION5) | |||
519 | continue; | |||
520 | sin = (struct sockaddr_in6 *)(next + rtm->rtm_hdrlen); | |||
521 | #ifdef __KAME__ | |||
522 | { | |||
523 | struct in6_addr *in6 = &sin->sin6_addr; | |||
524 | if ((IN6_IS_ADDR_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xfe) && (((in6 )->__u6_addr.__u6_addr8[1] & 0xc0) == 0x80)) || | |||
525 | IN6_IS_ADDR_MC_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x02)) || | |||
526 | IN6_IS_ADDR_MC_INTFACELOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x01))) && | |||
527 | sin->sin6_scope_id == 0) { | |||
528 | sin->sin6_scope_id = (u_int32_t) | |||
529 | ntohs(*(u_short *)&in6->s6_addr[2])(__uint16_t)(__builtin_constant_p(*(u_short *)&in6->__u6_addr .__u6_addr8[2]) ? (__uint16_t)(((__uint16_t)(*(u_short *)& in6->__u6_addr.__u6_addr8[2]) & 0xffU) << 8 | (( __uint16_t)(*(u_short *)&in6->__u6_addr.__u6_addr8[2]) & 0xff00U) >> 8) : __swap16md(*(u_short *)&in6 ->__u6_addr.__u6_addr8[2])); | |||
530 | *(u_short *)&in6->s6_addr__u6_addr.__u6_addr8[2] = 0; | |||
531 | } | |||
532 | } | |||
533 | #endif | |||
534 | sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len)((sin->sin6_len) > 0 ? (1 + (((sin->sin6_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); | |||
535 | ||||
536 | /* | |||
537 | * Some OSes can produce a route that has the LINK flag but | |||
538 | * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD | |||
539 | * and BSD/OS, where xx is not the interface identifier on | |||
540 | * lo0). Such routes entry would annoy getnbrinfo() below, | |||
541 | * so we skip them. | |||
542 | * XXX: such routes should have the GATEWAY flag, not the | |||
543 | * LINK flag. However, there is rotten routing software | |||
544 | * that advertises all routes that have the GATEWAY flag. | |||
545 | * Thus, KAME kernel intentionally does not set the LINK flag. | |||
546 | * What is to be fixed is not ndp, but such routing software | |||
547 | * (and the kernel workaround)... | |||
548 | */ | |||
549 | if (sdl->sdl_family != AF_LINK18) | |||
550 | continue; | |||
551 | ||||
552 | if (!(rtm->rtm_flags & RTF_HOST0x4)) | |||
553 | continue; | |||
554 | ||||
555 | if (addr) { | |||
556 | if (!IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,(memcmp(&(&addr->sin6_addr)->__u6_addr.__u6_addr8 [0], &(&sin->sin6_addr)->__u6_addr.__u6_addr8[0 ], sizeof(struct in6_addr)) == 0) | |||
557 | &sin->sin6_addr)(memcmp(&(&addr->sin6_addr)->__u6_addr.__u6_addr8 [0], &(&sin->sin6_addr)->__u6_addr.__u6_addr8[0 ], sizeof(struct in6_addr)) == 0) || addr->sin6_scope_id != | |||
558 | sin->sin6_scope_id) | |||
559 | continue; | |||
560 | found_entry = 1; | |||
561 | } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr)((&sin->sin6_addr)->__u6_addr.__u6_addr8[0] == 0xff )) | |||
562 | continue; | |||
563 | getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf, | |||
564 | sizeof(host_buf), NULL((void *)0), 0, (nflag ? NI_NUMERICHOST1 : 0)); | |||
565 | if (cflag) { | |||
566 | if (rtm->rtm_flags & RTF_CLONED0x10000) | |||
567 | delete(host_buf); | |||
568 | continue; | |||
569 | } | |||
570 | gettimeofday(&now, 0); | |||
571 | if (tflag) { | |||
572 | char buf[sizeof("00:00:00")]; | |||
573 | struct tm *tm; | |||
574 | ||||
575 | tm = localtime(&now.tv_sec); | |||
576 | if (tm != NULL((void *)0)) { | |||
577 | strftime(buf, sizeof(buf), "%H:%M:%S", tm); | |||
578 | printf("%s.%06ld ", buf, now.tv_usec); | |||
579 | } | |||
580 | } | |||
581 | ||||
582 | addrwidth = strlen(host_buf); | |||
583 | if (addrwidth < W_ADDR36) | |||
584 | addrwidth = W_ADDR36; | |||
585 | llwidth = strlen(ether_str(sdl)); | |||
586 | if (W_ADDR36 + W_LL17 - addrwidth > llwidth) | |||
587 | llwidth = W_ADDR36 + W_LL17 - addrwidth; | |||
588 | ifname = if_indextoname(sdl->sdl_index, ifix_buf); | |||
589 | if (!ifname) | |||
590 | ifname = "?"; | |||
591 | ifwidth = strlen(ifname); | |||
592 | if (W_ADDR36 + W_LL17 + W_IF7 - addrwidth - llwidth > ifwidth) | |||
593 | ifwidth = W_ADDR36 + W_LL17 + W_IF7 - addrwidth - llwidth; | |||
594 | ||||
595 | printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf, | |||
596 | llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname); | |||
597 | ||||
598 | /* Print neighbor discovery specific informations */ | |||
599 | nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1); | |||
600 | if (nbi) { | |||
601 | if (nbi->expire > now.tv_sec) { | |||
602 | printf(" %-9.9s", | |||
603 | sec2str(nbi->expire - now.tv_sec)); | |||
604 | } else if (nbi->expire == 0) | |||
605 | printf(" %-9.9s", "permanent"); | |||
606 | else | |||
607 | printf(" %-9.9s", "expired"); | |||
608 | ||||
609 | switch (nbi->state) { | |||
610 | case ND6_LLINFO_NOSTATE-2: | |||
611 | printf(" N"); | |||
612 | break; | |||
613 | case ND6_LLINFO_INCOMPLETE0: | |||
614 | printf(" I"); | |||
615 | break; | |||
616 | case ND6_LLINFO_REACHABLE1: | |||
617 | printf(" R"); | |||
618 | break; | |||
619 | case ND6_LLINFO_STALE2: | |||
620 | printf(" S"); | |||
621 | break; | |||
622 | case ND6_LLINFO_DELAY3: | |||
623 | printf(" D"); | |||
624 | break; | |||
625 | case ND6_LLINFO_PROBE4: | |||
626 | printf(" P"); | |||
627 | break; | |||
628 | default: | |||
629 | printf(" ?"); | |||
630 | break; | |||
631 | } | |||
632 | ||||
633 | isrouter = nbi->isrouter; | |||
634 | prbs = nbi->asked; | |||
635 | } else { | |||
636 | warnx("failed to get neighbor information"); | |||
637 | printf(" "); | |||
638 | } | |||
639 | ||||
640 | printf(" %s%s%s", | |||
641 | (rtm->rtm_flags & RTF_LOCAL0x200000) ? "l" : "", | |||
642 | isrouter ? "R" : "", | |||
643 | (rtm->rtm_flags & RTF_ANNOUNCE0x4000) ? "p" : ""); | |||
644 | ||||
645 | if (prbs) | |||
646 | printf(" %d", prbs); | |||
647 | ||||
648 | printf("\n"); | |||
649 | } | |||
650 | ||||
651 | if (repeat) { | |||
652 | printf("\n"); | |||
653 | fflush(stdout(&__sF[1])); | |||
654 | sleep(repeat); | |||
655 | goto again; | |||
656 | } | |||
657 | ||||
658 | free(buf); | |||
659 | } | |||
660 | ||||
661 | static struct in6_nbrinfo * | |||
662 | getnbrinfo(struct in6_addr *addr, int ifindex, int warning) | |||
663 | { | |||
664 | static struct in6_nbrinfo nbi; | |||
665 | int s; | |||
666 | ||||
667 | if ((s = socket(AF_INET624, SOCK_DGRAM2, 0)) == -1) | |||
668 | err(1, "socket"); | |||
669 | ||||
670 | bzero(&nbi, sizeof(nbi)); | |||
671 | if_indextoname(ifindex, nbi.ifname); | |||
672 | nbi.addr = *addr; | |||
673 | if (ioctl(s, SIOCGNBRINFO_IN6(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct in6_nbrinfo) & 0x1fff) << 16) | ((('i')) << 8) | ((78))), (caddr_t)&nbi) == -1) { | |||
674 | if (warning) | |||
675 | warn("ioctl(SIOCGNBRINFO_IN6)"); | |||
676 | close(s); | |||
677 | return(NULL((void *)0)); | |||
678 | } | |||
679 | ||||
680 | close(s); | |||
681 | return(&nbi); | |||
682 | } | |||
683 | ||||
684 | static char * | |||
685 | ether_str(struct sockaddr_dl *sdl) | |||
686 | { | |||
687 | static char hbuf[NI_MAXHOST256]; | |||
688 | u_char *cp; | |||
689 | ||||
690 | if (sdl->sdl_alen) { | |||
691 | cp = (u_char *)LLADDR(sdl)((caddr_t)((sdl)->sdl_data + (sdl)->sdl_nlen)); | |||
692 | snprintf(hbuf, sizeof(hbuf), "%02x:%02x:%02x:%02x:%02x:%02x", | |||
693 | cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]); | |||
694 | } else | |||
695 | snprintf(hbuf, sizeof(hbuf), "(incomplete)"); | |||
696 | ||||
697 | return(hbuf); | |||
698 | } | |||
699 | ||||
700 | int | |||
701 | ndp_ether_aton(const char *a, u_char *n) | |||
702 | { | |||
703 | int i, o[6]; | |||
704 | ||||
705 | i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2], | |||
706 | &o[3], &o[4], &o[5]); | |||
707 | if (i != 6) { | |||
708 | warnx("invalid Ethernet address '%s'", a); | |||
709 | return (1); | |||
710 | } | |||
711 | for (i = 0; i < 6; i++) | |||
712 | n[i] = o[i]; | |||
713 | return (0); | |||
714 | } | |||
715 | ||||
716 | void | |||
717 | usage(void) | |||
718 | { | |||
719 | printf("usage: ndp [-acnt] "); | |||
720 | printf("[-A wait] [-d hostname] [-f filename] [-i interface]\n"); | |||
721 | printf("\t[-s nodename ether_addr [temp] [proxy]] "); | |||
722 | printf("[-V rdomain] [hostname]\n"); | |||
723 | exit(1); | |||
724 | } | |||
725 | ||||
726 | int | |||
727 | rtmsg(int cmd) | |||
728 | { | |||
729 | static int seq; | |||
730 | int rlen; | |||
731 | struct rt_msghdr *rtm = &m_rtmsg.m_rtm; | |||
732 | char *cp = m_rtmsg.m_space; | |||
733 | int l; | |||
734 | ||||
735 | errno(*__errno()) = 0; | |||
736 | if (cmd == RTM_DELETE0x2) | |||
737 | goto doit; | |||
738 | bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); | |||
739 | rtm->rtm_flags = flags; | |||
740 | rtm->rtm_version = RTM_VERSION5; | |||
741 | rtm->rtm_tableid = rdomain; | |||
742 | ||||
743 | switch (cmd) { | |||
744 | default: | |||
745 | errx(1, "internal wrong cmd"); | |||
746 | case RTM_ADD0x1: | |||
747 | rtm->rtm_addrs |= RTA_GATEWAY0x2; | |||
748 | if (expire_time) { | |||
749 | rtm->rtm_rmx.rmx_expire = expire_time; | |||
750 | rtm->rtm_inits = RTV_EXPIRE0x4; | |||
751 | } | |||
752 | rtm->rtm_flags |= (RTF_HOST0x4 | RTF_STATIC0x800); | |||
753 | #if 0 /* we don't support ipv6addr/128 type proxying. */ | |||
754 | if (rtm->rtm_flags & RTF_ANNOUNCE0x4000) { | |||
755 | rtm->rtm_flags &= ~RTF_HOST0x4; | |||
756 | rtm->rtm_addrs |= RTA_NETMASK0x4; | |||
757 | } | |||
758 | #endif | |||
759 | /* FALLTHROUGH */ | |||
760 | case RTM_GET0x4: | |||
761 | rtm->rtm_addrs |= (RTA_DST0x1 | RTA_IFP0x10); | |||
762 | } | |||
763 | ||||
764 | #define NEXTADDR(w, s)if (rtm->rtm_addrs & (w)) { memcpy(cp, &(s), sizeof (s)); (cp += ((((struct sockaddr *)&(s))->sa_len) > 0 ? (1 + (((((struct sockaddr *)&(s))->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); } \ | |||
765 | if (rtm->rtm_addrs & (w)) { \ | |||
766 | memcpy(cp, &(s), sizeof(s)); \ | |||
767 | ADVANCE(cp, (struct sockaddr *)&(s))(cp += ((((struct sockaddr *)&(s))->sa_len) > 0 ? ( 1 + (((((struct sockaddr *)&(s))->sa_len) - 1) | (sizeof (long) - 1))) : sizeof(long))); \ | |||
768 | } | |||
769 | ||||
770 | #ifdef __KAME__ | |||
771 | { | |||
772 | struct sockaddr_in6 sin6 = sin_m; | |||
773 | struct in6_addr *in6 = &sin6.sin6_addr; | |||
774 | if (IN6_IS_ADDR_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xfe) && (((in6 )->__u6_addr.__u6_addr8[1] & 0xc0) == 0x80)) || | |||
775 | IN6_IS_ADDR_MC_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x02)) || | |||
776 | IN6_IS_ADDR_MC_INTFACELOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x01))) { | |||
777 | *(u_int16_t *)& in6->s6_addr__u6_addr.__u6_addr8[2] = | |||
778 | htons(sin6.sin6_scope_id)(__uint16_t)(__builtin_constant_p(sin6.sin6_scope_id) ? (__uint16_t )(((__uint16_t)(sin6.sin6_scope_id) & 0xffU) << 8 | ((__uint16_t)(sin6.sin6_scope_id) & 0xff00U) >> 8) : __swap16md(sin6.sin6_scope_id)); | |||
779 | sin6.sin6_scope_id = 0; | |||
780 | } | |||
781 | NEXTADDR(RTA_DST, sin6)if (rtm->rtm_addrs & (0x1)) { memcpy(cp, &(sin6), sizeof (sin6)); (cp += ((((struct sockaddr *)&(sin6))->sa_len ) > 0 ? (1 + (((((struct sockaddr *)&(sin6))->sa_len ) - 1) | (sizeof(long) - 1))) : sizeof(long))); }; | |||
782 | } | |||
783 | #else | |||
784 | NEXTADDR(RTA_DST, sin_m)if (rtm->rtm_addrs & (0x1)) { memcpy(cp, &(sin_m), sizeof(sin_m)); (cp += ((((struct sockaddr *)&(sin_m))-> sa_len) > 0 ? (1 + (((((struct sockaddr *)&(sin_m))-> sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); }; | |||
785 | #endif | |||
786 | NEXTADDR(RTA_GATEWAY, sdl_m)if (rtm->rtm_addrs & (0x2)) { memcpy(cp, &(sdl_m), sizeof(sdl_m)); (cp += ((((struct sockaddr *)&(sdl_m))-> sa_len) > 0 ? (1 + (((((struct sockaddr *)&(sdl_m))-> sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); }; | |||
787 | #if 0 /* we don't support ipv6addr/128 type proxying. */ | |||
788 | memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr)); | |||
789 | NEXTADDR(RTA_NETMASK, so_mask)if (rtm->rtm_addrs & (0x4)) { memcpy(cp, &(so_mask ), sizeof(so_mask)); (cp += ((((struct sockaddr *)&(so_mask ))->sa_len) > 0 ? (1 + (((((struct sockaddr *)&(so_mask ))->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); }; | |||
790 | #endif | |||
791 | NEXTADDR(RTA_IFP, ifp_m)if (rtm->rtm_addrs & (0x10)) { memcpy(cp, &(ifp_m) , sizeof(ifp_m)); (cp += ((((struct sockaddr *)&(ifp_m))-> sa_len) > 0 ? (1 + (((((struct sockaddr *)&(ifp_m))-> sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); }; | |||
792 | ||||
793 | rtm->rtm_msglen = cp - (char *)&m_rtmsg; | |||
794 | doit: | |||
795 | l = rtm->rtm_msglen; | |||
796 | rtm->rtm_seq = ++seq; | |||
797 | rtm->rtm_type = cmd; | |||
798 | if ((rlen = write(rtsock, (char *)&m_rtmsg, l)) == -1) { | |||
799 | if (errno(*__errno()) != ESRCH3 || cmd != RTM_DELETE0x2) { | |||
800 | err(1, "writing to routing socket"); | |||
801 | } | |||
802 | } | |||
803 | do { | |||
804 | l = read(rtsock, (char *)&m_rtmsg, sizeof(m_rtmsg)); | |||
805 | } while (l > 0 && (rtm->rtm_version != RTM_VERSION5 || | |||
806 | rtm->rtm_seq != seq || rtm->rtm_pid != pid)); | |||
807 | if (l == -1) | |||
808 | warn("read from routing socket"); | |||
809 | return (0); | |||
810 | } | |||
811 | ||||
812 | int | |||
813 | rtget(struct sockaddr_in6 **sinp, struct sockaddr_dl **sdlp) | |||
814 | { | |||
815 | struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); | |||
816 | struct sockaddr_in6 *sin = NULL((void *)0); | |||
817 | struct sockaddr_dl *sdl = NULL((void *)0); | |||
818 | struct sockaddr *sa; | |||
819 | char *cp; | |||
820 | unsigned int i; | |||
821 | ||||
822 | if (rtmsg(RTM_GET0x4) < 0) | |||
823 | return (1); | |||
824 | ||||
825 | if (rtm->rtm_addrs) { | |||
826 | cp = ((char *)rtm + rtm->rtm_hdrlen); | |||
827 | for (i = 1; i; i <<= 1) { | |||
828 | if (i & rtm->rtm_addrs) { | |||
829 | sa = (struct sockaddr *)cp; | |||
830 | switch (i) { | |||
831 | case RTA_DST0x1: | |||
832 | sin = (struct sockaddr_in6 *)sa; | |||
833 | break; | |||
834 | case RTA_IFP0x10: | |||
835 | sdl = (struct sockaddr_dl *)sa; | |||
836 | break; | |||
837 | default: | |||
838 | break; | |||
839 | } | |||
840 | ADVANCE(cp, sa)(cp += (((sa)->sa_len) > 0 ? (1 + ((((sa)->sa_len) - 1) | (sizeof(long) - 1))) : sizeof(long))); | |||
841 | } | |||
842 | } | |||
843 | } | |||
844 | ||||
845 | if (sin == NULL((void *)0) || sdl == NULL((void *)0)) | |||
846 | return (1); | |||
847 | ||||
848 | #ifdef __KAME__ | |||
849 | { | |||
850 | static struct sockaddr_in6 ksin; | |||
851 | struct in6_addr *in6; | |||
852 | ||||
853 | /* do not damage the route message, we need it for delete */ | |||
854 | ksin = *sin; | |||
855 | sin = &ksin; | |||
856 | in6 = &sin->sin6_addr; | |||
857 | ||||
858 | if ((IN6_IS_ADDR_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xfe) && (((in6 )->__u6_addr.__u6_addr8[1] & 0xc0) == 0x80)) || | |||
859 | IN6_IS_ADDR_MC_LINKLOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x02)) || | |||
860 | IN6_IS_ADDR_MC_INTFACELOCAL(in6)(((in6)->__u6_addr.__u6_addr8[0] == 0xff) && (((in6 )->__u6_addr.__u6_addr8[1] & 0x0f) == 0x01))) && | |||
861 | sin->sin6_scope_id == 0) { | |||
862 | sin->sin6_scope_id = (u_int32_t)ntohs(*(u_short *)(__uint16_t)(__builtin_constant_p(*(u_short *) &in6->__u6_addr .__u6_addr8[2]) ? (__uint16_t)(((__uint16_t)(*(u_short *) & in6->__u6_addr.__u6_addr8[2]) & 0xffU) << 8 | (( __uint16_t)(*(u_short *) &in6->__u6_addr.__u6_addr8[2] ) & 0xff00U) >> 8) : __swap16md(*(u_short *) &in6 ->__u6_addr.__u6_addr8[2])) | |||
863 | &in6->s6_addr[2])(__uint16_t)(__builtin_constant_p(*(u_short *) &in6->__u6_addr .__u6_addr8[2]) ? (__uint16_t)(((__uint16_t)(*(u_short *) & in6->__u6_addr.__u6_addr8[2]) & 0xffU) << 8 | (( __uint16_t)(*(u_short *) &in6->__u6_addr.__u6_addr8[2] ) & 0xff00U) >> 8) : __swap16md(*(u_short *) &in6 ->__u6_addr.__u6_addr8[2])); | |||
864 | *(u_short *)&in6->s6_addr__u6_addr.__u6_addr8[2] = 0; | |||
865 | } | |||
866 | } | |||
867 | #endif | |||
868 | *sinp = sin; | |||
869 | *sdlp = sdl; | |||
870 | ||||
871 | return (0); | |||
872 | } | |||
873 | ||||
874 | void | |||
875 | ifinfo(const char *ifname) | |||
876 | { | |||
877 | struct in6_ndireq nd; | |||
878 | int s; | |||
879 | ||||
880 | if ((s = socket(AF_INET624, SOCK_DGRAM2, 0)) == -1) { | |||
881 | err(1, "socket"); | |||
882 | } | |||
883 | bzero(&nd, sizeof(nd)); | |||
884 | strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); | |||
885 | if (ioctl(s, SIOCGIFINFO_IN6(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct in6_ndireq) & 0x1fff) << 16) | ((('i')) << 8) | ((108))), (caddr_t)&nd) == -1) | |||
886 | err(1, "ioctl(SIOCGIFINFO_IN6)"); | |||
887 | ||||
888 | if (!nd.ndi.initialized) | |||
889 | errx(1, "%s: not initialized yet", ifname); | |||
890 | ||||
891 | printf("basereachable=%ds%dms", | |||
892 | nd.ndi.basereachable / 1000, nd.ndi.basereachable % 1000); | |||
893 | printf(", reachable=%ds", nd.ndi.reachable); | |||
894 | printf(", retrans=%ds%dms\n", nd.ndi.retrans / 1000, | |||
895 | nd.ndi.retrans % 1000); | |||
896 | ||||
897 | close(s); | |||
898 | } | |||
899 | ||||
900 | static char * | |||
901 | sec2str(time_t total) | |||
902 | { | |||
903 | static char result[256]; | |||
904 | int days, hours, mins, secs; | |||
905 | int first = 1; | |||
906 | char *p = result; | |||
907 | char *ep = &result[sizeof(result)]; | |||
908 | int n; | |||
909 | ||||
910 | days = total / 3600 / 24; | |||
911 | hours = (total / 3600) % 24; | |||
912 | mins = (total / 60) % 60; | |||
913 | secs = total % 60; | |||
914 | ||||
915 | if (days) { | |||
916 | first = 0; | |||
917 | n = snprintf(p, ep - p, "%dd", days); | |||
918 | if (n < 0 || n >= ep - p) | |||
919 | return "?"; | |||
920 | p += n; | |||
921 | } | |||
922 | if (!first || hours) { | |||
923 | first = 0; | |||
924 | n = snprintf(p, ep - p, "%dh", hours); | |||
925 | if (n < 0 || n >= ep - p) | |||
926 | return "?"; | |||
927 | p += n; | |||
928 | } | |||
929 | if (!first || mins) { | |||
930 | first = 0; | |||
931 | n = snprintf(p, ep - p, "%dm", mins); | |||
932 | if (n < 0 || n >= ep - p) | |||
933 | return "?"; | |||
934 | p += n; | |||
935 | } | |||
936 | snprintf(p, ep - p, "%ds", secs); | |||
937 | ||||
938 | return(result); | |||
939 | } |