1 /*
2 * Copyright (c) 2019-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 #include <sys/types.h>
29 #include <sys/sysctl.h>
30 #include <sys/ioctl.h>
31 #include <net/ethernet.h>
32 #include <net/if.h>
33 #include <net/if_vlan_var.h>
34 #include <libkern/OSAtomic.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <pthread.h>
39 #include "skywalk_test_driver.h"
40 #include "skywalk_test_utils.h"
41 #include "skywalk_test_common.h"
42
43 /*
44 ****************************************************************
45 * Start of common section *
46 ****************************************************************
47 */
48 #define FILTER_RECV_PORT 20000
49 #define FILTER_SEND_PORT 20001
50
51 #define CMD_RECV_SOCKET_READY 0x11
52 #define CMD_RECV_CHANNEL_READY 0x12
53 #define CMD_RECV_ALL_DONE 0x13
54 #define CLIENT_CMD_SEND_SOCKET_START 0x21
55 #define CLIENT_CMD_SEND_CHANNEL_START 0x22
56
57 #define SOCKET_THREADS 16
58 #define SOCKET_BYTES 10000000
59 #define SOCKET_LISTENER_PORT 30000
60
61 #define CUSTOM_ETHER_THREADS 16
62 #define CUSTOM_ETHER_ETHERTYPE_BASE 3000
63 #define CUSTOM_ETHER_ITERATIONS 10000
64 #define CUSTOM_ETHER_PKT_LEN 512
65
66 #define SEPARATOR(opts) \
67 (*(opts) != '\0' ? ", " : "")
68 #define SKTDBG(name, opts, fmt, ...) \
69 SKD1("%s: %s%s" fmt, (name), (opts), SEPARATOR((opts)), \
70 ##__VA_ARGS__)
71
72 typedef struct {
73 int fd;
74 uint64_t bytes;
75 } socket_args_t;
76
77 typedef struct {
78 bool is_sender;
79 bool is_tagged;
80 uint16_t ethertype;
81 uint8_t priority;
82 uint64_t sent;
83 uint64_t received;
84 channel_t ch;
85 nexus_controller_t nx_ncd;
86 } custom_ether_args_t;
87
88 static char databuf[2048];
89 static socket_args_t socket_thread_args[SOCKET_THREADS];
90 static int socket_ready = 0;
91 static int socket_done = 0;
92
93 static uuid_t if_uuid;
94 static int custom_ether_ready = 0;
95 static int custom_ether_done = 0;
96 static ether_addr_t src_mac_addr, dst_mac_addr;
97 static custom_ether_args_t custom_ether_thread_args[CUSTOM_ETHER_THREADS];
98
99 static void
connect_to_server(uint16_t port,int * client_fd)100 connect_to_server(uint16_t port, int *client_fd)
101 {
102 struct sockaddr_in sin;
103 int fd, error = 0, retries = 0;
104 char *client;
105
106 client = (port == FILTER_RECV_PORT) ? "receiver" : "sender";
107 for (;;) {
108 SKD1("%s: connecting to server\n", client);
109 fd = socket(AF_INET, SOCK_STREAM, 0);
110 SKTC_ASSERT_ERR(fd != -1);
111
112 sin.sin_family = AF_INET;
113 sin.sin_addr.s_addr = inet_addr("127.0.0.1");
114 sin.sin_port = htons(port);
115 error = connect(fd, (struct sockaddr *)&sin,
116 sizeof(sin));
117 if (error == -1) {
118 if (errno == ECONNREFUSED) {
119 if (retries == 10) {
120 SKD0("%s: giving up\n", client);
121 exit(1);
122 }
123 (void) close(fd);
124 SKD0("%s: server not ready, retrying...\n",
125 client);
126 retries++;
127 sleep(1);
128 continue;
129 } else {
130 SKD0("%s: got unexpected error: %d\n",
131 client, errno);
132 exit(1);
133 }
134 }
135 SKD1("%s: connected to server\n", client);
136 SKTC_ASSERT_ERR(error == 0);
137 break;
138 }
139 *client_fd = fd;
140 }
141
142 static uint8_t
read_cmd(int fd)143 read_cmd(int fd)
144 {
145 int r, error = 0;
146 uint8_t cmd;
147
148 r = read(fd, &cmd, sizeof(cmd));
149 SKTC_ASSERT_ERR(r == 1);
150 return cmd;
151 }
152
153 static void
write_cmd(int fd,uint8_t cmd)154 write_cmd(int fd, uint8_t cmd)
155 {
156 int w, error = 0;
157
158 w = write(fd, &cmd, sizeof(cmd));
159 SKTC_ASSERT_ERR(w == 1);
160 }
161
162 static void
wait_for_start(void)163 wait_for_start(void)
164 {
165 write_cmd(MPTEST_SEQ_FILENO, 0);
166 (void) read_cmd(MPTEST_SEQ_FILENO);
167 }
168
169 static void
socket_test_status(char * name)170 socket_test_status(char *name)
171 {
172 int i;
173 double total_bytes = 0, total_expected;
174
175 total_expected = SOCKET_THREADS * SOCKET_BYTES;
176 for (i = 0; i < SOCKET_THREADS; i++) {
177 total_bytes += socket_thread_args[i].bytes;
178 }
179 SKD1("%s: %.2f%% complete\n", name,
180 (total_bytes * 100) / total_expected);
181 }
182
183 static void
put_packet(channel_t ch,ring_id_t ring_id,packet_t pkt)184 put_packet(channel_t ch, ring_id_t ring_id, packet_t pkt)
185 {
186 channel_ring_t tx_ring;
187 channel_slot_t tx_slot = NULL;
188 slot_prop_t prop;
189 int error = 0;
190
191 tx_ring = os_channel_tx_ring(ch, ring_id);
192 SKTC_ASSERT_ERR(tx_ring != NULL);
193
194 tx_slot = os_channel_get_next_slot(tx_ring, NULL, &prop);
195 SKTC_ASSERT_ERR(tx_slot != NULL);
196
197 error = os_channel_slot_attach_packet(tx_ring, tx_slot, pkt);
198 SKTC_ASSERT_ERR(error == 0);
199
200 error = os_channel_advance_slot(tx_ring, tx_slot);
201 SKTC_ASSERT_ERR(error == 0);
202 }
203
204 static int
get_packet(channel_t ch,ring_id_t r,packet_t * pktp)205 get_packet(channel_t ch, ring_id_t r, packet_t *pktp)
206 {
207 int error = 0;
208 slot_prop_t prop;
209 channel_slot_t rx_slot, last_rx_slot = NULL;
210 packet_t pkt;
211 void *buf;
212 buflet_t buflet;
213 channel_ring_t rx_ring;
214
215 rx_ring = os_channel_rx_ring(ch, r);
216 SKTC_ASSERT_ERR(rx_ring != NULL);
217
218 rx_slot = os_channel_get_next_slot(rx_ring, last_rx_slot, &prop);
219 if (rx_slot == NULL) {
220 return ENOENT;
221 }
222 SKTC_ASSERT_ERR(prop.sp_buf_ptr != 0);
223
224 pkt = os_channel_slot_get_packet(rx_ring, rx_slot);
225 SKTC_ASSERT_ERR(pkt != 0);
226
227 error = os_channel_slot_detach_packet(rx_ring, rx_slot, pkt);
228 SKTC_ASSERT_ERR(error == 0);
229
230 buflet = os_packet_get_next_buflet(pkt, NULL);
231 SKTC_ASSERT_ERR(buflet != NULL);
232
233 buf = os_buflet_get_object_address(buflet);
234 SKTC_ASSERT_ERR(buf != NULL);
235
236 last_rx_slot = rx_slot;
237 error = os_channel_advance_slot(rx_ring, last_rx_slot);
238 SKTC_ASSERT_ERR(error == 0);
239
240 *pktp = pkt;
241 return 0;
242 }
243
244 static void
custom_ether_build_packet(void * buf,custom_ether_args_t * args,size_t * len)245 custom_ether_build_packet(void *buf, custom_ether_args_t *args, size_t *len)
246 {
247 if (!args->is_tagged) {
248 ether_header_t *eh = buf;
249
250 bcopy(dst_mac_addr.octet, eh->ether_dhost, sizeof(ether_addr_t));
251 bcopy(src_mac_addr.octet, eh->ether_shost, sizeof(ether_addr_t));
252 eh->ether_type = htons(args->ethertype);
253 } else {
254 struct ether_vlan_header *evh = buf;
255 uint16_t tag;
256
257 bcopy(dst_mac_addr.octet, evh->evl_dhost, sizeof(ether_addr_t));
258 bcopy(src_mac_addr.octet, evh->evl_shost, sizeof(ether_addr_t));
259 evh->evl_encap_proto = htons(ETHERTYPE_VLAN);
260
261 /* priority tag only */
262 tag = args->priority << 13 & ~EVL_VLID_MASK;
263 evh->evl_tag = htons(tag);
264 evh->evl_proto = htons(args->ethertype);
265 }
266
267 /*
268 * TODO:
269 * Put contents here to checked by the receiver
270 */
271 *len = CUSTOM_ETHER_PKT_LEN;
272 }
273
274 static void
custom_ether_send(channel_t ch,custom_ether_args_t * args)275 custom_ether_send(channel_t ch, custom_ether_args_t *args)
276 {
277 packet_t pkt;
278 void *buf;
279 size_t pkt_len;
280 buflet_t buflet;
281 int error;
282
283 error = os_channel_packet_alloc(ch, &pkt);
284 SKTC_ASSERT_ERR(error == 0);
285
286 buflet = os_packet_get_next_buflet(pkt, NULL);
287 SKTC_ASSERT_ERR(buflet != NULL);
288 buf = os_buflet_get_object_address(buflet);
289 SKTC_ASSERT_ERR(buf != NULL);
290
291 custom_ether_build_packet(buf, args, &pkt_len);
292
293 error = os_buflet_set_data_length(buflet, pkt_len);
294 SKTC_ASSERT_ERR(error == 0);
295 error = os_packet_finalize(pkt);
296 SKTC_ASSERT_ERR(error == 0);
297
298 put_packet(ch, 0, pkt);
299
300 error = os_channel_sync(ch, CHANNEL_SYNC_TX);
301 SKTC_ASSERT_ERR(error == 0);
302 args->sent++;
303 }
304
305 static void
custom_ether_status(char * name,char * options)306 custom_ether_status(char *name, char *options)
307 {
308 int i;
309 custom_ether_args_t *args;
310 double total_sent = 0, total_received = 0, total_expected;
311
312 total_expected = CUSTOM_ETHER_THREADS * CUSTOM_ETHER_ITERATIONS;
313 for (i = 0; i < CUSTOM_ETHER_THREADS; i++) {
314 args = &custom_ether_thread_args[i];
315 total_sent += args->sent;
316 total_received += args->received;
317 }
318 SKTDBG(name, options, "%.2f%% sent, %.2f%% received\n",
319 (total_sent * 100) / total_expected,
320 (total_received * 100) / total_expected);
321 }
322
323 /*
324 * XXX
325 * This needs to be called outside of per-thread context because
326 * closing a channel could cause a pool flush which causes packet
327 * loss for unfinished threads who still have packets in flight.
328 */
329 static void
custom_ether_cleanup(void)330 custom_ether_cleanup(void)
331 {
332 int i;
333 custom_ether_args_t *args;
334
335 for (i = 0; i < CUSTOM_ETHER_THREADS; i++) {
336 args = &custom_ether_thread_args[i];
337
338 if (args->ch != NULL) {
339 os_channel_destroy(args->ch);
340 args->ch = NULL;
341 }
342 if (args->nx_ncd != NULL) {
343 os_nexus_controller_destroy(args->nx_ncd);
344 args->nx_ncd = NULL;
345 }
346 }
347 custom_ether_done = 0;
348 custom_ether_ready = 0;
349 }
350
351 static bool
custom_ether_verify(packet_t pkt,custom_ether_args_t * args)352 custom_ether_verify(packet_t pkt, custom_ether_args_t *args)
353 {
354 buflet_t buflet;
355 size_t len;
356 void *buf;
357 int error = 0;
358
359 buflet = os_packet_get_next_buflet(pkt, NULL);
360 SKTC_ASSERT_ERR(buflet != NULL);
361
362 len = os_buflet_get_data_length(buflet);
363 SKTC_ASSERT_ERR(len != 0);
364
365 buf = os_buflet_get_object_address(buflet) +
366 os_buflet_get_data_offset(buflet);
367 SKTC_ASSERT_ERR(buf != NULL);
368
369 if (len != CUSTOM_ETHER_PKT_LEN) {
370 SKD1("packet length mismatch: len %ld, expected %d\n",
371 len, CUSTOM_ETHER_PKT_LEN);
372 return FALSE;
373 }
374 if (!args->is_tagged) {
375 ether_header_t *eh = buf;
376 uint16_t etype;
377
378 etype = ntohs(eh->ether_type);
379 if (args->ethertype != etype) {
380 SKD1("ethertype mismatch: 0x%x != 0x%x\n",
381 args->ethertype, etype);
382 return FALSE;
383 }
384 } else {
385 struct ether_vlan_header *evh = buf;
386 uint16_t etype, evl_tag, tag;
387 int err;
388
389 etype = ntohs(evh->evl_encap_proto);
390 if (etype != ETHERTYPE_VLAN) {
391 SKD1("received non-vlan packet: 0x%x", etype);
392 return FALSE;
393 }
394 etype = ntohs(evh->evl_proto);
395 if (args->ethertype != etype) {
396 SKD1("ethertype mismatch: 0x%x != 0x%x\n",
397 args->ethertype, etype);
398 return FALSE;
399 }
400 evl_tag = ntohs(evh->evl_tag);
401
402 /* vlan tag metadata is not expected for this test case */
403 err = os_packet_get_vlan_tag(pkt, &tag);
404 if (err == 0) {
405 SKD1("tag not expected: 0x%x\n", tag);
406 return FALSE;
407 }
408 if (EVL_PRIOFTAG(evl_tag) != args->priority) {
409 SKD1("priority mismatch: 0x%x != 0x%x\n",
410 EVL_PRIOFTAG(evl_tag), args->priority);
411 return FALSE;
412 }
413 }
414 return TRUE;
415 }
416
417 static void
custom_ether_receive(channel_t ch,custom_ether_args_t * args,packet_t pkt)418 custom_ether_receive(channel_t ch, custom_ether_args_t *args, packet_t pkt)
419 {
420 bool valid;
421
422 valid = custom_ether_verify(pkt, args);
423 assert(valid);
424
425 os_channel_packet_free(ch, pkt);
426 args->received++;
427 }
428
429 static void
custom_ether_setup_args(int index,bool sender,bool tagged)430 custom_ether_setup_args(int index, bool sender, bool tagged)
431 {
432 custom_ether_args_t *args = &custom_ether_thread_args[index];
433
434 args->is_sender = sender;
435 args->is_tagged = tagged;
436 if (tagged) {
437 args->priority = index % 7;
438 }
439 args->ethertype = CUSTOM_ETHER_ETHERTYPE_BASE + index;
440 args->sent = 0;
441 args->received = 0;
442 }
443
444 static void
custom_ether_setup_flow(nexus_controller_t ncd,uuid_t uuid,custom_ether_args_t * args,nexus_port_t * nx_port,uuid_t bind_key)445 custom_ether_setup_flow(nexus_controller_t ncd, uuid_t uuid,
446 custom_ether_args_t *args, nexus_port_t *nx_port, uuid_t bind_key)
447 {
448 struct nx_flow_req nfr;
449 uuid_t flow_uuid;
450 uuid_string_t uuidstr;
451 int error;
452
453 uuid_generate(flow_uuid);
454 bzero(&nfr, sizeof(nfr));
455 uuid_copy(nfr.nfr_flow_uuid, flow_uuid);
456 nfr.nfr_nx_port = NEXUS_PORT_ANY;
457 nfr.nfr_ethertype = args->ethertype;
458 nfr.nfr_flags |= NXFLOWREQF_CUSTOM_ETHER;
459
460 error = __os_nexus_flow_add(ncd, uuid, &nfr);
461 SKTC_ASSERT_ERR(error == 0);
462
463 uuid_unparse(nfr.nfr_bind_key, uuidstr);
464 uuid_copy(bind_key, nfr.nfr_bind_key);
465 *nx_port = nfr.nfr_nx_port;
466 }
467
468 static void
custom_ether_handler(channel_t ch,custom_ether_args_t * args)469 custom_ether_handler(channel_t ch, custom_ether_args_t *args)
470 {
471 int error;
472 packet_t pkt = 0;
473
474 error = get_packet(ch, 0, &pkt);
475 assert(error == 0);
476 custom_ether_receive(ch, args, pkt);
477 if (args->sent < CUSTOM_ETHER_ITERATIONS) {
478 custom_ether_send(ch, args);
479 }
480 }
481
482 static void
custom_ether_thread(custom_ether_args_t * args)483 custom_ether_thread(custom_ether_args_t *args)
484 {
485 channel_attr_t ch_attr;
486 channel_t ch;
487 struct kevent evlist, kev[1];
488 nexus_controller_t nx_ncd;
489 nexus_port_t nx_port;
490 uuid_t bind_key;
491 int kq, ch_fd, error = 0;
492
493 nx_ncd = os_nexus_controller_create();
494 SKTC_ASSERT_ERR(nx_ncd != NULL);
495 args->nx_ncd = nx_ncd;
496
497 custom_ether_setup_flow(nx_ncd, if_uuid, args,
498 &nx_port, bind_key);
499
500 ch_attr = os_channel_attr_create();
501 error = os_channel_attr_set_key(ch_attr, bind_key, sizeof(bind_key));
502 SKTC_ASSERT_ERR(error == 0);
503 error = os_channel_attr_set(ch_attr, CHANNEL_ATTR_USER_PACKET_POOL, 1);
504 SKTC_ASSERT_ERR(error == 0);
505
506 ch = os_channel_create_extended(if_uuid, nx_port, CHANNEL_DIR_TX_RX,
507 CHANNEL_RING_ID_ANY, ch_attr);
508 SKTC_ASSERT_ERR(ch != NULL);
509 args->ch = ch;
510
511 kq = kqueue();
512 SKTC_ASSERT_ERR(kq != -1);
513
514 ch_fd = os_channel_get_fd(ch);
515 EV_SET(&kev[0], ch_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
516 error = kevent(kq, kev, 1, NULL, 0, NULL);
517 SKTC_ASSERT_ERR(error == 0);
518
519 /* Increment this count to tell the main thread that we are ready */
520 if (args->is_sender) {
521 /* send one packet to start the test */
522 custom_ether_send(ch, args);
523 }
524 (void) OSAtomicIncrement32(&custom_ether_ready);
525 for (;;) {
526 /* Wait for RX events */
527 error = kevent(kq, NULL, 0, &evlist, 1, NULL);
528 SKTC_ASSERT_ERR(error == 1);
529 if (evlist.filter == EVFILT_READ) {
530 custom_ether_handler(ch, args);
531 }
532 if (args->sent == CUSTOM_ETHER_ITERATIONS &&
533 args->received == CUSTOM_ETHER_ITERATIONS) {
534 break;
535 }
536 }
537 (void) OSAtomicIncrement32(&custom_ether_done);
538 }
539
540 /*
541 ****************************************************************
542 * End of common section *
543 ****************************************************************
544 */
545
546 /*
547 ****************************************************************
548 * Start of filter section *
549 ****************************************************************
550 */
551 #define FILTER_THREADS 8
552 static int recv_server_fd = -1;
553 static int send_server_fd = -1;
554 static int recv_client_fd = -1;
555 static int send_client_fd = -1;
556 static int32_t filter_ready = 0;
557
558 static void
filter_server_setup(uint16_t port,int * server_fd)559 filter_server_setup(uint16_t port, int *server_fd)
560 {
561 struct sockaddr_in sin;
562 int fd, flags, error = 0, on = 1;
563
564 fd = socket(AF_INET, SOCK_STREAM, 0);
565 SKTC_ASSERT_ERR(fd != -1);
566
567 error = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
568 SKTC_ASSERT_ERR(error == 0);
569
570 sin.sin_family = AF_INET;
571 sin.sin_addr.s_addr = INADDR_ANY;
572 sin.sin_port = htons(port);
573 error = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
574 SKTC_ASSERT_ERR(error == 0);
575
576 error = listen(fd, 1);
577 SKTC_ASSERT_ERR(error == 0);
578
579 flags = fcntl(fd, F_GETFL, 0);
580 SKTC_ASSERT_ERR(flags != -1);
581
582 error = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
583 SKTC_ASSERT_ERR(error != -1);
584
585 *server_fd = fd;
586 }
587
588 static void
filter_channel_setup(void)589 filter_channel_setup(void)
590 {
591 bool r;
592
593 /* Get the interface uuid we will be adding filters to */
594 r = sktc_get_netif_nexus(FETH0_NAME, if_uuid);
595 assert(r);
596 }
597
598 static void
filter_setup(void)599 filter_setup(void)
600 {
601 filter_server_setup(FILTER_RECV_PORT, &recv_server_fd);
602 filter_server_setup(FILTER_SEND_PORT, &send_server_fd);
603 filter_channel_setup();
604 }
605
606 static void
filter_wait_for_clients(void)607 filter_wait_for_clients(void)
608 {
609 fd_set server_fds;
610 struct sockaddr_in r, s;
611 socklen_t sz;
612 int error = 0, maxfd;
613
614 SKD1("filter: waiting for clients\n");
615 assert(recv_server_fd != -1);
616 assert(send_server_fd != -1);
617 maxfd = (send_server_fd > recv_server_fd) ? send_server_fd :
618 recv_server_fd;
619
620 for (;;) {
621 FD_ZERO(&server_fds);
622 if (recv_client_fd == -1) {
623 FD_SET(recv_server_fd, &server_fds);
624 }
625 if (send_client_fd == -1) {
626 FD_SET(send_server_fd, &server_fds);
627 }
628
629 error = select(maxfd + 1, &server_fds, NULL, NULL, NULL);
630 SKTC_ASSERT_ERR(error != -1);
631
632 if (FD_ISSET(recv_server_fd, &server_fds)) {
633 sz = sizeof(r);
634 recv_client_fd = accept(recv_server_fd,
635 (struct sockaddr *)&r, &sz);
636 SKTC_ASSERT_ERR(recv_client_fd != -1);
637 SKD1("filter: accepted receiver connection\n");
638 } else if (FD_ISSET(send_server_fd, &server_fds)) {
639 sz = sizeof(s);
640 send_client_fd = accept(send_server_fd,
641 (struct sockaddr *)&s, &sz);
642 SKTC_ASSERT_ERR(send_client_fd != -1);
643 SKD1("filter: accepted sender connection\n");
644 }
645 if (recv_client_fd != -1 && send_client_fd != -1) {
646 break;
647 }
648 }
649 (void) close(recv_server_fd);
650 (void) close(send_server_fd);
651 recv_server_fd = -1;
652 send_server_fd = -1;
653 }
654
655 static void
process_recv_client_cmd(void)656 process_recv_client_cmd(void)
657 {
658 uint8_t rcmd;
659
660 rcmd = read_cmd(recv_client_fd);
661 switch (rcmd) {
662 case CMD_RECV_SOCKET_READY: {
663 SKD1("filter: receiver ready to start socket test\n");
664
665 /* Tell sender to start socket test */
666 write_cmd(send_client_fd, CLIENT_CMD_SEND_SOCKET_START);
667 break;
668 }
669 case CMD_RECV_CHANNEL_READY: {
670 SKD1("filter: receiver ready to start channel test\n");
671
672 /* Tell sender to start channel test */
673 write_cmd(send_client_fd, CLIENT_CMD_SEND_CHANNEL_START);
674 break;
675 }
676 case CMD_RECV_ALL_DONE: {
677 SKD1("filter: receiver finished all tests\n");
678 exit(0);
679 }
680 default:
681 SKD0("unknown command %d\n", rcmd);
682 exit(1);
683 }
684 }
685
686 static void
process_send_client_cmd(void)687 process_send_client_cmd(void)
688 {
689 /* nothing yet */
690 }
691
692 static int
process_ring(channel_t ch,ring_id_t r)693 process_ring(channel_t ch, ring_id_t r)
694 {
695 int error, cnt = 0;
696 packet_t pkt = 0;
697
698 /*
699 * To be efficient, we process the full rx ring
700 * before calling tx sync.
701 */
702 while ((error = get_packet(ch, r, &pkt)) == 0) {
703 assert(pkt != 0);
704 put_packet(ch, r, pkt);
705 cnt++;
706 }
707 SKTC_ASSERT_ERR(error == ENOENT);
708 if (cnt == 0) {
709 return 0;
710 }
711
712 error = os_channel_sync(ch, CHANNEL_SYNC_TX);
713 SKTC_ASSERT_ERR(error == 0);
714 /* rx sync is done internally next time we call kevent */
715 return cnt;
716 }
717
718 static void
filter_handler(channel_t ch)719 filter_handler(channel_t ch)
720 {
721 int cnt = 0;
722
723 /*
724 * Filter packets could come in from both inbound/outbound
725 * directions. Check both RX rings.
726 */
727 for (ring_id_t r = 0; r < 2; r++) {
728 cnt += process_ring(ch, r);
729 }
730 if (cnt == 0) {
731 SKD0("filter: spurious wakeup!!\n");
732 }
733 }
734
735 static void
filter_setup_flow(nexus_controller_t ncd,uuid_t uuid,nexus_port_t * nx_port,uuid_t bind_key)736 filter_setup_flow(nexus_controller_t ncd, uuid_t uuid,
737 nexus_port_t *nx_port, uuid_t bind_key)
738 {
739 struct nx_flow_req nfr;
740 uuid_t flow_uuid;
741 uuid_string_t uuidstr;
742 int error;
743
744 uuid_generate(flow_uuid);
745 bzero(&nfr, sizeof(nfr));
746 uuid_copy(nfr.nfr_flow_uuid, flow_uuid);
747 nfr.nfr_nx_port = NEXUS_PORT_ANY;
748 nfr.nfr_flags |= NXFLOWREQF_FILTER;
749
750 error = __os_nexus_flow_add(ncd, uuid, &nfr);
751 SKTC_ASSERT_ERR(error == 0);
752
753 uuid_unparse(nfr.nfr_bind_key, uuidstr);
754 uuid_copy(bind_key, nfr.nfr_bind_key);
755 *nx_port = nfr.nfr_nx_port;
756 }
757
758 static void *
filter_thread(void * unused)759 filter_thread(void *unused)
760 {
761 channel_attr_t ch_attr;
762 channel_t ch;
763 struct kevent evlist, kev[1];
764 nexus_controller_t nx_ncd;
765 nexus_port_t nx_port;
766 uuid_t bind_key;
767 int kq, ch_fd, error = 0;
768
769 nx_ncd = os_nexus_controller_create();
770 SKTC_ASSERT_ERR(nx_ncd != NULL);
771 filter_setup_flow(nx_ncd, if_uuid, &nx_port, bind_key);
772
773 ch_attr = os_channel_attr_create();
774 error = os_channel_attr_set_key(ch_attr, bind_key, sizeof(bind_key));
775 SKTC_ASSERT_ERR(error == 0);
776 error = os_channel_attr_set(ch_attr, CHANNEL_ATTR_USER_PACKET_POOL, 1);
777 SKTC_ASSERT_ERR(error == 0);
778 error = os_channel_attr_set(ch_attr, CHANNEL_ATTR_FILTER, 1);
779 SKTC_ASSERT_ERR(error == 0);
780
781 ch = os_channel_create_extended(if_uuid, nx_port, CHANNEL_DIR_TX_RX,
782 CHANNEL_RING_ID_ANY, ch_attr);
783 SKTC_ASSERT_ERR(ch != NULL);
784
785 kq = kqueue();
786 SKTC_ASSERT_ERR(kq != -1);
787
788 ch_fd = os_channel_get_fd(ch);
789 EV_SET(&kev[0], ch_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
790 error = kevent(kq, kev, 1, NULL, 0, NULL);
791 SKTC_ASSERT_ERR(error == 0);
792
793 /* Increment this count to tell the main thread that we are ready */
794 (void) OSAtomicIncrement32(&filter_ready);
795 for (;;) {
796 /* Wait for RX events */
797 error = kevent(kq, NULL, 0, &evlist, 1, NULL);
798 SKTC_ASSERT_ERR(error == 1);
799 if (evlist.filter == EVFILT_READ) {
800 filter_handler(ch);
801 }
802 }
803 return NULL;
804 }
805
806 static void
filter_threads_start(void)807 filter_threads_start(void)
808 {
809 int error, i;
810 pthread_t t;
811
812 SKD1("filter: spawning filter threads\n");
813 for (i = 0; i < FILTER_THREADS; i++) {
814 error = pthread_create(&t, NULL, filter_thread, NULL);
815 SKTC_ASSERT_ERR(error == 0);
816 }
817 for (;;) {
818 SKD1("filter: %d threads ready\n", filter_ready);
819 if (filter_ready == FILTER_THREADS) {
820 break;
821 } else {
822 sleep(1);
823 }
824 }
825 }
826
827 static void
filter_loop(void)828 filter_loop(void)
829 {
830 fd_set client_fds;
831 int maxfd;
832
833 assert(recv_client_fd != -1);
834 assert(send_client_fd != -1);
835 maxfd = (send_client_fd > recv_client_fd) ? send_client_fd :
836 recv_client_fd;
837
838 SKD1("filter: waiting for commands\n");
839 for (;;) {
840 FD_ZERO(&client_fds);
841 FD_SET(recv_client_fd, &client_fds);
842 FD_SET(send_client_fd, &client_fds);
843
844 if (select(maxfd + 1, &client_fds, NULL, NULL, NULL) < 0) {
845 SKD0("filter: select failed: %d\n", errno);
846 exit(1);
847 }
848 if (FD_ISSET(recv_client_fd, &client_fds)) {
849 process_recv_client_cmd();
850 } else if (FD_ISSET(send_client_fd, &client_fds)) {
851 process_send_client_cmd();
852 }
853 }
854 }
855
856 static void
filter(int testid)857 filter(int testid)
858 {
859 SKD1("filter: start\n");
860 filter_setup();
861 filter_wait_for_clients();
862 filter_threads_start();
863 filter_loop();
864 }
865 /*
866 ****************************************************************
867 * End of filter section *
868 ****************************************************************
869 */
870
871 /*
872 ****************************************************************
873 * Start of receiver section *
874 ****************************************************************
875 */
876
877 /* Used for data transfer for the socket test case */
878 static int receiver_fd = -1;
879
880 /* Used for sending commands to filter server */
881 static int receiver_client_fd = -1;
882
883 static void
receiver_socket_setup(void)884 receiver_socket_setup(void)
885 {
886 struct sockaddr_in sin;
887 int fd, error = 0, on = 1;
888
889 fd = socket(AF_INET, SOCK_STREAM, 0);
890 SKTC_ASSERT_ERR(fd != -1);
891
892 error = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
893 SKTC_ASSERT_ERR(error == 0);
894
895 sin.sin_family = AF_INET;
896 sin.sin_addr.s_addr = INADDR_ANY;
897 sin.sin_port = htons(SOCKET_LISTENER_PORT);
898 error = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
899 SKTC_ASSERT_ERR(error == 0);
900
901 error = listen(fd, SOCKET_THREADS);
902 SKTC_ASSERT_ERR(error == 0);
903 receiver_fd = fd;
904 }
905
906 static void
receiver_channel_setup(void)907 receiver_channel_setup(void)
908 {
909 int err;
910 bool found;
911
912 /* receiver uses feth0 */
913 found = sktc_get_netif_nexus(FETH0_NAME, if_uuid);
914 assert(found);
915 err = sktc_get_mac_addr(FETH0_NAME, src_mac_addr.octet);
916 assert(err == 0);
917 err = sktc_get_mac_addr(FETH1_NAME, dst_mac_addr.octet);
918 assert(err == 0);
919 }
920
921 static void
receiver_setup(void)922 receiver_setup(void)
923 {
924 receiver_socket_setup();
925 receiver_channel_setup();
926 }
927
928 static void
receiver_connect_to_server(void)929 receiver_connect_to_server(void)
930 {
931 connect_to_server(FILTER_RECV_PORT, &receiver_client_fd);
932 }
933
934 static void *
receiver_socket_thread(void * arg)935 receiver_socket_thread(void *arg)
936 {
937 socket_args_t *args = arg;
938 int bytes, error = 0;
939
940 (void) OSAtomicIncrement32(&socket_ready);
941 while (args->bytes < SOCKET_BYTES) {
942 bytes = read(args->fd, databuf, sizeof(databuf));
943 SKTC_ASSERT_ERR(bytes != -1);
944 args->bytes += bytes;
945 }
946 (void) close(args->fd);
947 (void) OSAtomicIncrement32(&socket_done);
948 return NULL;
949 }
950
951 static void
receiver_socket_start(void)952 receiver_socket_start(void)
953 {
954 int i, fd, error = 0;
955 socket_args_t *args;
956 socklen_t sz;
957 struct sockaddr_in sin;
958 pthread_t t;
959
960 /*
961 * The sender can connect before we spawn our threads.
962 */
963 write_cmd(receiver_client_fd, CMD_RECV_SOCKET_READY);
964 SKD1("receiver: spawning socket threads\n");
965 for (i = 0; i < SOCKET_THREADS; i++) {
966 sz = sizeof(sin);
967 fd = accept(receiver_fd, (struct sockaddr *)&sin, &sz);
968 SKTC_ASSERT_ERR(fd != -1);
969
970 args = &socket_thread_args[i];
971 args->fd = fd;
972 args->bytes = 0;
973 error = pthread_create(&t, NULL, receiver_socket_thread, args);
974 SKTC_ASSERT_ERR(error == 0);
975 }
976 for (;;) {
977 SKD1("receiver: %d socket threads ready\n", socket_ready);
978 if (socket_ready == SOCKET_THREADS) {
979 break;
980 } else {
981 sleep(1);
982 }
983 }
984 }
985
986 static void
receiver_socket_wait(void)987 receiver_socket_wait(void)
988 {
989 for (;;) {
990 socket_test_status("receiver");
991 if (socket_done == SOCKET_THREADS) {
992 break;
993 } else {
994 sleep(1);
995 }
996 }
997 (void) close(receiver_fd);
998 receiver_fd = -1;
999 }
1000
1001 static void *
receiver_channel_thread(void * args)1002 receiver_channel_thread(void *args)
1003 {
1004 custom_ether_thread(args);
1005 return NULL;
1006 }
1007
1008 static void
receiver_channel_start(char * name,char * options,bool tagged)1009 receiver_channel_start(char *name, char *options, bool tagged)
1010 {
1011 int error, i;
1012 pthread_t t;
1013
1014 SKTDBG(name, options, "spawning channel threads\n");
1015 for (i = 0; i < CUSTOM_ETHER_THREADS; i++) {
1016 custom_ether_setup_args(i, false, tagged);
1017 error = pthread_create(&t, NULL, receiver_channel_thread,
1018 &custom_ether_thread_args[i]);
1019 SKTC_ASSERT_ERR(error == 0);
1020 }
1021 for (;;) {
1022 SKTDBG(name, options, "%d channel threads ready\n",
1023 custom_ether_ready);
1024 if (custom_ether_ready == CUSTOM_ETHER_THREADS) {
1025 break;
1026 } else {
1027 sleep(1);
1028 }
1029 }
1030 /* Tell sender we're ready */
1031 write_cmd(receiver_client_fd, CMD_RECV_CHANNEL_READY);
1032 }
1033
1034 static void
receiver_channel_wait(char * name,char * options)1035 receiver_channel_wait(char *name, char *options)
1036 {
1037 for (;;) {
1038 custom_ether_status(name, options);
1039 if (custom_ether_done == CUSTOM_ETHER_THREADS) {
1040 break;
1041 } else {
1042 sleep(1);
1043 }
1044 }
1045 SKTDBG(name, options, "%d threads done\n", custom_ether_done);
1046 custom_ether_cleanup();
1047 }
1048
1049 static void
receiver_custom_ether_test(char * name,char * options,bool tagged)1050 receiver_custom_ether_test(char *name, char *options, bool tagged)
1051 {
1052 receiver_channel_start(name, options, tagged);
1053 receiver_channel_wait(name, options);
1054 }
1055
1056 static void
receiver_done(void)1057 receiver_done(void)
1058 {
1059 write_cmd(receiver_client_fd, CMD_RECV_ALL_DONE);
1060 }
1061
1062 static void
receiver_start(void)1063 receiver_start(void)
1064 {
1065 receiver_socket_start();
1066 receiver_socket_wait();
1067 receiver_custom_ether_test("receiver", "", false);
1068 receiver_custom_ether_test("receiver", "tagged", true);
1069 receiver_done();
1070 }
1071
1072 static void
receiver(int testid)1073 receiver(int testid)
1074 {
1075 SKD1("receiver: start\n");
1076 receiver_setup();
1077 receiver_connect_to_server();
1078 receiver_start();
1079 }
1080
1081 /*
1082 ****************************************************************
1083 * End of receiver section *
1084 ****************************************************************
1085 */
1086
1087 /*
1088 ****************************************************************
1089 * Start of sender section *
1090 ****************************************************************
1091 */
1092
1093 /* Used for receiving commands from filter server */
1094 static int sender_client_fd = -1;
1095
1096 static void
sender_socket_setup(void)1097 sender_socket_setup(void)
1098 {
1099 /* nothing to do */
1100 }
1101
1102 static void
sender_channel_setup(void)1103 sender_channel_setup(void)
1104 {
1105 int err;
1106 bool found;
1107
1108 /* sender uses feth1 */
1109 found = sktc_get_netif_nexus(FETH1_NAME, if_uuid);
1110 assert(found);
1111 err = sktc_get_mac_addr(FETH1_NAME, src_mac_addr.octet);
1112 assert(err == 0);
1113 err = sktc_get_mac_addr(FETH0_NAME, dst_mac_addr.octet);
1114 assert(err == 0);
1115 }
1116
1117 static void
sender_setup(void)1118 sender_setup(void)
1119 {
1120 sender_socket_setup();
1121 sender_channel_setup();
1122 }
1123
1124 static void
sender_connect_to_server(void)1125 sender_connect_to_server(void)
1126 {
1127 connect_to_server(FILTER_SEND_PORT, &sender_client_fd);
1128 }
1129
1130 static void *
sender_socket_thread(void * arg)1131 sender_socket_thread(void *arg)
1132 {
1133 socket_args_t *args = arg;
1134 struct sockaddr_in sin;
1135 int fd, remain, bytes, ifscope, error = 0;
1136
1137 (void) OSAtomicIncrement32(&socket_ready);
1138 fd = socket(AF_INET, SOCK_STREAM, 0);
1139 SKTC_ASSERT_ERR(fd != -1);
1140
1141 /* must use feth1 as outgoing interface */
1142 ifscope = if_nametoindex(FETH1_NAME);
1143 assert(ifscope != 0);
1144 error = setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &ifscope,
1145 sizeof(ifscope));
1146 SKTC_ASSERT_ERR(error == 0);
1147
1148 /* connecting from feth1 to feth0 */
1149 sin.sin_family = AF_INET;
1150 sin.sin_addr = sktc_feth0_in_addr();
1151 sin.sin_port = htons(SOCKET_LISTENER_PORT);
1152 error = connect(fd, (struct sockaddr *)&sin, sizeof(sin));
1153 SKTC_ASSERT_ERR(error == 0);
1154
1155 remain = SOCKET_BYTES;
1156 while (remain > 0) {
1157 bytes = MIN(remain, sizeof(databuf));
1158 bytes = write(fd, databuf, bytes);
1159 SKTC_ASSERT_ERR(bytes != -1);
1160 remain -= bytes;
1161 args->bytes += bytes;
1162 }
1163 (void) close(fd);
1164 (void) OSAtomicIncrement32(&socket_done);
1165 return NULL;
1166 }
1167
1168 static void
sender_socket_start(void)1169 sender_socket_start(void)
1170 {
1171 uint8_t cmd;
1172 socket_args_t *args;
1173 int i, error = 0;
1174 pthread_t t;
1175
1176 /* wait for command from filter server */
1177 SKD1("sender: waiting for socket start command\n");
1178 cmd = read_cmd(sender_client_fd);
1179 SKTC_ASSERT_ERR(cmd == CLIENT_CMD_SEND_SOCKET_START);
1180
1181 SKD1("sender: spawning socket threads\n");
1182 for (i = 0; i < SOCKET_THREADS; i++) {
1183 args = &socket_thread_args[i];
1184 args->fd = -1;
1185 args->bytes = 0;
1186
1187 error = pthread_create(&t, NULL, sender_socket_thread, args);
1188 SKTC_ASSERT_ERR(error == 0);
1189 }
1190 for (;;) {
1191 SKD1("sender: %d socket threads ready\n", socket_ready);
1192 if (socket_ready == SOCKET_THREADS) {
1193 break;
1194 } else {
1195 sleep(1);
1196 }
1197 }
1198 }
1199
1200 static void
sender_socket_wait(void)1201 sender_socket_wait(void)
1202 {
1203 for (;;) {
1204 socket_test_status("sender");
1205 if (socket_done == SOCKET_THREADS) {
1206 break;
1207 } else {
1208 sleep(1);
1209 }
1210 }
1211 }
1212
1213 static void *
sender_channel_thread(void * args)1214 sender_channel_thread(void *args)
1215 {
1216 custom_ether_thread(args);
1217 return NULL;
1218 }
1219
1220 static void
sender_channel_start(char * name,char * options,bool tagged)1221 sender_channel_start(char *name, char *options, bool tagged)
1222 {
1223 int error = 0, i;
1224 pthread_t t;
1225 uint8_t cmd;
1226
1227 /* wait for command from filter server */
1228 SKTDBG(name, options, "waiting for channel start command\n");
1229 cmd = read_cmd(sender_client_fd);
1230 SKTC_ASSERT_ERR(cmd == CLIENT_CMD_SEND_CHANNEL_START);
1231
1232 SKTDBG(name, options, "spawning channel threads\n");
1233 for (i = 0; i < CUSTOM_ETHER_THREADS; i++) {
1234 custom_ether_setup_args(i, true, tagged);
1235 error = pthread_create(&t, NULL, sender_channel_thread,
1236 &custom_ether_thread_args[i]);
1237 SKTC_ASSERT_ERR(error == 0);
1238 }
1239 for (;;) {
1240 SKTDBG(name, options, "%d channel threads ready\n",
1241 custom_ether_ready);
1242 if (custom_ether_ready == CUSTOM_ETHER_THREADS) {
1243 break;
1244 } else {
1245 sleep(1);
1246 }
1247 }
1248 }
1249
1250 static void
sender_channel_wait(char * name,char * options)1251 sender_channel_wait(char *name, char *options)
1252 {
1253 for (;;) {
1254 custom_ether_status(name, options);
1255 if (custom_ether_done == CUSTOM_ETHER_THREADS) {
1256 break;
1257 } else {
1258 sleep(1);
1259 }
1260 }
1261 SKTDBG(name, options, "%d threads done\n", custom_ether_done);
1262 custom_ether_cleanup();
1263 }
1264
1265 static void
sender_custom_ether_test(char * name,char * options,bool tagged)1266 sender_custom_ether_test(char *name, char *options, bool tagged)
1267 {
1268 sender_channel_start(name, options, tagged);
1269 sender_channel_wait(name, options);
1270 }
1271
1272 static void
sender_start(void)1273 sender_start(void)
1274 {
1275 sender_socket_start();
1276 sender_socket_wait();
1277 sender_custom_ether_test("sender", "", false);
1278 sender_custom_ether_test("sender", "tagged", true);
1279 }
1280
1281 static void
sender(int testid)1282 sender(int testid)
1283 {
1284 SKD1("sender: start\n");
1285 sender_setup();
1286 sender_connect_to_server();
1287 sender_start();
1288 }
1289 /*
1290 ****************************************************************
1291 * End of sender section *
1292 ****************************************************************
1293 */
1294 static int
skt_filter_main(int argc,char * argv[])1295 skt_filter_main(int argc, char *argv[])
1296 {
1297 int child, test_id;
1298
1299 assert(!strcmp(argv[3], "--child"));
1300 child = atoi(argv[4]);
1301 test_id = 0;
1302
1303 wait_for_start();
1304 if (child == 0) {
1305 filter(test_id);
1306 } else if (child == 1) {
1307 receiver(test_id);
1308 } else if (child == 2) {
1309 sender(test_id);
1310 }
1311 return 0;
1312 }
1313
1314 static bool
skt_filter_supported(void)1315 skt_filter_supported(void)
1316 {
1317 uint32_t if_attach_nx;
1318 size_t len = sizeof(if_attach_nx);
1319 bool supported;
1320
1321 assert(sysctlbyname("net.link.generic.system.if_attach_nx",
1322 &if_attach_nx, &len, NULL, 0) == 0);
1323
1324 /* check for IF_ATTACH_NX_NETIF_NETAGENT */
1325 supported = ((if_attach_nx & 0x08) != 0);
1326 SKD1("test%ssupported, if_attach_nx=0x%x\n",
1327 supported ? " " : " not ", if_attach_nx);
1328 return supported;
1329 }
1330
1331 static uint32_t skt_netif_nxctl_check;
1332 static void
skt_filter_init(uint32_t flags)1333 skt_filter_init(uint32_t flags)
1334 {
1335 uint32_t nxctl_check = 1;
1336 size_t len = sizeof(skt_netif_nxctl_check);
1337
1338 assert(sysctlbyname("kern.skywalk.disable_nxctl_check",
1339 &skt_netif_nxctl_check, &len, &nxctl_check,
1340 sizeof(nxctl_check)) == 0);
1341 sktc_ifnet_feth_pair_create(flags);
1342 }
1343
1344 static void
skt_filter_fini(void)1345 skt_filter_fini(void)
1346 {
1347 assert(sysctlbyname("kern.skywalk.disable_nxctl_check",
1348 NULL, NULL, &skt_netif_nxctl_check,
1349 sizeof(skt_netif_nxctl_check)) == 0);
1350 sktc_ifnet_feth_pair_destroy();
1351 }
1352
1353 static void
skt_filter_native_init(void)1354 skt_filter_native_init(void)
1355 {
1356 skt_filter_init(FETH_FLAGS_NATIVE | FETH_FLAGS_NXATTACH);
1357 }
1358
1359 static void
skt_filter_native_fini(void)1360 skt_filter_native_fini(void)
1361 {
1362 skt_filter_fini();
1363 }
1364
1365 static void
skt_filter_compat_init(void)1366 skt_filter_compat_init(void)
1367 {
1368 skt_filter_init(FETH_FLAGS_TXSTART | FETH_FLAGS_NXATTACH);
1369 }
1370
1371 static void
skt_filter_compat_fini(void)1372 skt_filter_compat_fini(void)
1373 {
1374 skt_filter_fini();
1375 }
1376
1377 #define NATIVE_TEST "filternative"
1378 #define COMPAT_TEST "filtercompat"
1379 struct skywalk_mptest skt_filternative = {
1380 NATIVE_TEST,
1381 "filter native test",
1382 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF | SK_FEATURE_DEV_OR_DEBUG,
1383 3, skt_filter_main,
1384 { NULL, NULL, NULL, NULL, NULL, NULL},
1385 skt_filter_native_init, skt_filter_native_fini, {},
1386 };
1387
1388 struct skywalk_mptest skt_filtercompat = {
1389 COMPAT_TEST,
1390 "filter compat test",
1391 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_NETIF | SK_FEATURE_DEV_OR_DEBUG,
1392 3, skt_filter_main,
1393 { NULL, NULL, NULL, NULL, NULL, NULL},
1394 skt_filter_compat_init, skt_filter_compat_fini, {},
1395 };
1396
1397 struct skywalk_mptest_check skt_filternative_check = {
1398 NATIVE_TEST, skt_filter_supported,
1399 };
1400
1401 struct skywalk_mptest_check skt_filtercompat_check = {
1402 COMPAT_TEST, skt_filter_supported,
1403 };
1404