1 /* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of version 2 of the GNU General Public
5 * License as published by the Free Software Foundation.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
14 #include <sys/socket.h>
15 #include <sys/ioctl.h>
16 #include <sys/select.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
22 #include <sys/ioctl.h>
30 #include <sys/resource.h>
31 #include <sys/types.h>
33 #include <linux/netlink.h>
34 #include <linux/socket.h>
35 #include <linux/sock_diag.h>
36 #include <linux/bpf.h>
37 #include <linux/if_link.h>
43 #include "../bpf/bpf_load.h"
44 #include "../bpf/bpf_util.h"
45 #include "../bpf/libbpf.h"
48 void running_handler(int a
);
50 /* randomly selected ports for testing on lo */
55 int s1
, s2
, c1
, c2
, p1
, p2
;
57 static const struct option long_options
[] = {
58 {"help", no_argument
, NULL
, 'h' },
59 {"cgroup", required_argument
, NULL
, 'c' },
60 {"rate", required_argument
, NULL
, 'r' },
61 {"verbose", no_argument
, NULL
, 'v' },
62 {"iov_count", required_argument
, NULL
, 'i' },
63 {"length", required_argument
, NULL
, 'l' },
64 {"test", required_argument
, NULL
, 't' },
68 static void usage(char *argv
[])
72 printf(" Usage: %s --cgroup <cgroup_path>\n", argv
[0]);
73 printf(" options:\n");
74 for (i
= 0; long_options
[i
].name
!= 0; i
++) {
75 printf(" --%-12s", long_options
[i
].name
);
76 if (long_options
[i
].flag
!= NULL
)
77 printf(" flag (internal value:%d)\n",
78 *long_options
[i
].flag
);
80 printf(" -%c\n", long_options
[i
].val
);
85 static int sockmap_init_sockets(void)
88 struct sockaddr_in addr
;
89 int *fds
[4] = {&s1
, &s2
, &c1
, &c2
};
91 s1
= s2
= p1
= p2
= c1
= c2
= 0;
94 for (i
= 0; i
< 4; i
++) {
95 *fds
[i
] = socket(AF_INET
, SOCK_STREAM
, 0);
97 perror("socket s1 failed()");
103 for (i
= 0; i
< 2; i
++) {
104 err
= setsockopt(*fds
[i
], SOL_SOCKET
, SO_REUSEADDR
,
105 (char *)&one
, sizeof(one
));
107 perror("setsockopt failed()");
112 /* Non-blocking sockets */
113 for (i
= 0; i
< 2; i
++) {
114 err
= ioctl(*fds
[i
], FIONBIO
, (char *)&one
);
116 perror("ioctl s1 failed()");
121 /* Bind server sockets */
122 memset(&addr
, 0, sizeof(struct sockaddr_in
));
123 addr
.sin_family
= AF_INET
;
124 addr
.sin_addr
.s_addr
= inet_addr("127.0.0.1");
126 addr
.sin_port
= htons(S1_PORT
);
127 err
= bind(s1
, (struct sockaddr
*)&addr
, sizeof(addr
));
129 perror("bind s1 failed()\n");
133 addr
.sin_port
= htons(S2_PORT
);
134 err
= bind(s2
, (struct sockaddr
*)&addr
, sizeof(addr
));
136 perror("bind s2 failed()\n");
140 /* Listen server sockets */
141 addr
.sin_port
= htons(S1_PORT
);
142 err
= listen(s1
, 32);
144 perror("listen s1 failed()\n");
148 addr
.sin_port
= htons(S2_PORT
);
149 err
= listen(s2
, 32);
151 perror("listen s1 failed()\n");
155 /* Initiate Connect */
156 addr
.sin_port
= htons(S1_PORT
);
157 err
= connect(c1
, (struct sockaddr
*)&addr
, sizeof(addr
));
158 if (err
< 0 && errno
!= EINPROGRESS
) {
159 perror("connect c1 failed()\n");
163 addr
.sin_port
= htons(S2_PORT
);
164 err
= connect(c2
, (struct sockaddr
*)&addr
, sizeof(addr
));
165 if (err
< 0 && errno
!= EINPROGRESS
) {
166 perror("connect c2 failed()\n");
168 } else if (err
< 0) {
172 /* Accept Connecrtions */
173 p1
= accept(s1
, NULL
, NULL
);
175 perror("accept s1 failed()\n");
179 p2
= accept(s2
, NULL
, NULL
);
181 perror("accept s1 failed()\n");
185 printf("connected sockets: c1 <-> p1, c2 <-> p2\n");
186 printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n",
194 struct timespec start
;
198 static int msg_loop(int fd
, int iov_count
, int iov_length
, int cnt
,
199 struct msg_stats
*s
, bool tx
)
201 struct msghdr msg
= {0};
202 int err
, i
, flags
= MSG_NOSIGNAL
;
205 iov
= calloc(iov_count
, sizeof(struct iovec
));
209 for (i
= 0; i
< iov_count
; i
++) {
210 char *d
= calloc(iov_length
, sizeof(char));
213 fprintf(stderr
, "iov_count %i/%i OOM\n", i
, iov_count
);
217 iov
[i
].iov_len
= iov_length
;
221 msg
.msg_iovlen
= iov_count
;
224 clock_gettime(CLOCK_MONOTONIC
, &s
->start
);
225 for (i
= 0; i
< cnt
; i
++) {
226 int sent
= sendmsg(fd
, &msg
, flags
);
229 perror("send loop error:");
232 s
->bytes_sent
+= sent
;
234 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
236 int slct
, recv
, max_fd
= fd
;
237 struct timeval timeout
;
241 total_bytes
= (float)iov_count
* (float)iov_length
* (float)cnt
;
242 err
= clock_gettime(CLOCK_MONOTONIC
, &s
->start
);
244 perror("recv start time: ");
245 while (s
->bytes_recvd
< total_bytes
) {
253 slct
= select(max_fd
+ 1, &w
, NULL
, NULL
, &timeout
);
256 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
259 fprintf(stderr
, "unexpected timeout\n");
261 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
265 recv
= recvmsg(fd
, &msg
, flags
);
267 if (errno
!= EWOULDBLOCK
) {
268 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
269 perror("recv failed()\n");
274 s
->bytes_recvd
+= recv
;
276 clock_gettime(CLOCK_MONOTONIC
, &s
->end
);
279 for (i
= 0; i
< iov_count
; i
++)
280 free(iov
[i
].iov_base
);
284 for (i
= 0; i
< iov_count
; i
++)
285 free(iov
[i
].iov_base
);
290 static float giga
= 1000000000;
292 static inline float sentBps(struct msg_stats s
)
294 return s
.bytes_sent
/ (s
.end
.tv_sec
- s
.start
.tv_sec
);
297 static inline float recvdBps(struct msg_stats s
)
299 return s
.bytes_recvd
/ (s
.end
.tv_sec
- s
.start
.tv_sec
);
302 static int sendmsg_test(int iov_count
, int iov_buf
, int cnt
,
303 int verbose
, bool base
)
305 float sent_Bps
= 0, recvd_Bps
= 0;
306 int rx_fd
, txpid
, rxpid
, err
= 0;
307 struct msg_stats s
= {0};
319 err
= msg_loop(rx_fd
, iov_count
, iov_buf
, cnt
, &s
, false);
322 "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n",
323 iov_count
, iov_buf
, cnt
, err
);
324 shutdown(p2
, SHUT_RDWR
);
325 shutdown(p1
, SHUT_RDWR
);
326 if (s
.end
.tv_sec
- s
.start
.tv_sec
) {
327 sent_Bps
= sentBps(s
);
328 recvd_Bps
= recvdBps(s
);
331 "rx_sendmsg: TX: %zuB %fB/s %fGB/s RX: %zuB %fB/s %fGB/s\n",
332 s
.bytes_sent
, sent_Bps
, sent_Bps
/giga
,
333 s
.bytes_recvd
, recvd_Bps
, recvd_Bps
/giga
);
335 } else if (rxpid
== -1) {
336 perror("msg_loop_rx: ");
342 err
= msg_loop(c1
, iov_count
, iov_buf
, cnt
, &s
, true);
345 "msg_loop_tx: iov_count %i iov_buf %i cnt %i err %i\n",
346 iov_count
, iov_buf
, cnt
, err
);
347 shutdown(c1
, SHUT_RDWR
);
348 if (s
.end
.tv_sec
- s
.start
.tv_sec
) {
349 sent_Bps
= sentBps(s
);
350 recvd_Bps
= recvdBps(s
);
353 "tx_sendmsg: TX: %zuB %fB/s %f GB/s RX: %zuB %fB/s %fGB/s\n",
354 s
.bytes_sent
, sent_Bps
, sent_Bps
/giga
,
355 s
.bytes_recvd
, recvd_Bps
, recvd_Bps
/giga
);
357 } else if (txpid
== -1) {
358 perror("msg_loop_tx: ");
362 assert(waitpid(rxpid
, &status
, 0) == rxpid
);
363 assert(waitpid(txpid
, &status
, 0) == txpid
);
367 static int forever_ping_pong(int rate
, int verbose
)
369 struct timeval timeout
;
370 char buf
[1024] = {0};
376 /* Ping/Pong data from client to server */
377 sc
= send(c1
, buf
, sizeof(buf
), 0);
379 perror("send failed()\n");
384 int s
, rc
, i
, max_fd
= p2
;
394 s
= select(max_fd
+ 1, &w
, NULL
, NULL
, &timeout
);
399 fprintf(stderr
, "unexpected timeout\n");
403 for (i
= 0; i
<= max_fd
&& s
> 0; ++i
) {
404 if (!FD_ISSET(i
, &w
))
409 rc
= recv(i
, buf
, sizeof(buf
), 0);
411 if (errno
!= EWOULDBLOCK
) {
412 perror("recv failed()\n");
422 sc
= send(i
, buf
, rc
, 0);
424 perror("send failed()\n");
448 int main(int argc
, char **argv
)
450 int iov_count
= 1, length
= 1024, rate
= 1, verbose
= 0;
451 struct rlimit r
= {10 * 1024 * 1024, RLIM_INFINITY
};
452 int opt
, longindex
, err
, cg_fd
= 0;
453 int test
= PING_PONG
;
456 while ((opt
= getopt_long(argc
, argv
, "hvc:r:i:l:t:",
457 long_options
, &longindex
)) != -1) {
459 /* Cgroup configuration */
461 cg_fd
= open(optarg
, O_DIRECTORY
, O_RDONLY
);
464 "ERROR: (%i) open cg path failed: %s\n",
476 iov_count
= atoi(optarg
);
479 length
= atoi(optarg
);
482 if (strcmp(optarg
, "ping") == 0) {
484 } else if (strcmp(optarg
, "sendmsg") == 0) {
486 } else if (strcmp(optarg
, "base") == 0) {
501 fprintf(stderr
, "%s requires cgroup option: --cgroup <path>\n",
506 if (setrlimit(RLIMIT_MEMLOCK
, &r
)) {
507 perror("setrlimit(RLIMIT_MEMLOCK)");
511 snprintf(filename
, sizeof(filename
), "%s_kern.o", argv
[0]);
516 signal(SIGINT
, running_handler
);
518 /* If base test skip BPF setup */
522 if (load_bpf_file(filename
)) {
523 fprintf(stderr
, "load_bpf_file: (%s) %s\n",
524 filename
, strerror(errno
));
528 /* Attach programs to sockmap */
529 err
= bpf_prog_attach(prog_fd
[0], map_fd
[0],
530 BPF_SK_SKB_STREAM_PARSER
, 0);
532 fprintf(stderr
, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n",
533 err
, strerror(errno
));
537 err
= bpf_prog_attach(prog_fd
[1], map_fd
[0],
538 BPF_SK_SKB_STREAM_VERDICT
, 0);
540 fprintf(stderr
, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n",
541 err
, strerror(errno
));
545 /* Attach to cgroups */
546 err
= bpf_prog_attach(prog_fd
[2], cg_fd
, BPF_CGROUP_SOCK_OPS
, 0);
548 fprintf(stderr
, "ERROR: bpf_prog_attach (groups): %d (%s)\n",
549 err
, strerror(errno
));
554 err
= sockmap_init_sockets();
556 fprintf(stderr
, "ERROR: test socket failed: %d\n", err
);
560 if (test
== PING_PONG
)
561 err
= forever_ping_pong(rate
, verbose
);
562 else if (test
== SENDMSG
)
563 err
= sendmsg_test(iov_count
, length
, rate
, verbose
, false);
564 else if (test
== BASE
)
565 err
= sendmsg_test(iov_count
, length
, rate
, verbose
, true);
567 fprintf(stderr
, "unknown test\n");
579 void running_handler(int a
)