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