xref: /xnu-12377.81.4/tests/skywalk/skt_reass.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2017-2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <TargetConditionals.h>
30 #include <arpa/inet.h>
31 #include <assert.h>
32 #include <err.h>
33 #include <net/if_utun.h>
34 #include <netinet/icmp6.h>
35 #include <netinet/in.h>
36 #include <netinet/in.h>
37 #include <netinet/ip.h>
38 #include <netinet/ip6.h>
39 #include <netinet/tcp.h>
40 #include <netinet/udp.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <sys/event.h>
46 #include <sys/fcntl.h>
47 #include <sys/ioctl.h>
48 #include <sys/kern_control.h>
49 #include <sys/sys_domain.h>
50 #include <sys/sysctl.h>
51 #include <sys/time.h>
52 #include <unistd.h>
53 #include <darwintest.h>
54 
55 #include "skywalk_test_common.h"
56 #include "skywalk_test_driver.h"
57 #include "skywalk_test_utils.h"
58 
59 #define NX_PORT 3
60 #define BUF_SIZE 8192
61 #define MAX_FRAMES 8
62 
63 typedef size_t (*reass_packet_builder_fn)(uint8_t *buf);
64 typedef int (*reass_packet_matcher_fn)(uint8_t *pkt_out, size_t len_out,
65     uint8_t *pkt_in, size_t len_in);
66 
67 static int utun_fd;
68 static struct sktc_nexus_handles nexus;
69 static channel_port port;
70 
71 static char utun_ifname[IFNAMSIZ + 1];
72 
73 // to select one of the following global/linklocal addr
74 static char *utun_addr_str = NULL;
75 static char *peer_addr_str = NULL;
76 
77 static char *our_ip_str = "192.168.255.2";
78 static char *dst_ip_str = "10.0.0.1";
79 static char *broad_ip_str = "192.168.255.255";
80 
81 static char *utun_global_addr_str = "2607:1111::1111";
82 static char *peer_global_addr_str = "2607:2222::2222";
83 
84 static char *utun_linklocal_addr_str = "fe80::1111";
85 static char *peer_linklocal_addr_str = "fe80::2222";
86 
87 static int addr_prefix_length = 128;    // 128 for POINTOPOINT UTUN
88 
89 struct timeval timeout = {
90 	.tv_sec = 10,
91 	.tv_usec = 0,
92 };
93 
94 static void
die_perror(const char * str)95 die_perror(const char *str)
96 {
97 	perror(str);
98 	assert(0);
99 }
100 
101 void
reass_init()102 reass_init()
103 {
104 	int error;
105 
106 	utun_fd = sktu_create_interface(SKTU_IFT_UTUN, SKTU_IFF_ENABLE_NETIF);
107 
108 	sktu_get_interface_name(SKTU_IFT_UTUN, utun_fd, utun_ifname);
109 
110 	error = fcntl(utun_fd, F_SETFD, FD_CLOEXEC);
111 	if (error != 0) {
112 		die_perror("FD_CLOEXEC");
113 	}
114 
115 	struct in6_addr utun_addr, dst_addr;
116 	inet_pton(AF_INET6, utun_addr_str, &utun_addr);
117 	inet_pton(AF_INET6, peer_addr_str, &dst_addr);
118 	error = sktc_ifnet_add_addr6(utun_ifname, &utun_addr, &dst_addr,
119 	    addr_prefix_length, 0);
120 	assert(error == 0);
121 }
122 
123 void
reass_fini(void)124 reass_fini(void)
125 {
126 	close(utun_fd);
127 }
128 
129 void
reass_interface_send(reass_packet_builder_fn builder)130 reass_interface_send(reass_packet_builder_fn builder)
131 {
132 	uint8_t pkt_in[BUF_SIZE];
133 
134 	size_t len_in = builder(pkt_in);
135 
136 	T_LOG("sending to utun, len %ld\n", len_in);
137 	sktu_dump_buffer(stderr, NULL, pkt_in, len_in);
138 	int i = 0;
139 	while (i++ < 1000) {
140 		write(utun_fd, pkt_in, len_in);
141 	}
142 }
143 
144 void
reass_interface_send_rece(reass_packet_builder_fn builder,reass_packet_matcher_fn matcher,struct timeval * allowed)145 reass_interface_send_rece(reass_packet_builder_fn builder,
146     reass_packet_matcher_fn matcher, struct timeval *allowed)
147 {
148 	struct timeval start, now, elapsed, left;
149 	fd_set readfds, errorfds;
150 	uint8_t pkt_in[BUF_SIZE];
151 	uint8_t pkt_out[BUF_SIZE];
152 	size_t len_in, len_out;
153 	int retval;
154 
155 	len_in = builder(pkt_in);
156 
157 	T_LOG("sending to utun, len %ld\n", len_in);
158 	sktu_dump_buffer(stderr, NULL, pkt_in, len_in);
159 	write(utun_fd, pkt_in, len_in);
160 
161 	if (gettimeofday(&start, NULL) != 0) {
162 		die_perror("gettimeofday");
163 	}
164 	left = *allowed;
165 
166 	while (1) {
167 		FD_ZERO(&readfds);
168 		FD_ZERO(&errorfds);
169 		FD_SET(utun_fd, &readfds);
170 		FD_SET(utun_fd, &errorfds);
171 
172 		retval = select(utun_fd + 1, &readfds, NULL, &errorfds, &left);
173 		if (retval == -1) {
174 			die_perror("select()");
175 		}
176 
177 		if (!FD_ISSET(utun_fd, &readfds) && retval == 0) { // timeout
178 			T_LOG("recv timeout\n");
179 			assert(0);
180 		}
181 
182 		assert(!FD_ISSET(utun_fd, &errorfds));
183 		assert(retval == 1);
184 
185 		if (FD_ISSET(utun_fd, &readfds)) {
186 			len_out = read(utun_fd, pkt_out, BUF_SIZE);
187 			if (len_out < 1) {
188 				T_LOG("utun read error\n");
189 				assert(0);
190 			}
191 		}
192 
193 		T_LOG("read from utun, len %ld\n", len_out);
194 		sktu_dump_buffer(stderr, NULL, pkt_out, len_out);
195 
196 		if (matcher(pkt_out, len_out, pkt_in, len_in) == 0) {
197 			break;
198 		}
199 
200 		if (gettimeofday(&now, NULL) != 0) {
201 			die_perror("gettimeofday");
202 		}
203 
204 		timersub(&now, &start, &elapsed);
205 		timersub(allowed, &elapsed, &left);
206 	}
207 }
208 
209 void
reass_common(channel_port * ch_port,struct sktu_flow * flow,void * data,size_t data_len)210 reass_common(channel_port *ch_port, struct sktu_flow *flow, void *data,
211     size_t data_len)
212 {
213 	struct sktu_frame *rx_frames[MAX_FRAMES];
214 	struct sktu_frame *tx_frames[MAX_FRAMES];
215 	size_t n_rx_frames;
216 	size_t n_tx_frames;
217 
218 	n_tx_frames = flow->create_input_frames(flow, tx_frames, MAX_FRAMES,
219 	    data, data_len);
220 
221 	sktu_utun_fd_tx_burst(utun_fd, tx_frames, n_tx_frames);
222 
223 	n_rx_frames = sktu_channel_port_rx_burst(ch_port, rx_frames, MAX_FRAMES);
224 
225 	// verify rx_frames == tx_frames
226 	assert(n_rx_frames == n_tx_frames);
227 	assert(rx_frames[0]->len == tx_frames[0]->len);
228 	assert(memcmp(rx_frames[0]->bytes, tx_frames[0]->bytes,
229 	    tx_frames[0]->len) == 0);
230 	assert(rx_frames[1]->len == tx_frames[1]->len);
231 	assert(memcmp(rx_frames[1]->bytes, tx_frames[1]->bytes,
232 	    tx_frames[1]->len) == 0);
233 
234 	sktu_frames_free(rx_frames, n_rx_frames);
235 	sktu_frames_free(tx_frames, n_tx_frames);
236 }
237 
238 size_t
bad_fraglen_build(uint8_t * buf)239 bad_fraglen_build(uint8_t *buf)
240 {
241 	size_t i, len = 0;
242 	size_t plen = 35;
243 	uint32_t address_family = htonl(AF_INET6);
244 
245 	bcopy(&address_family, buf, sizeof(uint32_t));
246 	buf += sizeof(uint32_t);
247 	len += sizeof(uint32_t);
248 
249 	struct ip6_hdr *ip6 = (struct ip6_hdr *)buf;
250 	ip6->ip6_vfc = IPV6_VERSION;
251 	ip6->ip6_nxt = IPPROTO_FRAGMENT;
252 	ip6->ip6_hlim = IPV6_DEFHLIM;
253 	ip6->ip6_plen = htons(sizeof(struct ip6_frag) + plen);
254 
255 	inet_pton(AF_INET6, utun_addr_str, &ip6->ip6_dst);
256 	inet_pton(AF_INET6, peer_addr_str, &ip6->ip6_src);
257 
258 	buf += sizeof(struct ip6_hdr);
259 	len += sizeof(struct ip6_hdr);
260 
261 	struct ip6_frag *ip6f = (struct ip6_frag *)buf;
262 	ip6f->ip6f_ident = 0xee;
263 	ip6f->ip6f_nxt = IPPROTO_UDP;
264 	ip6f->ip6f_offlg = 0;
265 	ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
266 
267 	buf += sizeof(struct ip6_frag);
268 	len += sizeof(struct ip6_frag);
269 
270 	for (i = 0; i < plen; i++) {
271 		buf[i] = 'f';
272 	}
273 
274 	buf += plen;
275 	len += plen;
276 
277 	return len;
278 }
279 
280 int
bad_fraglen_match(uint8_t * pkt_out,size_t len_out,uint8_t * pkt_in,size_t len_in)281 bad_fraglen_match(uint8_t *pkt_out, size_t len_out, uint8_t *pkt_in, size_t len_in)
282 {
283 	uint8_t *scan = pkt_out;
284 	uint32_t af = ntohl(*(uint32_t *)pkt_out);
285 
286 	if (af != AF_INET6) {
287 		T_LOG("%s fails: af != AF_INET6", __func__);
288 		return -1;
289 	}
290 
291 	scan += sizeof(af);
292 	struct ip6_hdr *ip6 = (struct ip6_hdr *)scan;
293 
294 	//TODO check src/dst, etc.
295 
296 	if (ip6->ip6_nxt != IPPROTO_ICMPV6) {
297 		T_LOG("%s fails: ip6_nxt != IPPROTO_ICMPV6", __func__);
298 		return -1;
299 	}
300 
301 	scan += sizeof(struct ip6_hdr);
302 	struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)scan;
303 
304 	assert(icmp6->icmp6_type == ICMP6_PARAM_PROB);
305 	assert(icmp6->icmp6_code == ICMP6_PARAMPROB_HEADER);
306 	assert(icmp6->icmp6_pptr == htonl(__builtin_offsetof(struct ip6_hdr,
307 	    ip6_plen)));
308 
309 	return 0;
310 }
311 
312 size_t
timeout_build(uint8_t * buf)313 timeout_build(uint8_t *buf)
314 {
315 	size_t i, len = 0;
316 	size_t plen = 128;
317 	uint32_t address_family = htonl(AF_INET6);
318 
319 	bcopy(&address_family, buf, sizeof(uint32_t));
320 	buf += sizeof(uint32_t);
321 	len += sizeof(uint32_t);
322 
323 	struct ip6_hdr *ip6 = (struct ip6_hdr *)buf;
324 	ip6->ip6_vfc = IPV6_VERSION;
325 	ip6->ip6_nxt = IPPROTO_FRAGMENT;
326 	ip6->ip6_hlim = IPV6_DEFHLIM;
327 	ip6->ip6_plen = htons(sizeof(struct ip6_frag) + plen);
328 
329 	inet_pton(AF_INET6, utun_addr_str, &ip6->ip6_dst);
330 	inet_pton(AF_INET6, peer_addr_str, &ip6->ip6_src);
331 
332 	buf += sizeof(struct ip6_hdr);
333 	len += sizeof(struct ip6_hdr);
334 
335 	struct ip6_frag *ip6f = (struct ip6_frag *)buf;
336 	ip6f->ip6f_ident = 0xee;
337 	ip6f->ip6f_nxt = IPPROTO_UDP;
338 	ip6f->ip6f_offlg = 0;
339 	ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
340 
341 	buf += sizeof(struct ip6_frag);
342 	len += sizeof(struct ip6_frag);
343 
344 	for (i = 0; i < plen; i++) {
345 		buf[i] = 'f';
346 	}
347 
348 	buf += plen;
349 	len += plen;
350 
351 	return len;
352 }
353 
354 int
timeout_match(uint8_t * pkt_out,size_t len_out,uint8_t * pkt_in,size_t len_in)355 timeout_match(uint8_t *pkt_out, size_t len_out, uint8_t *pkt_in, size_t len_in)
356 {
357 	uint8_t *scan = pkt_out;
358 	uint32_t af = ntohl(*(uint32_t *)pkt_out);
359 
360 	if (af != AF_INET6) {
361 		T_LOG("%s fails: af != AF_INET6", __func__);
362 		return -1;
363 	}
364 
365 	scan += sizeof(af);
366 	struct ip6_hdr *ip6 = (struct ip6_hdr *)scan;
367 
368 	//TODO check src/dst, etc.
369 
370 	if (ip6->ip6_nxt != IPPROTO_ICMPV6) {
371 		T_LOG("%s fails: ip6_nxt != IPPROTO_ICMPV6", __func__);
372 		return -1;
373 	}
374 
375 	scan += sizeof(struct ip6_hdr);
376 	struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)scan;
377 
378 	assert(icmp6->icmp6_type == ICMP6_TIME_EXCEEDED);
379 	assert(icmp6->icmp6_code == ICMP6_TIME_EXCEED_REASSEMBLY);
380 	assert(icmp6->icmp6_pptr == 0);
381 
382 	return 0;
383 }
384 
385 #define ADDCARRY(_x)  do {                                              \
386 	while (((_x) >> 16) != 0)                                       \
387 	        (_x) = ((_x) >> 16) + ((_x) & 0xffff);                  \
388 } while (0)
389 
390 size_t
atomic_build(uint8_t * buf)391 atomic_build(uint8_t *buf)
392 {
393 	size_t i, len = 0;
394 	size_t plen, dlen = 16;
395 	struct in6_addr src, dst;
396 	uint32_t address_family = htonl(AF_INET6);
397 
398 	bzero(buf, BUF_SIZE);
399 
400 	bcopy(&address_family, buf, sizeof(uint32_t));
401 	buf += sizeof(uint32_t);
402 	len += sizeof(uint32_t);
403 
404 	struct ip6_hdr *ip6 = (struct ip6_hdr *)buf;
405 	ip6->ip6_vfc = IPV6_VERSION;
406 	ip6->ip6_nxt = IPPROTO_FRAGMENT;
407 	ip6->ip6_hlim = IPV6_DEFHLIM;
408 	plen = sizeof(struct ip6_frag) + sizeof(struct icmp6_hdr) + dlen;
409 	ip6->ip6_plen = htons(plen);
410 
411 	inet_pton(AF_INET6, peer_addr_str, &src);
412 	bcopy(&src, &ip6->ip6_src, sizeof(src));
413 	inet_pton(AF_INET6, utun_addr_str, &dst);
414 	bcopy(&dst, &ip6->ip6_dst, sizeof(dst));
415 
416 	buf += sizeof(struct ip6_hdr);
417 	len += sizeof(struct ip6_hdr);
418 
419 	struct ip6_frag *ip6f = (struct ip6_frag *)buf;
420 	ip6f->ip6f_ident = 0xee;
421 	ip6f->ip6f_nxt = IPPROTO_ICMPV6;
422 	ip6f->ip6f_offlg = 0;
423 
424 	buf += sizeof(struct ip6_frag);
425 	len += sizeof(struct ip6_frag);
426 
427 	struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)buf;
428 	icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
429 	icmp6->icmp6_code = 0;
430 	icmp6->icmp6_cksum = 0;
431 
432 	buf += sizeof(struct icmp6_hdr);
433 	len += sizeof(struct icmp6_hdr);
434 
435 	for (i = 0; i < dlen; i++) {
436 		buf[i] = 'f';
437 	}
438 
439 	uint16_t csum;
440 	icmp6->icmp6_cksum = in6_pseudo(&src, &dst,
441 	    htonl(IPPROTO_ICMPV6 + sizeof(struct icmp6_hdr) + dlen));
442 	csum = os_inet_checksum(icmp6, sizeof(struct icmp6_hdr) + dlen, 0);
443 	csum = ~csum;
444 	if (csum == 0) {
445 		csum = 0xffff;
446 	}
447 	icmp6->icmp6_cksum = csum;
448 
449 	buf += dlen;
450 	len += dlen;
451 
452 	return len;
453 }
454 
455 int
atomic_match(uint8_t * pkt_out,size_t len_out,uint8_t * pkt_in,size_t len_in)456 atomic_match(uint8_t *pkt_out, size_t len_out, uint8_t *pkt_in, size_t len_in)
457 {
458 	uint8_t *scan = pkt_out;
459 	uint32_t af = ntohl(*(uint32_t *)pkt_out);
460 
461 	if (af != AF_INET6) {
462 		T_LOG("%s fails: af != AF_INET6", __func__);
463 		return -1;
464 	}
465 
466 	scan += sizeof(af);
467 	struct ip6_hdr *ip6 = (struct ip6_hdr *)scan;
468 
469 	//TODO check src/dst, etc.
470 
471 	if (ip6->ip6_nxt != IPPROTO_ICMPV6) {
472 		T_LOG("%s fails: ip6_nxt != IPPROTO_ICMPV6", __func__);
473 		return -1;
474 	}
475 
476 	scan += sizeof(struct ip6_hdr);
477 	struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)scan;
478 
479 	assert(icmp6->icmp6_type == ICMP6_ECHO_REPLY);
480 	assert(icmp6->icmp6_code == 0);
481 
482 	return 0;
483 }
484 
485 size_t
queue_limit_build(uint8_t * buf)486 queue_limit_build(uint8_t *buf)
487 {
488 	size_t len = 0;
489 	struct in6_addr src, dst;
490 	uint32_t address_family = htonl(AF_INET6);
491 
492 	bzero(buf, BUF_SIZE);
493 
494 	bcopy(&address_family, buf, sizeof(uint32_t));
495 	buf += sizeof(uint32_t);
496 	len += sizeof(uint32_t);
497 
498 	struct ip6_hdr *ip6 = (struct ip6_hdr *)buf;
499 	ip6->ip6_vfc = IPV6_VERSION;
500 	ip6->ip6_nxt = IPPROTO_FRAGMENT;
501 	ip6->ip6_hlim = IPV6_DEFHLIM;
502 	ip6->ip6_plen = htons(sizeof(struct ip6_frag));
503 
504 	inet_pton(AF_INET6, peer_addr_str, &src);
505 	bcopy(&src, &ip6->ip6_src, sizeof(src));
506 	inet_pton(AF_INET6, utun_addr_str, &dst);
507 	bcopy(&dst, &ip6->ip6_dst, sizeof(dst));
508 
509 	buf += sizeof(struct ip6_hdr);
510 	len += sizeof(struct ip6_hdr);
511 
512 	struct ip6_frag *ip6f = (struct ip6_frag *)buf;
513 	ip6f->ip6f_ident = 0xee;
514 	ip6f->ip6f_nxt = IPPROTO_NONE;
515 	ip6f->ip6f_offlg = htons((u_short)((2048) & ~7));
516 
517 	buf += sizeof(struct ip6_frag);
518 	len += sizeof(struct ip6_frag);
519 
520 	return len;
521 }
522 
523 void
reass_lower_timeout(int * old_timeout,int new_timeout)524 reass_lower_timeout(int *old_timeout, int new_timeout)
525 {
526 	size_t old_size = sizeof(*old_timeout);
527 	int error;
528 
529 	error = sysctlbyname("kern.skywalk.flowswitch.ipfm_frag_ttl",
530 	    old_timeout, &old_size, &new_timeout, sizeof(int));
531 	if (error) {
532 		die_perror("kern.skywalk.flowswitch.ipfm_frag_ttl");
533 	}
534 }
535 
536 void
reass_restore_timeout(int old_timeout)537 reass_restore_timeout(int old_timeout)
538 {
539 	int error;
540 
541 	error = sysctlbyname("kern.skywalk.flowswitch.ipfm_frag_ttl",
542 	    NULL, NULL, &old_timeout, sizeof(int));
543 	if (error) {
544 		die_perror("kern.skywalk.flowswitch.ipfm_frag_ttl");
545 	}
546 }
547 
548 int
skt_reass_main(int argc,char * argv[])549 skt_reass_main(int argc, char *argv[])
550 {
551 	int error;
552 
553 	// setup full interface
554 	utun_fd = sktu_create_interface(SKTU_IFT_UTUN,
555 	    SKTU_IFF_ENABLE_NETIF | SKTU_IFF_NO_ATTACH_FSW);
556 
557 	sktu_get_interface_name(SKTU_IFT_UTUN, utun_fd, utun_ifname);
558 
559 	struct in_addr our_ip, mask, broad_ip, dst_ip;
560 	struct in6_addr our_ip_v6, dst_ip_v6;
561 
562 	inet_pton(AF_INET, our_ip_str, &our_ip);
563 	inet_pton(AF_INET, dst_ip_str, &dst_ip);
564 	inet_pton(AF_INET, broad_ip_str, &broad_ip);
565 	mask = sktc_make_in_addr(IN_CLASSC_NET);
566 	sktc_config_fsw_rx_agg_tcp(0);
567 
568 	if (sktc_ifnet_add_addr(utun_ifname, &our_ip, &mask, &broad_ip) != 0) {
569 		err(EX_OSERR, "Failed to add address for %s", utun_ifname);
570 	}
571 
572 	inet_pton(AF_INET6, utun_global_addr_str, &our_ip_v6);
573 	inet_pton(AF_INET6, peer_global_addr_str, &dst_ip_v6);
574 	error = sktc_ifnet_add_addr6(utun_ifname, &our_ip_v6, &dst_ip_v6,
575 	    addr_prefix_length, 0);
576 
577 	if (sktc_ifnet_add_scoped_default_route(utun_ifname, our_ip) != 0) {
578 		err(EX_OSERR, "Failed to add default route: %s\n", utun_ifname);
579 	}
580 
581 	bzero(&nexus, sizeof(nexus));
582 	strlcpy(nexus.netif_ifname, utun_ifname, sizeof(nexus.netif_ifname));
583 	nexus.netif_addr = our_ip;
584 	nexus.netif_mask = mask;
585 	sktc_create_flowswitch_no_address(&nexus, -1, -1, -1, -1, 0);
586 
587 	error = os_nexus_controller_bind_provider_instance(nexus.controller,
588 	    nexus.fsw_nx_uuid, NX_PORT, getpid(), NULL, NULL, 0,
589 	    NEXUS_BIND_PID);
590 	SKTC_ASSERT_ERR(error == 0);
591 
592 	struct sktu_flow *flow_ipv4, *flow_ipv6;
593 
594 	flow_ipv4 = sktu_create_nexus_flow_with_nx_port(&nexus, NX_PORT,
595 	    AF_INET, &our_ip, &dst_ip, IPPROTO_UDP, 0, 1234);
596 	assert(flow_ipv4);
597 
598 	flow_ipv6 = sktu_create_nexus_flow_with_nx_port(&nexus, NX_PORT,
599 	    AF_INET6, &our_ip_v6, &dst_ip_v6, IPPROTO_UDP, 0, 1234);
600 	assert(flow_ipv4);
601 
602 	assert(flow_ipv4->nfr.nfr_nx_port == flow_ipv6->nfr.nfr_nx_port);
603 
604 	sktu_channel_port_init(&port, nexus.fsw_nx_uuid,
605 	    flow_ipv4->nfr.nfr_nx_port, true, false, false);
606 	assert(port.chan != NULL);
607 
608 	uint8_t buf[8192];
609 	// test both 8-byte-multiple and non 8-byte-multple sizes
610 	reass_common(&port, flow_ipv4, buf, 2000);
611 	reass_common(&port, flow_ipv4, buf, 2001);
612 	reass_common(&port, flow_ipv4, buf, 8000);
613 	reass_common(&port, flow_ipv4, buf, 8001);
614 	reass_common(&port, flow_ipv6, buf, 2000);
615 	reass_common(&port, flow_ipv6, buf, 2001);
616 	reass_common(&port, flow_ipv6, buf, 8000);
617 	reass_common(&port, flow_ipv6, buf, 8001);
618 
619 	sktu_destroy_nexus_flow(flow_ipv4);
620 	sktu_destroy_nexus_flow(flow_ipv6);
621 
622 	reass_fini();
623 
624 	return 0;
625 }
626 
627 int
skt_reass_main_default_setting(int argc,char * argv[])628 skt_reass_main_default_setting(int argc, char *argv[])
629 {
630 	if (!sktc_is_netagent_enabled()) {
631 		T_LOG("netagent not enabled on this platform, skip\n");
632 		return 0;
633 	}
634 
635 	if (!sktc_is_ip_reass_enabled()) {
636 		T_LOG("ip reass not enabled on this platform, skip\n");
637 		return 0;
638 	}
639 
640 	return skt_reass_main(argc, argv);
641 }
642 
643 #define REASS_TEST_GLOBAL(builder, matcher, timeout)    \
644 	utun_addr_str = utun_global_addr_str;           \
645 	peer_addr_str = peer_global_addr_str;           \
646 	reass_init();                                   \
647 	reass_interface_send_rece(builder, matcher, &timeout);    \
648 	reass_fini();
649 
650 #define REASS_TEST_LINKLOCAL(builder, matcher, timeout) \
651 	utun_addr_str = utun_linklocal_addr_str;        \
652 	peer_addr_str = peer_linklocal_addr_str;        \
653 	reass_init();                                   \
654 	reass_interface_send_rece(builder, matcher, &timeout);    \
655 	reass_fini();
656 
657 #define REASS_TEST_ALL(builder, matcher, timeout)       \
658 	REASS_TEST_GLOBAL(builder, matcher, timeout)    \
659 	sleep(1);                                       \
660 	REASS_TEST_LINKLOCAL(builder, matcher, timeout)
661 
662 
663 #define REASS_FUZZ_GLOBAL(builder)                      \
664 	utun_addr_str = utun_global_addr_str;           \
665 	peer_addr_str = peer_global_addr_str;           \
666 	reass_init();                                   \
667 	reass_interface_send(builder);                            \
668 	reass_fini();
669 
670 #define REASS_FUZZ_LINKLOCAL(builder)                   \
671 	utun_addr_str = utun_linklocal_addr_str;        \
672 	peer_addr_str = peer_linklocal_addr_str;        \
673 	reass_init();                                   \
674 	reass_interface_send(builder);                            \
675 	reass_fini();
676 
677 #define REASS_FUZZ_ALL(builder)                         \
678 	REASS_FUZZ_GLOBAL(builder)                      \
679 	sleep(1);                                       \
680 	REASS_FUZZ_LINKLOCAL(builder)
681 
682 int
skt_reass_timeout_main(int argc,char * argv[])683 skt_reass_timeout_main(int argc, char *argv[])
684 {
685 	int old_timeout;
686 	int new_timeout = 5;
687 
688 	reass_lower_timeout(&old_timeout, new_timeout);
689 
690 	REASS_TEST_ALL(timeout_build, timeout_match, timeout);
691 
692 	reass_restore_timeout(old_timeout);
693 
694 	return 0;
695 }
696 
697 int
skt_reass_bad_fraglen_main(int argc,char * argv[])698 skt_reass_bad_fraglen_main(int argc, char *argv[])
699 {
700 	REASS_TEST_ALL(bad_fraglen_build, bad_fraglen_match, timeout);
701 	return 0;
702 }
703 
704 int
skt_reass_atomic_main(int argc,char * argv[])705 skt_reass_atomic_main(int argc, char *argv[])
706 {
707 	REASS_TEST_ALL(atomic_build, atomic_match, timeout);
708 	return 0;
709 }
710 
711 int
skt_reass_fuzz_queue_limit_main(int argc,char * argv[])712 skt_reass_fuzz_queue_limit_main(int argc, char *argv[])
713 {
714 	REASS_FUZZ_ALL(queue_limit_build);
715 	return 0;
716 }
717 
718 struct skywalk_test skt_reass_default_setting = {
719 	"reass_default_setting",
720 	"UDP fragmentation reassembly (channel flow Rx) (without forcing ip_reass sysctl)",
721 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
722 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
723 	skt_reass_main_default_setting, { NULL },
724 	NULL, NULL,
725 };
726 
727 struct skywalk_test skt_reass = {
728 	"reass",
729 	"UDP fragmentation reassembly (channel flow Rx)",
730 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
731 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
732 	skt_reass_main, { NULL },
733 	sktc_enable_ip_reass, sktc_restore_ip_reass,
734 };
735 
736 struct skywalk_test skt_reass_timeout = {
737 	"reass_timeout",
738 	"send partial fragment to flowswitch and check for ICMPv6 time exceeded reply",
739 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
740 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
741 	skt_reass_timeout_main, { NULL },
742 	sktc_enable_ip_reass, sktc_restore_ip_reass,
743 };
744 
745 struct skywalk_test skt_reass_bad_fraglen = {
746 	"reass_bad_fraglen",
747 	"send fragment with bad fragment length (!= 8*) to flowswitch and check for ICMPv6 param header reply",
748 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
749 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
750 	skt_reass_bad_fraglen_main, { NULL },
751 	sktc_enable_ip_reass, sktc_restore_ip_reass,
752 };
753 
754 struct skywalk_test skt_reass_atomic = {
755 	"reass_atomic",
756 	"send atomic ICMP echo fragment to flowswitch and check for reply",
757 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
758 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
759 	skt_reass_atomic_main, { NULL },
760 	sktc_enable_ip_reass, sktc_restore_ip_reass,
761 };
762 
763 struct skywalk_test skt_reass_fuzz_queue_limit = {
764 	"reass_fuzz_queue_limit",
765 	"fuzz flowswitch to hit fragment limit",
766 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF |
767 	SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
768 	skt_reass_fuzz_queue_limit_main, { NULL },
769 	sktc_enable_ip_reass, sktc_restore_ip_reass,
770 };
771