1797f979dSCody Peter Mello /*
2797f979dSCody Peter Mello  * This file and its contents are supplied under the terms of the
3797f979dSCody Peter Mello  * Common Development and Distribution License ("CDDL"), version 1.0.
4797f979dSCody Peter Mello  * You may only use this file in accordance with the terms of version
5797f979dSCody Peter Mello  * 1.0 of the CDDL.
6797f979dSCody Peter Mello  *
7797f979dSCody Peter Mello  * A full copy of the text of the CDDL should have accompanied this
8797f979dSCody Peter Mello  * source.  A copy of the CDDL is also available via the Internet at
9797f979dSCody Peter Mello  * http://www.illumos.org/license/CDDL.
10797f979dSCody Peter Mello  */
11797f979dSCody Peter Mello 
12797f979dSCody Peter Mello /*
13797f979dSCody Peter Mello  * Copyright 2015 Joyent, Inc. All rights reserved.
14797f979dSCody Peter Mello  */
15797f979dSCody Peter Mello 
16797f979dSCody Peter Mello #include <strings.h>
17797f979dSCody Peter Mello #include <stdio.h>
18797f979dSCody Peter Mello #include <stdlib.h>
19797f979dSCody Peter Mello #include <errno.h>
20797f979dSCody Peter Mello #include <err.h>
21797f979dSCody Peter Mello #include <sys/types.h>
22797f979dSCody Peter Mello #include <sys/socket.h>
23797f979dSCody Peter Mello #include <sys/sockio.h>
24797f979dSCody Peter Mello #include <sys/wait.h>
25797f979dSCody Peter Mello #include <unistd.h>
26797f979dSCody Peter Mello #include <signal.h>
27797f979dSCody Peter Mello #include <netinet/in_systm.h> /* legacy network types needed by ip_icmp.h */
28797f979dSCody Peter Mello #include <netinet/in.h>
29797f979dSCody Peter Mello #include <netinet/ip.h>
30797f979dSCody Peter Mello #include <netinet/ip6.h>
31797f979dSCody Peter Mello #include <netinet/ip_icmp.h>
32797f979dSCody Peter Mello #include <netinet/icmp6.h>
33797f979dSCody Peter Mello #include <net/if.h>
34797f979dSCody Peter Mello #include <arpa/inet.h>
35797f979dSCody Peter Mello #include <priv.h>
36797f979dSCody Peter Mello 
37797f979dSCody Peter Mello /*
38797f979dSCody Peter Mello  * This program is meant to test the behaviour of processing incoming Router
39797f979dSCody Peter Mello  * Advertisements when IP spoofing protection (ip-nospoof) is enabled. When
40797f979dSCody Peter Mello  * run, it creates an etherstub on which it places two VNICs: a source VNIC,
41797f979dSCody Peter Mello  * and a destination VNIC with protection enabled. It then sends out spoofed
42797f979dSCody Peter Mello  * Router Advertisements with varying incorrect values.
43797f979dSCody Peter Mello  *
44797f979dSCody Peter Mello  * IMPORTANT: These tests expect that there is no other IPv6 traffic on the
45797f979dSCody Peter Mello  * machine that would be delivered to a VNIC with spoofing protection enabled,
46797f979dSCody Peter Mello  * since this would trip the DTrace probes installed by this suite of tests.
47797f979dSCody Peter Mello  * Care should therefore be taken to not run it as a part of any series of
48797f979dSCody Peter Mello  * tests which may be executed in such an environment, as it could lead to
49797f979dSCody Peter Mello  * spurious failures.
50797f979dSCody Peter Mello  */
51797f979dSCody Peter Mello 
52797f979dSCody Peter Mello #define	DLADM(args...) spoof_run_proc("/usr/sbin/dladm", \
53797f979dSCody Peter Mello 	(char *[]) { "dladm", args, NULL })
54797f979dSCody Peter Mello #define	IFCONFIG(args...) spoof_run_proc("/usr/sbin/ifconfig", \
55797f979dSCody Peter Mello 	(char *[]) { "ifconfig", args, NULL })
56797f979dSCody Peter Mello 
57797f979dSCody Peter Mello typedef	struct	sockaddr_in6	sin6_t;
58797f979dSCody Peter Mello typedef	int	(spoof_test_f)(int, struct lif_nd_req *, sin6_t *);
59797f979dSCody Peter Mello 
60797f979dSCody Peter Mello /*
61797f979dSCody Peter Mello  * Get the link-layer address of the given interface by querying
62797f979dSCody Peter Mello  * the neighbour cache.
63797f979dSCody Peter Mello  */
64797f979dSCody Peter Mello static int
spoof_get_lla(int s,const char * iface,struct lifreq * addrp,struct lifreq * llap)65797f979dSCody Peter Mello spoof_get_lla(int s, const char *iface, struct lifreq *addrp,
66797f979dSCody Peter Mello     struct lifreq *llap)
67797f979dSCody Peter Mello {
68797f979dSCody Peter Mello 	if (strstr(iface, ":")) {
69797f979dSCody Peter Mello 		warnx("Specified interface should be the zeroth "
70797f979dSCody Peter Mello 		    "logical interface on the physical device.");
71797f979dSCody Peter Mello 	}
72797f979dSCody Peter Mello 
73797f979dSCody Peter Mello 	bzero(addrp, sizeof (*addrp));
74797f979dSCody Peter Mello 	bzero(llap, sizeof (*llap));
75797f979dSCody Peter Mello 
76797f979dSCody Peter Mello 	(void) strlcpy(addrp->lifr_name, iface, LIFNAMSIZ);
77797f979dSCody Peter Mello 	if (ioctl(s, SIOCGLIFADDR, addrp) < 0) {
78797f979dSCody Peter Mello 		warn("Unable to get link-local address");
79797f979dSCody Peter Mello 		return (-1);
80797f979dSCody Peter Mello 	}
81797f979dSCody Peter Mello 
82797f979dSCody Peter Mello 	(void) strlcpy(llap->lifr_name, iface, LIFNAMSIZ);
83797f979dSCody Peter Mello 	bcopy(&addrp->lifr_addr, &llap->lifr_nd.lnr_addr,
84797f979dSCody Peter Mello 	    sizeof (struct sockaddr_storage));
85797f979dSCody Peter Mello 
86797f979dSCody Peter Mello 	if (ioctl(s, SIOCLIFGETND, llap) < 0) {
87797f979dSCody Peter Mello 		warn("Failed to get link-layer address");
88797f979dSCody Peter Mello 		return (-1);
89797f979dSCody Peter Mello 	}
90797f979dSCody Peter Mello 
91797f979dSCody Peter Mello 	return (0);
92797f979dSCody Peter Mello }
93797f979dSCody Peter Mello 
94797f979dSCody Peter Mello static void
spoof_prepare_lla(struct nd_opt_lla * llap,struct lif_nd_req * nce,struct iovec * iov)95797f979dSCody Peter Mello spoof_prepare_lla(struct nd_opt_lla *llap, struct lif_nd_req *nce,
96797f979dSCody Peter Mello     struct iovec *iov)
97797f979dSCody Peter Mello {
98797f979dSCody Peter Mello 	uint_t optlen;
99797f979dSCody Peter Mello 
100797f979dSCody Peter Mello 	bzero(llap, sizeof (*llap));
101797f979dSCody Peter Mello 	llap->nd_opt_lla_type = ND_OPT_SOURCE_LINKADDR;
102797f979dSCody Peter Mello 	optlen = ((sizeof (struct nd_opt_hdr) +
103797f979dSCody Peter Mello 	    nce->lnr_hdw_len + 7) / 8) * 8;
104797f979dSCody Peter Mello 	llap->nd_opt_lla_len = optlen / 8;
105797f979dSCody Peter Mello 	bcopy(&nce->lnr_hdw_addr,
106797f979dSCody Peter Mello 	    &llap->nd_opt_lla_hdw_addr, nce->lnr_hdw_len);
107797f979dSCody Peter Mello 
108797f979dSCody Peter Mello 	iov->iov_base = (caddr_t)llap;
109797f979dSCody Peter Mello 	iov->iov_len = optlen;
110797f979dSCody Peter Mello }
111797f979dSCody Peter Mello 
112797f979dSCody Peter Mello static void
spoof_prepare_pi(const char * prefix,int prefix_len,struct nd_opt_prefix_info * pip,struct iovec * iov)113797f979dSCody Peter Mello spoof_prepare_pi(const char *prefix, int prefix_len,
114797f979dSCody Peter Mello     struct nd_opt_prefix_info *pip, struct iovec *iov)
115797f979dSCody Peter Mello {
116797f979dSCody Peter Mello 	bzero(pip, sizeof (*pip));
117797f979dSCody Peter Mello 
118797f979dSCody Peter Mello 	pip->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
119797f979dSCody Peter Mello 	pip->nd_opt_pi_len = 4;
120797f979dSCody Peter Mello 	pip->nd_opt_pi_prefix_len = prefix_len;
121797f979dSCody Peter Mello 	pip->nd_opt_pi_flags_reserved =
122797f979dSCody Peter Mello 	    ND_OPT_PI_FLAG_AUTO | ND_OPT_PI_FLAG_ONLINK;
123797f979dSCody Peter Mello 	pip->nd_opt_pi_valid_time = 86400;
124797f979dSCody Peter Mello 	pip->nd_opt_pi_preferred_time = 86400;
125797f979dSCody Peter Mello 	if (inet_pton(AF_INET6, prefix, &pip->nd_opt_pi_prefix) == 0) {
126797f979dSCody Peter Mello 		errx(EXIT_FAILURE, "The prefix \"%s\" is "
127797f979dSCody Peter Mello 		    "not a valid input prefix", prefix);
128797f979dSCody Peter Mello 	}
129797f979dSCody Peter Mello 
130797f979dSCody Peter Mello 	iov->iov_base = (caddr_t)pip;
131797f979dSCody Peter Mello 	iov->iov_len = sizeof (*pip);
132797f979dSCody Peter Mello }
133797f979dSCody Peter Mello 
134797f979dSCody Peter Mello static void
spoof_prepare_header(struct nd_router_advert * ichdrp,struct iovec * iov)135797f979dSCody Peter Mello spoof_prepare_header(struct nd_router_advert *ichdrp, struct iovec *iov)
136797f979dSCody Peter Mello {
137797f979dSCody Peter Mello 	bzero(ichdrp, sizeof (*ichdrp));
138797f979dSCody Peter Mello 
139797f979dSCody Peter Mello 	ichdrp->nd_ra_type = ND_ROUTER_ADVERT;
140797f979dSCody Peter Mello 	ichdrp->nd_ra_curhoplimit = 0;
141797f979dSCody Peter Mello 
142797f979dSCody Peter Mello 	iov->iov_base = (caddr_t)ichdrp;
143797f979dSCody Peter Mello 	iov->iov_len = sizeof (*ichdrp);
144797f979dSCody Peter Mello }
145797f979dSCody Peter Mello 
146797f979dSCody Peter Mello static int
spoof_set_max_hops(int s)147797f979dSCody Peter Mello spoof_set_max_hops(int s)
148797f979dSCody Peter Mello {
149797f979dSCody Peter Mello 	int ttl = 255;
150797f979dSCody Peter Mello 
151797f979dSCody Peter Mello 	if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
152797f979dSCody Peter Mello 	    (char *)&ttl, sizeof (ttl)) < 0) {
153797f979dSCody Peter Mello 		warn("Failed to set IPV6_UNICAST_HOPS socket option");
154797f979dSCody Peter Mello 		return (-1);
155797f979dSCody Peter Mello 	}
156797f979dSCody Peter Mello 	if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
157797f979dSCody Peter Mello 	    (char *)&ttl, sizeof (ttl)) < 0) {
158797f979dSCody Peter Mello 		warn("Failed to set IPV6_UNICAST_HOPS socket option");
159797f979dSCody Peter Mello 		return (-1);
160797f979dSCody Peter Mello 	}
161797f979dSCody Peter Mello 
162797f979dSCody Peter Mello 	return (0);
163797f979dSCody Peter Mello }
164797f979dSCody Peter Mello 
165797f979dSCody Peter Mello /*
166797f979dSCody Peter Mello  * Send bad option lengths in the Link-Layer Source Address option
167797f979dSCody Peter Mello  */
168797f979dSCody Peter Mello static int
spoof_bad_lla_optlen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)169797f979dSCody Peter Mello spoof_bad_lla_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast)
170797f979dSCody Peter Mello {
171797f979dSCody Peter Mello 	struct msghdr msg6;
172797f979dSCody Peter Mello 	struct iovec iovs[3];
173797f979dSCody Peter Mello 	struct nd_router_advert ichdr;
174797f979dSCody Peter Mello 	struct nd_opt_lla lla;
175797f979dSCody Peter Mello 	struct nd_opt_prefix_info pi;
176797f979dSCody Peter Mello 	uint8_t old_lla_len;
177797f979dSCody Peter Mello 
178797f979dSCody Peter Mello 	spoof_prepare_header(&ichdr, &iovs[0]);
179797f979dSCody Peter Mello 	spoof_prepare_lla(&lla, nce, &iovs[1]);
180797f979dSCody Peter Mello 	spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]);
181797f979dSCody Peter Mello 
182797f979dSCody Peter Mello 	/* Prepare message */
183797f979dSCody Peter Mello 	bzero(&msg6, sizeof (struct msghdr));
184797f979dSCody Peter Mello 	msg6.msg_name = multicast;
185797f979dSCody Peter Mello 	msg6.msg_namelen = sizeof (sin6_t);
186797f979dSCody Peter Mello 	msg6.msg_iov = iovs;
187797f979dSCody Peter Mello 	msg6.msg_iovlen = 3;
188797f979dSCody Peter Mello 
189797f979dSCody Peter Mello 	old_lla_len = lla.nd_opt_lla_len;
190797f979dSCody Peter Mello 
191797f979dSCody Peter Mello 
192797f979dSCody Peter Mello 	/*
193797f979dSCody Peter Mello 	 * Length is now smaller than the option is, so this should
194797f979dSCody Peter Mello 	 * be rejected.
195797f979dSCody Peter Mello 	 */
196797f979dSCody Peter Mello 	lla.nd_opt_lla_len = 0;
197797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
198797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
199797f979dSCody Peter Mello 		return (-1);
200797f979dSCody Peter Mello 	}
201797f979dSCody Peter Mello 
202797f979dSCody Peter Mello 	/*
203797f979dSCody Peter Mello 	 * Length is bigger than the option, so the following prefix
204797f979dSCody Peter Mello 	 * will be offset.
205797f979dSCody Peter Mello 	 */
206797f979dSCody Peter Mello 	lla.nd_opt_lla_len = 2;
207797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
208797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
209797f979dSCody Peter Mello 		return (-1);
210797f979dSCody Peter Mello 	}
211797f979dSCody Peter Mello 
212797f979dSCody Peter Mello 	/*
213797f979dSCody Peter Mello 	 * Restore the length, but shorten the amount of data to send, so we're
214797f979dSCody Peter Mello 	 * sending truncated packets. (Stop before 0, so that we still send part
215797f979dSCody Peter Mello 	 * of the option.)
216797f979dSCody Peter Mello 	 */
217797f979dSCody Peter Mello 	lla.nd_opt_lla_len = old_lla_len;
218797f979dSCody Peter Mello 	for (iovs[1].iov_len--; iovs[1].iov_len > 0; iovs[1].iov_len--) {
219797f979dSCody Peter Mello 		if (sendmsg(s, &msg6, 0) < 0) {
220797f979dSCody Peter Mello 			warn("Failed to send ICMPv6 message");
221797f979dSCody Peter Mello 			return (-1);
222797f979dSCody Peter Mello 		}
223797f979dSCody Peter Mello 	}
224797f979dSCody Peter Mello 
225797f979dSCody Peter Mello 	return (0);
226797f979dSCody Peter Mello }
227797f979dSCody Peter Mello 
228797f979dSCody Peter Mello /*
229797f979dSCody Peter Mello  * Send bad option lengths in the Prefix Information option
230797f979dSCody Peter Mello  */
231797f979dSCody Peter Mello static int
spoof_bad_pi_optlen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)232797f979dSCody Peter Mello spoof_bad_pi_optlen_test(int s, struct lif_nd_req *nce, sin6_t *multicast)
233797f979dSCody Peter Mello {
234797f979dSCody Peter Mello 	struct msghdr msg6;
235797f979dSCody Peter Mello 	struct iovec iovs[3];
236797f979dSCody Peter Mello 	struct nd_router_advert ichdr;
237797f979dSCody Peter Mello 	struct nd_opt_lla lla;
238797f979dSCody Peter Mello 	struct nd_opt_prefix_info pi;
239797f979dSCody Peter Mello 	uint8_t old_pi_len;
240797f979dSCody Peter Mello 
241797f979dSCody Peter Mello 	spoof_prepare_header(&ichdr, &iovs[0]);
242797f979dSCody Peter Mello 	spoof_prepare_lla(&lla, nce, &iovs[1]);
243797f979dSCody Peter Mello 	spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]);
244797f979dSCody Peter Mello 
245797f979dSCody Peter Mello 	/* Prepare message */
246797f979dSCody Peter Mello 	bzero(&msg6, sizeof (struct msghdr));
247797f979dSCody Peter Mello 	msg6.msg_name = multicast;
248797f979dSCody Peter Mello 	msg6.msg_namelen = sizeof (sin6_t);
249797f979dSCody Peter Mello 	msg6.msg_iov = iovs;
250797f979dSCody Peter Mello 	msg6.msg_iovlen = 3;
251797f979dSCody Peter Mello 
252797f979dSCody Peter Mello 	old_pi_len = pi.nd_opt_pi_len;
253797f979dSCody Peter Mello 
254797f979dSCody Peter Mello 	/*
255797f979dSCody Peter Mello 	 * Length is now smaller than the option is, so this should
256797f979dSCody Peter Mello 	 * be rejected.
257797f979dSCody Peter Mello 	 */
258797f979dSCody Peter Mello 	pi.nd_opt_pi_len = 0;
259797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
260797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
261797f979dSCody Peter Mello 		return (-1);
262797f979dSCody Peter Mello 	}
263797f979dSCody Peter Mello 
264797f979dSCody Peter Mello 	/*
265797f979dSCody Peter Mello 	 * Length is smaller than a PI option should be.
266797f979dSCody Peter Mello 	 */
267797f979dSCody Peter Mello 	pi.nd_opt_pi_len = 3;
268797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
269797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
270797f979dSCody Peter Mello 		return (-1);
271797f979dSCody Peter Mello 	}
272797f979dSCody Peter Mello 
273797f979dSCody Peter Mello 	/*
274797f979dSCody Peter Mello 	 * Length is bigger than the option, so the following prefix
275797f979dSCody Peter Mello 	 * will be offset.
276797f979dSCody Peter Mello 	 */
277797f979dSCody Peter Mello 	pi.nd_opt_pi_len = 5;
278797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
279797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
280797f979dSCody Peter Mello 		return (-1);
281797f979dSCody Peter Mello 	}
282797f979dSCody Peter Mello 
283797f979dSCody Peter Mello 	/*
284797f979dSCody Peter Mello 	 * Restore the length, but shorten the amount of data to send, so we're
285797f979dSCody Peter Mello 	 * sending truncated packets. (Stop before 0, so that we still send part
286797f979dSCody Peter Mello 	 * of the option.)
287797f979dSCody Peter Mello 	 */
288797f979dSCody Peter Mello 	pi.nd_opt_pi_len = old_pi_len;
289797f979dSCody Peter Mello 	for (iovs[2].iov_len--; iovs[2].iov_len > 0; iovs[2].iov_len--) {
290797f979dSCody Peter Mello 		if (sendmsg(s, &msg6, 0) < 0) {
291797f979dSCody Peter Mello 			warn("Failed to send ICMPv6 message");
292797f979dSCody Peter Mello 			return (-1);
293797f979dSCody Peter Mello 		}
294797f979dSCody Peter Mello 	}
295797f979dSCody Peter Mello 
296797f979dSCody Peter Mello 	return (0);
297797f979dSCody Peter Mello }
298797f979dSCody Peter Mello 
299797f979dSCody Peter Mello /*
300797f979dSCody Peter Mello  * Advertise a prefix with a prefix length greater than 128.
301797f979dSCody Peter Mello  */
302797f979dSCody Peter Mello static int
spoof_bad_plen_test(int s,struct lif_nd_req * nce,sin6_t * multicast)303797f979dSCody Peter Mello spoof_bad_plen_test(int s, struct lif_nd_req *nce, sin6_t *multicast)
304797f979dSCody Peter Mello {
305797f979dSCody Peter Mello 	struct msghdr msg6;
306797f979dSCody Peter Mello 	struct iovec iovs[3];
307797f979dSCody Peter Mello 	struct nd_router_advert ichdr;
308797f979dSCody Peter Mello 	struct nd_opt_lla lla;
309797f979dSCody Peter Mello 	struct nd_opt_prefix_info pi;
310797f979dSCody Peter Mello 
311797f979dSCody Peter Mello 	spoof_prepare_header(&ichdr, &iovs[0]);
312797f979dSCody Peter Mello 	spoof_prepare_lla(&lla, nce, &iovs[1]);
313797f979dSCody Peter Mello 	spoof_prepare_pi("fd00::", 130, &pi, &iovs[2]);
314797f979dSCody Peter Mello 
315797f979dSCody Peter Mello 	/* Prepare message */
316797f979dSCody Peter Mello 	bzero(&msg6, sizeof (struct msghdr));
317797f979dSCody Peter Mello 	msg6.msg_name = multicast;
318797f979dSCody Peter Mello 	msg6.msg_namelen = sizeof (sin6_t);
319797f979dSCody Peter Mello 	msg6.msg_iov = iovs;
320797f979dSCody Peter Mello 	msg6.msg_iovlen = 3;
321797f979dSCody Peter Mello 
322797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
323797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
324797f979dSCody Peter Mello 		return (-1);
325797f979dSCody Peter Mello 	}
326797f979dSCody Peter Mello 
327797f979dSCody Peter Mello 	return (0);
328797f979dSCody Peter Mello }
329797f979dSCody Peter Mello 
330797f979dSCody Peter Mello /*
331797f979dSCody Peter Mello  * Advertise a link-local prefix, which should be disallowed and ignored.
332797f979dSCody Peter Mello  */
333797f979dSCody Peter Mello static int
spoof_link_local_test(int s,struct lif_nd_req * nce,sin6_t * multicast)334797f979dSCody Peter Mello spoof_link_local_test(int s, struct lif_nd_req *nce, sin6_t *multicast)
335797f979dSCody Peter Mello {
336797f979dSCody Peter Mello 	struct msghdr msg6;
337797f979dSCody Peter Mello 	struct iovec iovs[3];
338797f979dSCody Peter Mello 	struct nd_router_advert ichdr;
339797f979dSCody Peter Mello 	struct nd_opt_lla lla;
340797f979dSCody Peter Mello 	struct nd_opt_prefix_info pi;
341797f979dSCody Peter Mello 
342797f979dSCody Peter Mello 	spoof_prepare_header(&ichdr, &iovs[0]);
343797f979dSCody Peter Mello 	spoof_prepare_lla(&lla, nce, &iovs[1]);
344797f979dSCody Peter Mello 	spoof_prepare_pi("fe80::", 64, &pi, &iovs[2]);
345797f979dSCody Peter Mello 
346797f979dSCody Peter Mello 	/* Prepare message */
347797f979dSCody Peter Mello 	bzero(&msg6, sizeof (struct msghdr));
348797f979dSCody Peter Mello 	msg6.msg_name = multicast;
349797f979dSCody Peter Mello 	msg6.msg_namelen = sizeof (sin6_t);
350797f979dSCody Peter Mello 	msg6.msg_iov = iovs;
351797f979dSCody Peter Mello 	msg6.msg_iovlen = 3;
352797f979dSCody Peter Mello 
353797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
354797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
355797f979dSCody Peter Mello 		return (-1);
356797f979dSCody Peter Mello 	}
357797f979dSCody Peter Mello 
358797f979dSCody Peter Mello 	return (0);
359797f979dSCody Peter Mello }
360797f979dSCody Peter Mello 
361797f979dSCody Peter Mello static int
spoof_good_test(int s,struct lif_nd_req * nce,sin6_t * multicast)362797f979dSCody Peter Mello spoof_good_test(int s, struct lif_nd_req *nce, sin6_t *multicast)
363797f979dSCody Peter Mello {
364797f979dSCody Peter Mello 	struct msghdr msg6;
365797f979dSCody Peter Mello 	struct iovec iovs[3];
366797f979dSCody Peter Mello 	struct nd_router_advert ichdr;
367797f979dSCody Peter Mello 	struct nd_opt_lla lla;
368797f979dSCody Peter Mello 	struct nd_opt_prefix_info pi;
369797f979dSCody Peter Mello 
370797f979dSCody Peter Mello 	spoof_prepare_header(&ichdr, &iovs[0]);
371797f979dSCody Peter Mello 	spoof_prepare_lla(&lla, nce, &iovs[1]);
372797f979dSCody Peter Mello 	spoof_prepare_pi("fd00::", 64, &pi, &iovs[2]);
373797f979dSCody Peter Mello 
374797f979dSCody Peter Mello 	/* Prepare message */
375797f979dSCody Peter Mello 	bzero(&msg6, sizeof (struct msghdr));
376797f979dSCody Peter Mello 	msg6.msg_name = multicast;
377797f979dSCody Peter Mello 	msg6.msg_namelen = sizeof (sin6_t);
378797f979dSCody Peter Mello 	msg6.msg_iov = iovs;
379797f979dSCody Peter Mello 	msg6.msg_iovlen = 3;
380797f979dSCody Peter Mello 
381797f979dSCody Peter Mello 	if (sendmsg(s, &msg6, 0) < 0) {
382797f979dSCody Peter Mello 		warn("Failed to send ICMPv6 message");
383797f979dSCody Peter Mello 		return (-1);
384797f979dSCody Peter Mello 	}
385797f979dSCody Peter Mello 
386797f979dSCody Peter Mello 	return (0);
387797f979dSCody Peter Mello }
388797f979dSCody Peter Mello 
389797f979dSCody Peter Mello static spoof_test_f *test_cases[] = {
390797f979dSCody Peter Mello 	spoof_bad_lla_optlen_test,
391797f979dSCody Peter Mello 	spoof_bad_pi_optlen_test,
392797f979dSCody Peter Mello 	spoof_bad_plen_test,
393797f979dSCody Peter Mello 	spoof_link_local_test
394797f979dSCody Peter Mello };
395797f979dSCody Peter Mello 
396797f979dSCody Peter Mello static int test_cases_count = sizeof (test_cases) / sizeof (spoof_test_f *);
397797f979dSCody Peter Mello 
398797f979dSCody Peter Mello static pid_t
spoof_dtrace_launch(void)399797f979dSCody Peter Mello spoof_dtrace_launch(void)
400797f979dSCody Peter Mello {
401797f979dSCody Peter Mello 	pid_t child_pid = fork();
402797f979dSCody Peter Mello 	if (child_pid == (pid_t)-1) {
403797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to fork to execute dtrace");
404797f979dSCody Peter Mello 	} else if (child_pid == (pid_t)0) {
405797f979dSCody Peter Mello 		(void) execl("/usr/sbin/dtrace", "dtrace", "-q",
406797f979dSCody Peter Mello 		    "-n", "sdt:mac:insert_slaac_ip:generated-addr { exit(10) }",
407797f979dSCody Peter Mello 		    NULL);
408797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to execute dtrace");
409797f979dSCody Peter Mello 	}
410797f979dSCody Peter Mello 
411797f979dSCody Peter Mello 	return (child_pid);
412797f979dSCody Peter Mello }
413797f979dSCody Peter Mello 
414797f979dSCody Peter Mello static pid_t
spoof_dtrace_wait(pid_t dtrace,int * stat)415797f979dSCody Peter Mello spoof_dtrace_wait(pid_t dtrace, int *stat)
416797f979dSCody Peter Mello {
417797f979dSCody Peter Mello 	int retpid;
418797f979dSCody Peter Mello 
419797f979dSCody Peter Mello 	/* Give time for probe to fire before checking status */
420797f979dSCody Peter Mello 	(void) sleep(5);
421797f979dSCody Peter Mello 
422797f979dSCody Peter Mello 	while ((retpid = waitpid(dtrace, stat, WNOHANG)) == -1) {
423797f979dSCody Peter Mello 		if (errno == EINTR)
424797f979dSCody Peter Mello 			continue;
425797f979dSCody Peter Mello 
426797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to wait on child");
427797f979dSCody Peter Mello 	}
428797f979dSCody Peter Mello 
429797f979dSCody Peter Mello 	return (retpid);
430797f979dSCody Peter Mello }
431797f979dSCody Peter Mello 
432797f979dSCody Peter Mello /*
433797f979dSCody Peter Mello  * Run a function that's going to exec in a child process, and don't return
434797f979dSCody Peter Mello  * until it exits.
435797f979dSCody Peter Mello  */
436797f979dSCody Peter Mello static int
spoof_run_proc(char * path,char * args[])437797f979dSCody Peter Mello spoof_run_proc(char *path, char *args[])
438797f979dSCody Peter Mello {
439797f979dSCody Peter Mello 	pid_t child_pid;
440797f979dSCody Peter Mello 	int childstat = 0, status = 0;
441797f979dSCody Peter Mello 
442797f979dSCody Peter Mello 	child_pid = fork();
443797f979dSCody Peter Mello 	if (child_pid == (pid_t)-1) {
444797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Unable to fork to execute %s", path);
445797f979dSCody Peter Mello 	} else if (child_pid == (pid_t)0) {
446797f979dSCody Peter Mello 		(void) execv(path, args);
447797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to execute %s", path);
448797f979dSCody Peter Mello 	}
449797f979dSCody Peter Mello 
450797f979dSCody Peter Mello 	while (waitpid(child_pid, &childstat, 0) == -1) {
451797f979dSCody Peter Mello 		if (errno == EINTR)
452797f979dSCody Peter Mello 			continue;
453797f979dSCody Peter Mello 
454797f979dSCody Peter Mello 		warn("Failed to wait on child");
455797f979dSCody Peter Mello 		return (-1);
456797f979dSCody Peter Mello 	}
457797f979dSCody Peter Mello 
458797f979dSCody Peter Mello 	status = WEXITSTATUS(childstat);
459797f979dSCody Peter Mello 	if (status != 0) {
460797f979dSCody Peter Mello 		warnx("Child process %s exited with %d", path, status);
461797f979dSCody Peter Mello 		return (-1);
462797f979dSCody Peter Mello 	}
463797f979dSCody Peter Mello 
464797f979dSCody Peter Mello 	return (0);
465797f979dSCody Peter Mello }
466797f979dSCody Peter Mello 
467797f979dSCody Peter Mello static void
spoof_network_teardown(char * testether,char * testvnic0,char * testvnic1)468797f979dSCody Peter Mello spoof_network_teardown(char *testether, char *testvnic0, char *testvnic1)
469797f979dSCody Peter Mello {
470797f979dSCody Peter Mello 	// Delete dest vnic
471797f979dSCody Peter Mello 	(void) IFCONFIG(testvnic1, "inet6", "unplumb");
472797f979dSCody Peter Mello 	(void) DLADM("delete-vnic", testvnic1);
473797f979dSCody Peter Mello 
474797f979dSCody Peter Mello 	// Delete source vnic
475797f979dSCody Peter Mello 	(void) IFCONFIG(testvnic0, "inet6", "unplumb");
476797f979dSCody Peter Mello 	(void) DLADM("delete-vnic", testvnic0);
477797f979dSCody Peter Mello 
478797f979dSCody Peter Mello 	// Delete etherstub
479797f979dSCody Peter Mello 	(void) DLADM("delete-etherstub", testether);
480797f979dSCody Peter Mello }
481797f979dSCody Peter Mello 
482797f979dSCody Peter Mello static int
spoof_network_setup(char * testether,char * testvnic0,char * testvnic1)483797f979dSCody Peter Mello spoof_network_setup(char *testether, char *testvnic0, char *testvnic1)
484797f979dSCody Peter Mello {
485797f979dSCody Peter Mello 	// Create etherstub
486797f979dSCody Peter Mello 	if (DLADM("create-etherstub", "-t", testether) != 0) {
487797f979dSCody Peter Mello 		warnx("Failed to create etherstub for test network");
488797f979dSCody Peter Mello 		return (-1);
489797f979dSCody Peter Mello 	}
490797f979dSCody Peter Mello 
491797f979dSCody Peter Mello 	// Create source vnic
492797f979dSCody Peter Mello 	if (DLADM("create-vnic", "-t", "-l", testether, testvnic0) != 0) {
493797f979dSCody Peter Mello 		warnx("Failed to create source VNIC for test network");
494797f979dSCody Peter Mello 		return (-1);
495797f979dSCody Peter Mello 	}
496797f979dSCody Peter Mello 
497797f979dSCody Peter Mello 	if (IFCONFIG(testvnic0, "inet6", "plumb", "up") != 0) {
498797f979dSCody Peter Mello 		warnx("Failed to plumb source VNIC for test network");
499797f979dSCody Peter Mello 		return (-1);
500797f979dSCody Peter Mello 	}
501797f979dSCody Peter Mello 
502797f979dSCody Peter Mello 	// Create dest vnic
503797f979dSCody Peter Mello 	if (DLADM("create-vnic", "-t", "-l", testether,
504797f979dSCody Peter Mello 	    "-p", "protection=mac-nospoof,restricted,ip-nospoof,dhcp-nospoof",
505797f979dSCody Peter Mello 	    testvnic1) != 0) {
506797f979dSCody Peter Mello 		warnx("Failed to create destination VNIC for test network");
507797f979dSCody Peter Mello 		return (-1);
508797f979dSCody Peter Mello 	}
509797f979dSCody Peter Mello 
510797f979dSCody Peter Mello 	if (IFCONFIG(testvnic1, "inet6", "plumb", "up") != 0) {
511797f979dSCody Peter Mello 		warnx("Failed to plumb destination VNIC for test network");
512797f979dSCody Peter Mello 		return (-1);
513797f979dSCody Peter Mello 	}
514797f979dSCody Peter Mello 
515797f979dSCody Peter Mello 	return (0);
516797f979dSCody Peter Mello }
517797f979dSCody Peter Mello 
518797f979dSCody Peter Mello static void
spoof_run_test(spoof_test_f * func,int s,struct lif_nd_req * nce,sin6_t * multicast)519797f979dSCody Peter Mello spoof_run_test(spoof_test_f *func, int s, struct lif_nd_req *nce,
520797f979dSCody Peter Mello     sin6_t *multicast)
521797f979dSCody Peter Mello {
522797f979dSCody Peter Mello 	static int cas = 1;
523797f979dSCody Peter Mello 	(void) printf("Executing test case #%d...", cas++);
524797f979dSCody Peter Mello 	if (func(s, nce, multicast) == 0) {
525797f979dSCody Peter Mello 		(void) printf(" Done.\n");
526797f979dSCody Peter Mello 	} else {
527797f979dSCody Peter Mello 		(void) printf(" Error while running!\n");
528797f979dSCody Peter Mello 	}
529797f979dSCody Peter Mello }
530797f979dSCody Peter Mello 
531797f979dSCody Peter Mello static int
spoof_run_tests(int s,struct lif_nd_req * nce)532797f979dSCody Peter Mello spoof_run_tests(int s, struct lif_nd_req *nce)
533797f979dSCody Peter Mello {
534797f979dSCody Peter Mello 	int cas, stat;
535797f979dSCody Peter Mello 	pid_t dtrace;
536797f979dSCody Peter Mello 	sin6_t multicast;
537797f979dSCody Peter Mello 
538797f979dSCody Peter Mello 	/* Prepare all-nodes multicast address */
539797f979dSCody Peter Mello 	bzero(&multicast, sizeof (multicast));
540797f979dSCody Peter Mello 	multicast.sin6_family = AF_INET6;
541797f979dSCody Peter Mello 	(void) inet_pton(AF_INET6, "ff02::1", &multicast.sin6_addr);
542797f979dSCody Peter Mello 
543797f979dSCody Peter Mello 	dtrace = spoof_dtrace_launch();
544797f979dSCody Peter Mello 
545797f979dSCody Peter Mello 	/* Wait an adequate amount of time for the probes to be installed */
546797f979dSCody Peter Mello 	(void) sleep(5);
547797f979dSCody Peter Mello 
548797f979dSCody Peter Mello 	/*
549797f979dSCody Peter Mello 	 * We send a packet where everything is good, except for the hop limit.
550797f979dSCody Peter Mello 	 * This packet should be rejected.
551797f979dSCody Peter Mello 	 */
552797f979dSCody Peter Mello 	spoof_run_test(spoof_good_test, s, nce, &multicast);
553797f979dSCody Peter Mello 
554797f979dSCody Peter Mello 	if (spoof_set_max_hops(s) != 0) {
555797f979dSCody Peter Mello 		warnx("Failed to set hop limit on socket");
556797f979dSCody Peter Mello 		return (EXIT_FAILURE);
557797f979dSCody Peter Mello 	}
558797f979dSCody Peter Mello 
559797f979dSCody Peter Mello 	for (cas = 0; cas < test_cases_count; cas++) {
560797f979dSCody Peter Mello 		spoof_run_test(test_cases[cas], s, nce, &multicast);
561797f979dSCody Peter Mello 	}
562797f979dSCody Peter Mello 
563797f979dSCody Peter Mello 
564797f979dSCody Peter Mello 	if (spoof_dtrace_wait(dtrace, &stat) != 0) {
565797f979dSCody Peter Mello 		(void) printf("One or more tests of bad behaviour failed!\n");
566797f979dSCody Peter Mello 		return (EXIT_FAILURE);
567797f979dSCody Peter Mello 	}
568797f979dSCody Peter Mello 
569797f979dSCody Peter Mello 	/*
570797f979dSCody Peter Mello 	 * Now that we've executed all of the test cases that should fail, we
571797f979dSCody Peter Mello 	 * can execute the test that should succeed, to make sure the normal
572797f979dSCody Peter Mello 	 * case works properly. This should trip the dtrace probe.
573797f979dSCody Peter Mello 	 */
574797f979dSCody Peter Mello 	spoof_run_test(spoof_good_test, s, nce, &multicast);
575797f979dSCody Peter Mello 
576797f979dSCody Peter Mello 	if (spoof_dtrace_wait(dtrace, &stat) != 0 && WIFEXITED(stat) &&
577797f979dSCody Peter Mello 	    WEXITSTATUS(stat) == 10) {
578797f979dSCody Peter Mello 		(void) printf("Tests completed successfully!\n");
579797f979dSCody Peter Mello 	} else {
580797f979dSCody Peter Mello 		if (kill(dtrace, SIGKILL) != 0)  {
581*1e56f352SRobert Mustacchi 			warn("Failed to kill dtrace child (pid %" _PRIdID ")",
582*1e56f352SRobert Mustacchi 			    dtrace);
583797f979dSCody Peter Mello 		}
584797f979dSCody Peter Mello 		(void) printf("Test of normal behaviour didn't succeed!\n");
585797f979dSCody Peter Mello 		return (EXIT_FAILURE);
586797f979dSCody Peter Mello 	}
587797f979dSCody Peter Mello 
588797f979dSCody Peter Mello 	return (0);
589797f979dSCody Peter Mello }
590797f979dSCody Peter Mello 
591797f979dSCody Peter Mello /*
592797f979dSCody Peter Mello  * Make sure that we have all of the privileges we need to execute these tests,
593797f979dSCody Peter Mello  * so that we can error out before we would fail.
594797f979dSCody Peter Mello  */
595797f979dSCody Peter Mello void
spoof_check_privs(void)596797f979dSCody Peter Mello spoof_check_privs(void)
597797f979dSCody Peter Mello {
598797f979dSCody Peter Mello 	priv_set_t *privset = priv_allocset();
599797f979dSCody Peter Mello 
600797f979dSCody Peter Mello 	if (privset == NULL) {
601797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to allocate memory for "
602797f979dSCody Peter Mello 		    "checking privileges");
603797f979dSCody Peter Mello 	}
604797f979dSCody Peter Mello 
605797f979dSCody Peter Mello 	if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
606797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to get current privileges");
607797f979dSCody Peter Mello 	}
608797f979dSCody Peter Mello 
609797f979dSCody Peter Mello 	if (!priv_ismember(privset, PRIV_DTRACE_KERNEL)) {
610797f979dSCody Peter Mello 		errx(EXIT_FAILURE, "These tests need to be run as a user "
611797f979dSCody Peter Mello 		    "capable of tracing the kernel.");
612797f979dSCody Peter Mello 	}
613797f979dSCody Peter Mello 
614797f979dSCody Peter Mello 	if (!priv_ismember(privset, PRIV_SYS_NET_CONFIG)) {
615797f979dSCody Peter Mello 		errx(EXIT_FAILURE, "These tests need to be run as a user "
616797f979dSCody Peter Mello 		    "capable of creating and configuring network interfaces.");
617797f979dSCody Peter Mello 	}
618797f979dSCody Peter Mello 
619797f979dSCody Peter Mello 	if (!priv_ismember(privset, PRIV_NET_ICMPACCESS)) {
620797f979dSCody Peter Mello 		errx(EXIT_FAILURE, "These tests need to be run as a user "
621797f979dSCody Peter Mello 		    "capable of sending ICMP packets.");
622797f979dSCody Peter Mello 	}
623797f979dSCody Peter Mello 
624797f979dSCody Peter Mello 	priv_freeset(privset);
625797f979dSCody Peter Mello }
626797f979dSCody Peter Mello 
627797f979dSCody Peter Mello int
main(void)628797f979dSCody Peter Mello main(void)
629797f979dSCody Peter Mello {
630797f979dSCody Peter Mello 	struct lifreq addr, llar;
631797f979dSCody Peter Mello 	int error, s;
632797f979dSCody Peter Mello 	char testether[LIFNAMSIZ];
633797f979dSCody Peter Mello 	char testvnic0[LIFNAMSIZ];
634797f979dSCody Peter Mello 	char testvnic1[LIFNAMSIZ];
635797f979dSCody Peter Mello 	pid_t curpid = getpid();
636797f979dSCody Peter Mello 
637797f979dSCody Peter Mello 	spoof_check_privs();
638797f979dSCody Peter Mello 
639797f979dSCody Peter Mello 	/*
640797f979dSCody Peter Mello 	 * Set up the socket and test network for sending
641797f979dSCody Peter Mello 	 */
642797f979dSCody Peter Mello 	s = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
643797f979dSCody Peter Mello 	if (s < 0) {
644797f979dSCody Peter Mello 		err(EXIT_FAILURE, "Failed to open ICMPv6 socket");
645797f979dSCody Peter Mello 	}
646797f979dSCody Peter Mello 
647797f979dSCody Peter Mello 	(void) snprintf(testether, sizeof (testether), "testether%d", curpid);
648797f979dSCody Peter Mello 	(void) snprintf(testvnic0, sizeof (testvnic0), "testvnic%d_0", curpid);
649797f979dSCody Peter Mello 	(void) snprintf(testvnic1, sizeof (testvnic1), "testvnic%d_1", curpid);
650797f979dSCody Peter Mello 
651797f979dSCody Peter Mello 	if (spoof_network_setup(testether, testvnic0, testvnic1) != 0) {
652797f979dSCody Peter Mello 		warnx("Failed to set up test network");
653797f979dSCody Peter Mello 		error = EXIT_FAILURE;
654797f979dSCody Peter Mello 		goto cleanup;
655797f979dSCody Peter Mello 	}
656797f979dSCody Peter Mello 
657797f979dSCody Peter Mello 	if (spoof_get_lla(s, testvnic0, &addr, &llar) != 0) {
658797f979dSCody Peter Mello 		warnx("Failed to get link-layer address");
659797f979dSCody Peter Mello 		error = EXIT_FAILURE;
660797f979dSCody Peter Mello 		goto cleanup;
661797f979dSCody Peter Mello 	}
662797f979dSCody Peter Mello 
663797f979dSCody Peter Mello 	if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF,
664797f979dSCody Peter Mello 	    (char *)&((sin6_t *)&addr.lifr_addr)->sin6_scope_id,
665797f979dSCody Peter Mello 	    sizeof (int)) < 0) {
666797f979dSCody Peter Mello 		warn("Failed to set IPV6_UNICAST_HOPS socket option");
667797f979dSCody Peter Mello 		return (-1);
668797f979dSCody Peter Mello 	}
669797f979dSCody Peter Mello 
670797f979dSCody Peter Mello 	if (bind(s, (struct sockaddr *)&addr.lifr_addr, sizeof (sin6_t)) != 0) {
671797f979dSCody Peter Mello 		warnx("Failed to bind to link-local address");
672797f979dSCody Peter Mello 		error = EXIT_FAILURE;
673797f979dSCody Peter Mello 		goto cleanup;
674797f979dSCody Peter Mello 	}
675797f979dSCody Peter Mello 
676797f979dSCody Peter Mello 	error = spoof_run_tests(s, &llar.lifr_nd);
677797f979dSCody Peter Mello 
678797f979dSCody Peter Mello cleanup:
679797f979dSCody Peter Mello 	if (close(s) != 0) {
680797f979dSCody Peter Mello 		warnx("Failed to close ICMPv6 socket");
681797f979dSCody Peter Mello 	}
682797f979dSCody Peter Mello 	spoof_network_teardown(testether, testvnic0, testvnic1);
683797f979dSCody Peter Mello 	return (error);
684797f979dSCody Peter Mello }
685