blob: 6ab3ec233bdf7c95df222002b44d233bff2b5918 [file] [log] [blame]
John Fastabend69e8cc12017-08-15 22:33:32 -07001/* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io
2 *
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.
6 *
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.
11 */
12#include <stdio.h>
13#include <stdlib.h>
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>
19#include <unistd.h>
20#include <string.h>
21#include <errno.h>
22#include <sys/ioctl.h>
23#include <stdbool.h>
24#include <signal.h>
25#include <fcntl.h>
John Fastabendd7d64372018-01-22 10:36:02 -080026#include <sys/wait.h>
John Fastabend69e8cc12017-08-15 22:33:32 -070027
28#include <sys/time.h>
29#include <sys/types.h>
30
31#include <linux/netlink.h>
32#include <linux/socket.h>
33#include <linux/sock_diag.h>
34#include <linux/bpf.h>
35#include <linux/if_link.h>
36#include <assert.h>
37#include <libgen.h>
38
John Fastabend6627426f2018-01-22 10:35:27 -080039#include <getopt.h>
40
John Fastabend69e8cc12017-08-15 22:33:32 -070041#include "../bpf/bpf_load.h"
42#include "../bpf/bpf_util.h"
43#include "../bpf/libbpf.h"
44
45int running;
46void running_handler(int a);
47
48/* randomly selected ports for testing on lo */
49#define S1_PORT 10000
50#define S2_PORT 10001
51
John Fastabend6627426f2018-01-22 10:35:27 -080052/* global sockets */
53int s1, s2, c1, c2, p1, p2;
54
55static const struct option long_options[] = {
56 {"help", no_argument, NULL, 'h' },
57 {"cgroup", required_argument, NULL, 'c' },
58 {"rate", required_argument, NULL, 'r' },
59 {"verbose", no_argument, NULL, 'v' },
John Fastabendeaf8c6e2018-01-22 10:35:45 -080060 {"iov_count", required_argument, NULL, 'i' },
61 {"length", required_argument, NULL, 'l' },
62 {"test", required_argument, NULL, 't' },
John Fastabend6627426f2018-01-22 10:35:27 -080063 {0, 0, NULL, 0 }
64};
65
66static void usage(char *argv[])
John Fastabend69e8cc12017-08-15 22:33:32 -070067{
John Fastabend6627426f2018-01-22 10:35:27 -080068 int i;
69
70 printf(" Usage: %s --cgroup <cgroup_path>\n", argv[0]);
71 printf(" options:\n");
72 for (i = 0; long_options[i].name != 0; i++) {
73 printf(" --%-12s", long_options[i].name);
74 if (long_options[i].flag != NULL)
75 printf(" flag (internal value:%d)\n",
76 *long_options[i].flag);
77 else
78 printf(" -%c\n", long_options[i].val);
79 }
80 printf("\n");
81}
82
83static int sockmap_init_sockets(void)
84{
85 int i, err, one = 1;
John Fastabend69e8cc12017-08-15 22:33:32 -070086 struct sockaddr_in addr;
John Fastabend69e8cc12017-08-15 22:33:32 -070087 int *fds[4] = {&s1, &s2, &c1, &c2};
John Fastabend69e8cc12017-08-15 22:33:32 -070088
89 s1 = s2 = p1 = p2 = c1 = c2 = 0;
90
91 /* Init sockets */
92 for (i = 0; i < 4; i++) {
93 *fds[i] = socket(AF_INET, SOCK_STREAM, 0);
94 if (*fds[i] < 0) {
95 perror("socket s1 failed()");
John Fastabend6627426f2018-01-22 10:35:27 -080096 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -070097 }
98 }
99
100 /* Allow reuse */
101 for (i = 0; i < 2; i++) {
102 err = setsockopt(*fds[i], SOL_SOCKET, SO_REUSEADDR,
103 (char *)&one, sizeof(one));
104 if (err) {
105 perror("setsockopt failed()");
John Fastabend6627426f2018-01-22 10:35:27 -0800106 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700107 }
108 }
109
110 /* Non-blocking sockets */
111 for (i = 0; i < 4; i++) {
112 err = ioctl(*fds[i], FIONBIO, (char *)&one);
113 if (err < 0) {
114 perror("ioctl s1 failed()");
John Fastabend6627426f2018-01-22 10:35:27 -0800115 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700116 }
117 }
118
119 /* Bind server sockets */
120 memset(&addr, 0, sizeof(struct sockaddr_in));
121 addr.sin_family = AF_INET;
122 addr.sin_addr.s_addr = inet_addr("127.0.0.1");
123
124 addr.sin_port = htons(S1_PORT);
125 err = bind(s1, (struct sockaddr *)&addr, sizeof(addr));
126 if (err < 0) {
127 perror("bind s1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800128 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700129 }
130
131 addr.sin_port = htons(S2_PORT);
132 err = bind(s2, (struct sockaddr *)&addr, sizeof(addr));
133 if (err < 0) {
134 perror("bind s2 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800135 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700136 }
137
138 /* Listen server sockets */
139 addr.sin_port = htons(S1_PORT);
140 err = listen(s1, 32);
141 if (err < 0) {
142 perror("listen s1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800143 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700144 }
145
146 addr.sin_port = htons(S2_PORT);
147 err = listen(s2, 32);
148 if (err < 0) {
149 perror("listen s1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800150 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700151 }
152
153 /* Initiate Connect */
154 addr.sin_port = htons(S1_PORT);
155 err = connect(c1, (struct sockaddr *)&addr, sizeof(addr));
156 if (err < 0 && errno != EINPROGRESS) {
157 perror("connect c1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800158 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700159 }
160
161 addr.sin_port = htons(S2_PORT);
162 err = connect(c2, (struct sockaddr *)&addr, sizeof(addr));
163 if (err < 0 && errno != EINPROGRESS) {
164 perror("connect c2 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800165 return errno;
166 } else if (err < 0) {
167 err = 0;
John Fastabend69e8cc12017-08-15 22:33:32 -0700168 }
169
170 /* Accept Connecrtions */
171 p1 = accept(s1, NULL, NULL);
172 if (p1 < 0) {
173 perror("accept s1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800174 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700175 }
176
177 p2 = accept(s2, NULL, NULL);
178 if (p2 < 0) {
179 perror("accept s1 failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800180 return errno;
John Fastabend69e8cc12017-08-15 22:33:32 -0700181 }
182
John Fastabend69e8cc12017-08-15 22:33:32 -0700183 printf("connected sockets: c1 <-> p1, c2 <-> p2\n");
184 printf("cgroups binding: c1(%i) <-> s1(%i) - - - c2(%i) <-> s2(%i)\n",
185 c1, s1, c2, s2);
John Fastabend6627426f2018-01-22 10:35:27 -0800186 return 0;
187}
188
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800189struct msg_stats {
190 size_t bytes_sent;
191 size_t bytes_recvd;
192};
193
194static int msg_loop(int fd, int iov_count, int iov_length, int cnt,
195 struct msg_stats *s, bool tx)
196{
197 struct msghdr msg = {0};
198 struct iovec *iov;
John Fastabendd7d64372018-01-22 10:36:02 -0800199 int i, flags = MSG_NOSIGNAL;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800200
201 iov = calloc(iov_count, sizeof(struct iovec));
202 if (!iov)
203 return errno;
204
205 for (i = 0; i < iov_count; i++) {
206 char *d = calloc(iov_length, sizeof(char));
207
208 if (!d) {
209 fprintf(stderr, "iov_count %i/%i OOM\n", i, iov_count);
210 goto out_errno;
211 }
212 iov[i].iov_base = d;
213 iov[i].iov_len = iov_length;
214 }
215
216 msg.msg_iov = iov;
217 msg.msg_iovlen = iov_count;
218
219 if (tx) {
220 for (i = 0; i < cnt; i++) {
221 int sent = sendmsg(fd, &msg, flags);
222
223 if (sent < 0) {
224 perror("send loop error:");
225 goto out_errno;
226 }
227 s->bytes_sent += sent;
228 }
229 } else {
230 int slct, recv, max_fd = fd;
231 struct timeval timeout;
232 float total_bytes;
233 fd_set w;
234
235 total_bytes = (float)iov_count * (float)iov_length * (float)cnt;
236 while (s->bytes_recvd < total_bytes) {
237 timeout.tv_sec = 1;
238 timeout.tv_usec = 0;
239
240 /* FD sets */
241 FD_ZERO(&w);
242 FD_SET(fd, &w);
243
244 slct = select(max_fd + 1, &w, NULL, NULL, &timeout);
245 if (slct == -1) {
246 perror("select()");
247 goto out_errno;
248 } else if (!slct) {
249 fprintf(stderr, "unexpected timeout\n");
250 errno = -EIO;
251 goto out_errno;
252 }
253
254 recv = recvmsg(fd, &msg, flags);
255 if (recv < 0) {
256 if (errno != EWOULDBLOCK) {
257 perror("recv failed()\n");
258 goto out_errno;
259 }
260 }
261
262 s->bytes_recvd += recv;
263 }
264 }
265
266 for (i = 0; i < iov_count; i++)
267 free(iov[i].iov_base);
268 free(iov);
269 return 0;
270out_errno:
271 for (i = 0; i < iov_count; i++)
272 free(iov[i].iov_base);
273 free(iov);
274 return errno;
275}
276
277static int sendmsg_test(int iov_count, int iov_buf, int cnt, int verbose)
278{
John Fastabendd7d64372018-01-22 10:36:02 -0800279 int txpid, rxpid, err = 0;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800280 struct msg_stats s = {0};
John Fastabendd7d64372018-01-22 10:36:02 -0800281 int status;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800282
John Fastabendd7d64372018-01-22 10:36:02 -0800283 errno = 0;
284
285 rxpid = fork();
286 if (rxpid == 0) {
287 err = msg_loop(p2, iov_count, iov_buf, cnt, &s, false);
288 if (err)
289 fprintf(stderr,
290 "msg_loop_rx: iov_count %i iov_buf %i cnt %i err %i\n",
291 iov_count, iov_buf, cnt, err);
292 fprintf(stdout, "rx_sendmsg: TX_bytes %zu RX_bytes %zu\n",
293 s.bytes_sent, s.bytes_recvd);
294 shutdown(p2, SHUT_RDWR);
295 shutdown(p1, SHUT_RDWR);
296 exit(1);
297 } else if (rxpid == -1) {
298 perror("msg_loop_rx: ");
299 return errno;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800300 }
301
John Fastabendd7d64372018-01-22 10:36:02 -0800302 txpid = fork();
303 if (txpid == 0) {
304 err = msg_loop(c1, iov_count, iov_buf, cnt, &s, true);
305 if (err)
306 fprintf(stderr,
307 "msg_loop_tx: iov_count %i iov_buf %i cnt %i err %i\n",
308 iov_count, iov_buf, cnt, err);
309 fprintf(stdout, "tx_sendmsg: TX_bytes %zu RX_bytes %zu\n",
310 s.bytes_sent, s.bytes_recvd);
311 shutdown(c1, SHUT_RDWR);
312 exit(1);
313 } else if (txpid == -1) {
314 perror("msg_loop_tx: ");
315 return errno;
316 }
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800317
John Fastabendd7d64372018-01-22 10:36:02 -0800318 assert(waitpid(rxpid, &status, 0) == rxpid);
319 assert(waitpid(txpid, &status, 0) == txpid);
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800320 return err;
321}
322
John Fastabend6627426f2018-01-22 10:35:27 -0800323static int forever_ping_pong(int rate, int verbose)
324{
325 struct timeval timeout;
326 char buf[1024] = {0};
327 int sc;
328
329 timeout.tv_sec = 10;
330 timeout.tv_usec = 0;
John Fastabend69e8cc12017-08-15 22:33:32 -0700331
332 /* Ping/Pong data from client to server */
333 sc = send(c1, buf, sizeof(buf), 0);
334 if (sc < 0) {
335 perror("send failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800336 return sc;
John Fastabend69e8cc12017-08-15 22:33:32 -0700337 }
338
339 do {
John Fastabend6627426f2018-01-22 10:35:27 -0800340 int s, rc, i, max_fd = p2;
341 fd_set w;
John Fastabend69e8cc12017-08-15 22:33:32 -0700342
343 /* FD sets */
344 FD_ZERO(&w);
345 FD_SET(c1, &w);
346 FD_SET(c2, &w);
347 FD_SET(p1, &w);
348 FD_SET(p2, &w);
349
350 s = select(max_fd + 1, &w, NULL, NULL, &timeout);
351 if (s == -1) {
352 perror("select()");
353 break;
354 } else if (!s) {
355 fprintf(stderr, "unexpected timeout\n");
356 break;
357 }
358
359 for (i = 0; i <= max_fd && s > 0; ++i) {
360 if (!FD_ISSET(i, &w))
361 continue;
362
363 s--;
364
365 rc = recv(i, buf, sizeof(buf), 0);
366 if (rc < 0) {
367 if (errno != EWOULDBLOCK) {
368 perror("recv failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800369 return rc;
John Fastabend69e8cc12017-08-15 22:33:32 -0700370 }
371 }
372
373 if (rc == 0) {
374 close(i);
375 break;
376 }
377
378 sc = send(i, buf, rc, 0);
379 if (sc < 0) {
380 perror("send failed()\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800381 return sc;
John Fastabend69e8cc12017-08-15 22:33:32 -0700382 }
383 }
John Fastabend6627426f2018-01-22 10:35:27 -0800384
385 if (rate)
386 sleep(rate);
387
388 if (verbose) {
John Fastabend69e8cc12017-08-15 22:33:32 -0700389 printf(".");
390 fflush(stdout);
391
392 }
393 } while (running);
394
John Fastabend6627426f2018-01-22 10:35:27 -0800395 return 0;
John Fastabend69e8cc12017-08-15 22:33:32 -0700396}
397
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800398enum {
399 PING_PONG,
400 SENDMSG,
401};
402
John Fastabend69e8cc12017-08-15 22:33:32 -0700403int main(int argc, char **argv)
404{
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800405 int iov_count = 1, length = 1024, rate = 1, verbose = 0;
John Fastabend6627426f2018-01-22 10:35:27 -0800406 int opt, longindex, err, cg_fd = 0;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800407 int test = PING_PONG;
John Fastabend69e8cc12017-08-15 22:33:32 -0700408 char filename[256];
John Fastabend69e8cc12017-08-15 22:33:32 -0700409
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800410 while ((opt = getopt_long(argc, argv, "hvc:r:i:l:t:",
John Fastabend6627426f2018-01-22 10:35:27 -0800411 long_options, &longindex)) != -1) {
412 switch (opt) {
413 /* Cgroup configuration */
414 case 'c':
415 cg_fd = open(optarg, O_DIRECTORY, O_RDONLY);
416 if (cg_fd < 0) {
417 fprintf(stderr,
418 "ERROR: (%i) open cg path failed: %s\n",
419 cg_fd, optarg);
420 return cg_fd;
421 }
422 break;
423 case 'r':
424 rate = atoi(optarg);
425 break;
426 case 'v':
427 verbose = 1;
428 break;
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800429 case 'i':
430 iov_count = atoi(optarg);
431 break;
432 case 'l':
433 length = atoi(optarg);
434 break;
435 case 't':
436 if (strcmp(optarg, "ping") == 0) {
437 test = PING_PONG;
438 } else if (strcmp(optarg, "sendmsg") == 0) {
439 test = SENDMSG;
440 } else {
441 usage(argv);
442 return -1;
443 }
444 break;
John Fastabend6627426f2018-01-22 10:35:27 -0800445 case 'h':
446 default:
447 usage(argv);
448 return -1;
449 }
450 }
451
452 if (!cg_fd) {
453 fprintf(stderr, "%s requires cgroup option: --cgroup <path>\n",
454 argv[0]);
455 return -1;
456 }
457
John Fastabend69e8cc12017-08-15 22:33:32 -0700458 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
459
460 running = 1;
461
462 /* catch SIGINT */
463 signal(SIGINT, running_handler);
464
465 if (load_bpf_file(filename)) {
466 fprintf(stderr, "load_bpf_file: (%s) %s\n",
467 filename, strerror(errno));
468 return 1;
469 }
470
John Fastabend69e8cc12017-08-15 22:33:32 -0700471 /* Attach programs to sockmap */
John Fastabend464bc0f2017-08-28 07:10:04 -0700472 err = bpf_prog_attach(prog_fd[0], map_fd[0],
473 BPF_SK_SKB_STREAM_PARSER, 0);
474 if (err) {
475 fprintf(stderr, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n",
476 err, strerror(errno));
477 return err;
478 }
479
480 err = bpf_prog_attach(prog_fd[1], map_fd[0],
481 BPF_SK_SKB_STREAM_VERDICT, 0);
John Fastabend69e8cc12017-08-15 22:33:32 -0700482 if (err) {
483 fprintf(stderr, "ERROR: bpf_prog_attach (sockmap): %d (%s)\n",
484 err, strerror(errno));
485 return err;
486 }
487
488 /* Attach to cgroups */
489 err = bpf_prog_attach(prog_fd[2], cg_fd, BPF_CGROUP_SOCK_OPS, 0);
490 if (err) {
491 fprintf(stderr, "ERROR: bpf_prog_attach (groups): %d (%s)\n",
492 err, strerror(errno));
493 return err;
494 }
495
John Fastabend6627426f2018-01-22 10:35:27 -0800496 err = sockmap_init_sockets();
John Fastabend69e8cc12017-08-15 22:33:32 -0700497 if (err) {
498 fprintf(stderr, "ERROR: test socket failed: %d\n", err);
John Fastabend6627426f2018-01-22 10:35:27 -0800499 goto out;
John Fastabend69e8cc12017-08-15 22:33:32 -0700500 }
John Fastabend6627426f2018-01-22 10:35:27 -0800501
John Fastabendeaf8c6e2018-01-22 10:35:45 -0800502 if (test == PING_PONG)
503 err = forever_ping_pong(rate, verbose);
504 else if (test == SENDMSG)
505 err = sendmsg_test(iov_count, length, rate, verbose);
506 else
507 fprintf(stderr, "unknown test\n");
John Fastabend6627426f2018-01-22 10:35:27 -0800508out:
509 close(s1);
510 close(s2);
511 close(p1);
512 close(p2);
513 close(c1);
514 close(c2);
515 close(cg_fd);
516 return err;
John Fastabend69e8cc12017-08-15 22:33:32 -0700517}
518
519void running_handler(int a)
520{
521 running = 0;
522}