1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2017-2018 Covalent IO, Inc. http://covalent.io
5 #include <sys/socket.h>
7 #include <sys/select.h>
8 #include <netinet/in.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <sys/sendfile.h>
25 #include <linux/netlink.h>
26 #include <linux/socket.h>
27 #include <linux/sock_diag.h>
28 #include <linux/bpf.h>
29 #include <linux/if_link.h>
30 #include <linux/tls.h>
37 #include <bpf/libbpf.h>
40 #include "bpf_rlimit.h"
41 #include "cgroup_helpers.h"
44 static void running_handler(int a
);
53 /* randomly selected ports for testing on lo */
57 #define BPF_SOCKMAP_FILENAME "test_sockmap_kern.o"
58 #define BPF_SOCKHASH_FILENAME "test_sockhash_kern.o"
59 #define CG_PATH "/sockmap"
62 int s1
, s2
, c1
, c2
, p1
, p2
;
67 struct bpf_map
*maps
[8];
73 int txmsg_redir_noisy
;
88 static const struct option long_options
[] = {
89 {"help", no_argument
, NULL
, 'h' },
90 {"cgroup", required_argument
, NULL
, 'c' },
91 {"rate", required_argument
, NULL
, 'r' },
92 {"verbose", no_argument
, NULL
, 'v' },
93 {"iov_count", required_argument
, NULL
, 'i' },
94 {"length", required_argument
, NULL
, 'l' },
95 {"test", required_argument
, NULL
, 't' },
96 {"data_test", no_argument
, NULL
, 'd' },
97 {"txmsg", no_argument
, &txmsg_pass
, 1 },
98 {"txmsg_noisy", no_argument
, &txmsg_noisy
, 1 },
99 {"txmsg_redir", no_argument
, &txmsg_redir
, 1 },
100 {"txmsg_redir_noisy", no_argument
, &txmsg_redir_noisy
, 1},
101 {"txmsg_drop", no_argument
, &txmsg_drop
, 1 },
102 {"txmsg_apply", required_argument
, NULL
, 'a'},
103 {"txmsg_cork", required_argument
, NULL
, 'k'},
104 {"txmsg_start", required_argument
, NULL
, 's'},
105 {"txmsg_end", required_argument
, NULL
, 'e'},
106 {"txmsg_start_push", required_argument
, NULL
, 'p'},
107 {"txmsg_end_push", required_argument
, NULL
, 'q'},
108 {"txmsg_start_pop", required_argument
, NULL
, 'w'},
109 {"txmsg_pop", required_argument
, NULL
, 'x'},
110 {"txmsg_ingress", no_argument
, &txmsg_ingress
, 1 },
111 {"txmsg_skb", no_argument
, &txmsg_skb
, 1 },
112 {"ktls", no_argument
, &ktls
, 1 },
113 {"peek", no_argument
, &peek_flag
, 1 },
117 static void usage(char *argv
[])
121 printf(" Usage: %s --cgroup <cgroup_path>\n", argv
[0]);
122 printf(" options:\n");
123 for (i
= 0; long_options
[i
].name
!= 0; i
++) {
124 printf(" --%-12s", long_options
[i
].name
);
125 if (long_options
[i
].flag
!= NULL
)
126 printf(" flag (internal value:%d)\n",
127 *long_options
[i
].flag
);
129 printf(" -%c\n", long_options
[i
].val
);
134 char *sock_to_string(int s
)
152 static int sockmap_init_ktls(int verbose
, int s
)
154 struct tls12_crypto_info_aes_gcm_128 tls_tx
= {
156 .version
= TLS_1_2_VERSION
,
157 .cipher_type
= TLS_CIPHER_AES_GCM_128
,
160 struct tls12_crypto_info_aes_gcm_128 tls_rx
= {
162 .version
= TLS_1_2_VERSION
,
163 .cipher_type
= TLS_CIPHER_AES_GCM_128
,
166 int so_buf
= 6553500;
169 err
= setsockopt(s
, 6, TCP_ULP
, "tls", sizeof("tls"));
171 fprintf(stderr
, "setsockopt: TCP_ULP(%s) failed with error %i\n", sock_to_string(s
), err
);
174 err
= setsockopt(s
, SOL_TLS
, TLS_TX
, (void *)&tls_tx
, sizeof(tls_tx
));
176 fprintf(stderr
, "setsockopt: TLS_TX(%s) failed with error %i\n", sock_to_string(s
), err
);
179 err
= setsockopt(s
, SOL_TLS
, TLS_RX
, (void *)&tls_rx
, sizeof(tls_rx
));
181 fprintf(stderr
, "setsockopt: TLS_RX(%s) failed with error %i\n", sock_to_string(s
), err
);
184 err
= setsockopt(s
, SOL_SOCKET
, SO_SNDBUF
, &so_buf
, sizeof(so_buf
));
186 fprintf(stderr
, "setsockopt: (%s) failed sndbuf with error %i\n", sock_to_string(s
), err
);
189 err
= setsockopt(s
, SOL_SOCKET
, SO_RCVBUF
, &so_buf
, sizeof(so_buf
));
191 fprintf(stderr
, "setsockopt: (%s) failed rcvbuf with error %i\n", sock_to_string(s
), err
);
196 fprintf(stdout
, "socket(%s) kTLS enabled\n", sock_to_string(s
));
199 static int sockmap_init_sockets(int verbose
)
202 struct sockaddr_in addr
;
203 int *fds
[4] = {&s1
, &s2
, &c1
, &c2
};
205 s1
= s2
= p1
= p2
= c1
= c2
= 0;
208 for (i
= 0; i
< 4; i
++) {
209 *fds
[i
] = socket(AF_INET
, SOCK_STREAM
, 0);
211 perror("socket s1 failed()");
217 for (i
= 0; i
< 2; i
++) {
218 err
= setsockopt(*fds
[i
], SOL_SOCKET
, SO_REUSEADDR
,
219 (char *)&one
, sizeof(one
));
221 perror("setsockopt failed()");
226 /* Non-blocking sockets */
227 for (i
= 0; i
< 2; i
++) {
228 err
= ioctl(*fds
[i
], FIONBIO
, (char *)&one
);
230 perror("ioctl s1 failed()");
235 /* Bind server sockets */
236 memset(&addr
, 0, sizeof(struct sockaddr_in
));
237 addr
.sin_family
= AF_INET
;
238 addr
.sin_addr
.s_addr
= inet_addr("127.0.0.1");
240 addr
.sin_port
= htons(S1_PORT
);
241 err
= bind(s1
, (struct sockaddr
*)&addr
, sizeof(addr
));
243 perror("bind s1 failed()");
247 addr
.sin_port
= htons(S2_PORT
);
248 err
= bind(s2
, (struct sockaddr
*)&addr
, sizeof(addr
));
250 perror("bind s2 failed()");
254 /* Listen server sockets */
255 addr
.sin_port
= htons(S1_PORT
);
256 err
= listen(s1
, 32);
258 perror("listen s1 failed()");
262 addr
.sin_port
= htons(S2_PORT
);
263 err
= listen(s2
, 32);
265 perror("listen s1 failed()");
269 /* Initiate Connect */
270 addr
.sin_port
= htons(S1_PORT
);
271 err
= connect(c1
, (struct sockaddr
*)&addr
, sizeof(addr
));
272 if (err
< 0 && errno
!= EINPROGRESS
) {
273 perror("connect c1 failed()");
277 addr
.sin_port
= htons(S2_PORT
);
278 err
= connect(c2
, (struct sockaddr
*)&addr
, sizeof(addr
));
279 if (err
< 0 && errno
!= EINPROGRESS
) {
280 perror("connect c2 failed()");
282 } else if (err
< 0) {
286 /* Accept Connecrtions */
287 p1
= accept(s1
, NULL
, NULL
);
289 perror("accept s1 failed()");
293 p2
= accept(s2
, NULL
, NULL
);
295 perror("accept s1 failed()");
300 printf("connected sockets: c1 <-> p1, c2 <-> p2\n");
301 printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n",
310 struct timespec start
;
314 struct sockmap_options
{
325 static int msg_loop_sendpage(int fd
, int iov_length
, int cnt
,
327 struct sockmap_options
*opt
)
329 bool drop
= opt
->drop_expected
;
336 perror("create file for sendpage");
339 for (i
= 0; i
< iov_length
* cnt
; i
++, k
++)
340 fwrite(&k
, sizeof(char), 1, file
);
342 fseek(file
, 0, SEEK_SET
);
346 clock_gettime(CLOCK_MONOTONIC
, &s
->start
);
347 for (i
= 0; i
< cnt
; i
++) {
348 int sent
= sendfile(fd
, fp
, NULL
, iov_length
);
350 if (!drop
&& sent
< 0) {
351 perror("send loop error");
354 } else if (drop
&& sent
>= 0) {
355 printf("sendpage loop error expected: %i\n", sent
);
361 s
->bytes_sent
+= sent
;
363 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
368 static void msg_free_iov(struct msghdr
*msg
)
372 for (i
= 0; i
< msg
->msg_iovlen
; i
++)
373 free(msg
->msg_iov
[i
].iov_base
);
379 static int msg_alloc_iov(struct msghdr
*msg
,
380 int iov_count
, int iov_length
,
381 bool data
, bool xmit
)
387 iov
= calloc(iov_count
, sizeof(struct iovec
));
391 for (i
= 0; i
< iov_count
; i
++) {
392 unsigned char *d
= calloc(iov_length
, sizeof(char));
395 fprintf(stderr
, "iov_count %i/%i OOM\n", i
, iov_count
);
399 iov
[i
].iov_len
= iov_length
;
404 for (j
= 0; j
< iov_length
; j
++)
410 msg
->msg_iovlen
= iov_count
;
414 for (i
--; i
>= 0 ; i
--)
415 free(msg
->msg_iov
[i
].iov_base
);
419 static int msg_verify_data(struct msghdr
*msg
, int size
, int chunk_sz
)
421 int i
, j
, bytes_cnt
= 0;
424 for (i
= 0; i
< msg
->msg_iovlen
; i
++) {
425 unsigned char *d
= msg
->msg_iov
[i
].iov_base
;
428 j
< msg
->msg_iov
[i
].iov_len
&& size
; j
++) {
431 "detected data corruption @iov[%i]:%i %02x != %02x, %02x ?= %02x\n",
432 i
, j
, d
[j
], k
- 1, d
[j
+1], k
);
436 if (bytes_cnt
== chunk_sz
) {
446 static int msg_loop(int fd
, int iov_count
, int iov_length
, int cnt
,
447 struct msg_stats
*s
, bool tx
,
448 struct sockmap_options
*opt
)
450 struct msghdr msg
= {0}, msg_peek
= {0};
451 int err
, i
, flags
= MSG_NOSIGNAL
;
452 bool drop
= opt
->drop_expected
;
453 bool data
= opt
->data_test
;
455 err
= msg_alloc_iov(&msg
, iov_count
, iov_length
, data
, tx
);
459 err
= msg_alloc_iov(&msg_peek
, iov_count
, iov_length
, data
, tx
);
465 clock_gettime(CLOCK_MONOTONIC
, &s
->start
);
466 for (i
= 0; i
< cnt
; i
++) {
467 int sent
= sendmsg(fd
, &msg
, flags
);
469 if (!drop
&& sent
< 0) {
470 perror("send loop error");
472 } else if (drop
&& sent
>= 0) {
473 printf("send loop error expected: %i\n", sent
);
478 s
->bytes_sent
+= sent
;
480 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
482 int slct
, recvp
= 0, recv
, max_fd
= fd
;
483 float total_bytes
, txmsg_pop_total
;
484 int fd_flags
= O_NONBLOCK
;
485 struct timeval timeout
;
489 /* Account for pop bytes noting each iteration of apply will
490 * call msg_pop_data helper so we need to account for this
491 * by calculating the number of apply iterations. Note user
492 * of the tool can create cases where no data is sent by
493 * manipulating pop/push/pull/etc. For example txmsg_apply 1
494 * with txmsg_pop 1 will try to apply 1B at a time but each
495 * iteration will then pop 1B so no data will ever be sent.
496 * This is really only useful for testing edge cases in code
499 total_bytes
= (float)iov_count
* (float)iov_length
* (float)cnt
;
500 txmsg_pop_total
= txmsg_pop
;
502 txmsg_pop_total
*= (total_bytes
/ txmsg_apply
);
503 total_bytes
-= txmsg_pop_total
;
504 err
= clock_gettime(CLOCK_MONOTONIC
, &s
->start
);
506 perror("recv start time");
507 while (s
->bytes_recvd
< total_bytes
) {
510 timeout
.tv_usec
= 300000;
520 slct
= select(max_fd
+ 1, &w
, NULL
, NULL
, &timeout
);
523 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
527 fprintf(stderr
, "unexpected timeout: recved %zu/%f pop_total %f\n", s
->bytes_recvd
, total_bytes
, txmsg_pop_total
);
529 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
536 recvp
= recvmsg(fd
, &msg_peek
, flags
);
538 if (errno
!= EWOULDBLOCK
) {
539 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
546 recv
= recvmsg(fd
, &msg
, flags
);
548 if (errno
!= EWOULDBLOCK
) {
549 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
550 perror("recv failed()");
555 s
->bytes_recvd
+= recv
;
558 int chunk_sz
= opt
->sendpage
?
560 iov_length
* iov_count
;
562 errno
= msg_verify_data(&msg
, recv
, chunk_sz
);
564 perror("data verify msg failed");
568 errno
= msg_verify_data(&msg_peek
,
572 perror("data verify msg_peek failed");
578 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
582 msg_free_iov(&msg_peek
);
586 msg_free_iov(&msg_peek
);
590 static float giga
= 1000000000;
592 static inline float sentBps(struct msg_stats s
)
594 return s
.bytes_sent
/ (s
.end
.tv_sec
- s
.start
.tv_sec
);
597 static inline float recvdBps(struct msg_stats s
)
599 return s
.bytes_recvd
/ (s
.end
.tv_sec
- s
.start
.tv_sec
);
602 static int sendmsg_test(struct sockmap_options
*opt
)
604 float sent_Bps
= 0, recvd_Bps
= 0;
605 int rx_fd
, txpid
, rxpid
, err
= 0;
606 struct msg_stats s
= {0};
607 int iov_count
= opt
->iov_count
;
608 int iov_buf
= opt
->iov_length
;
609 int rx_status
, tx_status
;
620 /* Redirecting into non-TLS socket which sends into a TLS
621 * socket is not a valid test. So in this case lets not
622 * enable kTLS but still run the test.
624 if (!txmsg_redir
|| (txmsg_redir
&& txmsg_ingress
)) {
625 err
= sockmap_init_ktls(opt
->verbose
, rx_fd
);
629 err
= sockmap_init_ktls(opt
->verbose
, c1
);
636 if (opt
->drop_expected
)
641 err
= msg_loop(rx_fd
, iov_count
, iov_buf
,
642 cnt
, &s
, false, opt
);
645 "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n",
646 iov_count
, iov_buf
, cnt
, err
);
647 if (s
.end
.tv_sec
- s
.start
.tv_sec
) {
648 sent_Bps
= sentBps(s
);
649 recvd_Bps
= recvdBps(s
);
653 "rx_sendmsg: TX: %zuB %fB/s %fGB/s RX: %zuB %fB/s %fGB/s %s\n",
654 s
.bytes_sent
, sent_Bps
, sent_Bps
/giga
,
655 s
.bytes_recvd
, recvd_Bps
, recvd_Bps
/giga
,
656 peek_flag
? "(peek_msg)" : "");
657 if (err
&& txmsg_cork
)
660 } else if (rxpid
== -1) {
661 perror("msg_loop_rx");
668 err
= msg_loop_sendpage(c1
, iov_buf
, cnt
, &s
, opt
);
670 err
= msg_loop(c1
, iov_count
, iov_buf
,
675 "msg_loop_tx: iov_count %i iov_buf %i cnt %i err %i\n",
676 iov_count
, iov_buf
, cnt
, err
);
677 if (s
.end
.tv_sec
- s
.start
.tv_sec
) {
678 sent_Bps
= sentBps(s
);
679 recvd_Bps
= recvdBps(s
);
683 "tx_sendmsg: TX: %zuB %fB/s %f GB/s RX: %zuB %fB/s %fGB/s\n",
684 s
.bytes_sent
, sent_Bps
, sent_Bps
/giga
,
685 s
.bytes_recvd
, recvd_Bps
, recvd_Bps
/giga
);
687 } else if (txpid
== -1) {
688 perror("msg_loop_tx");
692 assert(waitpid(rxpid
, &rx_status
, 0) == rxpid
);
693 assert(waitpid(txpid
, &tx_status
, 0) == txpid
);
694 if (WIFEXITED(rx_status
)) {
695 err
= WEXITSTATUS(rx_status
);
697 fprintf(stderr
, "rx thread exited with err %d. ", err
);
701 if (WIFEXITED(tx_status
)) {
702 err
= WEXITSTATUS(tx_status
);
704 fprintf(stderr
, "tx thread exited with err %d. ", err
);
710 static int forever_ping_pong(int rate
, struct sockmap_options
*opt
)
712 struct timeval timeout
;
713 char buf
[1024] = {0};
719 /* Ping/Pong data from client to server */
720 sc
= send(c1
, buf
, sizeof(buf
), 0);
722 perror("send failed()");
727 int s
, rc
, i
, max_fd
= p2
;
737 s
= select(max_fd
+ 1, &w
, NULL
, NULL
, &timeout
);
742 fprintf(stderr
, "unexpected timeout\n");
746 for (i
= 0; i
<= max_fd
&& s
> 0; ++i
) {
747 if (!FD_ISSET(i
, &w
))
752 rc
= recv(i
, buf
, sizeof(buf
), 0);
754 if (errno
!= EWOULDBLOCK
) {
755 perror("recv failed()");
765 sc
= send(i
, buf
, rc
, 0);
767 perror("send failed()");
793 static int run_options(struct sockmap_options
*options
, int cg_fd
, int test
)
795 int i
, key
, next_key
, err
, tx_prog_fd
= -1, zero
= 0;
797 /* If base test skip BPF setup */
798 if (test
== BASE
|| test
== BASE_SENDPAGE
)
801 /* Attach programs to sockmap */
802 err
= bpf_prog_attach(prog_fd
[0], map_fd
[0],
803 BPF_SK_SKB_STREAM_PARSER
, 0);
806 "ERROR: bpf_prog_attach (sockmap %i->%i): %d (%s)\n",
807 prog_fd
[0], map_fd
[0], err
, strerror(errno
));
811 err
= bpf_prog_attach(prog_fd
[1], map_fd
[0],
812 BPF_SK_SKB_STREAM_VERDICT
, 0);
814 fprintf(stderr
, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n",
815 err
, strerror(errno
));
819 /* Attach to cgroups */
820 err
= bpf_prog_attach(prog_fd
[2], cg_fd
, BPF_CGROUP_SOCK_OPS
, 0);
822 fprintf(stderr
, "ERROR: bpf_prog_attach (groups): %d (%s)\n",
823 err
, strerror(errno
));
828 err
= sockmap_init_sockets(options
->verbose
);
830 fprintf(stderr
, "ERROR: test socket failed: %d\n", err
);
834 /* Attach txmsg program to sockmap */
836 tx_prog_fd
= prog_fd
[3];
837 else if (txmsg_noisy
)
838 tx_prog_fd
= prog_fd
[4];
839 else if (txmsg_redir
)
840 tx_prog_fd
= prog_fd
[5];
841 else if (txmsg_redir_noisy
)
842 tx_prog_fd
= prog_fd
[6];
844 tx_prog_fd
= prog_fd
[9];
845 /* apply and cork must be last */
846 else if (txmsg_apply
)
847 tx_prog_fd
= prog_fd
[7];
849 tx_prog_fd
= prog_fd
[8];
856 err
= bpf_prog_attach(tx_prog_fd
,
857 map_fd
[1], BPF_SK_MSG_VERDICT
, 0);
860 "ERROR: bpf_prog_attach (txmsg): %d (%s)\n",
861 err
, strerror(errno
));
865 err
= bpf_map_update_elem(map_fd
[1], &i
, &c1
, BPF_ANY
);
868 "ERROR: bpf_map_update_elem (txmsg): %d (%s\n",
869 err
, strerror(errno
));
873 if (txmsg_redir
|| txmsg_redir_noisy
)
878 err
= bpf_map_update_elem(map_fd
[2], &i
, &redir_fd
, BPF_ANY
);
881 "ERROR: bpf_map_update_elem (txmsg): %d (%s\n",
882 err
, strerror(errno
));
887 err
= bpf_map_update_elem(map_fd
[3],
888 &i
, &txmsg_apply
, BPF_ANY
);
891 "ERROR: bpf_map_update_elem (apply_bytes): %d (%s\n",
892 err
, strerror(errno
));
898 err
= bpf_map_update_elem(map_fd
[4],
899 &i
, &txmsg_cork
, BPF_ANY
);
902 "ERROR: bpf_map_update_elem (cork_bytes): %d (%s\n",
903 err
, strerror(errno
));
909 err
= bpf_map_update_elem(map_fd
[5],
910 &i
, &txmsg_start
, BPF_ANY
);
913 "ERROR: bpf_map_update_elem (txmsg_start): %d (%s)\n",
914 err
, strerror(errno
));
921 err
= bpf_map_update_elem(map_fd
[5],
922 &i
, &txmsg_end
, BPF_ANY
);
925 "ERROR: bpf_map_update_elem (txmsg_end): %d (%s)\n",
926 err
, strerror(errno
));
931 if (txmsg_start_push
) {
933 err
= bpf_map_update_elem(map_fd
[5],
934 &i
, &txmsg_start_push
, BPF_ANY
);
937 "ERROR: bpf_map_update_elem (txmsg_start_push): %d (%s)\n",
938 err
, strerror(errno
));
943 if (txmsg_end_push
) {
945 err
= bpf_map_update_elem(map_fd
[5],
946 &i
, &txmsg_end_push
, BPF_ANY
);
949 "ERROR: bpf_map_update_elem %i@%i (txmsg_end_push): %d (%s)\n",
950 txmsg_end_push
, i
, err
, strerror(errno
));
955 if (txmsg_start_pop
) {
957 err
= bpf_map_update_elem(map_fd
[5],
958 &i
, &txmsg_start_pop
, BPF_ANY
);
961 "ERROR: bpf_map_update_elem %i@%i (txmsg_start_pop): %d (%s)\n",
962 txmsg_start_pop
, i
, err
, strerror(errno
));
967 bpf_map_update_elem(map_fd
[5],
968 &i
, &txmsg_start_pop
, BPF_ANY
);
973 err
= bpf_map_update_elem(map_fd
[5],
974 &i
, &txmsg_pop
, BPF_ANY
);
977 "ERROR: bpf_map_update_elem %i@%i (txmsg_pop): %d (%s)\n",
978 txmsg_pop
, i
, err
, strerror(errno
));
983 bpf_map_update_elem(map_fd
[5],
984 &i
, &txmsg_pop
, BPF_ANY
);
989 int in
= BPF_F_INGRESS
;
992 err
= bpf_map_update_elem(map_fd
[6], &i
, &in
, BPF_ANY
);
995 "ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s)\n",
996 err
, strerror(errno
));
999 err
= bpf_map_update_elem(map_fd
[1], &i
, &p1
, BPF_ANY
);
1002 "ERROR: bpf_map_update_elem (p1 txmsg): %d (%s)\n",
1003 err
, strerror(errno
));
1005 err
= bpf_map_update_elem(map_fd
[2], &i
, &p1
, BPF_ANY
);
1008 "ERROR: bpf_map_update_elem (p1 redir): %d (%s)\n",
1009 err
, strerror(errno
));
1013 err
= bpf_map_update_elem(map_fd
[2], &i
, &p2
, BPF_ANY
);
1016 "ERROR: bpf_map_update_elem (p2 txmsg): %d (%s)\n",
1017 err
, strerror(errno
));
1022 int skb_fd
= (test
== SENDMSG
|| test
== SENDPAGE
) ?
1024 int ingress
= BPF_F_INGRESS
;
1027 err
= bpf_map_update_elem(map_fd
[7],
1028 &i
, &ingress
, BPF_ANY
);
1031 "ERROR: bpf_map_update_elem (txmsg_ingress): %d (%s)\n",
1032 err
, strerror(errno
));
1036 err
= bpf_map_update_elem(map_fd
[0],
1037 &i
, &skb_fd
, BPF_ANY
);
1040 "ERROR: bpf_map_update_elem (c1 sockmap): %d (%s)\n",
1041 err
, strerror(errno
));
1047 options
->drop_expected
= true;
1049 if (test
== PING_PONG
)
1050 err
= forever_ping_pong(options
->rate
, options
);
1051 else if (test
== SENDMSG
) {
1052 options
->base
= false;
1053 options
->sendpage
= false;
1054 err
= sendmsg_test(options
);
1055 } else if (test
== SENDPAGE
) {
1056 options
->base
= false;
1057 options
->sendpage
= true;
1058 err
= sendmsg_test(options
);
1059 } else if (test
== BASE
) {
1060 options
->base
= true;
1061 options
->sendpage
= false;
1062 err
= sendmsg_test(options
);
1063 } else if (test
== BASE_SENDPAGE
) {
1064 options
->base
= true;
1065 options
->sendpage
= true;
1066 err
= sendmsg_test(options
);
1068 fprintf(stderr
, "unknown test\n");
1070 /* Detatch and zero all the maps */
1071 bpf_prog_detach2(prog_fd
[2], cg_fd
, BPF_CGROUP_SOCK_OPS
);
1072 bpf_prog_detach2(prog_fd
[0], map_fd
[0], BPF_SK_SKB_STREAM_PARSER
);
1073 bpf_prog_detach2(prog_fd
[1], map_fd
[0], BPF_SK_SKB_STREAM_VERDICT
);
1074 if (tx_prog_fd
>= 0)
1075 bpf_prog_detach2(tx_prog_fd
, map_fd
[1], BPF_SK_MSG_VERDICT
);
1077 for (i
= 0; i
< 8; i
++) {
1079 bpf_map_update_elem(map_fd
[i
], &key
, &zero
, BPF_ANY
);
1080 while (bpf_map_get_next_key(map_fd
[i
], &key
, &next_key
) == 0) {
1081 bpf_map_update_elem(map_fd
[i
], &key
, &zero
, BPF_ANY
);
1095 static char *test_to_str(int test
)
1106 #define OPTSTRING 60
1107 static void test_options(char *options
)
1109 char tstr
[OPTSTRING
];
1111 memset(options
, 0, OPTSTRING
);
1114 strncat(options
, "pass,", OPTSTRING
);
1116 strncat(options
, "pass_noisy,", OPTSTRING
);
1118 strncat(options
, "redir,", OPTSTRING
);
1119 if (txmsg_redir_noisy
)
1120 strncat(options
, "redir_noisy,", OPTSTRING
);
1122 strncat(options
, "drop,", OPTSTRING
);
1124 snprintf(tstr
, OPTSTRING
, "apply %d,", txmsg_apply
);
1125 strncat(options
, tstr
, OPTSTRING
);
1128 snprintf(tstr
, OPTSTRING
, "cork %d,", txmsg_cork
);
1129 strncat(options
, tstr
, OPTSTRING
);
1132 snprintf(tstr
, OPTSTRING
, "start %d,", txmsg_start
);
1133 strncat(options
, tstr
, OPTSTRING
);
1136 snprintf(tstr
, OPTSTRING
, "end %d,", txmsg_end
);
1137 strncat(options
, tstr
, OPTSTRING
);
1139 if (txmsg_start_pop
) {
1140 snprintf(tstr
, OPTSTRING
, "pop (%d,%d),",
1141 txmsg_start_pop
, txmsg_start_pop
+ txmsg_pop
);
1142 strncat(options
, tstr
, OPTSTRING
);
1145 strncat(options
, "ingress,", OPTSTRING
);
1147 strncat(options
, "skb,", OPTSTRING
);
1149 strncat(options
, "ktls,", OPTSTRING
);
1151 strncat(options
, "peek,", OPTSTRING
);
1154 static int __test_exec(int cgrp
, int test
, struct sockmap_options
*opt
)
1156 char *options
= calloc(OPTSTRING
, sizeof(char));
1159 if (test
== SENDPAGE
)
1160 opt
->sendpage
= true;
1162 opt
->sendpage
= false;
1165 opt
->drop_expected
= true;
1167 opt
->drop_expected
= false;
1169 test_options(options
);
1172 "[TEST %i]: (%i, %i, %i, %s, %s): ",
1173 test_cnt
, opt
->rate
, opt
->iov_count
, opt
->iov_length
,
1174 test_to_str(test
), options
);
1176 err
= run_options(opt
, cgrp
, test
);
1177 fprintf(stdout
, "%s\n", !err
? "PASS" : "FAILED");
1179 !err
? passed
++ : failed
++;
1184 static int test_exec(int cgrp
, struct sockmap_options
*opt
)
1186 int err
= __test_exec(cgrp
, SENDMSG
, opt
);
1191 err
= __test_exec(cgrp
, SENDPAGE
, opt
);
1196 static int test_loop(int cgrp
)
1198 struct sockmap_options opt
;
1204 opt
.sendpage
= false;
1205 opt
.data_test
= false;
1206 opt
.drop_expected
= false;
1212 for (i
= 1; i
< 100; i
+= 33) {
1213 for (l
= 1; l
< 100; l
+= 33) {
1217 err
= test_exec(cgrp
, &opt
);
1227 static int test_txmsg(int cgrp
)
1231 txmsg_pass
= txmsg_noisy
= txmsg_redir_noisy
= txmsg_drop
= 0;
1232 txmsg_apply
= txmsg_cork
= 0;
1233 txmsg_ingress
= txmsg_skb
= 0;
1236 err
= test_loop(cgrp
);
1242 err
= test_loop(cgrp
);
1248 err
= test_loop(cgrp
);
1255 err
= test_loop(cgrp
);
1267 static int test_send(struct sockmap_options
*opt
, int cgrp
)
1271 opt
->iov_length
= 1;
1274 err
= test_exec(cgrp
, opt
);
1278 opt
->iov_length
= 1;
1279 opt
->iov_count
= 1024;
1281 err
= test_exec(cgrp
, opt
);
1285 opt
->iov_length
= 1024;
1288 err
= test_exec(cgrp
, opt
);
1292 opt
->iov_length
= 1;
1295 err
= test_exec(cgrp
, opt
);
1299 opt
->iov_length
= 256;
1300 opt
->iov_count
= 1024;
1302 err
= test_exec(cgrp
, opt
);
1308 opt
->iov_length
= 5;
1309 err
= test_exec(cgrp
, opt
);
1317 static int test_mixed(int cgrp
)
1319 struct sockmap_options opt
= {0};
1322 txmsg_pass
= txmsg_noisy
= txmsg_redir_noisy
= txmsg_drop
= 0;
1323 txmsg_apply
= txmsg_cork
= 0;
1324 txmsg_start
= txmsg_end
= 0;
1325 txmsg_start_push
= txmsg_end_push
= 0;
1326 txmsg_start_pop
= txmsg_pop
= 0;
1328 /* Test small and large iov_count values with pass/redir/apply/cork */
1333 err
= test_send(&opt
, cgrp
);
1341 err
= test_send(&opt
, cgrp
);
1349 err
= test_send(&opt
, cgrp
);
1357 err
= test_send(&opt
, cgrp
);
1365 err
= test_send(&opt
, cgrp
);
1373 err
= test_send(&opt
, cgrp
);
1381 err
= test_send(&opt
, cgrp
);
1389 err
= test_send(&opt
, cgrp
);
1397 err
= test_send(&opt
, cgrp
);
1405 err
= test_send(&opt
, cgrp
);
1413 err
= test_send(&opt
, cgrp
);
1421 err
= test_send(&opt
, cgrp
);
1429 err
= test_send(&opt
, cgrp
);
1436 static int test_start_end(int cgrp
)
1438 struct sockmap_options opt
= {0};
1441 /* Test basic start/end with lots of iov_count and iov_lengths */
1444 txmsg_start_push
= 1;
1446 txmsg_start_pop
= 1;
1448 err
= test_txmsg(cgrp
);
1452 /* Cut a byte of pushed data but leave reamining in place */
1455 txmsg_start_push
= 1;
1457 txmsg_start_pop
= 1;
1459 err
= test_txmsg(cgrp
);
1463 /* Test start/end with cork */
1466 opt
.iov_length
= 100;
1469 txmsg_start_pop
= 0;
1472 for (i
= 99; i
<= 1600; i
+= 500) {
1475 txmsg_start_push
= 0;
1477 err
= test_exec(cgrp
, &opt
);
1482 /* Test pop data in middle of cork */
1483 for (i
= 99; i
<= 1600; i
+= 500) {
1484 txmsg_start_pop
= 10;
1486 err
= test_exec(cgrp
, &opt
);
1490 txmsg_start_pop
= 0;
1493 /* Test start/end with cork but pull data in middle */
1494 for (i
= 199; i
<= 1600; i
+= 500) {
1497 txmsg_start_push
= 100;
1499 err
= test_exec(cgrp
, &opt
);
1504 /* Test start/end with cork pulling last sg entry */
1507 txmsg_start_push
= 1500;
1508 txmsg_end_push
= 1600;
1509 err
= test_exec(cgrp
, &opt
);
1513 /* Test pop with cork pulling last sg entry */
1514 txmsg_start_pop
= 1500;
1516 err
= test_exec(cgrp
, &opt
);
1519 txmsg_start_pop
= 0;
1522 /* Test start/end pull of single byte in last page */
1525 txmsg_start_push
= 1111;
1526 txmsg_end_push
= 1112;
1527 err
= test_exec(cgrp
, &opt
);
1531 /* Test pop of single byte in last page */
1532 txmsg_start_pop
= 1111;
1534 err
= test_exec(cgrp
, &opt
);
1538 /* Test start/end with end < start */
1541 txmsg_start_push
= 1111;
1543 err
= test_exec(cgrp
, &opt
);
1547 /* Test start/end with end > data */
1550 txmsg_start_push
= 0;
1551 txmsg_end_push
= 1601;
1552 err
= test_exec(cgrp
, &opt
);
1556 /* Test start/end with start > data */
1559 txmsg_start_push
= 1601;
1560 txmsg_end_push
= 1600;
1561 err
= test_exec(cgrp
, &opt
);
1565 /* Test pop with start > data */
1566 txmsg_start_pop
= 1601;
1568 err
= test_exec(cgrp
, &opt
);
1572 /* Test pop with pop range > data */
1573 txmsg_start_pop
= 1599;
1575 err
= test_exec(cgrp
, &opt
);
1583 char *map_names
[] = {
1594 int prog_attach_type
[] = {
1595 BPF_SK_SKB_STREAM_PARSER
,
1596 BPF_SK_SKB_STREAM_VERDICT
,
1597 BPF_CGROUP_SOCK_OPS
,
1608 BPF_PROG_TYPE_SK_SKB
,
1609 BPF_PROG_TYPE_SK_SKB
,
1610 BPF_PROG_TYPE_SOCK_OPS
,
1611 BPF_PROG_TYPE_SK_MSG
,
1612 BPF_PROG_TYPE_SK_MSG
,
1613 BPF_PROG_TYPE_SK_MSG
,
1614 BPF_PROG_TYPE_SK_MSG
,
1615 BPF_PROG_TYPE_SK_MSG
,
1616 BPF_PROG_TYPE_SK_MSG
,
1617 BPF_PROG_TYPE_SK_MSG
,
1620 static int populate_progs(char *bpf_file
)
1622 struct bpf_program
*prog
;
1623 struct bpf_object
*obj
;
1627 obj
= bpf_object__open(bpf_file
);
1628 err
= libbpf_get_error(obj
);
1632 libbpf_strerror(err
, err_buf
, sizeof(err_buf
));
1633 printf("Unable to load eBPF objects in file '%s' : %s\n",
1638 bpf_object__for_each_program(prog
, obj
) {
1639 bpf_program__set_type(prog
, prog_type
[i
]);
1640 bpf_program__set_expected_attach_type(prog
,
1641 prog_attach_type
[i
]);
1645 i
= bpf_object__load(obj
);
1647 bpf_object__for_each_program(prog
, obj
) {
1648 prog_fd
[i
] = bpf_program__fd(prog
);
1652 for (i
= 0; i
< sizeof(map_fd
)/sizeof(int); i
++) {
1653 maps
[i
] = bpf_object__find_map_by_name(obj
, map_names
[i
]);
1654 map_fd
[i
] = bpf_map__fd(maps
[i
]);
1655 if (map_fd
[i
] < 0) {
1656 fprintf(stderr
, "load_bpf_file: (%i) %s\n",
1657 map_fd
[i
], strerror(errno
));
1665 static int __test_suite(int cg_fd
, char *bpf_file
)
1667 int err
, cleanup
= cg_fd
;
1669 err
= populate_progs(bpf_file
);
1671 fprintf(stderr
, "ERROR: (%i) load bpf failed\n", err
);
1676 if (setup_cgroup_environment()) {
1677 fprintf(stderr
, "ERROR: cgroup env failed\n");
1681 cg_fd
= create_and_get_cgroup(CG_PATH
);
1684 "ERROR: (%i) open cg path failed: %s\n",
1689 if (join_cgroup(CG_PATH
)) {
1690 fprintf(stderr
, "ERROR: failed to join cgroup\n");
1695 /* Tests basic commands and APIs with range of iov values */
1696 txmsg_start
= txmsg_end
= txmsg_start_push
= txmsg_end_push
= 0;
1697 err
= test_txmsg(cg_fd
);
1701 /* Tests interesting combinations of APIs used together */
1702 err
= test_mixed(cg_fd
);
1706 /* Tests pull_data API using start/end API */
1707 err
= test_start_end(cg_fd
);
1712 printf("Summary: %i PASSED %i FAILED\n", passed
, failed
);
1714 cleanup_cgroup_environment();
1720 static int test_suite(int cg_fd
)
1724 err
= __test_suite(cg_fd
, BPF_SOCKMAP_FILENAME
);
1727 err
= __test_suite(cg_fd
, BPF_SOCKHASH_FILENAME
);
1734 int main(int argc
, char **argv
)
1736 int iov_count
= 1, length
= 1024, rate
= 1;
1737 struct sockmap_options options
= {0};
1738 int opt
, longindex
, err
, cg_fd
= 0;
1739 char *bpf_file
= BPF_SOCKMAP_FILENAME
;
1740 int test
= PING_PONG
;
1743 return test_suite(-1);
1745 while ((opt
= getopt_long(argc
, argv
, ":dhvc:r:i:l:t:p:q:",
1746 long_options
, &longindex
)) != -1) {
1749 txmsg_start
= atoi(optarg
);
1752 txmsg_end
= atoi(optarg
);
1755 txmsg_start_push
= atoi(optarg
);
1758 txmsg_end_push
= atoi(optarg
);
1761 txmsg_start_pop
= atoi(optarg
);
1764 txmsg_pop
= atoi(optarg
);
1767 txmsg_apply
= atoi(optarg
);
1770 txmsg_cork
= atoi(optarg
);
1773 cg_fd
= open(optarg
, O_DIRECTORY
, O_RDONLY
);
1776 "ERROR: (%i) open cg path failed: %s\n",
1782 rate
= atoi(optarg
);
1785 options
.verbose
= 1;
1788 iov_count
= atoi(optarg
);
1791 length
= atoi(optarg
);
1794 options
.data_test
= true;
1797 if (strcmp(optarg
, "ping") == 0) {
1799 } else if (strcmp(optarg
, "sendmsg") == 0) {
1801 } else if (strcmp(optarg
, "base") == 0) {
1803 } else if (strcmp(optarg
, "base_sendpage") == 0) {
1804 test
= BASE_SENDPAGE
;
1805 } else if (strcmp(optarg
, "sendpage") == 0) {
1821 if (argc
<= 3 && cg_fd
)
1822 return test_suite(cg_fd
);
1825 fprintf(stderr
, "%s requires cgroup option: --cgroup <path>\n",
1830 err
= populate_progs(bpf_file
);
1832 fprintf(stderr
, "populate program: (%s) %s\n",
1833 bpf_file
, strerror(errno
));
1839 signal(SIGINT
, running_handler
);
1841 options
.iov_count
= iov_count
;
1842 options
.iov_length
= length
;
1843 options
.rate
= rate
;
1845 err
= run_options(&options
, cg_fd
, test
);
1850 void running_handler(int a
)