xref: /xnu-11215.41.3/tests/skywalk/skt_flow.c (revision 33de042d024d46de5ff4e89f2471de6608e37fa4)
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 <assert.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <spawn.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/socket.h>
36 #include <uuid/uuid.h>
37 #include <unistd.h>
38 #include <arpa/inet.h>
39 #include <sys/sysctl.h>
40 #include <darwintest.h>
41 #include "skywalk_test_driver.h"
42 #include "skywalk_test_utils.h"
43 #include "skywalk_test_common.h"
44 
45 #define MULTICAST_IP "239.0.0.1"
46 
47 const char * ifname;
48 struct in_addr our_ip, dst_ip, zero_ip, nowhere_ip, multicast_ip, lo_ip;
49 struct in_addr our_mask;
50 struct sktc_nexus_handles handles;
51 uuid_t ipflow;
52 
53 static void
skt_flow_add_del(bool expect_success,sa_family_t af,void * src,void * dst,uint8_t protocol,uint16_t sport,uint16_t dport)54 skt_flow_add_del(bool expect_success, sa_family_t af,
55     void *src, void *dst, uint8_t protocol, uint16_t sport, uint16_t dport)
56 {
57 	struct sktu_flow *flow;
58 
59 	flow = sktu_create_nexus_flow(&handles, af, src, dst, protocol, sport, dport);
60 
61 	if (expect_success) {
62 		assert(flow);
63 		sktu_destroy_nexus_flow(flow);
64 	} else {
65 		assert(!flow);
66 	}
67 }
68 
69 static void
skt_flow_req_should_success(sa_family_t af,void * src,void * dst,uint8_t protocol,uint16_t sport,uint16_t dport)70 skt_flow_req_should_success(sa_family_t af, void *src, void *dst,
71     uint8_t protocol, uint16_t sport, uint16_t dport)
72 {
73 	skt_flow_add_del(true, af, src, dst, protocol, sport, dport);
74 }
75 
76 static void
skt_flow_req_should_fail(sa_family_t af,void * src,void * dst,uint8_t protocol,uint16_t sport,uint16_t dport)77 skt_flow_req_should_fail(sa_family_t af, void *src, void *dst,
78     uint8_t protocol, uint16_t sport, uint16_t dport)
79 {
80 	skt_flow_add_del(false, af, src, dst, protocol, sport, dport);
81 }
82 
83 static void
skt_flow_req_low_latency(sa_family_t af,void * src,void * dst,uint8_t protocol,uint16_t sport,uint16_t dport)84 skt_flow_req_low_latency(sa_family_t af, void *src, void *dst,
85     uint8_t protocol, uint16_t sport, uint16_t dport)
86 {
87 	struct sktu_flow *regular_flow_0, *regular_flow_1;
88 	struct sktu_flow *low_latency_flow_0, *low_latency_flow_1;
89 
90 	/* add a regular flow */
91 	regular_flow_0 = sktu_create_nexus_flow(&handles, af, src, dst,
92 	    protocol, sport, dport);
93 	assert(regular_flow_0);
94 
95 	/* add another regular flow */
96 	sport++;
97 	dport++;
98 	regular_flow_1 = sktu_create_nexus_flow(&handles, af, src, dst,
99 	    protocol, sport, dport);
100 	assert(regular_flow_1);
101 
102 	/* Both regular flows should get the same fsw port */
103 	assert(regular_flow_0->nfr.nfr_nx_port ==
104 	    regular_flow_1->nfr.nfr_nx_port);
105 
106 	/* add a low-latency flow */
107 	sport++;
108 	dport++;
109 	low_latency_flow_0 = sktu_create_nexus_low_latency_flow(&handles,
110 	    af, src, dst, protocol, sport, dport);
111 	assert(low_latency_flow_0);
112 
113 	/* low-latency flow should get a different fsw port */
114 	assert(low_latency_flow_0->nfr.nfr_nx_port !=
115 	    regular_flow_0->nfr.nfr_nx_port);
116 
117 	/* add another low-latency flow */
118 	sport++;
119 	dport++;
120 	low_latency_flow_1 = sktu_create_nexus_low_latency_flow(&handles,
121 	    af, src, dst, protocol, sport, dport);
122 	assert(low_latency_flow_1);
123 
124 	/* Both low-latency flows should get the same fsw port */
125 	assert(low_latency_flow_0->nfr.nfr_nx_port ==
126 	    low_latency_flow_1->nfr.nfr_nx_port);
127 
128 	sktu_destroy_nexus_flow(regular_flow_0);
129 	sktu_destroy_nexus_flow(regular_flow_1);
130 	sktu_destroy_nexus_flow(low_latency_flow_0);
131 	sktu_destroy_nexus_flow(low_latency_flow_1);
132 }
133 
134 int
skt_flow_req_ll_main(int argc,char * argv[])135 skt_flow_req_ll_main(int argc, char *argv[])
136 {
137 	ifname = FETH0_NAME;
138 	our_mask = sktc_make_in_addr(IN_CLASSC_NET);
139 	our_ip = sktc_feth0_in_addr();
140 	dst_ip = sktc_feth1_in_addr();
141 
142 	bzero(&handles, sizeof(handles));
143 	strlcpy(handles.netif_ifname, ifname, sizeof(handles.netif_ifname));
144 	handles.netif_addr = our_ip;
145 	handles.netif_mask = our_mask;
146 	sktc_create_flowswitch_no_address(&handles, -1, -1, -1, -1, 0);
147 
148 	// Low latency requests
149 	T_LOG("\nTesting with low latency flow requests\n\n");
150 	skt_flow_req_low_latency(AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, 1234, 1234);
151 
152 	sktc_cleanup_flowswitch(&handles);
153 
154 	return 0;
155 }
156 
157 int
skt_flow_config_main(int argc,char * argv[])158 skt_flow_config_main(int argc, char *argv[])
159 {
160 	ifname = FETH0_NAME;
161 	our_mask = sktc_make_in_addr(IN_CLASSC_NET);
162 	our_ip = sktc_feth0_in_addr();
163 	dst_ip = sktc_feth1_in_addr();
164 
165 	T_LOG("\nTesting flow config API\n");
166 
167 	bzero(&handles, sizeof(handles));
168 	strlcpy(handles.netif_ifname, ifname, sizeof(handles.netif_ifname));
169 	handles.netif_addr = our_ip;
170 	handles.netif_mask = our_mask;
171 	sktc_create_flowswitch_no_address(&handles, -1, -1, -1, -1, 0);
172 
173 	T_LOG("add a flow\n");
174 	struct sktu_flow *flow;
175 	flow = sktu_create_nexus_flow(&handles, AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, 1234, 1234);
176 	assert(flow);
177 
178 	T_LOG("verify flow default (negative) NOWAKEFROMSLEEP flag\n");
179 	struct sk_stats_flow sf;
180 	int ret = sktu_get_nexus_flow_stats(flow->uuid, &sf);
181 	assert(ret == 0);
182 	assert((sf.sf_flags & SFLOWF_NOWAKEFROMSLEEP) == 0);
183 
184 	uuid_t rand_uuid;
185 	do {
186 		uuid_generate(rand_uuid);
187 	} while (uuid_compare(rand_uuid, flow->uuid) == 0);
188 
189 	// should return ENOENT with mismatching flow uuid
190 	T_LOG("verify ENOENT with INVALID flow\n");
191 	ret = os_nexus_flow_set_wake_from_sleep(handles.fsw_nx_uuid, rand_uuid, false);
192 	assert(ret != 0);
193 	assert(errno == ENOENT);
194 
195 	/* should fail with EPERM from another PID */
196 	T_LOG("verify EPERM with INVALID PID\n");
197 	int child_pid;
198 	if ((child_pid = fork()) == -1) {
199 		SKT_LOG("fork: %s\n", strerror(errno));
200 		exit(1);
201 	}
202 	if (child_pid == 0) {
203 		ret = os_nexus_flow_set_wake_from_sleep(handles.fsw_nx_uuid, flow->uuid, false);
204 		exit(errno);
205 	} else {
206 		int child_status;
207 		wait(&child_status);
208 		assert(WIFEXITED(child_status));
209 		assert(WEXITSTATUS(child_status) == EPERM);
210 	}
211 
212 	T_LOG("verify setting flow NOWAKEFROMSLEEP\n");
213 	ret = os_nexus_flow_set_wake_from_sleep(handles.fsw_nx_uuid, flow->uuid, false);
214 	assert(ret == 0);
215 
216 	ret = sktu_get_nexus_flow_stats(flow->uuid, &sf);
217 	assert(ret == 0);
218 	assert((sf.sf_flags & SFLOWF_NOWAKEFROMSLEEP) != 0);
219 
220 	T_LOG("verify clearing flow NOWAKEFROMSLEEP\n");
221 	ret = os_nexus_flow_set_wake_from_sleep(handles.fsw_nx_uuid, flow->uuid, true);
222 	assert(ret == 0);
223 
224 	ret = sktu_get_nexus_flow_stats(flow->uuid, &sf);
225 	assert(ret == 0);
226 	assert((sf.sf_flags & SFLOWF_NOWAKEFROMSLEEP) == 0);
227 
228 	T_LOG("verify EPERM with netif nexus\n");
229 	ret = os_nexus_flow_set_wake_from_sleep(handles.netif_nx_uuid, flow->uuid, true);
230 	assert(ret != 0);
231 	assert(errno == EPERM);
232 
233 	T_LOG("\n");
234 
235 	return 0;
236 }
237 
238 int
skt_flow_req_main(int argc,char * argv[])239 skt_flow_req_main(int argc, char *argv[])
240 {
241 	ifname = FETH0_NAME;
242 	our_mask = sktc_make_in_addr(IN_CLASSC_NET);
243 	our_ip = sktc_feth0_in_addr();
244 	dst_ip = sktc_feth1_in_addr();
245 	zero_ip = (struct in_addr){.s_addr = htonl(INADDR_ANY)};
246 	nowhere_ip = sktc_nowhere_in_addr();
247 	multicast_ip.s_addr = inet_addr(MULTICAST_IP);
248 	inet_pton(AF_INET, "127.0.0.1", &lo_ip.s_addr);
249 
250 	bzero(&handles, sizeof(handles));
251 	strlcpy(handles.netif_ifname, ifname, sizeof(handles.netif_ifname));
252 	handles.netif_addr = our_ip;
253 	handles.netif_mask = our_mask;
254 	sktc_create_flowswitch_no_address(&handles, -1, -1, -1, -1, 0);
255 
256 	// Valid requests
257 	T_LOG("\nTesting with valid flow requests\n\n");
258 
259 	// 5 tuple nexus chosen src ip/port
260 	skt_flow_req_should_success(AF_INET, &zero_ip, &dst_ip, IPPROTO_TCP, 0, 1234);
261 	// 5 tuple fully specified
262 	skt_flow_req_should_success(AF_INET, &our_ip, &dst_ip, IPPROTO_TCP, 1234, 1234);
263 	// Custom IP protocol (connect mode)
264 	skt_flow_req_should_success(AF_INET, &our_ip, &dst_ip, IPPROTO_IPEIP, 0, 0);
265 	// Custom IP protocol (listen mode)
266 	skt_flow_req_should_success(AF_INET, &our_ip, &zero_ip, IPPROTO_IPEIP, 0, 0);
267 	// 3 tuple TCP listener with specified local ip
268 	skt_flow_req_should_success(AF_INET, &our_ip, &zero_ip, IPPROTO_TCP, 1234, 0);
269 	// 2 tuple TCP listener
270 	skt_flow_req_should_success(AF_INET, &zero_ip, &zero_ip, IPPROTO_TCP, 1234, 0);
271 
272 	// Invalid requests
273 	T_LOG("\nTesting with INVALID flow requests, should fail them\n\n");
274 
275 	// 5 tuple zero dst ip
276 	skt_flow_req_should_fail(AF_INET, &our_ip, &zero_ip, IPPROTO_TCP, 1234, 1234);
277 	// 5 tuple multicast src ip
278 	skt_flow_req_should_fail(AF_INET, &multicast_ip, &dst_ip, IPPROTO_TCP, 1234, 1234);
279 	// 5 tuple loopback
280 	skt_flow_req_should_fail(AF_INET, &our_ip, &lo_ip, IPPROTO_TCP, 1234, 1234);
281 	// 3 tuple invalid src ip
282 	skt_flow_req_should_fail(AF_INET, &nowhere_ip, &zero_ip, IPPROTO_TCP, 1234, 0);
283 	// 3 tuple multicast src ip
284 	skt_flow_req_should_fail(AF_INET, &multicast_ip, &zero_ip, IPPROTO_TCP, 1234, 0);
285 
286 	sktc_cleanup_flowswitch(&handles);
287 
288 	return 0;
289 }
290 
291 void
skt_flow_req_net_init(void)292 skt_flow_req_net_init(void)
293 {
294 	sktc_ifnet_feth_pair_create(FETH_FLAGS_TXSTART);
295 }
296 
297 void
skt_flow_req_net_fini(void)298 skt_flow_req_net_fini(void)
299 {
300 	sktc_ifnet_feth_pair_destroy();
301 }
302 
303 struct skywalk_test skt_flow_req = {
304 	"flowreq", "test skywalk flow request api",
305 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
306 	skt_flow_req_main, { NULL },
307 	skt_flow_req_net_init, skt_flow_req_net_fini,
308 };
309 
310 struct skywalk_test skt_flow_req_ll = {
311 	"flowreqll", "test skywalk flow request api for low latency flows",
312 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS | SK_FEATURE_DEV_OR_DEBUG,
313 	skt_flow_req_ll_main, { NULL },
314 	skt_flow_req_net_init, skt_flow_req_net_fini,
315 };
316 
317 struct skywalk_test skt_flow_config = {
318 	"flowconfig", "test skywalk flow config api",
319 	SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_FLOWSWITCH | SK_FEATURE_NETNS,
320 	skt_flow_config_main, { NULL },
321 	skt_flow_req_net_init, skt_flow_req_net_fini,
322 };
323