1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Joyent, Inc. All rights reserved. 14 */ 15 16 #include <strings.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <errno.h> 20 #include <err.h> 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <sys/sockio.h> 24 #include <sys/wait.h> 25 #include <unistd.h> 26 #include <signal.h> 27 #include <netinet/in_systm.h> /* legacy network types needed by ip_icmp.h */ 28 #include <netinet/in.h> 29 #include <netinet/ip.h> 30 #include <netinet/ip6.h> 31 #include <netinet/ip_icmp.h> 32 #include <netinet/icmp6.h> 33 #include <net/if.h> 34 #include <arpa/inet.h> 35 #include <priv.h> 36 37 /* 38 * This program is meant to test the behaviour of processing incoming Router 39 * Advertisements when IP spoofing protection (ip-nospoof) is enabled. When 40 * run, it creates an etherstub on which it places two VNICs: a source VNIC, 41 * and a destination VNIC with protection enabled. It then sends out spoofed 42 * Router Advertisements with varying incorrect values. 43 * 44 * IMPORTANT: These tests expect that there is no other IPv6 traffic on the 45 * machine that would be delivered to a VNIC with spoofing protection enabled, 46 * since this would trip the DTrace probes installed by this suite of tests. 47 * Care should therefore be taken to not run it as a part of any series of 48 * tests which may be executed in such an environment, as it could lead to 49 * spurious failures. 50 */ 51 52 #define DLADM(args...) spoof_run_proc("/usr/sbin/dladm", \ 53 (char *[]) { "dladm", args, NULL }) 54 #define IFCONFIG(args...) spoof_run_proc("/usr/sbin/ifconfig", \ 55 (char *[]) { "ifconfig", args, NULL }) 56 57 typedef struct sockaddr_in6 sin6_t; 58 typedef int (spoof_test_f)(int, struct lif_nd_req *, sin6_t *); 59 60 /* 61 * Get the link-layer address of the given interface by querying 62 * the neighbour cache. 63 */ 64 static int 65 spoof_get_lla(int s, const char *iface, struct lifreq *addrp, 66 struct lifreq *llap) 67 { 68 if (strstr(iface, ":")) { 69 warnx("Specified interface should be the zeroth " 70 "logical interface on the physical device."); 71 } 72 73 bzero(addrp, sizeof (*addrp)); 74 bzero(llap, sizeof (*llap)); 75 76 (void) strlcpy(addrp->lifr_name, iface, LIFNAMSIZ); 77 if (ioctl(s, SIOCGLIFADDR, addrp) < 0) { 78 warn("Unable to get link-local address"); 79 return (-1); 80 } 81 82 (void) strlcpy(llap->lifr_name, iface, LIFNAMSIZ); 83 bcopy(&addrp->lifr_addr, &llap->lifr_nd.lnr_addr, 84 sizeof (struct sockaddr_storage)); 85 86 if (ioctl(s, SIOCLIFGETND, llap) < 0) { 87 warn("Failed to get link-layer address"); 88 return (-1); 89 } 90 91 return (0); 92 } 93 94 static void 95 spoof_prepare_lla(struct nd_opt_lla *llap, struct lif_nd_req *nce, 96 struct iovec *iov) 97 { 98 uint_t optlen; 99 100 bzero(llap, sizeof (*llap)); 101 llap->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR; 102 optlen = ((sizeof (struct nd_opt_hdr) + 103 nce->lnr_hdw_len + 7) / 8) * 8; 104 llap->nd_opt_lla_len = optlen / 8; 105 bcopy(&nce->lnr_hdw_addr, 106 &llap->nd_opt_lla_hdw_addr, nce->lnr_hdw_len); 107 108 iov->iov_base = (caddr_t)llap; 109 iov->iov_len = optlen; 110 } 111 112 static void 113 spoof_prepare_pi(const char *prefix, int prefix_len, 114 struct nd_opt_prefix_info *pip, struct iovec *iov) 115 { 116 bzero(pip, sizeof (*pip)); 117 118 pip->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; 119 pip->nd_opt_pi_len = 4; 120 pip->nd_opt_pi_prefix_len = prefix_len; 121 pip->nd_opt_pi_flags_reserved = 122 ND_OPT_PI_FLAG_AUTO | ND_OPT_PI_FLAG_ONLINK; 123 pip->nd_opt_pi_valid_time = 86400; 124 pip->nd_opt_pi_preferred_time = 86400; 125 if (inet_pton(AF_INET6, prefix, &pip->nd_opt_pi_prefix) == 0) { 126 errx(EXIT_FAILURE, "The prefix \"%s\" is " 127 "not a valid input prefix", prefix); 128 } 129 130 iov->iov_base = (caddr_t)pip; 131 iov->iov_len = sizeof (*pip); 132 } 133 134 static void 135 spoof_prepare_header(struct nd_router_advert *ichdrp, struct iovec *iov) 136 { 137 bzero(ichdrp, sizeof (*ichdrp)); 138 139 ichdrp->nd_ra_type = ND_ROUTER_ADVERT; 140 ichdrp->nd_ra_curhoplimit = 0; 141 142 iov->iov_base = (caddr_t)ichdrp; 143 iov->iov_len = sizeof (*ichdrp); 144 } 145 146 static int 147 spoof_set_max_hops(int s) 148 { 149 int ttl = 255; 150 151 if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 152 (char *)&ttl, sizeof (ttl)) < 0) { 153 warn("Failed to set IPV6_UNICAST_HOPS socket option"); 154 return (-1); 155 } 156 if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 157 (char *)&ttl, sizeof (ttl)) < 0) { 158 warn("Failed to set IPV6_UNICAST_HOPS socket option"); 159 return (-1); 160 } 161 162 return (0); 163 } 164 165 /* 166 * Send bad option lengths in the Link-Layer Source Address option 167 */ 168 static int 169 spoof_bad_lla_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) 170 { 171 struct msghdr msg6; 172 struct iovec iovs[3]; 173 struct nd_router_advert ichdr; 174 struct nd_opt_lla lla; 175 struct nd_opt_prefix_info pi; 176 uint8_t old_lla_len; 177 178 spoof_prepare_header(&ichdr, &iovs[0]); 179 spoof_prepare_lla(&lla, nce, &iovs[1]); 180 spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); 181 182 /* Prepare message */ 183 bzero(&msg6, sizeof (struct msghdr)); 184 msg6.msg_name = multicast; 185 msg6.msg_namelen = sizeof (sin6_t); 186 msg6.msg_iov = iovs; 187 msg6.msg_iovlen = 3; 188 189 old_lla_len = lla.nd_opt_lla_len; 190 191 192 /* 193 * Length is now smaller than the option is, so this should 194 * be rejected. 195 */ 196 lla.nd_opt_lla_len = 0; 197 if (sendmsg(s, &msg6, 0) < 0) { 198 warn("Failed to send ICMPv6 message"); 199 return (-1); 200 } 201 202 /* 203 * Length is bigger than the option, so the following prefix 204 * will be offset. 205 */ 206 lla.nd_opt_lla_len = 2; 207 if (sendmsg(s, &msg6, 0) < 0) { 208 warn("Failed to send ICMPv6 message"); 209 return (-1); 210 } 211 212 /* 213 * Restore the length, but shorten the amount of data to send, so we're 214 * sending truncated packets. (Stop before 0, so that we still send part 215 * of the option.) 216 */ 217 lla.nd_opt_lla_len = old_lla_len; 218 for (iovs[1].iov_len--; iovs[1].iov_len > 0; iovs[1].iov_len--) { 219 if (sendmsg(s, &msg6, 0) < 0) { 220 warn("Failed to send ICMPv6 message"); 221 return (-1); 222 } 223 } 224 225 return (0); 226 } 227 228 /* 229 * Send bad option lengths in the Prefix Information option 230 */ 231 static int 232 spoof_bad_pi_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) 233 { 234 struct msghdr msg6; 235 struct iovec iovs[3]; 236 struct nd_router_advert ichdr; 237 struct nd_opt_lla lla; 238 struct nd_opt_prefix_info pi; 239 uint8_t old_pi_len; 240 241 spoof_prepare_header(&ichdr, &iovs[0]); 242 spoof_prepare_lla(&lla, nce, &iovs[1]); 243 spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); 244 245 /* Prepare message */ 246 bzero(&msg6, sizeof (struct msghdr)); 247 msg6.msg_name = multicast; 248 msg6.msg_namelen = sizeof (sin6_t); 249 msg6.msg_iov = iovs; 250 msg6.msg_iovlen = 3; 251 252 old_pi_len = pi.nd_opt_pi_len; 253 254 /* 255 * Length is now smaller than the option is, so this should 256 * be rejected. 257 */ 258 pi.nd_opt_pi_len = 0; 259 if (sendmsg(s, &msg6, 0) < 0) { 260 warn("Failed to send ICMPv6 message"); 261 return (-1); 262 } 263 264 /* 265 * Length is smaller than a PI option should be. 266 */ 267 pi.nd_opt_pi_len = 3; 268 if (sendmsg(s, &msg6, 0) < 0) { 269 warn("Failed to send ICMPv6 message"); 270 return (-1); 271 } 272 273 /* 274 * Length is bigger than the option, so the following prefix 275 * will be offset. 276 */ 277 pi.nd_opt_pi_len = 5; 278 if (sendmsg(s, &msg6, 0) < 0) { 279 warn("Failed to send ICMPv6 message"); 280 return (-1); 281 } 282 283 /* 284 * Restore the length, but shorten the amount of data to send, so we're 285 * sending truncated packets. (Stop before 0, so that we still send part 286 * of the option.) 287 */ 288 pi.nd_opt_pi_len = old_pi_len; 289 for (iovs[2].iov_len--; iovs[2].iov_len > 0; iovs[2].iov_len--) { 290 if (sendmsg(s, &msg6, 0) < 0) { 291 warn("Failed to send ICMPv6 message"); 292 return (-1); 293 } 294 } 295 296 return (0); 297 } 298 299 /* 300 * Advertise a prefix with a prefix length greater than 128. 301 */ 302 static int 303 spoof_bad_plen_test(int s, struct lif_nd_req *nce, sin6_t *multicast) 304 { 305 struct msghdr msg6; 306 struct iovec iovs[3]; 307 struct nd_router_advert ichdr; 308 struct nd_opt_lla lla; 309 struct nd_opt_prefix_info pi; 310 311 spoof_prepare_header(&ichdr, &iovs[0]); 312 spoof_prepare_lla(&lla, nce, &iovs[1]); 313 spoof_prepare_pi("fd00::", 130, &pi, &iovs[2]); 314 315 /* Prepare message */ 316 bzero(&msg6, sizeof (struct msghdr)); 317 msg6.msg_name = multicast; 318 msg6.msg_namelen = sizeof (sin6_t); 319 msg6.msg_iov = iovs; 320 msg6.msg_iovlen = 3; 321 322 if (sendmsg(s, &msg6, 0) < 0) { 323 warn("Failed to send ICMPv6 message"); 324 return (-1); 325 } 326 327 return (0); 328 } 329 330 /* 331 * Advertise a link-local prefix, which should be disallowed and ignored. 332 */ 333 static int 334 spoof_link_local_test(int s, struct lif_nd_req *nce, sin6_t *multicast) 335 { 336 struct msghdr msg6; 337 struct iovec iovs[3]; 338 struct nd_router_advert ichdr; 339 struct nd_opt_lla lla; 340 struct nd_opt_prefix_info pi; 341 342 spoof_prepare_header(&ichdr, &iovs[0]); 343 spoof_prepare_lla(&lla, nce, &iovs[1]); 344 spoof_prepare_pi("fe80::", 64, &pi, &iovs[2]); 345 346 /* Prepare message */ 347 bzero(&msg6, sizeof (struct msghdr)); 348 msg6.msg_name = multicast; 349 msg6.msg_namelen = sizeof (sin6_t); 350 msg6.msg_iov = iovs; 351 msg6.msg_iovlen = 3; 352 353 if (sendmsg(s, &msg6, 0) < 0) { 354 warn("Failed to send ICMPv6 message"); 355 return (-1); 356 } 357 358 return (0); 359 } 360 361 static int 362 spoof_good_test(int s, struct lif_nd_req *nce, sin6_t *multicast) 363 { 364 struct msghdr msg6; 365 struct iovec iovs[3]; 366 struct nd_router_advert ichdr; 367 struct nd_opt_lla lla; 368 struct nd_opt_prefix_info pi; 369 370 spoof_prepare_header(&ichdr, &iovs[0]); 371 spoof_prepare_lla(&lla, nce, &iovs[1]); 372 spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]); 373 374 /* Prepare message */ 375 bzero(&msg6, sizeof (struct msghdr)); 376 msg6.msg_name = multicast; 377 msg6.msg_namelen = sizeof (sin6_t); 378 msg6.msg_iov = iovs; 379 msg6.msg_iovlen = 3; 380 381 if (sendmsg(s, &msg6, 0) < 0) { 382 warn("Failed to send ICMPv6 message"); 383 return (-1); 384 } 385 386 return (0); 387 } 388 389 static spoof_test_f *test_cases[] = { 390 spoof_bad_lla_optlen_test, 391 spoof_bad_pi_optlen_test, 392 spoof_bad_plen_test, 393 spoof_link_local_test 394 }; 395 396 static int test_cases_count = sizeof (test_cases) / sizeof (spoof_test_f *); 397 398 static pid_t 399 spoof_dtrace_launch(void) 400 { 401 pid_t child_pid = fork(); 402 if (child_pid == (pid_t)-1) { 403 err(EXIT_FAILURE, "Failed to fork to execute dtrace"); 404 } else if (child_pid == (pid_t)0) { 405 (void) execl("/usr/sbin/dtrace", "dtrace", "-q", 406 "-n", "sdt:mac:insert_slaac_ip:generated-addr { exit(10) }", 407 NULL); 408 err(EXIT_FAILURE, "Failed to execute dtrace"); 409 } 410 411 return (child_pid); 412 } 413 414 static pid_t 415 spoof_dtrace_wait(pid_t dtrace, int *stat) 416 { 417 int retpid; 418 419 /* Give time for probe to fire before checking status */ 420 (void) sleep(5); 421 422 while ((retpid = waitpid(dtrace, stat, WNOHANG)) == -1) { 423 if (errno == EINTR) 424 continue; 425 426 err(EXIT_FAILURE, "Failed to wait on child"); 427 } 428 429 return (retpid); 430 } 431 432 /* 433 * Run a function that's going to exec in a child process, and don't return 434 * until it exits. 435 */ 436 static int 437 spoof_run_proc(char *path, char *args[]) 438 { 439 pid_t child_pid; 440 int childstat = 0, status = 0; 441 442 child_pid = fork(); 443 if (child_pid == (pid_t)-1) { 444 err(EXIT_FAILURE, "Unable to fork to execute %s", path); 445 } else if (child_pid == (pid_t)0) { 446 (void) execv(path, args); 447 err(EXIT_FAILURE, "Failed to execute %s", path); 448 } 449 450 while (waitpid(child_pid, &childstat, 0) == -1) { 451 if (errno == EINTR) 452 continue; 453 454 warn("Failed to wait on child"); 455 return (-1); 456 } 457 458 status = WEXITSTATUS(childstat); 459 if (status != 0) { 460 warnx("Child process %s exited with %d", path, status); 461 return (-1); 462 } 463 464 return (0); 465 } 466 467 static void 468 spoof_network_teardown(char *testether, char *testvnic0, char *testvnic1) 469 { 470 // Delete dest vnic 471 (void) IFCONFIG(testvnic1, "inet6", "unplumb"); 472 (void) DLADM("delete-vnic", testvnic1); 473 474 // Delete source vnic 475 (void) IFCONFIG(testvnic0, "inet6", "unplumb"); 476 (void) DLADM("delete-vnic", testvnic0); 477 478 // Delete etherstub 479 (void) DLADM("delete-etherstub", testether); 480 } 481 482 static int 483 spoof_network_setup(char *testether, char *testvnic0, char *testvnic1) 484 { 485 // Create etherstub 486 if (DLADM("create-etherstub", "-t", testether) != 0) { 487 warnx("Failed to create etherstub for test network"); 488 return (-1); 489 } 490 491 // Create source vnic 492 if (DLADM("create-vnic", "-t", "-l", testether, testvnic0) != 0) { 493 warnx("Failed to create source VNIC for test network"); 494 return (-1); 495 } 496 497 if (IFCONFIG(testvnic0, "inet6", "plumb", "up") != 0) { 498 warnx("Failed to plumb source VNIC for test network"); 499 return (-1); 500 } 501 502 // Create dest vnic 503 if (DLADM("create-vnic", "-t", "-l", testether, 504 "-p", "protection=mac-nospoof,restricted,ip-nospoof,dhcp-nospoof", 505 testvnic1) != 0) { 506 warnx("Failed to create destination VNIC for test network"); 507 return (-1); 508 } 509 510 if (IFCONFIG(testvnic1, "inet6", "plumb", "up") != 0) { 511 warnx("Failed to plumb destination VNIC for test network"); 512 return (-1); 513 } 514 515 return (0); 516 } 517 518 static void 519 spoof_run_test(spoof_test_f *func, int s, struct lif_nd_req *nce, 520 sin6_t *multicast) 521 { 522 static int cas = 1; 523 (void) printf("Executing test case #%d...", cas++); 524 if (func(s, nce, multicast) == 0) { 525 (void) printf(" Done.\n"); 526 } else { 527 (void) printf(" Error while running!\n"); 528 } 529 } 530 531 static int 532 spoof_run_tests(int s, struct lif_nd_req *nce) 533 { 534 int cas, stat; 535 pid_t dtrace; 536 sin6_t multicast; 537 538 /* Prepare all-nodes multicast address */ 539 bzero(&multicast, sizeof (multicast)); 540 multicast.sin6_family = AF_INET6; 541 (void) inet_pton(AF_INET6, "ff02::1", &multicast.sin6_addr); 542 543 dtrace = spoof_dtrace_launch(); 544 545 /* Wait an adequate amount of time for the probes to be installed */ 546 (void) sleep(5); 547 548 /* 549 * We send a packet where everything is good, except for the hop limit. 550 * This packet should be rejected. 551 */ 552 spoof_run_test(spoof_good_test, s, nce, &multicast); 553 554 if (spoof_set_max_hops(s) != 0) { 555 warnx("Failed to set hop limit on socket"); 556 return (EXIT_FAILURE); 557 } 558 559 for (cas = 0; cas < test_cases_count; cas++) { 560 spoof_run_test(test_cases[cas], s, nce, &multicast); 561 } 562 563 564 if (spoof_dtrace_wait(dtrace, &stat) != 0) { 565 (void) printf("One or more tests of bad behaviour failed!\n"); 566 return (EXIT_FAILURE); 567 } 568 569 /* 570 * Now that we've executed all of the test cases that should fail, we 571 * can execute the test that should succeed, to make sure the normal 572 * case works properly. This should trip the dtrace probe. 573 */ 574 spoof_run_test(spoof_good_test, s, nce, &multicast); 575 576 if (spoof_dtrace_wait(dtrace, &stat) != 0 && WIFEXITED(stat) && 577 WEXITSTATUS(stat) == 10) { 578 (void) printf("Tests completed successfully!\n"); 579 } else { 580 if (kill(dtrace, SIGKILL) != 0) { 581 warn("Failed to kill dtrace child (pid %d)", dtrace); 582 } 583 (void) printf("Test of normal behaviour didn't succeed!\n"); 584 return (EXIT_FAILURE); 585 } 586 587 return (0); 588 } 589 590 /* 591 * Make sure that we have all of the privileges we need to execute these tests, 592 * so that we can error out before we would fail. 593 */ 594 void 595 spoof_check_privs(void) 596 { 597 priv_set_t *privset = priv_allocset(); 598 599 if (privset == NULL) { 600 err(EXIT_FAILURE, "Failed to allocate memory for " 601 "checking privileges"); 602 } 603 604 if (getppriv(PRIV_EFFECTIVE, privset) != 0) { 605 err(EXIT_FAILURE, "Failed to get current privileges"); 606 } 607 608 if (!priv_ismember(privset, PRIV_DTRACE_KERNEL)) { 609 errx(EXIT_FAILURE, "These tests need to be run as a user " 610 "capable of tracing the kernel."); 611 } 612 613 if (!priv_ismember(privset, PRIV_SYS_NET_CONFIG)) { 614 errx(EXIT_FAILURE, "These tests need to be run as a user " 615 "capable of creating and configuring network interfaces."); 616 } 617 618 if (!priv_ismember(privset, PRIV_NET_ICMPACCESS)) { 619 errx(EXIT_FAILURE, "These tests need to be run as a user " 620 "capable of sending ICMP packets."); 621 } 622 623 priv_freeset(privset); 624 } 625 626 int 627 main(void) 628 { 629 struct lifreq addr, llar; 630 int error, s; 631 char testether[LIFNAMSIZ]; 632 char testvnic0[LIFNAMSIZ]; 633 char testvnic1[LIFNAMSIZ]; 634 pid_t curpid = getpid(); 635 636 spoof_check_privs(); 637 638 /* 639 * Set up the socket and test network for sending 640 */ 641 s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 642 if (s < 0) { 643 err(EXIT_FAILURE, "Failed to open ICMPv6 socket"); 644 } 645 646 (void) snprintf(testether, sizeof (testether), "testether%d", curpid); 647 (void) snprintf(testvnic0, sizeof (testvnic0), "testvnic%d_0", curpid); 648 (void) snprintf(testvnic1, sizeof (testvnic1), "testvnic%d_1", curpid); 649 650 if (spoof_network_setup(testether, testvnic0, testvnic1) != 0) { 651 warnx("Failed to set up test network"); 652 error = EXIT_FAILURE; 653 goto cleanup; 654 } 655 656 if (spoof_get_lla(s, testvnic0, &addr, &llar) != 0) { 657 warnx("Failed to get link-layer address"); 658 error = EXIT_FAILURE; 659 goto cleanup; 660 } 661 662 if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, 663 (char *)&((sin6_t *)&addr.lifr_addr)->sin6_scope_id, 664 sizeof (int)) < 0) { 665 warn("Failed to set IPV6_UNICAST_HOPS socket option"); 666 return (-1); 667 } 668 669 if (bind(s, (struct sockaddr *)&addr.lifr_addr, sizeof (sin6_t)) != 0) { 670 warnx("Failed to bind to link-local address"); 671 error = EXIT_FAILURE; 672 goto cleanup; 673 } 674 675 error = spoof_run_tests(s, &llar.lifr_nd); 676 677 cleanup: 678 if (close(s) != 0) { 679 warnx("Failed to close ICMPv6 socket"); 680 } 681 spoof_network_teardown(testether, testvnic0, testvnic1); 682 return (error); 683 } 684