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
spoof_get_lla(int s,const char * iface,struct lifreq * addrp,struct lifreq * llap)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
spoof_prepare_lla(struct nd_opt_lla * llap,struct lif_nd_req * nce,struct iovec * iov)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
spoof_prepare_pi(const char * prefix,int prefix_len,struct nd_opt_prefix_info * pip,struct iovec * iov)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
spoof_prepare_header(struct nd_router_advert * ichdrp,struct iovec * iov)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
spoof_set_max_hops(int s)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
spoof_bad_lla_optlen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_bad_pi_optlen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_bad_plen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_link_local_test(int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_good_test(int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_dtrace_launch(void)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
spoof_dtrace_wait(pid_t dtrace,int * stat)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
spoof_run_proc(char * path,char * args[])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
spoof_network_teardown(char * testether,char * testvnic0,char * testvnic1)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
spoof_network_setup(char * testether,char * testvnic0,char * testvnic1)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
spoof_run_test(spoof_test_f * func,int s,struct lif_nd_req * nce,sin6_t * multicast)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
spoof_run_tests(int s,struct lif_nd_req * nce)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
spoof_check_privs(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
main(void)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