Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
+++ new/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 13 * Copyright 2015 Joyent, Inc. All rights reserved.
14 14 */
15 15
16 16 /*
17 17 * ndp - display and manipulate Neighbor Cache Entries from NDP
18 18 */
19 19
20 20 #include <stdio.h>
21 21 #include <stdarg.h>
22 22 #include <signal.h>
23 23 #include <time.h>
24 24 #include <err.h>
25 25 #include <errno.h>
26 26 #include <stdlib.h>
27 27 #include <strings.h>
28 28 #include <unistd.h>
29 29 #include <libgen.h>
30 30 #include <sys/ioctl.h>
31 31 #include <sys/types.h>
32 32 #include <wait.h>
33 33 #include <sys/mac.h>
34 34 #include <sys/socket.h>
35 35 #include <sys/sockio.h>
36 36 #include <netdb.h>
37 37 #include <net/if_types.h>
38 38 #include <netinet/in.h>
39 39 #include <arpa/inet.h>
40 40 #include <inet/ip.h>
41 41 #include <net/if_dl.h>
42 42 #include <net/route.h>
43 43 #include <zone.h>
44 44
45 45 typedef struct sockaddr_in6 sin6_t;
46 46
47 47 #define BUF_SIZE 2048
48 48 typedef struct rtmsg_pkt {
49 49 struct rt_msghdr m_rtm;
50 50 char m_space[BUF_SIZE];
51 51 } rtmsg_pkt_t;
52 52
53 53 enum ndp_action {
54 54 NDP_A_DEFAULT,
55 55 NDP_A_GET, /* Show a single NDP entry */
56 56 NDP_A_GET_ALL, /* Show NDP entries */
57 57 NDP_A_GET_FOREVER, /* Repeatedly show entries */
58 58 NDP_A_DELETE, /* Delete an NDP entry */
59 59 NDP_A_SET_NCE, /* Set NDP entry */
60 60 NDP_A_SET_FILE /* Read in & set NDP entries */
61 61 };
62 62
63 63 typedef int (ndp_addr_f)(int, struct lifreq *, void *);
64 64 typedef void (ndp_void_f)(void);
65 65
66 66 static void ndp_usage(const char *, ...);
67 67 static void ndp_fatal(const char *, ...);
68 68 static void ndp_badflag(enum ndp_action);
69 69 static void ndp_missingarg(char);
70 70
71 71 static void ndp_run_in_child(ndp_void_f *);
72 72 static void ndp_do_run(void);
73 73 static void ndp_setup_handler(sigset_t *);
74 74 static void ndp_start_timer(time_t period);
75 75 static void ndp_run_periodically(time_t, ndp_void_f *);
76 76
77 77 static int ndp_salen(const struct sockaddr *sa);
78 78 static int ndp_extract_sockaddrs(struct rt_msghdr *, struct sockaddr **,
79 79 struct sockaddr **, struct sockaddr **, struct sockaddr **,
80 80 struct sockaddr_dl **);
81 81 static int ndp_rtmsg_get(int, rtmsg_pkt_t *, struct sockaddr *);
82 82 static int ndp_find_interface(int, struct sockaddr *, char *, int);
83 83
84 84 static int ndp_initialize_lifreq(int, struct lifreq *, struct sockaddr *);
85 85 static int ndp_host_enumerate(char *, ndp_addr_f *, void *);
86 86
87 87 static int ndp_display(struct lifreq *);
88 88 static int ndp_display_missing(struct lifreq *);
89 89 static void ndp_lifr2ip(struct lifreq *, char *, int);
90 90
91 91 static int ndp_get(int, struct lifreq *, void *);
92 92 static void ndp_get_all(void);
93 93 static int ndp_delete(int, struct lifreq *, void *);
94 94 static int ndp_set(int, struct lifreq *, void *);
95 95 static int ndp_set_nce(char *, char *, char *[], int);
96 96 static int ndp_set_file(char *);
97 97
98 98 static char *ndp_iface = NULL;
99 99 static pid_t ndp_pid;
100 100 static boolean_t ndp_noresolve = B_FALSE; /* Don't lookup addresses */
101 101 static boolean_t ndp_run = B_TRUE;
102 102
103 103 #define MAX_ATTEMPTS 5
104 104 #define MAX_OPTS 5
105 105 #define WORDSEPS " \t\r\n"
106 106 #define NETSTAT_PATH "/usr/bin/netstat"
107 107
108 108 /*
109 109 * Macros borrowed from route(1M) for working with PF_ROUTE messages
110 110 */
111 111 #define RT_ADVANCE(x, n) ((x) += ndp_salen(n))
112 112 #define RT_NEXTADDR(cp, w, u) \
113 113 l = ndp_salen(u); \
114 114 (void) memmove(cp, u, l); \
115 115 cp += l;
116 116
117 117 /*
118 118 * Print an error to stderr and then exit non-zero.
119 119 */
120 120 static void
121 121 ndp_fatal(const char *format, ...)
122 122 {
123 123 va_list ap;
124 124
125 125 va_start(ap, format);
126 126 vwarnx(format, ap);
127 127 va_end(ap);
128 128 exit(EXIT_FAILURE);
129 129 }
130 130
131 131 /*
132 132 * Print out the command usage to stderr, along with any reason why it's being
133 133 * printed, and then exit non-zero.
134 134 */
135 135 static void
136 136 ndp_usage(const char *reason, ...)
137 137 {
138 138 va_list ap;
139 139 const char *ndp_progname = getprogname();
140 140
141 141 if (reason != NULL) {
142 142 va_start(ap, reason);
143 143 (void) fprintf(stderr, "%s: ", ndp_progname);
144 144 (void) vfprintf(stderr, reason, ap);
145 145 (void) fprintf(stderr, "\n");
146 146 va_end(ap);
147 147 }
148 148
149 149 (void) fprintf(stderr,
150 150 "Usage: %s [-n] [-i iface] hostname\n"
151 151 " %s [-n] [-i iface] -s nodeaddr etheraddr [temp] [proxy]\n"
152 152 " %s [-n] [-i iface] -d nodeaddr\n"
153 153 " %s [-n] [-i iface] -f filename\n"
154 154 " %s [-n] -a\n"
155 155 " %s [-n] -A period\n",
156 156 ndp_progname, ndp_progname, ndp_progname,
157 157 ndp_progname, ndp_progname, ndp_progname);
158 158 exit(EXIT_FAILURE);
159 159 }
160 160
161 161 static void
162 162 ndp_badflag(enum ndp_action action)
163 163 {
164 164 switch (action) {
165 165 case NDP_A_DEFAULT:
166 166 case NDP_A_GET:
167 167 ndp_usage("Already going to print an entry, "
168 168 "but extra -%c given", optopt);
169 169 break;
170 170 case NDP_A_GET_ALL:
171 171 ndp_usage("Already going to print all entries (-a), "
172 172 "but extra -%c given", optopt);
173 173 break;
174 174 case NDP_A_GET_FOREVER:
175 175 ndp_usage("Already going to repeatedly print all entries (-A), "
176 176 "but extra -%c given", optopt);
177 177 break;
178 178 case NDP_A_DELETE:
179 179 ndp_usage("Already going to delete an entry (-d), "
180 180 "but extra -%c given", optopt);
181 181 break;
182 182 case NDP_A_SET_NCE:
183 183 ndp_usage("Already going to set an entry (-s), "
184 184 "but extra -%c given", optopt);
185 185 break;
186 186 case NDP_A_SET_FILE:
187 187 ndp_usage("Already going to set from file (-f), "
188 188 "but extra -%c given", optopt);
189 189 break;
190 190 }
191 191 }
192 192
193 193 static void
194 194 ndp_missingarg(char flag)
195 195 {
196 196 switch (flag) {
197 197 case 'A':
198 198 ndp_usage("Missing time period after -%c", flag);
199 199 break;
200 200 case 'd':
201 201 ndp_usage("Missing node name after -%c", flag);
202 202 break;
203 203 case 'f':
204 204 ndp_usage("Missing filename after -%c", flag);
205 205 break;
206 206 case 's':
207 207 ndp_usage("Missing node name after -%c", flag);
208 208 break;
209 209 case 'i':
210 210 ndp_usage("Missing interface name after -%c", flag);
211 211 break;
212 212 default:
213 213 ndp_usage("Missing option argument after -%c", flag);
214 214 break;
215 215 }
216 216 }
217 217
218 218 /*
219 219 * Run a function that's going to exec in a child process, and don't return
220 220 * until it exits.
221 221 */
222 222 static void
223 223 ndp_run_in_child(ndp_void_f *func)
224 224 {
225 225 pid_t child_pid;
226 226 int childstat = 0, status = 0;
227 227
228 228 child_pid = fork();
229 229 if (child_pid == (pid_t)-1) {
230 230 ndp_fatal("Unable to fork: %s", strerror(errno));
231 231 } else if (child_pid == (pid_t)0) {
232 232 func();
233 233 exit(EXIT_FAILURE);
234 234 }
235 235
236 236 while (waitpid(child_pid, &childstat, 0) == -1) {
237 237 if (errno == EINTR)
238 238 continue;
239 239
240 240 ndp_fatal("Failed to wait on child: %s", strerror(errno));
241 241 }
242 242
243 243 status = WEXITSTATUS(childstat);
244 244 if (status != 0) {
245 245 ndp_fatal("Child process exited with %d", status);
246 246 }
247 247 }
248 248
249 249 /*
250 250 * SIGALRM handler to schedule a run.
251 251 */
252 252 static void
253 253 ndp_do_run(void)
254 254 {
255 255 ndp_run = B_TRUE;
256 256 }
257 257
258 258
259 259 /*
260 260 * Prepare signal masks, and install the SIGALRM handler. Return old signal
261 261 * masks through the first argument.
262 262 */
263 263 static void
264 264 ndp_setup_handler(sigset_t *oset)
265 265 {
266 266 struct sigaction sa;
267 267
268 268 /*
269 269 * Mask off SIGALRM so we only trigger the handler when we're ready
270 270 * using sigsuspend(3C), in case the child process takes longer to
271 271 * run than the alarm interval.
272 272 */
273 273 if (sigprocmask(0, NULL, oset) != 0) {
274 274 ndp_fatal("Unable to set signal mask: %s", strerror(errno));
275 275 }
276 276
277 277 if (sighold(SIGALRM) != 0) {
278 278 ndp_fatal("Unable to add SIGALRM to signal mask: %s",
279 279 strerror(errno));
280 280 }
281 281
282 282 sa.sa_flags = 0;
283 283 sa.sa_handler = ndp_do_run;
284 284
285 285 if (sigemptyset(&sa.sa_mask) != 0) {
286 286 ndp_fatal("Unable to prepare empty signal set: %s",
287 287 strerror(errno));
288 288 }
289 289
290 290 if (sigaction(SIGALRM, &sa, NULL) != 0) {
291 291 ndp_fatal("Unable to install timer handler: %s",
292 292 strerror(errno));
293 293 }
294 294 }
295 295
296 296 /*
297 297 * Start the printing timer.
298 298 */
299 299 static void
300 300 ndp_start_timer(time_t period)
301 301 {
302 302 timer_t timer;
303 303 struct itimerspec interval;
304 304 interval.it_value.tv_sec = interval.it_interval.tv_sec = period;
305 305 interval.it_value.tv_nsec = interval.it_interval.tv_nsec = 0;
306 306
307 307 if (timer_create(CLOCK_REALTIME, NULL, &timer) != 0) {
308 308 ndp_fatal("Unable to create timer: %s", strerror(errno));
309 309 }
310 310
311 311 if (timer_settime(timer, 0, &interval, NULL) != 0) {
312 312 ndp_fatal("Unable to set time on timer: %s", strerror(errno));
313 313 }
314 314 }
315 315
316 316
317 317 /*
318 318 * Run a given function forever periodically in a child process.
319 319 */
320 320 static void
321 321 ndp_run_periodically(time_t period, ndp_void_f *func)
322 322 {
323 323 sigset_t oset;
324 324
325 325 ndp_setup_handler(&oset);
326 326 ndp_start_timer(period);
327 327
328 328 do {
329 329 if (ndp_run) {
330 330 ndp_run = B_FALSE;
331 331 ndp_run_in_child(func);
332 332 }
333 333 (void) sigsuspend(&oset);
334 334 } while (errno == EINTR);
335 335
336 336 /*
337 337 * Only an EFAULT should get us here. Abort so we get a core dump.
338 338 */
339 339 warnx("Failure while waiting on timer: %s", strerror(errno));
340 340 abort();
341 341 }
342 342
343 343 /*
344 344 * Given an address, return its size.
345 345 */
346 346 static int
347 347 ndp_salen(const struct sockaddr *sa)
348 348 {
349 349 switch (sa->sa_family) {
350 350 case AF_INET:
351 351 return (sizeof (struct sockaddr_in));
352 352 case AF_LINK:
353 353 return (sizeof (struct sockaddr_dl));
354 354 case AF_INET6:
355 355 return (sizeof (struct sockaddr_in6));
356 356 default:
357 357 warnx("Unrecognized sockaddr with address family %d!",
358 358 sa->sa_family);
359 359 abort();
360 360 }
361 361 /*NOTREACHED*/
362 362 }
363 363
364 364 /*
365 365 * Extract all socket addresses from a routing message, and return them
366 366 * through the pointers given as arguments to ndp_extract_sockaddrs. None
367 367 * of the pointers should be null.
368 368 */
369 369 static int
370 370 ndp_extract_sockaddrs(struct rt_msghdr *rtm, struct sockaddr **dst,
371 371 struct sockaddr **gate, struct sockaddr **mask, struct sockaddr **src,
372 372 struct sockaddr_dl **ifp)
373 373 {
374 374 struct sockaddr *sa;
375 375 char *cp;
376 376 int i;
377 377
378 378 if (rtm->rtm_version != RTM_VERSION) {
379 379 warnx("Routing message version %d not understood",
380 380 rtm->rtm_version);
381 381 return (-1);
382 382 }
383 383
384 384 if (rtm->rtm_errno != 0) {
385 385 warnx("Routing message couldn't be processed: %s",
386 386 strerror(rtm->rtm_errno));
387 387 return (-1);
388 388 }
389 389
390 390 cp = ((char *)(rtm + 1));
391 391 if (rtm->rtm_addrs != 0) {
392 392 for (i = 1; i != 0; i <<= 1) {
393 393 if ((i & rtm->rtm_addrs) == 0)
394 394 continue;
395 395
396 396 /*LINTED*/
397 397 sa = (struct sockaddr *)cp;
398 398 switch (i) {
399 399 case RTA_DST:
400 400 *dst = sa;
401 401 break;
402 402 case RTA_GATEWAY:
403 403 *gate = sa;
404 404 break;
405 405 case RTA_NETMASK:
406 406 *mask = sa;
407 407 break;
408 408 case RTA_IFP:
409 409 if (sa->sa_family == AF_LINK &&
410 410 ((struct sockaddr_dl *)sa)->sdl_nlen != 0)
411 411 *ifp = (struct sockaddr_dl *)sa;
412 412 break;
413 413 case RTA_SRC:
414 414 *src = sa;
415 415 break;
416 416 }
417 417 RT_ADVANCE(cp, sa);
418 418 }
419 419 }
420 420
421 421 return (0);
422 422 }
423 423
424 424 /*
425 425 * Given an IPv6 address, use routing information to look up
426 426 * the destination and interface it would pass through.
427 427 */
428 428 static int
429 429 ndp_rtmsg_get(int fd, rtmsg_pkt_t *msg, struct sockaddr *sin6p)
430 430 {
431 431 static int seq = 0;
432 432 struct sockaddr_dl sdl;
433 433 int mlen, l;
434 434 char ipaddr[INET6_ADDRSTRLEN];
435 435 char *cp = msg->m_space;
436 436 struct rt_msghdr *m_rtm = &msg->m_rtm;
437 437
438 438 bzero(msg, sizeof (rtmsg_pkt_t));
439 439 bzero(&sdl, sizeof (struct sockaddr_dl));
440 440
441 441 m_rtm->rtm_type = RTM_GET;
442 442 m_rtm->rtm_version = RTM_VERSION;
443 443 m_rtm->rtm_seq = ++seq;
444 444 m_rtm->rtm_addrs = RTA_DST | RTA_IFP;
445 445 m_rtm->rtm_msglen = sizeof (rtmsg_pkt_t);
446 446
447 447 /* Place the address we're looking up after the header */
448 448 RT_NEXTADDR(cp, RTA_DST, sin6p);
449 449
450 450 /* Load an empty link-level address, so we get an interface back */
451 451 sdl.sdl_family = AF_LINK;
452 452 RT_NEXTADDR(cp, RTA_IFP, (struct sockaddr *)&sdl);
453 453
454 454 m_rtm->rtm_msglen = cp - (char *)msg;
455 455
456 456 if ((mlen = write(fd, (char *)msg, m_rtm->rtm_msglen)) < 0) {
457 457 if (errno == ESRCH) {
458 458 /*LINTED*/
459 459 if (inet_ntop(AF_INET6, &((sin6_t *)sin6p)->sin6_addr,
460 460 ipaddr, sizeof (ipaddr)) == NULL) {
461 461 (void) snprintf(ipaddr, sizeof (ipaddr),
462 462 "(failed to format IP)");
463 463 };
464 464 warnx("An appropriate interface for the address %s "
465 465 "is not in the routing table; use -i to force an "
466 466 "interface", ipaddr);
467 467 return (-1);
468 468 } else {
469 469 warnx("Failed to send routing message: %s",
470 470 strerror(errno));
471 471 return (-1);
472 472 }
473 473 } else if (mlen < (int)m_rtm->rtm_msglen) {
474 474 warnx("Failed to write all bytes to routing socket");
475 475 return (-1);
476 476 }
477 477
478 478 /*
479 479 * Keep reading routing messages until we find the response to the one
480 480 * we just sent. Note that we depend on the sequence number being unique
481 481 * to the running program.
482 482 */
483 483 do {
484 484 mlen = read(fd, (char *)msg, sizeof (rtmsg_pkt_t));
485 485 } while (mlen > 0 &&
486 486 (m_rtm->rtm_seq != seq || m_rtm->rtm_pid != ndp_pid));
487 487 if (mlen < 0) {
488 488 warnx("Failed to read from routing socket: %s",
489 489 strerror(errno));
490 490 return (-1);
491 491 }
492 492
493 493 return (0);
494 494 }
495 495
496 496 /*
497 497 * Find the interface that the IPv6 address would be routed through, and store
498 498 * the name of the interface in the buffer passed in.
499 499 */
500 500 static int
501 501 ndp_find_interface(int fd, struct sockaddr *sin6p, char *buf, int buflen)
502 502 {
503 503 struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *src = NULL;
504 504 struct sockaddr_dl *ifp = NULL;
505 505 rtmsg_pkt_t msg;
506 506
507 507 if (ndp_rtmsg_get(fd, &msg, sin6p) != 0) {
508 508 return (-1);
509 509 }
510 510
511 511 if (ndp_extract_sockaddrs(&msg.m_rtm, &dst, &gate,
512 512 &mask, &src, &ifp) != 0) {
513 513 return (-1);
514 514 }
515 515
516 516 if (ifp == NULL) {
517 517 warnx("Unable to find appropriate interface for address");
518 518 return (-1);
519 519 } else {
520 520 if (ifp->sdl_nlen >= buflen) {
521 521 warnx("The interface name \"%.*s\" is too big for the "
522 522 "available buffer", ifp->sdl_nlen, ifp->sdl_data);
523 523 return (-1);
524 524 } else {
525 525 (void) snprintf(buf, buflen, "%.*s", ifp->sdl_nlen,
526 526 ifp->sdl_data);
527 527 }
528 528 }
529 529
530 530 return (0);
531 531 }
532 532
533 533 /*
534 534 * Zero out a lifreq struct for a SIOCLIF*ND ioctl, set the address, and fetch
535 535 * the appropriate interface using the given routing socket.
536 536 */
537 537 static int
538 538 ndp_initialize_lifreq(int route, struct lifreq *lifrp, struct sockaddr *sap)
539 539 {
540 540 struct sockaddr_storage *lnr_addr;
541 541 /* LINTED E_BAD_PTR_CAST_ALIGN */
542 542 struct sockaddr_in6 *sin6p = (sin6_t *)sap;
543 543 char *lifr_name = lifrp->lifr_name;
544 544
545 545 bzero(lifrp, sizeof (struct lifreq));
546 546 lnr_addr = &lifrp->lifr_nd.lnr_addr;
547 547
548 548 if (ndp_iface != NULL) {
549 549 (void) strlcpy(lifr_name, ndp_iface, LIFNAMSIZ);
550 550 } else if (sin6p->sin6_scope_id != 0) {
551 551 int zone_id = sin6p->sin6_scope_id;
552 552 if (if_indextoname(zone_id, lifr_name) == NULL) {
553 553 warnx("Invalid zone identifier: %d", zone_id);
554 554 return (-1);
555 555 }
556 556 } else if (IN6_IS_ADDR_LINKSCOPE(&sin6p->sin6_addr)) {
557 557 warnx("Link-scope addresses should specify an interface with "
558 558 "a zone ID, or with -i.");
559 559 return (-1);
560 560 } else {
561 561 if (ndp_find_interface(route, sap, lifr_name, LIFNAMSIZ) != 0)
562 562 return (-1);
563 563 }
564 564
565 565 (void) memcpy(lnr_addr, sap, sizeof (struct sockaddr_storage));
566 566
567 567 return (0);
568 568 }
569 569
570 570 /*
571 571 * Take a host identifier, find the corresponding IPv6 addresses and then pass
572 572 * them to the specified function, along with any desired data.
573 573 */
574 574 static int
575 575 ndp_host_enumerate(char *host, ndp_addr_f *addr_func, void *data)
576 576 {
577 577 struct lifreq lifr;
578 578 struct addrinfo hints, *serverinfo, *p;
579 579 int err, attempts = 0;
580 580 int inet6, route;
581 581
582 582 bzero(&hints, sizeof (struct addrinfo));
583 583 hints.ai_family = AF_INET6;
584 584 hints.ai_protocol = IPPROTO_IPV6;
585 585
586 586 while (attempts < MAX_ATTEMPTS) {
587 587 err = getaddrinfo(host, NULL, &hints, &serverinfo);
588 588
589 589 if (err == 0) {
590 590 break;
591 591 } else if (err == EAI_AGAIN) {
592 592 attempts++;
593 593 } else {
594 594 warnx("Unable to lookup %s: %s", host,
595 595 gai_strerror(err));
596 596 return (-1);
597 597 }
598 598 }
599 599
600 600 if (attempts == MAX_ATTEMPTS) {
601 601 warnx("Failed multiple times to lookup %s", host);
602 602 return (-1);
603 603 }
604 604
605 605 inet6 = socket(PF_INET6, SOCK_DGRAM, 0);
606 606 if (inet6 < 0) {
607 607 warnx("Failed to open IPv6 socket: %s", strerror(errno));
608 608 err = -1;
609 609 }
610 610
611 611 route = socket(PF_ROUTE, SOCK_RAW, 0);
612 612 if (route < 0) {
613 613 warnx("Failed to open routing socket: %s", strerror(errno));
614 614 err = -1;
615 615 }
616 616
617 617 if (err == 0) {
618 618 for (p = serverinfo; p != NULL; p = p->ai_next) {
619 619 if (ndp_initialize_lifreq(route, &lifr, p->ai_addr)
620 620 != 0) {
621 621 err = -1;
622 622 continue;
623 623 }
624 624
625 625 if (addr_func(inet6, &lifr, data) != 0) {
626 626 err = -1;
627 627 continue;
628 628 }
629 629 }
630 630 }
631 631
632 632 if (close(route) != 0) {
633 633 warnx("Failed to close routing socket: %s", strerror(errno));
634 634 err = -1;
635 635 }
636 636
637 637 if (close(inet6) != 0) {
638 638 warnx("Failed to close IPv6 socket: %s", strerror(errno));
639 639 err = -1;
640 640 }
641 641
642 642 /* Clean up linked list */
643 643 freeaddrinfo(serverinfo);
644 644
645 645 return (err);
646 646 }
647 647
648 648 static int
649 649 ndp_display(struct lifreq *lifrp)
650 650 {
651 651 struct sockaddr_in6 *lnr_addr;
652 652 char ipaddr[INET6_ADDRSTRLEN];
653 653 char *lladdr = NULL;
654 654 char hostname[NI_MAXHOST];
655 655 int flags, gni_flags;
656 656
657 657 lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
658 658 flags = lifrp->lifr_nd.lnr_flags;
659 659
660 660 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
661 661 sizeof (ipaddr)) == NULL) {
662 662 warnx("Couldn't convert IPv6 address to string: %s",
663 663 strerror(errno));
664 664 return (-1);
665 665 };
666 666
667 667 if ((lladdr = _link_ntoa((uchar_t *)lifrp->lifr_nd.lnr_hdw_addr,
668 668 NULL, lifrp->lifr_nd.lnr_hdw_len, IFT_ETHER)) == NULL) {
669 669 warnx("Couldn't convert link-layer address to string: %s",
670 670 strerror(errno));
671 671 return (-1);
672 672 }
673 673
674 674 gni_flags = ndp_noresolve ? NI_NUMERICHOST : 0;
675 675
676 676 if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
677 677 sizeof (hostname), NULL, 0, gni_flags) != 0) {
678 678 warnx("Unable to lookup hostname for %s", ipaddr);
679 679 free(lladdr);
680 680 return (-1);
681 681 }
682 682
683 683 (void) printf("%s (%s) at %s", ipaddr, hostname, lladdr);
684 684
685 685 if (flags & NDF_ISROUTER_ON) {
686 686 (void) printf(" router");
687 687 }
688 688
689 689 if (flags & NDF_ANYCAST_ON) {
690 690 (void) printf(" any");
691 691 }
692 692
693 693 if (!(flags & NDF_STATIC)) {
694 694 (void) printf(" temp");
695 695 }
696 696
697 697 if (flags & NDF_PROXY_ON) {
698 698 (void) printf(" proxy");
699 699 }
700 700
701 701 (void) printf("\n");
702 702
703 703 free(lladdr);
704 704 return (0);
705 705 }
706 706
707 707 static int
708 708 ndp_display_missing(struct lifreq *lifrp)
709 709 {
710 710 struct sockaddr_in6 *lnr_addr;
711 711 char ipaddr[INET6_ADDRSTRLEN];
712 712 char hostname[NI_MAXHOST];
713 713 int flags = ndp_noresolve ? NI_NUMERICHOST : 0;
714 714 lnr_addr = (struct sockaddr_in6 *)&lifrp->lifr_nd.lnr_addr;
715 715
716 716 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
717 717 sizeof (ipaddr)) == NULL) {
718 718 warnx("Couldn't convert IPv6 address to string: %s",
719 719 strerror(errno));
720 720 return (-1);
721 721 };
722 722
723 723 if (getnameinfo((struct sockaddr *)lnr_addr, sizeof (sin6_t), hostname,
724 724 sizeof (hostname), NULL, 0, flags) != 0) {
725 725 warnx("Unable to lookup hostname for %s", ipaddr);
726 726 return (-1);
727 727 }
728 728
729 729 (void) printf("%s (%s) -- no entry\n", ipaddr, hostname);
730 730 return (0);
731 731 }
732 732
733 733 static void
734 734 ndp_lifr2ip(struct lifreq *lifrp, char *ipaddr, int buflen)
735 735 {
736 736 sin6_t *lnr_addr = (sin6_t *)&lifrp->lifr_nd.lnr_addr;
737 737 if (inet_ntop(AF_INET6, &lnr_addr->sin6_addr, ipaddr,
738 738 buflen) == NULL) {
739 739 (void) snprintf(ipaddr, buflen, "(failed to format IP)");
740 740 };
741 741 }
742 742
743 743 /*
744 744 * Perform a SIOCLIFGETND and print out information about it
745 745 */
746 746 /*ARGSUSED*/
747 747 static int
748 748 ndp_get(int fd, struct lifreq *lifrp, void *unused)
749 749 {
750 750 char ipaddr[INET6_ADDRSTRLEN];
751 751 if (ioctl(fd, SIOCLIFGETND, lifrp) < 0) {
752 752 if (errno == ESRCH) {
753 753 return (ndp_display_missing(lifrp));
754 754 } else {
755 755 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
756 756 warnx("Couldn't lookup %s: %s",
757 757 ipaddr, strerror(errno));
758 758 return (-1);
759 759 }
760 760 }
761 761
762 762 return (ndp_display(lifrp));
763 763 }
764 764
765 765 /*
766 766 * Print out all NDP entries
767 767 */
768 768 static void
769 769 ndp_get_all(void)
770 770 {
771 771 char netstat_path[MAXPATHLEN];
772 772 const char *zroot = zone_get_nroot();
773 773
774 774 (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
775 775 zroot : "", NETSTAT_PATH);
776 776
777 777 (void) execl(netstat_path, "netstat",
778 778 (ndp_noresolve ? "-np" : "-p"),
779 779 "-f", "inet6", (char *)0);
780 780 ndp_fatal("Coudn't exec %s: %s", netstat_path, strerror(errno));
781 781 }
782 782
783 783 /*
784 784 * Perform a SIOCLIFDELND ioctl
785 785 */
786 786 /*ARGSUSED*/
787 787 static int
788 788 ndp_delete(int fd, struct lifreq *lifrp, void *unused)
789 789 {
790 790 char ipaddr[INET6_ADDRSTRLEN];
791 791
792 792 if (ioctl(fd, SIOCLIFDELND, lifrp) < 0) {
793 793 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
794 794 if (errno == ESRCH) {
795 795 warnx("No entry for %s", ipaddr);
796 796 return (-1);
797 797 } else if (errno == EPERM) {
798 798 warnx("Permission denied, "
799 799 "could not delete entry for %s", ipaddr);
800 800 return (-1);
801 801 } else {
802 802 warnx("Couldn't delete mapping for %s: %s",
803 803 ipaddr, strerror(errno));
804 804 return (-1);
805 805 }
806 806 }
807 807
808 808 return (0);
809 809 }
810 810
811 811 /*
812 812 * Perform a SIOCLIFSETND ioctl using properties from the example structure.
813 813 */
814 814 static int
815 815 ndp_set(int fd, struct lifreq *lifrp, void *data)
816 816 {
817 817 char ipaddr[INET6_ADDRSTRLEN];
818 818 const lif_nd_req_t *nd_attrs = data;
819 819
820 820 (void) memcpy(lifrp->lifr_nd.lnr_hdw_addr, nd_attrs->lnr_hdw_addr,
821 821 ND_MAX_HDW_LEN);
822 822 lifrp->lifr_nd.lnr_hdw_len = nd_attrs->lnr_hdw_len;
823 823 lifrp->lifr_nd.lnr_flags = nd_attrs->lnr_flags;
824 824
825 825 lifrp->lifr_nd.lnr_state_create = nd_attrs->lnr_state_create;
826 826 lifrp->lifr_nd.lnr_state_same_lla = nd_attrs->lnr_state_same_lla;
827 827 lifrp->lifr_nd.lnr_state_diff_lla = nd_attrs->lnr_state_diff_lla;
828 828
829 829 if (ioctl(fd, SIOCLIFSETND, lifrp) < 0) {
830 830 ndp_lifr2ip(lifrp, ipaddr, sizeof (ipaddr));
831 831 if (errno == EPERM) {
832 832 warnx("Permission denied, "
833 833 "could not set entry for %s", ipaddr);
834 834 return (-1);
835 835 } else {
836 836 warnx("Failed to set mapping for %s: %s",
837 837 ipaddr, strerror(errno));
838 838 return (-1);
839 839 }
840 840 }
841 841
842 842 return (0);
843 843 }
844 844
845 845 /*
846 846 * Given a host identifier, a link-layer address and possible options,
847 847 * add/update the NDP mappings.
848 848 */
849 849 static int
850 850 ndp_set_nce(char *host, char *lladdr, char *opts[], int optlen)
851 851 {
852 852 lif_nd_req_t nd_attrs;
853 853 uchar_t *ea;
854 854 char *opt;
855 855 int i;
856 856 boolean_t temp = B_FALSE;
857 857 boolean_t any = B_FALSE;
858 858 boolean_t router = B_FALSE;
859 859
860 860 bzero(&nd_attrs, sizeof (lif_nd_req_t));
861 861
862 862 ea = _link_aton(lladdr, &nd_attrs.lnr_hdw_len);
863 863
864 864 if (ea == NULL) {
865 865 warnx("Unable to parse link-layer address \"%s\"", lladdr);
866 866 return (-1);
867 867 }
868 868
869 869 if (nd_attrs.lnr_hdw_len > sizeof (nd_attrs.lnr_hdw_addr)) {
870 870 warnx("The size of the link-layer address is "
871 871 "too large to set\n");
872 872 free(ea);
873 873 return (-1);
874 874 }
875 875
876 876 (void) memcpy(nd_attrs.lnr_hdw_addr, ea, nd_attrs.lnr_hdw_len);
877 877
878 878 free(ea);
879 879
880 880 nd_attrs.lnr_state_create = ND_REACHABLE;
881 881 nd_attrs.lnr_state_same_lla = ND_UNCHANGED;
882 882 nd_attrs.lnr_state_diff_lla = ND_STALE;
883 883
884 884 for (i = 0; i < optlen; i++) {
885 885 opt = opts[i];
886 886 if (strcmp(opt, "temp") == 0) {
887 887 temp = B_TRUE;
888 888 } else if (strcmp(opt, "any") == 0) {
889 889 any = B_TRUE;
890 890 } else if (strcmp(opt, "router") == 0) {
891 891 router = B_TRUE;
892 892 } else if (strcmp(opt, "proxy") == 0) {
893 893 warnx("NDP proxying is currently not supported");
894 894 return (-1);
895 895 } else {
896 896 warnx("Unrecognized option \"%s\"", opt);
897 897 return (-1);
898 898 }
899 899 }
900 900
901 901 if (!temp) {
902 902 nd_attrs.lnr_flags |= NDF_STATIC;
903 903 }
904 904
905 905 if (any) {
906 906 nd_attrs.lnr_flags |= NDF_ANYCAST_ON;
907 907 } else {
908 908 nd_attrs.lnr_flags |= NDF_ANYCAST_OFF;
909 909 }
910 910
911 911 if (router) {
912 912 nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
913 913 } else {
914 914 nd_attrs.lnr_flags |= NDF_ISROUTER_OFF;
915 915 }
916 916
917 917 return (ndp_host_enumerate(host, ndp_set, &nd_attrs));
918 918 }
919 919
920 920 /*
921 921 * Read in a file and set the mappings from each line.
922 922 */
923 923 static int
924 924 ndp_set_file(char *filename)
925 925 {
926 926 char *line = NULL, *lasts = NULL, *curr;
927 927 char *host, *lladdr;
928 928 char *opts[MAX_OPTS];
929 929 int optlen = 0, lineno = 0;
930 930 size_t cap = 0;
931 931 boolean_t failed_line = B_FALSE;
932 932 FILE *stream = fopen(filename, "r");
933 933
934 934 if (stream == NULL) {
935 935 ndp_fatal("Error while opening file %s: %s",
936 936 filename, strerror(errno));
937 937 }
938 938
939 939 errno = 0;
940 940 while (getline(&line, &cap, stream) != -1) {
941 941 lineno++;
942 942
943 943 if (line[0] == '#')
944 944 continue;
945 945
946 946 host = strtok_r(line, WORDSEPS, &lasts);
947 947 if (host == NULL) {
948 948 warnx("Line %d incomplete, skipping: "
949 949 "missing host identifier", lineno);
950 950 failed_line = B_TRUE;
951 951 continue;
952 952 }
953 953
954 954 lladdr = strtok_r(NULL, WORDSEPS, &lasts);
955 955 if (lladdr == NULL) {
956 956 warnx("Line %d incomplete, skipping: "
957 957 "missing link-layer address", lineno);
958 958 failed_line = B_TRUE;
959 959 continue;
960 960 }
961 961
962 962 for (optlen = 0; optlen < MAX_OPTS; optlen++) {
963 963 curr = strtok_r(NULL, WORDSEPS, &lasts);
964 964 if (curr == NULL)
965 965 break;
966 966 opts[optlen] = curr;
967 967 }
968 968
969 969 if (ndp_set_nce(host, lladdr, opts, optlen) != 0) {
970 970 failed_line = B_TRUE;
971 971 continue;
972 972 }
973 973 }
974 974
975 975 free(line);
976 976
977 977 if (errno != 0 || ferror(stream)) {
978 978 ndp_fatal("Error while reading from file %s: %s", filename,
979 979 strerror(errno));
980 980 }
981 981
982 982 if (fclose(stream) != 0) {
983 983 ndp_fatal("Error close file %s: %s", filename, strerror(errno));
984 984 }
985 985
986 986 return (failed_line ? -1 : 0);
987 987 }
988 988
989 989 int
990 990 main(int argc, char *argv[])
991 991 {
992 992 char *flagarg = NULL, *lladdr = NULL;
993 993 char **opts;
994 994 char *endptr;
995 995 int c, argsleft, optlen = 0, err = 0;
996 996 long long period;
997 997 enum ndp_action action = NDP_A_DEFAULT;
998 998
999 999 setprogname(basename(argv[0]));
1000 1000
1001 1001 if (argc < 2) {
1002 1002 ndp_usage("No arguments given.");
1003 1003 }
1004 1004
1005 1005 while ((c = getopt(argc, argv, ":naA:d:f:i:s:")) != -1) {
1006 1006 switch (c) {
1007 1007 case 'n':
1008 1008 ndp_noresolve = B_TRUE;
1009 1009 break;
1010 1010 case 'i':
1011 1011 ndp_iface = optarg;
1012 1012 break;
1013 1013 case 's':
1014 1014 if (action != NDP_A_DEFAULT)
1015 1015 ndp_badflag(action);
1016 1016 action = NDP_A_SET_NCE;
1017 1017 flagarg = optarg;
1018 1018
1019 1019 if ((argc - optind) < 1) {
1020 1020 ndp_usage("Missing link-layer address after "
1021 1021 "the node address, \"%s\"", flagarg);
1022 1022 }
1023 1023 lladdr = argv[optind++];
1024 1024
1025 1025 /*
1026 1026 * Grab any following keywords up to the next flag
1027 1027 */
1028 1028 opts = argv + optind;
1029 1029 while ((argc - optind) > 0) {
1030 1030 if (argv[optind][0] == '-')
1031 1031 ndp_usage("Encountered \"%s\" after "
1032 1032 "flag parsing is done",
1033 1033 argv[optind]);
1034 1034 optind++;
1035 1035 optlen++;
1036 1036 }
1037 1037 break;
1038 1038 case 'a':
1039 1039 if (action != NDP_A_DEFAULT)
1040 1040 ndp_badflag(action);
1041 1041 action = NDP_A_GET_ALL;
1042 1042 break;
1043 1043 case 'A':
1044 1044 if (action != NDP_A_DEFAULT)
1045 1045 ndp_badflag(action);
1046 1046 action = NDP_A_GET_FOREVER;
1047 1047 flagarg = optarg;
1048 1048 break;
1049 1049 case 'd':
1050 1050 if (action != NDP_A_DEFAULT)
1051 1051 ndp_badflag(action);
1052 1052 action = NDP_A_DELETE;
1053 1053 flagarg = optarg;
1054 1054 break;
1055 1055 case 'f':
1056 1056 if (action != NDP_A_DEFAULT)
1057 1057 ndp_badflag(action);
1058 1058 action = NDP_A_SET_FILE;
1059 1059 flagarg = optarg;
1060 1060 break;
1061 1061 case ':':
1062 1062 ndp_missingarg(optopt);
1063 1063 break;
1064 1064 case '?':
1065 1065 ndp_usage("Unrecognized flag \"-%c\"", optopt);
1066 1066 default:
1067 1067 ndp_usage(NULL);
1068 1068 }
1069 1069 }
1070 1070
1071 1071 argsleft = argc - optind;
1072 1072 ndp_pid = getpid();
1073 1073
1074 1074 if (action != NDP_A_DEFAULT && argsleft != 0) {
1075 1075 ndp_usage("Extra arguments leftover after parsing flags");
1076 1076 }
1077 1077
1078 1078 switch (action) {
1079 1079 case NDP_A_DEFAULT:
1080 1080 case NDP_A_GET:
1081 1081 if (argsleft != 1) {
1082 1082 ndp_usage("Multiple arguments given without any flags");
1083 1083 }
1084 1084 err = ndp_host_enumerate(argv[optind], ndp_get, NULL);
1085 1085 break;
1086 1086 case NDP_A_GET_ALL:
1087 1087 ndp_get_all();
1088 1088 /*NOTREACHED*/
1089 1089 break;
1090 1090 case NDP_A_GET_FOREVER:
1091 1091 errno = 0;
1092 1092 period = strtoll(flagarg, &endptr, 10);
1093 1093 if ((period == 0 && errno != 0) ||
1094 1094 (endptr[0] != '\0') ||
1095 1095 (period < 0)) {
1096 1096 ndp_usage("Given period should be a positive integer,"
1097 1097 " not \"%s\"", flagarg);
1098 1098 }
1099 1099 if (period > 86400) {
1100 1100 ndp_usage("Given period should be shorter than a day;"
1101 1101 " given \"%s\" seconds", flagarg);
1102 1102 }
1103 1103 ndp_run_periodically(period, ndp_get_all);
1104 1104 /*NOTREACHED*/
1105 1105 break;
1106 1106 case NDP_A_DELETE:
1107 1107 err = ndp_host_enumerate(flagarg, ndp_delete, NULL);
1108 1108 break;
1109 1109 case NDP_A_SET_NCE:
1110 1110 err = ndp_set_nce(flagarg, lladdr, opts, optlen);
1111 1111 break;
1112 1112 case NDP_A_SET_FILE:
1113 1113 err = ndp_set_file(flagarg);
1114 1114 break;
1115 1115 }
1116 1116
1117 1117 return (err == 0 ? 0 : 1);
1118 1118 }
|
↓ open down ↓ |
1118 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX