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 /*
30 * <rdar://problem/31245751> User space forwarding for testing utun/ipsec
31 *
32 * A process that opens 2 channels, each one to a separate utun/ipsec interface
33 * The process would then shuttle packets from one to another.
34 *
35 */
36
37 #include <assert.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <pthread.h>
44 #include <fcntl.h>
45 #include <mach/mach.h>
46 #include <mach/host_reboot.h>
47
48 #include <uuid/uuid.h>
49 #include <sys/types.h>
50 #include <sys/event.h>
51 #include <net/if_utun.h>
52 #include <sys/ioctl.h>
53 #include <sys/socket.h>
54 #include <sys/kern_control.h>
55 #include <sys/reboot.h>
56 #include <sys/sys_domain.h>
57 #include <sys/sysctl.h>
58
59 #include <arpa/inet.h> // for inet_ntop
60
61 #include <skywalk/os_skywalk.h>
62 #include <darwintest.h>
63
64 #include "skywalk_test_driver.h"
65 #include "skywalk_test_utils.h"
66 #include "skywalk_test_common.h"
67
68 static volatile bool g_die;
69
70 static volatile bool g_verbose;
71
72 char debugcmd[1024];
73
74 FILE *g_dumpfile;
75
76 #define VLOG(_fmt, ...) \
77 do { \
78 if (g_verbose) { \
79 struct timeval _stamp, _delta; \
80 if (!g_dumpfile) g_dumpfile = stderr; \
81 gettimeofday(&_stamp, NULL); \
82 timersub(&_stamp, &prevtime, &_delta); \
83 fprintf(g_dumpfile, "% 10ld.%06d % 10ld.%06d %s: " _fmt "\n", \
84 _stamp.tv_sec, _stamp.tv_usec, \
85 _delta.tv_sec, _delta.tv_usec, threadname, ##__VA_ARGS__); \
86 fflush(g_dumpfile); \
87 prevtime = _stamp; \
88 } \
89 } while (0)
90
91 static bool g_assert_stalls12;
92 static bool g_assert_stalls21;
93
94 static void
skt_utunloop_xfer_slots(int kq,channel_t rxchannel,int rxindex,channel_t txchannel,int txindex,const char * threadname,bool xfer12)95 skt_utunloop_xfer_slots(int kq,
96 channel_t rxchannel, int rxindex,
97 channel_t txchannel, int txindex,
98 const char *threadname, bool xfer12)
99 {
100 int error;
101 channel_ring_t rxring, txring;
102 struct kevent kev;
103 int rxfd, txfd;
104 bool rxenable, txenable;
105 time_t start, then, now;
106 uint64_t slotcount, bytecount;
107 uint64_t prevslotcount, prevbytecount;
108 struct timeval prevtime;
109
110 gettimeofday(&prevtime, NULL);
111
112 rxring = os_channel_rx_ring(rxchannel, rxindex +
113 os_channel_ring_id(rxchannel, CHANNEL_FIRST_RX_RING));
114 assert(rxring);
115 txring = os_channel_tx_ring(txchannel, txindex +
116 os_channel_ring_id(txchannel, CHANNEL_FIRST_TX_RING));
117 assert(txring);
118
119 rxfd = os_channel_get_fd(rxchannel);
120 EV_SET(&kev, rxfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
121 error = kevent(kq, &kev, 1, NULL, 0, NULL);
122 SKTC_ASSERT_ERR(!error);
123 rxenable = true;
124
125 txfd = os_channel_get_fd(txchannel);
126 EV_SET(&kev, txfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL);
127 error = kevent(kq, &kev, 1, NULL, 0, NULL);
128 SKTC_ASSERT_ERR(!error);
129 txenable = true;
130
131 T_LOG("thread %s has kq %d rxfd %d txfd %d\n",
132 threadname, kq, rxfd, txfd);
133
134 prevslotcount = slotcount = 0;
135 prevbytecount = bytecount = 0;
136 int stallcount = 0;
137 start = time(NULL);
138
139 while (!g_die) {
140 uint32_t rxavail, txavail, xfer;
141
142 do {
143 rxavail = os_channel_available_slot_count(rxring);
144 txavail = os_channel_available_slot_count(txring);
145 VLOG("rxavail %u txavail %u", rxavail, txavail);
146
147 /* If there's no data to receive stop asking for output notifications
148 * but make sure that if both rx and tx are not available, that
149 * the tx remains enabled to we can wake up to reenable rx when tx
150 * eventually becomes available
151 */
152 if (txenable && !rxavail && txavail) {
153 EV_SET(&kev, txfd, EVFILT_WRITE, EV_DISABLE, 0, 0, NULL);
154 error = kevent(kq, &kev, 1, NULL, 0, NULL);
155 SKTC_ASSERT_ERR(!error);
156 txenable = false;
157 VLOG("txenable = false");
158 } else if (!txenable && (rxavail || (!rxavail && !txavail))) {
159 EV_SET(&kev, txfd, EVFILT_WRITE, EV_ENABLE, 0, 0, NULL);
160 error = kevent(kq, &kev, 1, NULL, 0, NULL);
161 SKTC_ASSERT_ERR(!error);
162 txenable = true;
163 VLOG("txenable = true");
164 }
165
166 /* If there's no place to put data, stop asking for input notifications */
167 if (rxenable && !txavail) {
168 EV_SET(&kev, rxfd, EVFILT_READ, EV_DISABLE, 0, 0, NULL);
169 error = kevent(kq, &kev, 1, NULL, 0, NULL);
170 SKTC_ASSERT_ERR(!error);
171 rxenable = false;
172 VLOG("rxenable = false");
173 } else if (!rxenable && txavail) {
174 EV_SET(&kev, rxfd, EVFILT_READ, EV_ENABLE, 0, 0, NULL);
175 error = kevent(kq, &kev, 1, NULL, 0, NULL);
176 SKTC_ASSERT_ERR(!error);
177 rxenable = true;
178 VLOG("rxenable = true");
179 }
180
181 if (!rxavail || !txavail) {
182 struct timespec timeout = {.tv_sec = 1, .tv_nsec = 0}; // 1 second
183 VLOG("waiting rxen %d rx %u txen %d tx %u",
184 rxenable, rxavail, txenable, txavail);
185
186 assert(txenable || rxenable);
187
188 error = kevent(kq, NULL, 0, &kev, 1, &timeout);
189 if (error == 0) {
190 //T_LOG("%s: kevent tick\n", threadname);
191 if (g_die) {
192 T_LOG("%s: die set, exiting\n", threadname);
193 goto out;
194 }
195 } else {
196 SKTC_ASSERT_ERR(error != -1);
197 SKTC_ASSERT_ERR(error == 1);
198 if (kev.filter == EVFILT_USER) {
199 T_LOG("%s: user event, exiting\n",
200 threadname);
201 goto out;
202 } else if (kev.filter == EVFILT_WRITE) {
203 VLOG("write event");
204 } else if (kev.filter == EVFILT_READ) {
205 VLOG("read event");
206 } else {
207 assert(false);
208 }
209 }
210 }
211
212 now = time(NULL);
213 if (now > then) {
214 T_LOG("%s: time %ld slotcount %llu "
215 "(total %llu) bytecount %llu (total %llu)\n",
216 threadname, now - start,
217 slotcount - prevslotcount, slotcount,
218 bytecount - prevbytecount, bytecount);
219
220 if ((now - start) > 0 && (slotcount - prevslotcount) == 0) {
221 stallcount++;
222 VLOG("STALLING");
223 if ((xfer12 && g_assert_stalls12) || (!xfer12 && g_assert_stalls21)) {
224 if (stallcount > 2) {
225 T_LOG("%s: STALLING count %d rxavail %u txavail %u\n",
226 threadname, stallcount, rxavail, txavail);
227 }
228 assert(stallcount < 10);
229 if (stallcount == 5) {
230 #if 0
231 reboot_np(RB_PANIC | RB_QUICK, "skt_utunloop stalled");
232 host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER);
233 #elif 0
234 if (!strcmp(threadname, "sktc_channel_worker_xfer21")) {
235 T_LOG("%s: Running %s\n", threadname, debugcmd);
236 pclose(popen(debugcmd, "r"));
237 //system(debugcmd);
238 }
239 // T_LOG("%s: Sleeping\n", threadname);
240 // sleep(3600000);
241 T_LOG("%s: exiting because of stall\n", threadname);
242 exit(252);
243 #elif 0
244 T_LOG("%s: enabling verbose\n", threadname);
245 uint64_t verbose = (1ULL << 50);
246 error = sysctlbyname("kern.skywalk.verbose", NULL, NULL, &verbose, sizeof(verbose));
247 SKTC_ASSERT_ERR(!error);
248 #endif
249 }
250 }
251 } else {
252 stallcount = 0;
253 }
254
255 then = now;
256 prevslotcount = slotcount;
257 prevbytecount = bytecount;
258 }
259 } while (!rxavail || !txavail);
260
261 #if 0
262 /*
263 * Yes this distribution includes syncs with 0 slots,
264 * but that should be handled ok, so lets include it.
265 */
266 xfer = arc4random_uniform(1 + MIN(txavail, rxavail));
267 #else
268 /* IDS only transfers one slot i think */
269 xfer = 1;
270 //xfer = MIN(txavail,rxavail);
271 #endif
272
273 VLOG("rx %u tx %u xfer %u", rxavail, txavail, xfer);
274
275 channel_slot_t rxprev = NULL, txprev = NULL;
276
277 for (uint32_t i = 0; i < xfer; i++) {
278 slot_prop_t rxprop, txprop;
279 channel_slot_t rxslot, txslot;
280
281 rxslot = os_channel_get_next_slot(rxring, rxprev, &rxprop);
282 assert(rxslot);
283 txslot = os_channel_get_next_slot(txring, txprev, &txprop);
284 assert(txslot);
285
286 assert(txprop.sp_len >= rxprop.sp_len);
287 memcpy((void *)txprop.sp_buf_ptr,
288 (void *)rxprop.sp_buf_ptr, rxprop.sp_len);
289 txprop.sp_len = rxprop.sp_len;
290 os_channel_set_slot_properties(txring, txslot, &txprop);
291
292 slotcount += 1;
293 bytecount += txprop.sp_len;
294
295 rxprev = rxslot;
296 txprev = txslot;
297
298 #if 1 // this tries to be like IDS which syncs every outgoing packet
299 error = os_channel_advance_slot(txring, txprev);
300 SKTC_ASSERT_ERR(!error);
301 error = os_channel_sync(txchannel, CHANNEL_SYNC_TX);
302 SKTC_ASSERT_ERR(!error);
303 txprev = NULL;
304 #endif
305 }
306
307 if (txprev) {
308 // If we don't sync every slot above we would do this
309 error = os_channel_advance_slot(txring, txprev);
310 SKTC_ASSERT_ERR(!error);
311 error = os_channel_sync(txchannel, CHANNEL_SYNC_TX);
312 SKTC_ASSERT_ERR(!error);
313 }
314
315 // IDS calls rx sync, so we do it here.
316 error = os_channel_advance_slot(rxring, rxprev);
317 SKTC_ASSERT_ERR(!error);
318 error = os_channel_sync(rxchannel, CHANNEL_SYNC_RX);
319 SKTC_ASSERT_ERR(!error);
320 }
321
322 out:
323 return;
324 }
325
326 static channel_t g_channel1, g_channel2;
327 static int g_kq1, g_kq2;
328
329 static void *
sktc_channel_worker_xfer12(void * ignored)330 sktc_channel_worker_xfer12(void *ignored)
331 {
332 pthread_setname_np(__func__);
333 skt_utunloop_xfer_slots(g_kq1, g_channel1, 0, g_channel2, 0, __func__, true);
334 return NULL;
335 }
336
337 static void *
sktc_channel_worker_xfer21(void * ignored)338 sktc_channel_worker_xfer21(void *ignored)
339 {
340 pthread_setname_np(__func__);
341 skt_utunloop_xfer_slots(g_kq2, g_channel2, 0, g_channel1, 0, __func__, false);
342 return NULL;
343 }
344
345 static bool
setblocking(int s,bool blocking)346 setblocking(int s, bool blocking)
347 {
348 int error, flags;
349 bool ret;
350 error = fcntl(s, F_GETFL, 0);
351 SKTC_ASSERT_ERR(error >= 0);
352 flags = error;
353
354 ret = !(flags & O_NONBLOCK);
355
356 if (blocking) {
357 flags &= ~O_NONBLOCK;
358 } else {
359 flags |= O_NONBLOCK;
360 }
361
362 T_LOG("Setting fd %d from %s to %s\n",
363 s, ret ? "blocking" : "nonblocking",
364 blocking ? "blocking" : "nonblocking");
365
366 error = fcntl(s, F_SETFL, flags);
367 SKTC_ASSERT_ERR(!error);
368
369 return ret;
370 }
371
372
373 static int
makesocket(int type,in_addr_t addr)374 makesocket(int type, in_addr_t addr)
375 {
376 int error;
377 int s;
378 char sbuf[INET6_ADDRSTRLEN];
379 struct sockaddr_in sin;
380
381 s = socket(PF_INET, type, 0);
382 assert(s != -1);
383
384 #if 0
385 unsigned int ifidx;
386 ifidx = if_nametoindex(ifname1); // xxx
387 assert(ifidx != 0);
388 error = setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifidx, sizeof(ifidx));
389 SKTC_ASSERT_ERR(!error);
390 #endif
391
392 memset(&sin, 0, sizeof(sin));
393 sin.sin_len = sizeof(sin);
394 sin.sin_family = AF_INET;
395 sin.sin_addr.s_addr = htonl(addr);
396
397 error = bind(s, (struct sockaddr *)&sin, sizeof(sin));
398 SKTC_ASSERT_ERR(!error);
399
400 inet_ntop(sin.sin_family, &sin.sin_addr.s_addr, sbuf, sizeof(sbuf));
401 T_LOG("%s socket %d bound to %s port %d\n",
402 type == SOCK_DGRAM ? "udp" : "tcp",
403 s, sbuf, ntohs(sin.sin_port));
404
405 return s;
406 }
407
408 static void
connectsocks(int s1,int s2,bool block)409 connectsocks(int s1, int s2, bool block)
410 {
411 int error;
412 struct sockaddr_in sin;
413 socklen_t slen;
414 char sbuf[INET6_ADDRSTRLEN];
415 bool oblock;
416
417 slen = sizeof(sin);
418 error = getsockname(s2, (struct sockaddr *)&sin, &slen);
419 SKTC_ASSERT_ERR(!error);
420 assert(slen <= sizeof(sin));
421
422 oblock = setblocking(s1, block);
423
424 inet_ntop(sin.sin_family, &sin.sin_addr.s_addr, sbuf, sizeof(sbuf));
425 T_LOG("socket %d attempting to connect to %s port %d\n", s1, sbuf, ntohs(sin.sin_port));
426
427 error = connect(s1, (struct sockaddr *)&sin, slen);
428 if (block) {
429 SKTC_ASSERT_ERR(!error);
430 } else {
431 if (error == -1 && (errno == ENETDOWN || errno == EHOSTUNREACH)) {
432 SKT_LOG("socket %d waiting 1 second for net to come up (errno %d)\n",
433 s1, errno);
434 sleep(1);
435 error = connect(s1, (struct sockaddr *)&sin, slen);
436 }
437 SKTC_ASSERT_ERR(error == -1);
438 SKTC_ASSERT_ERR(errno == EINPROGRESS);
439 }
440
441 setblocking(s1, oblock);
442
443 inet_ntop(AF_INET, &sin.sin_addr.s_addr, sbuf, sizeof(sbuf));
444 T_LOG("socket %d connect%s to %s port %d\n", s1,
445 block ? "ed" : "ing", sbuf, ntohs(sin.sin_port));
446 }
447
448 static int
acceptsock(int s)449 acceptsock(int s)
450 {
451 int error;
452 struct sockaddr_in sin;
453 socklen_t slen;
454 char sbuf[INET6_ADDRSTRLEN];
455
456 slen = sizeof(sin);
457 error = accept(s, (struct sockaddr *)&sin, &slen);
458 SKTC_ASSERT_ERR(error >= 0);
459
460 inet_ntop(AF_INET, &sin.sin_addr.s_addr, sbuf, sizeof(sbuf));
461 T_LOG("tcp socket %d accepted connection from %s port %d\n", error, sbuf, ntohs(sin.sin_port));
462
463 return error;
464 }
465
466 #if __LP64__
467 #define UDPXFER 100000 /* 100k */
468 #define UDPLOSSOK 3000 /* 3% */
469 #define UDPXFER_MEMFAIL 30000 /* 30k */
470 #define UDPLOSSOK_MEMFAIL 9000 /* 30% */
471 #define UDPPACE 100003 /* 100us (prime) */
472 #else
473 /* On 32 bit platforms, only try to xfer 10k slots */
474 #define UDPXFER 10000 /* 10k */
475 #define UDPLOSSOK 300 /* 3% */
476 #define UDPXFER_MEMFAIL 3000 /* 30k */
477 #define UDPLOSSOK_MEMFAIL 900 /* 30% */
478 #define UDPPACE 150001 /* 150us (prime) */
479 #endif
480 #define UDPSIZE 1000
481
482 static uint32_t udpxfer;
483 static uint32_t udplossok;
484
485 static void *
sinkudp(void * sockfd)486 sinkudp(void *sockfd)
487 {
488 int s = *(int *)sockfd;
489 ssize_t len;
490 char buf[UDPSIZE];
491 char threadname[20];
492 int missed = 0;
493 int readcount = 0;
494 int i;
495 struct timeval prevtime;
496
497 gettimeofday(&prevtime, NULL);
498
499 snprintf(threadname, sizeof(threadname), "%s%d", __func__, s);
500 pthread_setname_np(threadname);
501
502 assert(udpxfer != 0);
503
504 for (i = 0; i < udpxfer; i++) {
505 len = read(s, buf, sizeof(buf));
506 VLOG("read %zd/%zd", len, sizeof(buf));
507 if (len != sizeof(buf)) {
508 SKT_LOG("%s read returned %zd errno %d count %d/%d\n",
509 threadname, len, errno, i, udpxfer);
510 if (len == -1 && errno == EBADF) {
511 goto out;
512 }
513 }
514 readcount++;
515 if (memcmp(buf, &i, sizeof(i))) {
516 int tmp;
517 memcpy(&tmp, buf, sizeof(tmp));
518 if (tmp < i) {
519 T_LOG("%s out of order expecting %d got %d\n",
520 threadname, i, tmp);
521 }
522 assert(tmp > i); // out of order will crash
523 missed += tmp - i;
524 i = tmp; // skip missing packets
525 }
526 assert(len == sizeof(buf));
527 }
528
529 out:
530 T_LOG("%s received %d packets, missed %d, i = %d\n",
531 threadname, readcount, missed, i);
532 assert(missed <= udplossok);
533 assert(readcount >= udpxfer - udplossok);
534
535 return NULL;
536 }
537
538 static void *
sourceudp(void * sockfd)539 sourceudp(void *sockfd)
540 {
541 int s = *(int *)sockfd;
542 ssize_t len;
543 char buf[UDPSIZE];
544 char threadname[20];
545 int error;
546 int kq;
547 struct kevent kev;
548 struct timeval prevtime;
549
550 gettimeofday(&prevtime, NULL);
551
552 snprintf(threadname, sizeof(threadname), "%s%d", __func__, s);
553 pthread_setname_np(threadname);
554
555 kq = kqueue();
556 EV_SET(&kev, s, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL);
557 error = kevent(kq, &kev, 1, NULL, 0, NULL);
558 SKTC_ASSERT_ERR(!error);
559
560 assert(udpxfer != 0);
561
562 uint64_t totalloop = 0;
563 uint32_t loops = 0;
564 for (int i = 0; i < udpxfer; i++) {
565 uint32_t loopcnt = 0;
566 struct timespec ts;
567
568 memcpy(buf, &i, sizeof(i));
569
570 do {
571 len = write(s, buf, sizeof(buf));
572 VLOG("wrote %zd/%zd", len, sizeof(buf));
573
574 /* If the very first write gets ENETDOWN, wait 1 second */
575 if (i == 0 && loopcnt == 0 &&
576 len == -1 && (errno == ENETDOWN || errno == EHOSTUNREACH)) {
577 SKT_LOG("%s waiting 1 second for net to come up (errno %d)\n",
578 threadname, errno);
579 sleep(1);
580 len = write(s, buf, sizeof(buf));
581 VLOG("wrote %zd/%zd", len, sizeof(buf));
582 }
583
584 /* Wait for buffers to be available */
585 if (len == -1 && errno == ENOBUFS) {
586 loopcnt++;
587 //T_LOG("%s waiting in kevent for buffers\n", threadname);
588 error = kevent(kq, NULL, 0, &kev, 1, NULL);
589 SKTC_ASSERT_ERR(error == 1);
590 assert(kev.filter == EVFILT_WRITE);
591 assert(kev.ident == s);
592 assert(kev.udata == NULL);
593 assert((kev.flags & EV_ERROR) == 0);
594 } else {
595 if (len != sizeof(buf)) {
596 SKT_LOG("%s write returned %zd errno %d count %d/%d\n",
597 threadname, len, errno, i, udpxfer);
598 }
599 assert(len == sizeof(buf));
600 }
601
602 if (loopcnt > 1) {
603 /* if we got ENOBUFS more than once, then sleep
604 * to avoid tight looping on write
605 */
606 ts.tv_sec = 0;
607 ts.tv_nsec = 1000003; // 1ms (prime)
608 nanosleep(&ts, NULL);
609 } else {
610 ts.tv_sec = 0;
611 ts.tv_nsec = UDPPACE;
612 nanosleep(&ts, NULL);
613 }
614
615 /* If we're starved for a full five seconds, crash */
616 if (loopcnt >= 5000) {
617 T_LOG("loopcount %d\n", loopcnt);
618 }
619 assert(loopcnt < 5000);
620 } while (len != sizeof(buf));
621
622 /* Ideally we wouldn't get ENOBUFS immediately after getting
623 * a writable kevent. However, these are coming from nx_netif_host
624 * when ms_classq_mbuf_to_kpkt can't allocate a packet. In this
625 * case, flow control doesn't apply, so just tally the occurances.
626 */
627 if (loopcnt > 1) {
628 loops++;
629 totalloop += loopcnt - 1;
630 //T_LOG("%s spun in kevent %d times\n", threadname, loopcnt);
631 }
632 }
633
634 error = close(kq);
635 SKTC_ASSERT_ERR(!error);
636
637 T_LOG("%s wrote %d packets, looped %u times (avg %f) exiting\n",
638 threadname, udpxfer, loops, (double)totalloop / loops);
639
640 return NULL;
641 }
642
643 #if __LP64__
644 #define TCPXFER 100000000 /* 100mb */
645 #define TCPXFER_MEMFAIL 5000000 /* 5mb */
646 #else
647 #define TCPXFER 10000000 /* 10mb */
648 #define TCPXFER_MEMFAIL 500000 /* 0.5mb */
649 #endif
650
651 static uint32_t tcpxfer;
652
653 static void *
sinktcp(void * sockfd)654 sinktcp(void *sockfd)
655 {
656 int s = *(int *)sockfd;
657 ssize_t len;
658 char *buf;
659 int buflen;
660 socklen_t optlen;
661 char threadname[20];
662 int error;
663 size_t nxfer;
664 struct timeval prevtime;
665
666 gettimeofday(&prevtime, NULL);
667
668 snprintf(threadname, sizeof(threadname), "%s%d", __func__, s);
669 pthread_setname_np(threadname);
670
671 optlen = sizeof(buflen);
672 error = getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buflen, &optlen);
673 SKTC_ASSERT_ERR(!error);
674
675 T_LOG("%s fd %d rcvbuf size %d\n", threadname, s, buflen);
676
677 buf = calloc(buflen, 1);
678 assert(buf);
679
680 assert(tcpxfer != 0);
681
682 nxfer = 0;
683 while (nxfer < tcpxfer) {
684 size_t thisxfer = MIN(tcpxfer - nxfer, buflen);
685 len = read(s, buf, thisxfer);
686 VLOG("read %zd/%zd", len, thisxfer);
687 //T_LOG("%s fd %d read of %zu returned %zd\n", threadname, s, thisxfer, len);
688 error = len;
689 SKTC_ASSERT_ERR(error != -1);
690 SKTC_ASSERT_ERR(error > 0);
691 SKTC_ASSERT_ERR(error <= thisxfer);
692 nxfer += len;
693 }
694
695 assert(nxfer == tcpxfer);
696
697 free(buf);
698
699 T_LOG("%s read %zu bytes exiting\n",
700 threadname, nxfer);
701
702 return NULL;
703 }
704
705
706 static void *
sourcetcp(void * sockfd)707 sourcetcp(void *sockfd)
708 {
709 int s = *(int *)sockfd;
710 ssize_t len;
711 char *buf;
712 int buflen;
713 socklen_t optlen;
714 char threadname[20];
715 int error;
716 size_t nxfer;
717 struct timeval prevtime;
718
719 gettimeofday(&prevtime, NULL);
720
721 snprintf(threadname, sizeof(threadname), "%s%d", __func__, s);
722 pthread_setname_np(threadname);
723
724 optlen = sizeof(buflen);
725 error = getsockopt(s, SOL_SOCKET, SO_SNDBUF, &buflen, &optlen);
726 SKTC_ASSERT_ERR(!error);
727
728 T_LOG("%s fd %d sndbuf size %d\n", threadname, s, buflen);
729
730 buf = calloc(buflen, 1);
731 assert(buf);
732
733 assert(tcpxfer != 0);
734
735 nxfer = 0;
736 while (nxfer < tcpxfer) {
737 size_t thisxfer = MIN(tcpxfer - nxfer, buflen);
738 len = write(s, buf, thisxfer);
739 VLOG("wrote %zd/%zd", len, thisxfer);
740
741 /* If the very first write gets ENETDOWN, wait 1 second */
742 if (nxfer == 0 && len == -1 &&
743 (errno == ENETDOWN || errno == EHOSTUNREACH)) {
744 SKT_LOG("%s waiting 1 second for net to come up (errno %d)\n",
745 threadname, errno);
746 sleep(1);
747 len = write(s, buf, thisxfer);
748 VLOG("wrote %zd/%zd", len, thisxfer);
749 }
750
751 //T_LOG("%s fd %d write of %zu returned %zd\n", threadname, s, thisxfer, len);
752 error = len;
753 SKTC_ASSERT_ERR(error == thisxfer);
754 nxfer += len;
755 }
756
757 assert(nxfer == tcpxfer);
758
759 free(buf);
760
761 T_LOG("%s wrote %zu bytes exiting\n",
762 threadname, nxfer);
763
764 return NULL;
765 }
766
767 static void
dotraffic(void * (* sourcefunc)(void *),void * (* sinkfunc)(void *),int sourcesock1,int sinksock2,int sourcesock2,int sinksock1)768 dotraffic(void *(*sourcefunc)(void *), void *(*sinkfunc)(void *),
769 int sourcesock1, int sinksock2, int sourcesock2, int sinksock1)
770 {
771 int error;
772 pthread_t sinkthread1, sinkthread2;
773 pthread_t sourcethread1, sourcethread2;
774
775 assert(sourcesock1 != -1);
776 assert(sinksock2 != -1);
777 assert((sourcesock2 == -1) == (sinksock1 == -1));
778
779 if (sinksock1 != -1) {
780 error = pthread_create(&sinkthread1, NULL, sinkfunc, &sinksock1);
781 SKTC_ASSERT_ERR(!error);
782 }
783 error = pthread_create(&sinkthread2, NULL, sinkfunc, &sinksock2);
784 SKTC_ASSERT_ERR(!error);
785 error = pthread_create(&sourcethread1, NULL, sourcefunc, &sourcesock1);
786 SKTC_ASSERT_ERR(!error);
787 if (sourcesock2 != -1) {
788 T_LOG("waiting 1 second before starting reverse traffic\n");
789 sleep(1);
790 error = pthread_create(&sourcethread2, NULL, sourcefunc, &sourcesock2);
791 SKTC_ASSERT_ERR(!error);
792 }
793
794 /* Wait for all data to be sent */
795 error = pthread_join(sourcethread1, NULL);
796 SKTC_ASSERT_ERR(!error);
797 if (sourcesock2 != -1) {
798 error = pthread_join(sourcethread2, NULL);
799 SKTC_ASSERT_ERR(!error);
800 }
801
802 /* Give it 1 second to drain */
803 T_LOG("waiting 1 second for reads to drain\n");
804 sleep(1);
805
806 /* Force the reads to exit by closing sockets */
807 if (sinksock1 != -1) {
808 T_LOG("closing sinksock1 %d\n", sinksock1);
809 error = close(sinksock1);
810 SKTC_ASSERT_ERR(!error);
811 }
812 T_LOG("closing sinksock2 %d\n", sinksock2);
813 error = close(sinksock2);
814 SKTC_ASSERT_ERR(!error);
815
816 if (sinksock1 != -1) {
817 error = pthread_join(sinkthread1, NULL);
818 SKTC_ASSERT_ERR(!error);
819 }
820 error = pthread_join(sinkthread2, NULL);
821 SKTC_ASSERT_ERR(!error);
822
823 if (sourcesock1 != sinksock1) {
824 T_LOG("closing sourcesock1 %d\n", sourcesock1);
825 error = close(sourcesock1);
826 SKTC_ASSERT_ERR(!error);
827 }
828 if (sourcesock2 != sinksock2 && sourcesock2 != -1) {
829 T_LOG("closing sourcesock2 %d\n", sourcesock2);
830 error = close(sourcesock2);
831 SKTC_ASSERT_ERR(!error);
832 }
833 }
834
835
836 static void
skt_tunloop_common(bool doutun,bool enable_netif,bool udp,bool udpduplex,bool tcp,bool tcpduplex,bool dualstream)837 skt_tunloop_common(bool doutun, bool enable_netif, bool udp, bool udpduplex, bool tcp, bool tcpduplex, bool dualstream)
838 {
839 int error;
840 int utun1, utun2;
841 char ifname1[IFNAMSIZ];
842 char ifname2[IFNAMSIZ];
843 pthread_t thread1, thread2;
844 struct kevent kev;
845 uint32_t memfail = 0;
846 size_t len;
847 int keysock;
848
849 len = sizeof(memfail);
850 if (sysctlbyname("kern.skywalk.mem.region_mtbf", &memfail, &len,
851 NULL, 0) != 0) {
852 SKT_LOG("warning got errno %d getting "
853 "kern.skywalk.mem.region_mtbf: %s\n", errno,
854 strerror(errno));
855 }
856
857 if (memfail) {
858 udpxfer = UDPXFER_MEMFAIL;
859 udplossok = UDPLOSSOK_MEMFAIL;
860 tcpxfer = TCPXFER_MEMFAIL;
861 } else {
862 udpxfer = UDPXFER;
863 udplossok = UDPLOSSOK;
864 tcpxfer = TCPXFER;
865 }
866
867 g_dumpfile = fopen(getenv("SKT_UTUNLOOP_DUMPFILE"), "w");
868 if (g_dumpfile) {
869 g_verbose = 1;
870 }
871
872 sktu_if_type_t type = doutun ? SKTU_IFT_UTUN : SKTU_IFT_IPSEC;
873 sktu_if_flag_t flags = enable_netif ? SKTU_IFF_ENABLE_NETIF : 0;
874 utun1 = sktu_create_interface(type, flags);
875 utun2 = sktu_create_interface(type, flags);
876
877 sktu_get_interface_name(type, utun1, ifname1);
878 sktu_get_interface_name(type, utun2, ifname2);
879 snprintf(debugcmd, sizeof(debugcmd), "netstat -qq -I %s > netstatqq.%s.txt; netstat -qq -I %s > netstatqq.%s.txt; skywalkctl netstat --flowswitch --netif > skywalkctl.txt",
880 ifname1, ifname1, ifname2, ifname2);
881
882 uint32_t utun1addr = (10 << 24) | (getpid() & 0xffff) << 8 | 150;
883 uint32_t utun2addr = utun1addr + 1;
884
885 struct in_addr addr1, addr2, mask;
886 mask = sktc_make_in_addr(0xffffffff);
887 addr1 = sktc_make_in_addr(utun1addr);
888 addr2 = sktc_make_in_addr(utun2addr);
889
890 error = sktc_ifnet_add_addr(ifname1, &addr1, &mask, &addr2);
891 SKTC_ASSERT_ERR(!error);
892 error = sktc_ifnet_add_addr(ifname2, &addr2, &mask, &addr1);
893 SKTC_ASSERT_ERR(!error);
894
895 if (!doutun) {
896 keysock = sktu_create_pfkeysock();
897 sktu_create_sa(keysock, ifname1, 12345, &addr1, &addr2);
898 sktu_create_sa(keysock, ifname1, 12346, &addr2, &addr1);
899 sktu_create_sa(keysock, ifname2, 12345, &addr2, &addr1);
900 sktu_create_sa(keysock, ifname2, 12346, &addr1, &addr2);
901 }
902
903 g_channel1 = sktu_create_interface_channel(type, utun1);
904 g_channel2 = sktu_create_interface_channel(type, utun2);
905
906 T_LOG("Created %s and %s\n", ifname1, ifname2);
907
908 g_kq1 = kqueue();
909 EV_SET(&kev, (uintptr_t)&g_die, EVFILT_USER,
910 EV_ADD | EV_ENABLE, 0, 0, NULL);
911 error = kevent(g_kq1, &kev, 1, NULL, 0, NULL);
912 SKTC_ASSERT_ERR(!error);
913
914 g_kq2 = kqueue();
915 EV_SET(&kev, (uintptr_t)&g_die, EVFILT_USER,
916 EV_ADD | EV_ENABLE, 0, 0, NULL);
917 error = kevent(g_kq2, &kev, 1, NULL, 0, NULL);
918 SKTC_ASSERT_ERR(!error);
919
920 // T_LOG("Sleeping 10 seconds at startup\n");
921 // sleep(10);
922
923 error = pthread_create(&thread1, NULL, sktc_channel_worker_xfer12, NULL);
924 SKTC_ASSERT_ERR(!error);
925 error = pthread_create(&thread2, NULL, sktc_channel_worker_xfer21, NULL);
926 SKTC_ASSERT_ERR(!error);
927
928 if (udp) {
929 int usock1, usock2;
930 usock1 = makesocket(SOCK_DGRAM, utun1addr);
931 usock2 = makesocket(SOCK_DGRAM, utun2addr);
932 connectsocks(usock1, usock2, true);
933 connectsocks(usock2, usock1, true);
934 if (udpduplex) {
935 if (dualstream) {
936 int usock3, usock4;
937 usock3 = makesocket(SOCK_DGRAM, utun2addr);
938 usock4 = makesocket(SOCK_DGRAM, utun1addr);
939 connectsocks(usock3, usock4, true);
940 connectsocks(usock4, usock3, true);
941 dotraffic(sourceudp, sinkudp, usock1, usock2, usock3, usock4);
942 } else {
943 dotraffic(sourceudp, sinkudp, usock1, usock2, usock2, usock1);
944 }
945 } else {
946 dotraffic(sourceudp, sinkudp, usock1, usock2, -1, -1);
947 }
948 }
949
950 if (tcp) {
951 int tsock1, tsock2, lsock; // listening socket
952 tsock1 = makesocket(SOCK_STREAM, utun1addr);
953 lsock = makesocket(SOCK_STREAM, utun2addr);
954 error = listen(lsock, 1);
955 SKTC_ASSERT_ERR(!error);
956 connectsocks(tsock1, lsock, false);
957 tsock2 = acceptsock(lsock);
958 error = close(lsock);
959 SKTC_ASSERT_ERR(!error);
960 if (tcpduplex) {
961 if (dualstream) {
962 int tsock3, tsock4;
963 tsock3 = makesocket(SOCK_STREAM, utun2addr);
964 lsock = makesocket(SOCK_STREAM, utun1addr);
965 error = listen(lsock, 1);
966 SKTC_ASSERT_ERR(!error);
967 connectsocks(tsock3, lsock, false);
968 tsock4 = acceptsock(lsock);
969 error = close(lsock);
970 SKTC_ASSERT_ERR(!error);
971 dotraffic(sourcetcp, sinktcp, tsock1, tsock2, tsock3, tsock4);
972 } else {
973 dotraffic(sourcetcp, sinktcp, tsock1, tsock2, tsock2, tsock1);
974 }
975 } else {
976 dotraffic(sourcetcp, sinktcp, tsock1, tsock2, -1, -1);
977 }
978 }
979
980 /* This can be useful for just setting up two utuns */
981 if (!udp && !tcp) {
982 sleep(1000);
983 }
984
985 /* Tell utun threads to exit */
986 g_die = true;
987 EV_SET(&kev, (uintptr_t)&g_die, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
988 error = kevent(g_kq1, &kev, 1, NULL, 0, NULL);
989 SKTC_ASSERT_ERR(!error);
990 EV_SET(&kev, (uintptr_t)&g_die, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
991 error = kevent(g_kq2, &kev, 1, NULL, 0, NULL);
992 SKTC_ASSERT_ERR(!error);
993
994 error = pthread_join(thread1, NULL);
995 SKTC_ASSERT_ERR(!error);
996 error = pthread_join(thread2, NULL);
997 SKTC_ASSERT_ERR(!error);
998
999 os_channel_destroy(g_channel1);
1000 os_channel_destroy(g_channel2);
1001
1002 if (!doutun) {
1003 error = close(keysock);
1004 SKTC_ASSERT_ERR(!error);
1005 }
1006 error = close(utun1);
1007 SKTC_ASSERT_ERR(!error);
1008 error = close(utun2);
1009 SKTC_ASSERT_ERR(!error);
1010 }
1011
1012 /****************************************************************/
1013
1014 static int
skt_utunloopn4u1_main(int argc,char * argv[])1015 skt_utunloopn4u1_main(int argc, char *argv[])
1016 {
1017 g_assert_stalls12 = true;
1018 skt_tunloop_common(true, false, true, false, false, false, false);
1019 return 0;
1020 }
1021
1022 static int
skt_utunloopn4u2_main(int argc,char * argv[])1023 skt_utunloopn4u2_main(int argc, char *argv[])
1024 {
1025 g_assert_stalls12 = true;
1026 g_assert_stalls21 = true;
1027 skt_tunloop_common(true, false, true, true, false, false, false);
1028 return 0;
1029 }
1030
1031 static int
skt_utunloopn4t1_main(int argc,char * argv[])1032 skt_utunloopn4t1_main(int argc, char *argv[])
1033 {
1034 g_assert_stalls12 = true;
1035 skt_tunloop_common(true, false, false, false, true, false, false);
1036 return 0;
1037 }
1038
1039 static int
skt_utunloopn4t2_main(int argc,char * argv[])1040 skt_utunloopn4t2_main(int argc, char *argv[])
1041 {
1042 g_assert_stalls12 = true;
1043 g_assert_stalls21 = true;
1044 skt_tunloop_common(true, false, false, false, true, true, false);
1045 return 0;
1046 }
1047
1048 static int
skt_utunloopy4u1_main(int argc,char * argv[])1049 skt_utunloopy4u1_main(int argc, char *argv[])
1050 {
1051 g_assert_stalls12 = true;
1052 skt_tunloop_common(true, true, true, false, false, false, false);
1053 return 0;
1054 }
1055
1056 static int
skt_utunloopy4u2_main(int argc,char * argv[])1057 skt_utunloopy4u2_main(int argc, char *argv[])
1058 {
1059 g_assert_stalls12 = true;
1060 g_assert_stalls21 = true;
1061 skt_tunloop_common(true, true, true, true, false, false, false);
1062 return 0;
1063 }
1064
1065 static int
skt_utunloopy4t1_main(int argc,char * argv[])1066 skt_utunloopy4t1_main(int argc, char *argv[])
1067 {
1068 g_assert_stalls12 = true;
1069 skt_tunloop_common(true, true, false, false, true, false, false);
1070 return 0;
1071 }
1072
1073 static int
skt_utunloopy4t2_main(int argc,char * argv[])1074 skt_utunloopy4t2_main(int argc, char *argv[])
1075 {
1076 g_assert_stalls12 = true;
1077 g_assert_stalls21 = true;
1078 skt_tunloop_common(true, true, false, false, true, true, false);
1079 return 0;
1080 }
1081
1082 static int
skt_utunloopn1000_main(int argc,char * argv[])1083 skt_utunloopn1000_main(int argc, char *argv[])
1084 {
1085 skt_tunloop_common(true, false, false, false, false, false, false);
1086 return 0;
1087 }
1088
1089 static int
skt_utunloopy1000_main(int argc,char * argv[])1090 skt_utunloopy1000_main(int argc, char *argv[])
1091 {
1092 skt_tunloop_common(true, true, false, false, false, false, false);
1093 return 0;
1094 }
1095
1096 struct skywalk_test skt_utunloopn4u1 = {
1097 "utunloopn4u1", "open 2 utuns without netif and floods ipv4 udp packets in one direction",
1098 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1099 skt_utunloopn4u1_main,
1100 };
1101
1102 struct skywalk_test skt_utunloopn4u2 = {
1103 "utunloopn4u2", "open 2 utuns without netif and floods ipv4 udp packets in two directions",
1104 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1105 skt_utunloopn4u2_main,
1106 };
1107
1108 struct skywalk_test skt_utunloopn4t1 = {
1109 "utunloopn4t1", "open 2 utuns without netif and floods ipv4 tcp packets in one direction",
1110 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1111 skt_utunloopn4t1_main,
1112 };
1113
1114 struct skywalk_test skt_utunloopn4t2 = {
1115 "utunloopn4t2", "open 2 utuns without netif and floods ipv4 tcp packets in two directions",
1116 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1117 skt_utunloopn4t2_main,
1118 };
1119
1120 struct skywalk_test skt_utunloopy4u1 = {
1121 "utunloopy4u1", "open 2 utuns with netif and floods ipv4 udp packets in one direction",
1122 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1123 skt_utunloopy4u1_main,
1124 };
1125
1126 struct skywalk_test skt_utunloopy4u2 = {
1127 "utunloopy4u2", "open 2 utuns with netif and floods ipv4 udp packets in two directions",
1128 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1129 skt_utunloopy4u2_main,
1130 };
1131
1132 struct skywalk_test skt_utunloopy4t1 = {
1133 "utunloopy4t1", "open 2 utuns with netif and floods ipv4 tcp packets in one direction",
1134 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1135 skt_utunloopy4t1_main,
1136 };
1137
1138 struct skywalk_test skt_utunloopy4t2 = {
1139 "utunloopy4t2", "open 2 utuns with netif and floods ipv4 tcp packets in two directions",
1140 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1141 skt_utunloopy4t2_main,
1142 };
1143
1144 struct skywalk_test skt_utunloopn1000 = {
1145 "utunloopn1000", "open 2 utuns without netif and sleeps for 1000 seconds",
1146 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1147 skt_utunloopn1000_main,
1148 };
1149
1150 struct skywalk_test skt_utunloopy1000 = {
1151 "utunloopy1000", "open 2 utuns with netif and sleeps for 1000 seconds",
1152 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1153 skt_utunloopy1000_main,
1154 };
1155
1156 /****************************************************************/
1157
1158 static int
skt_ipsecloopy4u1_main(int argc,char * argv[])1159 skt_ipsecloopy4u1_main(int argc, char *argv[])
1160 {
1161 g_assert_stalls12 = true;
1162 skt_tunloop_common(false, true, true, false, false, false, false);
1163 return 0;
1164 }
1165
1166 static int
skt_ipsecloopy4u2_main(int argc,char * argv[])1167 skt_ipsecloopy4u2_main(int argc, char *argv[])
1168 {
1169 g_assert_stalls12 = true;
1170 g_assert_stalls21 = true;
1171 skt_tunloop_common(false, true, true, true, false, false, false);
1172 return 0;
1173 }
1174
1175 static int
skt_ipsecloopy4t1_main(int argc,char * argv[])1176 skt_ipsecloopy4t1_main(int argc, char *argv[])
1177 {
1178 g_assert_stalls12 = true;
1179 skt_tunloop_common(false, true, false, false, true, false, false);
1180 return 0;
1181 }
1182
1183 static int
skt_ipsecloopy4t2_main(int argc,char * argv[])1184 skt_ipsecloopy4t2_main(int argc, char *argv[])
1185 {
1186 g_assert_stalls12 = true;
1187 g_assert_stalls21 = true;
1188 skt_tunloop_common(false, true, false, false, true, true, false);
1189 return 0;
1190 }
1191
1192 static int
skt_ipsecloopy1000_main(int argc,char * argv[])1193 skt_ipsecloopy1000_main(int argc, char *argv[])
1194 {
1195 skt_tunloop_common(false, true, false, false, false, false, false);
1196 return 0;
1197 }
1198
1199 struct skywalk_test skt_ipsecloopy4u1 = {
1200 "ipsecloopy4u1", "open 2 ipsecs with netif and floods ipv4 udp packets in one direction",
1201 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1202 skt_ipsecloopy4u1_main,
1203 };
1204
1205 struct skywalk_test skt_ipsecloopy4u2 = {
1206 "ipsecloopy4u2", "open 2 ipsecs with netif and floods ipv4 udp packets in two directions",
1207 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1208 skt_ipsecloopy4u2_main,
1209 };
1210
1211 struct skywalk_test skt_ipsecloopy4t1 = {
1212 "ipsecloopy4t1", "open 2 ipsecs with netif and floods ipv4 tcp packets in one direction",
1213 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1214 skt_ipsecloopy4t1_main,
1215 };
1216
1217 struct skywalk_test skt_ipsecloopy4t2 = {
1218 "ipsecloopy4t2", "open 2 ipsecs with netif and floods ipv4 tcp packets in two directions",
1219 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1220 skt_ipsecloopy4t2_main,
1221 };
1222
1223 struct skywalk_test skt_ipsecloopy1000 = {
1224 "ipsecloopy1000", "open 2 ipsecs with netif and sleeps for 1000 seconds",
1225 SK_FEATURE_SKYWALK | SK_FEATURE_NEXUS_KERNEL_PIPE,
1226 skt_ipsecloopy1000_main,
1227 };
1228
1229 /****************************************************************/
1230