xref: /linux/tools/testing/selftests/net/icmp_rfc4884.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1*d07d7c3dSDanielle Ratson // SPDX-License-Identifier: GPL-2.0
2*d07d7c3dSDanielle Ratson 
3*d07d7c3dSDanielle Ratson #include <arpa/inet.h>
4*d07d7c3dSDanielle Ratson #include <error.h>
5*d07d7c3dSDanielle Ratson #include <linux/errqueue.h>
6*d07d7c3dSDanielle Ratson #include <linux/icmp.h>
7*d07d7c3dSDanielle Ratson #include <linux/icmpv6.h>
8*d07d7c3dSDanielle Ratson #include <linux/in6.h>
9*d07d7c3dSDanielle Ratson #include <linux/ip.h>
10*d07d7c3dSDanielle Ratson #include <linux/ipv6.h>
11*d07d7c3dSDanielle Ratson #include <netinet/in.h>
12*d07d7c3dSDanielle Ratson #include <netinet/udp.h>
13*d07d7c3dSDanielle Ratson #include <poll.h>
14*d07d7c3dSDanielle Ratson #include <sched.h>
15*d07d7c3dSDanielle Ratson #include <stdbool.h>
16*d07d7c3dSDanielle Ratson #include <stdint.h>
17*d07d7c3dSDanielle Ratson #include <stdio.h>
18*d07d7c3dSDanielle Ratson #include <stdlib.h>
19*d07d7c3dSDanielle Ratson #include <sys/ioctl.h>
20*d07d7c3dSDanielle Ratson #include <sys/socket.h>
21*d07d7c3dSDanielle Ratson 
22*d07d7c3dSDanielle Ratson #include "../kselftest_harness.h"
23*d07d7c3dSDanielle Ratson 
24*d07d7c3dSDanielle Ratson static const unsigned short src_port = 44444;
25*d07d7c3dSDanielle Ratson static const unsigned short dst_port = 55555;
26*d07d7c3dSDanielle Ratson static const int min_orig_dgram_len = 128;
27*d07d7c3dSDanielle Ratson static const int min_payload_len_v4 =
28*d07d7c3dSDanielle Ratson 	min_orig_dgram_len - sizeof(struct iphdr) - sizeof(struct udphdr);
29*d07d7c3dSDanielle Ratson static const int min_payload_len_v6 =
30*d07d7c3dSDanielle Ratson 	min_orig_dgram_len - sizeof(struct ipv6hdr) - sizeof(struct udphdr);
31*d07d7c3dSDanielle Ratson static const uint8_t orig_payload_byte =  0xAA;
32*d07d7c3dSDanielle Ratson 
33*d07d7c3dSDanielle Ratson struct sockaddr_inet {
34*d07d7c3dSDanielle Ratson 	union {
35*d07d7c3dSDanielle Ratson 		struct sockaddr_in6 v6;
36*d07d7c3dSDanielle Ratson 		struct sockaddr_in v4;
37*d07d7c3dSDanielle Ratson 		struct sockaddr sa;
38*d07d7c3dSDanielle Ratson 	};
39*d07d7c3dSDanielle Ratson 	socklen_t len;
40*d07d7c3dSDanielle Ratson };
41*d07d7c3dSDanielle Ratson 
42*d07d7c3dSDanielle Ratson struct ip_case_info {
43*d07d7c3dSDanielle Ratson 	int	domain;
44*d07d7c3dSDanielle Ratson 	int	level;
45*d07d7c3dSDanielle Ratson 	int	opt1;
46*d07d7c3dSDanielle Ratson 	int	opt2;
47*d07d7c3dSDanielle Ratson 	int	proto;
48*d07d7c3dSDanielle Ratson 	int	(*build_func)(uint8_t *buf, ssize_t buflen, bool with_ext,
49*d07d7c3dSDanielle Ratson 			      int payload_len, bool bad_csum, bool bad_len,
50*d07d7c3dSDanielle Ratson 			      bool smaller_len);
51*d07d7c3dSDanielle Ratson 	int	min_payload;
52*d07d7c3dSDanielle Ratson };
53*d07d7c3dSDanielle Ratson 
54*d07d7c3dSDanielle Ratson static int bringup_loopback(void)
55*d07d7c3dSDanielle Ratson {
56*d07d7c3dSDanielle Ratson 	struct ifreq ifr = {
57*d07d7c3dSDanielle Ratson 		.ifr_name = "lo"
58*d07d7c3dSDanielle Ratson 	};
59*d07d7c3dSDanielle Ratson 	int fd;
60*d07d7c3dSDanielle Ratson 
61*d07d7c3dSDanielle Ratson 	fd = socket(AF_INET, SOCK_DGRAM, 0);
62*d07d7c3dSDanielle Ratson 	if (fd < 0)
63*d07d7c3dSDanielle Ratson 		return -1;
64*d07d7c3dSDanielle Ratson 
65*d07d7c3dSDanielle Ratson 	if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
66*d07d7c3dSDanielle Ratson 		goto err;
67*d07d7c3dSDanielle Ratson 
68*d07d7c3dSDanielle Ratson 	ifr.ifr_flags = ifr.ifr_flags | IFF_UP;
69*d07d7c3dSDanielle Ratson 
70*d07d7c3dSDanielle Ratson 	if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
71*d07d7c3dSDanielle Ratson 		goto err;
72*d07d7c3dSDanielle Ratson 
73*d07d7c3dSDanielle Ratson 	close(fd);
74*d07d7c3dSDanielle Ratson 	return 0;
75*d07d7c3dSDanielle Ratson 
76*d07d7c3dSDanielle Ratson err:
77*d07d7c3dSDanielle Ratson 	close(fd);
78*d07d7c3dSDanielle Ratson 	return -1;
79*d07d7c3dSDanielle Ratson }
80*d07d7c3dSDanielle Ratson 
81*d07d7c3dSDanielle Ratson static uint16_t csum(const void *buf, size_t len)
82*d07d7c3dSDanielle Ratson {
83*d07d7c3dSDanielle Ratson 	const uint8_t *data = buf;
84*d07d7c3dSDanielle Ratson 	uint32_t sum = 0;
85*d07d7c3dSDanielle Ratson 
86*d07d7c3dSDanielle Ratson 	while (len > 1) {
87*d07d7c3dSDanielle Ratson 		sum += (data[0] << 8) | data[1];
88*d07d7c3dSDanielle Ratson 		data += 2;
89*d07d7c3dSDanielle Ratson 		len -= 2;
90*d07d7c3dSDanielle Ratson 	}
91*d07d7c3dSDanielle Ratson 
92*d07d7c3dSDanielle Ratson 	if (len == 1)
93*d07d7c3dSDanielle Ratson 		sum += data[0] << 8;
94*d07d7c3dSDanielle Ratson 
95*d07d7c3dSDanielle Ratson 	while (sum >> 16)
96*d07d7c3dSDanielle Ratson 		sum = (sum & 0xFFFF) + (sum >> 16);
97*d07d7c3dSDanielle Ratson 
98*d07d7c3dSDanielle Ratson 	return ~sum & 0xFFFF;
99*d07d7c3dSDanielle Ratson }
100*d07d7c3dSDanielle Ratson 
101*d07d7c3dSDanielle Ratson static int poll_err(int fd)
102*d07d7c3dSDanielle Ratson {
103*d07d7c3dSDanielle Ratson 	struct pollfd pfd;
104*d07d7c3dSDanielle Ratson 
105*d07d7c3dSDanielle Ratson 	memset(&pfd, 0, sizeof(pfd));
106*d07d7c3dSDanielle Ratson 	pfd.fd = fd;
107*d07d7c3dSDanielle Ratson 
108*d07d7c3dSDanielle Ratson 	if (poll(&pfd, 1, 5000) != 1 || pfd.revents != POLLERR)
109*d07d7c3dSDanielle Ratson 		return -1;
110*d07d7c3dSDanielle Ratson 
111*d07d7c3dSDanielle Ratson 	return 0;
112*d07d7c3dSDanielle Ratson }
113*d07d7c3dSDanielle Ratson 
114*d07d7c3dSDanielle Ratson static void set_addr(struct sockaddr_inet *addr, int domain,
115*d07d7c3dSDanielle Ratson 		     unsigned short port)
116*d07d7c3dSDanielle Ratson {
117*d07d7c3dSDanielle Ratson 	memset(addr, 0, sizeof(*addr));
118*d07d7c3dSDanielle Ratson 
119*d07d7c3dSDanielle Ratson 	switch (domain) {
120*d07d7c3dSDanielle Ratson 	case AF_INET:
121*d07d7c3dSDanielle Ratson 		addr->v4.sin_family = AF_INET;
122*d07d7c3dSDanielle Ratson 		addr->v4.sin_port = htons(port);
123*d07d7c3dSDanielle Ratson 		addr->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
124*d07d7c3dSDanielle Ratson 		addr->len = sizeof(addr->v4);
125*d07d7c3dSDanielle Ratson 		break;
126*d07d7c3dSDanielle Ratson 	case AF_INET6:
127*d07d7c3dSDanielle Ratson 		addr->v6.sin6_family = AF_INET6;
128*d07d7c3dSDanielle Ratson 		addr->v6.sin6_port = htons(port);
129*d07d7c3dSDanielle Ratson 		addr->v6.sin6_addr = in6addr_loopback;
130*d07d7c3dSDanielle Ratson 		addr->len = sizeof(addr->v6);
131*d07d7c3dSDanielle Ratson 		break;
132*d07d7c3dSDanielle Ratson 	}
133*d07d7c3dSDanielle Ratson }
134*d07d7c3dSDanielle Ratson 
135*d07d7c3dSDanielle Ratson static int bind_and_setsockopt(int fd, const struct ip_case_info *info)
136*d07d7c3dSDanielle Ratson {
137*d07d7c3dSDanielle Ratson 	struct sockaddr_inet addr;
138*d07d7c3dSDanielle Ratson 	int opt = 1;
139*d07d7c3dSDanielle Ratson 
140*d07d7c3dSDanielle Ratson 	set_addr(&addr, info->domain, src_port);
141*d07d7c3dSDanielle Ratson 
142*d07d7c3dSDanielle Ratson 	if (setsockopt(fd, info->level, info->opt1, &opt, sizeof(opt)) < 0)
143*d07d7c3dSDanielle Ratson 		return -1;
144*d07d7c3dSDanielle Ratson 
145*d07d7c3dSDanielle Ratson 	if (setsockopt(fd, info->level, info->opt2, &opt, sizeof(opt)) < 0)
146*d07d7c3dSDanielle Ratson 		return -1;
147*d07d7c3dSDanielle Ratson 
148*d07d7c3dSDanielle Ratson 	return bind(fd, &addr.sa, addr.len);
149*d07d7c3dSDanielle Ratson }
150*d07d7c3dSDanielle Ratson 
151*d07d7c3dSDanielle Ratson static int build_rfc4884_ext(uint8_t *buf, size_t buflen, bool bad_csum,
152*d07d7c3dSDanielle Ratson 			     bool bad_len, bool smaller_len)
153*d07d7c3dSDanielle Ratson {
154*d07d7c3dSDanielle Ratson 	struct icmp_extobj_hdr *objh;
155*d07d7c3dSDanielle Ratson 	struct icmp_ext_hdr *exthdr;
156*d07d7c3dSDanielle Ratson 	size_t obj_len, ext_len;
157*d07d7c3dSDanielle Ratson 	uint16_t sum;
158*d07d7c3dSDanielle Ratson 
159*d07d7c3dSDanielle Ratson 	/* Use an object payload of 4 bytes */
160*d07d7c3dSDanielle Ratson 	obj_len = sizeof(*objh) + sizeof(uint32_t);
161*d07d7c3dSDanielle Ratson 	ext_len = sizeof(*exthdr) + obj_len;
162*d07d7c3dSDanielle Ratson 
163*d07d7c3dSDanielle Ratson 	if (ext_len > buflen)
164*d07d7c3dSDanielle Ratson 		return -EINVAL;
165*d07d7c3dSDanielle Ratson 
166*d07d7c3dSDanielle Ratson 	exthdr = (struct icmp_ext_hdr *)buf;
167*d07d7c3dSDanielle Ratson 	objh = (struct icmp_extobj_hdr *)(buf + sizeof(*exthdr));
168*d07d7c3dSDanielle Ratson 
169*d07d7c3dSDanielle Ratson 	exthdr->version = 2;
170*d07d7c3dSDanielle Ratson 	/* When encoding a bad object length, either encode a length too small
171*d07d7c3dSDanielle Ratson 	 * to fit the object header or too big to fit in the packet.
172*d07d7c3dSDanielle Ratson 	 */
173*d07d7c3dSDanielle Ratson 	if (bad_len)
174*d07d7c3dSDanielle Ratson 		obj_len = smaller_len ? sizeof(*objh) - 1 : obj_len * 2;
175*d07d7c3dSDanielle Ratson 	objh->length = htons(obj_len);
176*d07d7c3dSDanielle Ratson 
177*d07d7c3dSDanielle Ratson 	sum = csum(buf, ext_len);
178*d07d7c3dSDanielle Ratson 	exthdr->checksum = htons(bad_csum ? sum - 1 : sum);
179*d07d7c3dSDanielle Ratson 
180*d07d7c3dSDanielle Ratson 	return ext_len;
181*d07d7c3dSDanielle Ratson }
182*d07d7c3dSDanielle Ratson 
183*d07d7c3dSDanielle Ratson static int build_orig_dgram_v4(uint8_t *buf, ssize_t buflen, int payload_len)
184*d07d7c3dSDanielle Ratson {
185*d07d7c3dSDanielle Ratson 	struct udphdr *udph;
186*d07d7c3dSDanielle Ratson 	struct iphdr *iph;
187*d07d7c3dSDanielle Ratson 	size_t len = 0;
188*d07d7c3dSDanielle Ratson 
189*d07d7c3dSDanielle Ratson 	len = sizeof(*iph) + sizeof(*udph) + payload_len;
190*d07d7c3dSDanielle Ratson 	if (len > buflen)
191*d07d7c3dSDanielle Ratson 		return -EINVAL;
192*d07d7c3dSDanielle Ratson 
193*d07d7c3dSDanielle Ratson 	iph = (struct iphdr *)buf;
194*d07d7c3dSDanielle Ratson 	udph = (struct udphdr *)(buf + sizeof(*iph));
195*d07d7c3dSDanielle Ratson 
196*d07d7c3dSDanielle Ratson 	iph->version = 4;
197*d07d7c3dSDanielle Ratson 	iph->ihl = 5;
198*d07d7c3dSDanielle Ratson 	iph->protocol = IPPROTO_UDP;
199*d07d7c3dSDanielle Ratson 	iph->saddr = htonl(INADDR_LOOPBACK);
200*d07d7c3dSDanielle Ratson 	iph->daddr = htonl(INADDR_LOOPBACK);
201*d07d7c3dSDanielle Ratson 	iph->tot_len = htons(len);
202*d07d7c3dSDanielle Ratson 	iph->check = htons(csum(iph, sizeof(*iph)));
203*d07d7c3dSDanielle Ratson 
204*d07d7c3dSDanielle Ratson 	udph->source = htons(src_port);
205*d07d7c3dSDanielle Ratson 	udph->dest = htons(dst_port);
206*d07d7c3dSDanielle Ratson 	udph->len = htons(sizeof(*udph) + payload_len);
207*d07d7c3dSDanielle Ratson 
208*d07d7c3dSDanielle Ratson 	memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte,
209*d07d7c3dSDanielle Ratson 	       payload_len);
210*d07d7c3dSDanielle Ratson 
211*d07d7c3dSDanielle Ratson 	return len;
212*d07d7c3dSDanielle Ratson }
213*d07d7c3dSDanielle Ratson 
214*d07d7c3dSDanielle Ratson static int build_orig_dgram_v6(uint8_t *buf, ssize_t buflen, int payload_len)
215*d07d7c3dSDanielle Ratson {
216*d07d7c3dSDanielle Ratson 	struct udphdr *udph;
217*d07d7c3dSDanielle Ratson 	struct ipv6hdr *iph;
218*d07d7c3dSDanielle Ratson 	size_t len = 0;
219*d07d7c3dSDanielle Ratson 
220*d07d7c3dSDanielle Ratson 	len = sizeof(*iph) + sizeof(*udph) + payload_len;
221*d07d7c3dSDanielle Ratson 	if (len > buflen)
222*d07d7c3dSDanielle Ratson 		return -EINVAL;
223*d07d7c3dSDanielle Ratson 
224*d07d7c3dSDanielle Ratson 	iph = (struct ipv6hdr *)buf;
225*d07d7c3dSDanielle Ratson 	udph = (struct udphdr *)(buf + sizeof(*iph));
226*d07d7c3dSDanielle Ratson 
227*d07d7c3dSDanielle Ratson 	iph->version = 6;
228*d07d7c3dSDanielle Ratson 	iph->payload_len = htons(sizeof(*udph) + payload_len);
229*d07d7c3dSDanielle Ratson 	iph->nexthdr = IPPROTO_UDP;
230*d07d7c3dSDanielle Ratson 	iph->saddr = in6addr_loopback;
231*d07d7c3dSDanielle Ratson 	iph->daddr = in6addr_loopback;
232*d07d7c3dSDanielle Ratson 
233*d07d7c3dSDanielle Ratson 	udph->source = htons(src_port);
234*d07d7c3dSDanielle Ratson 	udph->dest = htons(dst_port);
235*d07d7c3dSDanielle Ratson 	udph->len = htons(sizeof(*udph) + payload_len);
236*d07d7c3dSDanielle Ratson 
237*d07d7c3dSDanielle Ratson 	memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte,
238*d07d7c3dSDanielle Ratson 	       payload_len);
239*d07d7c3dSDanielle Ratson 
240*d07d7c3dSDanielle Ratson 	return len;
241*d07d7c3dSDanielle Ratson }
242*d07d7c3dSDanielle Ratson 
243*d07d7c3dSDanielle Ratson static int build_icmpv4_pkt(uint8_t *buf, ssize_t buflen, bool with_ext,
244*d07d7c3dSDanielle Ratson 			    int payload_len, bool bad_csum, bool bad_len,
245*d07d7c3dSDanielle Ratson 			    bool smaller_len)
246*d07d7c3dSDanielle Ratson {
247*d07d7c3dSDanielle Ratson 	struct icmphdr *icmph;
248*d07d7c3dSDanielle Ratson 	int len, ret;
249*d07d7c3dSDanielle Ratson 
250*d07d7c3dSDanielle Ratson 	len = sizeof(*icmph);
251*d07d7c3dSDanielle Ratson 	memset(buf, 0, buflen);
252*d07d7c3dSDanielle Ratson 
253*d07d7c3dSDanielle Ratson 	icmph = (struct icmphdr *)buf;
254*d07d7c3dSDanielle Ratson 	icmph->type = ICMP_DEST_UNREACH;
255*d07d7c3dSDanielle Ratson 	icmph->code = ICMP_PORT_UNREACH;
256*d07d7c3dSDanielle Ratson 	icmph->checksum = 0;
257*d07d7c3dSDanielle Ratson 
258*d07d7c3dSDanielle Ratson 	ret = build_orig_dgram_v4(buf + len, buflen - len, payload_len);
259*d07d7c3dSDanielle Ratson 	if (ret < 0)
260*d07d7c3dSDanielle Ratson 		return ret;
261*d07d7c3dSDanielle Ratson 
262*d07d7c3dSDanielle Ratson 	len += ret;
263*d07d7c3dSDanielle Ratson 
264*d07d7c3dSDanielle Ratson 	icmph->un.reserved[1] = (len - sizeof(*icmph)) / sizeof(uint32_t);
265*d07d7c3dSDanielle Ratson 
266*d07d7c3dSDanielle Ratson 	if (with_ext) {
267*d07d7c3dSDanielle Ratson 		ret = build_rfc4884_ext(buf + len, buflen - len,
268*d07d7c3dSDanielle Ratson 					bad_csum, bad_len, smaller_len);
269*d07d7c3dSDanielle Ratson 		if (ret < 0)
270*d07d7c3dSDanielle Ratson 			return ret;
271*d07d7c3dSDanielle Ratson 
272*d07d7c3dSDanielle Ratson 		len += ret;
273*d07d7c3dSDanielle Ratson 	}
274*d07d7c3dSDanielle Ratson 
275*d07d7c3dSDanielle Ratson 	icmph->checksum = htons(csum(icmph, len));
276*d07d7c3dSDanielle Ratson 	return len;
277*d07d7c3dSDanielle Ratson }
278*d07d7c3dSDanielle Ratson 
279*d07d7c3dSDanielle Ratson static int build_icmpv6_pkt(uint8_t *buf, ssize_t buflen, bool with_ext,
280*d07d7c3dSDanielle Ratson 			    int payload_len, bool bad_csum, bool bad_len,
281*d07d7c3dSDanielle Ratson 			    bool smaller_len)
282*d07d7c3dSDanielle Ratson {
283*d07d7c3dSDanielle Ratson 	struct icmp6hdr *icmph;
284*d07d7c3dSDanielle Ratson 	int len, ret;
285*d07d7c3dSDanielle Ratson 
286*d07d7c3dSDanielle Ratson 	len = sizeof(*icmph);
287*d07d7c3dSDanielle Ratson 	memset(buf, 0, buflen);
288*d07d7c3dSDanielle Ratson 
289*d07d7c3dSDanielle Ratson 	icmph = (struct icmp6hdr *)buf;
290*d07d7c3dSDanielle Ratson 	icmph->icmp6_type = ICMPV6_DEST_UNREACH;
291*d07d7c3dSDanielle Ratson 	icmph->icmp6_code = ICMPV6_PORT_UNREACH;
292*d07d7c3dSDanielle Ratson 	icmph->icmp6_cksum = 0;
293*d07d7c3dSDanielle Ratson 
294*d07d7c3dSDanielle Ratson 	ret = build_orig_dgram_v6(buf + len, buflen - len, payload_len);
295*d07d7c3dSDanielle Ratson 	if (ret < 0)
296*d07d7c3dSDanielle Ratson 		return ret;
297*d07d7c3dSDanielle Ratson 
298*d07d7c3dSDanielle Ratson 	len += ret;
299*d07d7c3dSDanielle Ratson 
300*d07d7c3dSDanielle Ratson 	icmph->icmp6_datagram_len = (len - sizeof(*icmph)) / sizeof(uint64_t);
301*d07d7c3dSDanielle Ratson 
302*d07d7c3dSDanielle Ratson 	if (with_ext) {
303*d07d7c3dSDanielle Ratson 		ret = build_rfc4884_ext(buf + len, buflen - len,
304*d07d7c3dSDanielle Ratson 					bad_csum, bad_len, smaller_len);
305*d07d7c3dSDanielle Ratson 		if (ret < 0)
306*d07d7c3dSDanielle Ratson 			return ret;
307*d07d7c3dSDanielle Ratson 
308*d07d7c3dSDanielle Ratson 		len += ret;
309*d07d7c3dSDanielle Ratson 	}
310*d07d7c3dSDanielle Ratson 
311*d07d7c3dSDanielle Ratson 	icmph->icmp6_cksum = htons(csum(icmph, len));
312*d07d7c3dSDanielle Ratson 	return len;
313*d07d7c3dSDanielle Ratson }
314*d07d7c3dSDanielle Ratson 
315*d07d7c3dSDanielle Ratson FIXTURE(rfc4884) {};
316*d07d7c3dSDanielle Ratson 
317*d07d7c3dSDanielle Ratson FIXTURE_SETUP(rfc4884)
318*d07d7c3dSDanielle Ratson {
319*d07d7c3dSDanielle Ratson 	int ret;
320*d07d7c3dSDanielle Ratson 
321*d07d7c3dSDanielle Ratson 	ret = unshare(CLONE_NEWNET);
322*d07d7c3dSDanielle Ratson 	ASSERT_EQ(ret, 0) {
323*d07d7c3dSDanielle Ratson 		TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno));
324*d07d7c3dSDanielle Ratson 	}
325*d07d7c3dSDanielle Ratson 
326*d07d7c3dSDanielle Ratson 	ret = bringup_loopback();
327*d07d7c3dSDanielle Ratson 	ASSERT_EQ(ret, 0) TH_LOG("Failed to bring up loopback interface");
328*d07d7c3dSDanielle Ratson }
329*d07d7c3dSDanielle Ratson 
330*d07d7c3dSDanielle Ratson FIXTURE_TEARDOWN(rfc4884)
331*d07d7c3dSDanielle Ratson {
332*d07d7c3dSDanielle Ratson }
333*d07d7c3dSDanielle Ratson 
334*d07d7c3dSDanielle Ratson const struct ip_case_info ipv4_info = {
335*d07d7c3dSDanielle Ratson 	.domain		= AF_INET,
336*d07d7c3dSDanielle Ratson 	.level		= SOL_IP,
337*d07d7c3dSDanielle Ratson 	.opt1		= IP_RECVERR,
338*d07d7c3dSDanielle Ratson 	.opt2		= IP_RECVERR_RFC4884,
339*d07d7c3dSDanielle Ratson 	.proto		= IPPROTO_ICMP,
340*d07d7c3dSDanielle Ratson 	.build_func	= build_icmpv4_pkt,
341*d07d7c3dSDanielle Ratson 	.min_payload	= min_payload_len_v4,
342*d07d7c3dSDanielle Ratson };
343*d07d7c3dSDanielle Ratson 
344*d07d7c3dSDanielle Ratson const struct ip_case_info ipv6_info = {
345*d07d7c3dSDanielle Ratson 	.domain		= AF_INET6,
346*d07d7c3dSDanielle Ratson 	.level		= SOL_IPV6,
347*d07d7c3dSDanielle Ratson 	.opt1		= IPV6_RECVERR,
348*d07d7c3dSDanielle Ratson 	.opt2		= IPV6_RECVERR_RFC4884,
349*d07d7c3dSDanielle Ratson 	.proto		= IPPROTO_ICMPV6,
350*d07d7c3dSDanielle Ratson 	.build_func	= build_icmpv6_pkt,
351*d07d7c3dSDanielle Ratson 	.min_payload	= min_payload_len_v6,
352*d07d7c3dSDanielle Ratson };
353*d07d7c3dSDanielle Ratson 
354*d07d7c3dSDanielle Ratson FIXTURE_VARIANT(rfc4884) {
355*d07d7c3dSDanielle Ratson 	/* IPv4/v6 related information */
356*d07d7c3dSDanielle Ratson 	struct ip_case_info	info;
357*d07d7c3dSDanielle Ratson 	/* Whether to append an ICMP extension or not */
358*d07d7c3dSDanielle Ratson 	bool			with_ext;
359*d07d7c3dSDanielle Ratson 	/* UDP payload length */
360*d07d7c3dSDanielle Ratson 	int			payload_len;
361*d07d7c3dSDanielle Ratson 	/* Whether to generate a bad checksum in the ICMP extension structure */
362*d07d7c3dSDanielle Ratson 	bool			bad_csum;
363*d07d7c3dSDanielle Ratson 	/* Whether to generate a bad length in the ICMP object header */
364*d07d7c3dSDanielle Ratson 	bool			bad_len;
365*d07d7c3dSDanielle Ratson 	/* Whether it is too small to fit the object header or too big to fit
366*d07d7c3dSDanielle Ratson 	 * in the packet
367*d07d7c3dSDanielle Ratson 	 */
368*d07d7c3dSDanielle Ratson 	bool			smaller_len;
369*d07d7c3dSDanielle Ratson };
370*d07d7c3dSDanielle Ratson 
371*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message with extension and the original
372*d07d7c3dSDanielle Ratson  * datagram is smaller than 128 bytes, generates an error with zero offset,
373*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
374*d07d7c3dSDanielle Ratson  */
375*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_small_payload) {
376*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
377*d07d7c3dSDanielle Ratson 	.with_ext	= true,
378*d07d7c3dSDanielle Ratson 	.payload_len	= 64,
379*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
380*d07d7c3dSDanielle Ratson 	.bad_len	= false,
381*d07d7c3dSDanielle Ratson };
382*d07d7c3dSDanielle Ratson 
383*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message with extension and 128 bytes original
384*d07d7c3dSDanielle Ratson  * datagram, generates an error with the expected offset, and does not raise the
385*d07d7c3dSDanielle Ratson  * SO_EE_RFC4884_FLAG_INVALID flag.
386*d07d7c3dSDanielle Ratson  */
387*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext) {
388*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
389*d07d7c3dSDanielle Ratson 	.with_ext	= true,
390*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v4,
391*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
392*d07d7c3dSDanielle Ratson 	.bad_len	= false,
393*d07d7c3dSDanielle Ratson };
394*d07d7c3dSDanielle Ratson 
395*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message with extension and the original
396*d07d7c3dSDanielle Ratson  * datagram is larger than 128 bytes, generates an error with the expected
397*d07d7c3dSDanielle Ratson  * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
398*d07d7c3dSDanielle Ratson  */
399*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_large_payload) {
400*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
401*d07d7c3dSDanielle Ratson 	.with_ext	= true,
402*d07d7c3dSDanielle Ratson 	.payload_len	= 256,
403*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
404*d07d7c3dSDanielle Ratson 	.bad_len	= false,
405*d07d7c3dSDanielle Ratson };
406*d07d7c3dSDanielle Ratson 
407*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message without extension and the original
408*d07d7c3dSDanielle Ratson  * datagram is smaller than 128 bytes, generates an error with zero offset,
409*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
410*d07d7c3dSDanielle Ratson  */
411*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_small_payload) {
412*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
413*d07d7c3dSDanielle Ratson 	.with_ext	= false,
414*d07d7c3dSDanielle Ratson 	.payload_len	= 64,
415*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
416*d07d7c3dSDanielle Ratson 	.bad_len	= false,
417*d07d7c3dSDanielle Ratson };
418*d07d7c3dSDanielle Ratson 
419*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message without extension and 128 bytes
420*d07d7c3dSDanielle Ratson  * original datagram, generates an error with zero offset, and does not raise
421*d07d7c3dSDanielle Ratson  * the SO_EE_RFC4884_FLAG_INVALID flag.
422*d07d7c3dSDanielle Ratson  */
423*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_min_payload) {
424*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
425*d07d7c3dSDanielle Ratson 	.with_ext	= false,
426*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v4,
427*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
428*d07d7c3dSDanielle Ratson 	.bad_len	= false,
429*d07d7c3dSDanielle Ratson };
430*d07d7c3dSDanielle Ratson 
431*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv4 error message without extension and the original
432*d07d7c3dSDanielle Ratson  * datagram is larger than 128 bytes, generates an error with zero offset,
433*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
434*d07d7c3dSDanielle Ratson  */
435*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_large_payload) {
436*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
437*d07d7c3dSDanielle Ratson 	.with_ext	= false,
438*d07d7c3dSDanielle Ratson 	.payload_len	= 256,
439*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
440*d07d7c3dSDanielle Ratson 	.bad_len	= false,
441*d07d7c3dSDanielle Ratson };
442*d07d7c3dSDanielle Ratson 
443*d07d7c3dSDanielle Ratson /* Tests that an ICMPv4 error message with extension and an invalid checksum,
444*d07d7c3dSDanielle Ratson  * generates an error with the expected offset, and raises the
445*d07d7c3dSDanielle Ratson  * SO_EE_RFC4884_FLAG_INVALID flag.
446*d07d7c3dSDanielle Ratson  */
447*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_checksum) {
448*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
449*d07d7c3dSDanielle Ratson 	.with_ext	= true,
450*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v4,
451*d07d7c3dSDanielle Ratson 	.bad_csum	= true,
452*d07d7c3dSDanielle Ratson 	.bad_len	= false,
453*d07d7c3dSDanielle Ratson };
454*d07d7c3dSDanielle Ratson 
455*d07d7c3dSDanielle Ratson /* Tests that an ICMPv4 error message with extension and an object length
456*d07d7c3dSDanielle Ratson  * smaller than the object header, generates an error with the expected offset,
457*d07d7c3dSDanielle Ratson  * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
458*d07d7c3dSDanielle Ratson  */
459*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_small) {
460*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
461*d07d7c3dSDanielle Ratson 	.with_ext	= true,
462*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v4,
463*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
464*d07d7c3dSDanielle Ratson 	.bad_len	= true,
465*d07d7c3dSDanielle Ratson 	.smaller_len	= true,
466*d07d7c3dSDanielle Ratson };
467*d07d7c3dSDanielle Ratson 
468*d07d7c3dSDanielle Ratson /* Tests that an ICMPv4 error message with extension and an object length that
469*d07d7c3dSDanielle Ratson  * is too big to fit in the packet, generates an error with the expected offset,
470*d07d7c3dSDanielle Ratson  * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
471*d07d7c3dSDanielle Ratson  */
472*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_large) {
473*d07d7c3dSDanielle Ratson 	.info		= ipv4_info,
474*d07d7c3dSDanielle Ratson 	.with_ext	= true,
475*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v4,
476*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
477*d07d7c3dSDanielle Ratson 	.bad_len	= true,
478*d07d7c3dSDanielle Ratson 	.smaller_len	= false,
479*d07d7c3dSDanielle Ratson };
480*d07d7c3dSDanielle Ratson 
481*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message with extension and the original
482*d07d7c3dSDanielle Ratson  * datagram is smaller than 128 bytes, generates an error with zero offset,
483*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
484*d07d7c3dSDanielle Ratson  */
485*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_small_payload) {
486*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
487*d07d7c3dSDanielle Ratson 	.with_ext	= true,
488*d07d7c3dSDanielle Ratson 	.payload_len	= 64,
489*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
490*d07d7c3dSDanielle Ratson 	.bad_len	= false,
491*d07d7c3dSDanielle Ratson };
492*d07d7c3dSDanielle Ratson 
493*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message with extension and 128 bytes original
494*d07d7c3dSDanielle Ratson  * datagram, generates an error with the expected offset, and does not raise the
495*d07d7c3dSDanielle Ratson  * SO_EE_RFC4884_FLAG_INVALID flag.
496*d07d7c3dSDanielle Ratson  */
497*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext) {
498*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
499*d07d7c3dSDanielle Ratson 	.with_ext	= true,
500*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v6,
501*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
502*d07d7c3dSDanielle Ratson 	.bad_len	= false,
503*d07d7c3dSDanielle Ratson };
504*d07d7c3dSDanielle Ratson 
505*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message with extension and the original
506*d07d7c3dSDanielle Ratson  * datagram is larger than 128 bytes, generates an error with the expected
507*d07d7c3dSDanielle Ratson  * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
508*d07d7c3dSDanielle Ratson  */
509*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_large_payload) {
510*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
511*d07d7c3dSDanielle Ratson 	.with_ext	= true,
512*d07d7c3dSDanielle Ratson 	.payload_len	= 256,
513*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
514*d07d7c3dSDanielle Ratson 	.bad_len	= false,
515*d07d7c3dSDanielle Ratson };
516*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message without extension and the original
517*d07d7c3dSDanielle Ratson  * datagram is smaller than 128 bytes, generates an error with zero offset,
518*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
519*d07d7c3dSDanielle Ratson  */
520*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_small_payload) {
521*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
522*d07d7c3dSDanielle Ratson 	.with_ext	= false,
523*d07d7c3dSDanielle Ratson 	.payload_len	= 64,
524*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
525*d07d7c3dSDanielle Ratson 	.bad_len	= false,
526*d07d7c3dSDanielle Ratson };
527*d07d7c3dSDanielle Ratson 
528*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message without extension and 128 bytes
529*d07d7c3dSDanielle Ratson  * original datagram, generates an error with zero offset, and does not
530*d07d7c3dSDanielle Ratson  * raise the SO_EE_RFC4884_FLAG_INVALID flag.
531*d07d7c3dSDanielle Ratson  */
532*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_min_payload) {
533*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
534*d07d7c3dSDanielle Ratson 	.with_ext	= false,
535*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v6,
536*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
537*d07d7c3dSDanielle Ratson 	.bad_len	= false,
538*d07d7c3dSDanielle Ratson };
539*d07d7c3dSDanielle Ratson 
540*d07d7c3dSDanielle Ratson /* Tests that a valid ICMPv6 error message without extension and the original
541*d07d7c3dSDanielle Ratson  * datagram is larger than 128 bytes, generates an error with zero offset,
542*d07d7c3dSDanielle Ratson  * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag.
543*d07d7c3dSDanielle Ratson  */
544*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_large_payload) {
545*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
546*d07d7c3dSDanielle Ratson 	.with_ext	= false,
547*d07d7c3dSDanielle Ratson 	.payload_len	= 256,
548*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
549*d07d7c3dSDanielle Ratson 	.bad_len	= false,
550*d07d7c3dSDanielle Ratson };
551*d07d7c3dSDanielle Ratson 
552*d07d7c3dSDanielle Ratson /* Tests that an ICMPv6 error message with extension and an invalid checksum,
553*d07d7c3dSDanielle Ratson  * generates an error with the expected offset, and raises the
554*d07d7c3dSDanielle Ratson  * SO_EE_RFC4884_FLAG_INVALID flag.
555*d07d7c3dSDanielle Ratson  */
556*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_checksum) {
557*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
558*d07d7c3dSDanielle Ratson 	.with_ext	= true,
559*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v6,
560*d07d7c3dSDanielle Ratson 	.bad_csum	= true,
561*d07d7c3dSDanielle Ratson 	.bad_len	= false,
562*d07d7c3dSDanielle Ratson };
563*d07d7c3dSDanielle Ratson 
564*d07d7c3dSDanielle Ratson /* Tests that an ICMPv6 error message with extension and an object length
565*d07d7c3dSDanielle Ratson  * smaller than the object header, generates an error with the expected offset,
566*d07d7c3dSDanielle Ratson  * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
567*d07d7c3dSDanielle Ratson  */
568*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_small) {
569*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
570*d07d7c3dSDanielle Ratson 	.with_ext	= true,
571*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v6,
572*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
573*d07d7c3dSDanielle Ratson 	.bad_len	= true,
574*d07d7c3dSDanielle Ratson 	.smaller_len	= true,
575*d07d7c3dSDanielle Ratson };
576*d07d7c3dSDanielle Ratson 
577*d07d7c3dSDanielle Ratson /* Tests that an ICMPv6 error message with extension and an object length that
578*d07d7c3dSDanielle Ratson  * is too big to fit in the packet, generates an error with the expected offset,
579*d07d7c3dSDanielle Ratson  * and raises the SO_EE_RFC4884_FLAG_INVALID flag.
580*d07d7c3dSDanielle Ratson  */
581*d07d7c3dSDanielle Ratson FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_large) {
582*d07d7c3dSDanielle Ratson 	.info		= ipv6_info,
583*d07d7c3dSDanielle Ratson 	.with_ext	= true,
584*d07d7c3dSDanielle Ratson 	.payload_len	= min_payload_len_v6,
585*d07d7c3dSDanielle Ratson 	.bad_csum	= false,
586*d07d7c3dSDanielle Ratson 	.bad_len	= true,
587*d07d7c3dSDanielle Ratson 	.smaller_len	= false,
588*d07d7c3dSDanielle Ratson };
589*d07d7c3dSDanielle Ratson 
590*d07d7c3dSDanielle Ratson static void
591*d07d7c3dSDanielle Ratson check_rfc4884_offset(struct __test_metadata *_metadata, int sock,
592*d07d7c3dSDanielle Ratson 		     const FIXTURE_VARIANT(rfc4884) *v)
593*d07d7c3dSDanielle Ratson {
594*d07d7c3dSDanielle Ratson 	char rxbuf[1024];
595*d07d7c3dSDanielle Ratson 	char ctrl[1024];
596*d07d7c3dSDanielle Ratson 	struct iovec iov = {
597*d07d7c3dSDanielle Ratson 		.iov_base = rxbuf,
598*d07d7c3dSDanielle Ratson 		.iov_len = sizeof(rxbuf)
599*d07d7c3dSDanielle Ratson 	};
600*d07d7c3dSDanielle Ratson 	struct msghdr msg = {
601*d07d7c3dSDanielle Ratson 		.msg_iov = &iov,
602*d07d7c3dSDanielle Ratson 		.msg_iovlen = 1,
603*d07d7c3dSDanielle Ratson 		.msg_control = ctrl,
604*d07d7c3dSDanielle Ratson 		.msg_controllen = sizeof(ctrl),
605*d07d7c3dSDanielle Ratson 	};
606*d07d7c3dSDanielle Ratson 	struct cmsghdr *cmsg;
607*d07d7c3dSDanielle Ratson 	int recv;
608*d07d7c3dSDanielle Ratson 
609*d07d7c3dSDanielle Ratson 	ASSERT_EQ(poll_err(sock), 0);
610*d07d7c3dSDanielle Ratson 
611*d07d7c3dSDanielle Ratson 	recv = recvmsg(sock, &msg, MSG_ERRQUEUE);
612*d07d7c3dSDanielle Ratson 	ASSERT_GE(recv, 0) TH_LOG("recvmsg(MSG_ERRQUEUE) failed");
613*d07d7c3dSDanielle Ratson 
614*d07d7c3dSDanielle Ratson 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
615*d07d7c3dSDanielle Ratson 		bool is_invalid, expected_invalid;
616*d07d7c3dSDanielle Ratson 		struct sock_extended_err *ee;
617*d07d7c3dSDanielle Ratson 		int expected_off;
618*d07d7c3dSDanielle Ratson 		uint16_t off;
619*d07d7c3dSDanielle Ratson 
620*d07d7c3dSDanielle Ratson 		if (cmsg->cmsg_level != v->info.level ||
621*d07d7c3dSDanielle Ratson 		    cmsg->cmsg_type != v->info.opt1) {
622*d07d7c3dSDanielle Ratson 			TH_LOG("Unrelated cmsgs were encountered in recvmsg()");
623*d07d7c3dSDanielle Ratson 			continue;
624*d07d7c3dSDanielle Ratson 		}
625*d07d7c3dSDanielle Ratson 
626*d07d7c3dSDanielle Ratson 		ee = (struct sock_extended_err *)CMSG_DATA(cmsg);
627*d07d7c3dSDanielle Ratson 		off = ee->ee_rfc4884.len;
628*d07d7c3dSDanielle Ratson 		is_invalid = ee->ee_rfc4884.flags & SO_EE_RFC4884_FLAG_INVALID;
629*d07d7c3dSDanielle Ratson 
630*d07d7c3dSDanielle Ratson 		expected_invalid = v->bad_csum || v->bad_len;
631*d07d7c3dSDanielle Ratson 		ASSERT_EQ(is_invalid, expected_invalid) {
632*d07d7c3dSDanielle Ratson 			TH_LOG("Expected invalidity flag to be %d, but got %d",
633*d07d7c3dSDanielle Ratson 			       expected_invalid, is_invalid);
634*d07d7c3dSDanielle Ratson 		}
635*d07d7c3dSDanielle Ratson 
636*d07d7c3dSDanielle Ratson 		expected_off =
637*d07d7c3dSDanielle Ratson 			(v->with_ext && v->payload_len >= v->info.min_payload) ?
638*d07d7c3dSDanielle Ratson 			v->payload_len : 0;
639*d07d7c3dSDanielle Ratson 		ASSERT_EQ(off, expected_off) {
640*d07d7c3dSDanielle Ratson 			TH_LOG("Expected RFC4884 offset %u, got %u",
641*d07d7c3dSDanielle Ratson 			       expected_off, off);
642*d07d7c3dSDanielle Ratson 		}
643*d07d7c3dSDanielle Ratson 		break;
644*d07d7c3dSDanielle Ratson 	}
645*d07d7c3dSDanielle Ratson }
646*d07d7c3dSDanielle Ratson 
647*d07d7c3dSDanielle Ratson TEST_F(rfc4884, rfc4884)
648*d07d7c3dSDanielle Ratson {
649*d07d7c3dSDanielle Ratson 	const typeof(variant) v = variant;
650*d07d7c3dSDanielle Ratson 	struct sockaddr_inet addr;
651*d07d7c3dSDanielle Ratson 	uint8_t pkt[1024];
652*d07d7c3dSDanielle Ratson 	int dgram, raw;
653*d07d7c3dSDanielle Ratson 	int len, sent;
654*d07d7c3dSDanielle Ratson 	int err;
655*d07d7c3dSDanielle Ratson 
656*d07d7c3dSDanielle Ratson 	dgram = socket(v->info.domain, SOCK_DGRAM, 0);
657*d07d7c3dSDanielle Ratson 	ASSERT_GE(dgram, 0) TH_LOG("Opening datagram socket failed");
658*d07d7c3dSDanielle Ratson 
659*d07d7c3dSDanielle Ratson 	err = bind_and_setsockopt(dgram, &v->info);
660*d07d7c3dSDanielle Ratson 	ASSERT_EQ(err, 0) TH_LOG("Bind failed");
661*d07d7c3dSDanielle Ratson 
662*d07d7c3dSDanielle Ratson 	raw = socket(v->info.domain, SOCK_RAW, v->info.proto);
663*d07d7c3dSDanielle Ratson 	ASSERT_GE(raw, 0) TH_LOG("Opening raw socket failed");
664*d07d7c3dSDanielle Ratson 
665*d07d7c3dSDanielle Ratson 	len = v->info.build_func(pkt, sizeof(pkt), v->with_ext, v->payload_len,
666*d07d7c3dSDanielle Ratson 				 v->bad_csum, v->bad_len, v->smaller_len);
667*d07d7c3dSDanielle Ratson 	ASSERT_GT(len, 0) TH_LOG("Building packet failed");
668*d07d7c3dSDanielle Ratson 
669*d07d7c3dSDanielle Ratson 	set_addr(&addr, v->info.domain, 0);
670*d07d7c3dSDanielle Ratson 	sent = sendto(raw, pkt, len, 0, &addr.sa, addr.len);
671*d07d7c3dSDanielle Ratson 	ASSERT_EQ(len, sent) TH_LOG("Sending packet failed");
672*d07d7c3dSDanielle Ratson 
673*d07d7c3dSDanielle Ratson 	check_rfc4884_offset(_metadata, dgram, v);
674*d07d7c3dSDanielle Ratson 
675*d07d7c3dSDanielle Ratson 	close(dgram);
676*d07d7c3dSDanielle Ratson 	close(raw);
677*d07d7c3dSDanielle Ratson }
678*d07d7c3dSDanielle Ratson 
679*d07d7c3dSDanielle Ratson TEST_HARNESS_MAIN
680