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