1 // SPDX-License-Identifier: GPL-2.0
9 #include <linux/errqueue.h>
10 #include <linux/if_packet.h>
11 #include <linux/socket.h>
12 #include <linux/sockios.h>
13 #include <net/ethernet.h>
15 #include <netinet/ip.h>
16 #include <netinet/ip6.h>
17 #include <netinet/tcp.h>
18 #include <netinet/udp.h>
26 #include <sys/ioctl.h>
27 #include <sys/socket.h>
30 #include <sys/types.h>
38 static int cfg_port
= 8000;
40 static bool cfg_verify
;
41 static bool cfg_read_all
;
42 static bool cfg_gro_segment
;
43 static int cfg_family
= PF_INET6
;
44 static int cfg_alen
= sizeof(struct sockaddr_in6
);
45 static int cfg_expected_pkt_nr
;
46 static int cfg_expected_pkt_len
;
47 static int cfg_expected_gso_size
;
48 static int cfg_connect_timeout_ms
;
49 static int cfg_rcv_timeout_ms
;
50 static struct sockaddr_storage cfg_bind_addr
;
52 static bool interrupted
;
53 static unsigned long packets
, bytes
;
55 static void sigint_handler(int signum
)
61 static void setup_sockaddr(int domain
, const char *str_addr
, void *sockaddr
)
63 struct sockaddr_in6
*addr6
= (void *) sockaddr
;
64 struct sockaddr_in
*addr4
= (void *) sockaddr
;
68 addr4
->sin_family
= AF_INET
;
69 addr4
->sin_port
= htons(cfg_port
);
70 if (inet_pton(AF_INET
, str_addr
, &(addr4
->sin_addr
)) != 1)
71 error(1, 0, "ipv4 parse error: %s", str_addr
);
74 addr6
->sin6_family
= AF_INET6
;
75 addr6
->sin6_port
= htons(cfg_port
);
76 if (inet_pton(AF_INET6
, str_addr
, &(addr6
->sin6_addr
)) != 1)
77 error(1, 0, "ipv6 parse error: %s", str_addr
);
80 error(1, 0, "illegal domain");
84 static unsigned long gettimeofday_ms(void)
88 gettimeofday(&tv
, NULL
);
89 return (tv
.tv_sec
* 1000) + (tv
.tv_usec
/ 1000);
92 static void do_poll(int fd
, int timeout_ms
)
102 ret
= poll(&pfd
, 1, 10);
106 error(1, errno
, "poll");
112 if (timeout_ms
<= 0) {
117 /* no events and more time to wait, do poll again */
120 if (pfd
.revents
!= POLLIN
)
121 error(1, errno
, "poll: 0x%x expected 0x%x\n",
122 pfd
.revents
, POLLIN
);
126 static int do_socket(bool do_tcp
)
130 fd
= socket(cfg_family
, cfg_tcp
? SOCK_STREAM
: SOCK_DGRAM
, 0);
132 error(1, errno
, "socket");
135 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
, &val
, sizeof(val
)))
136 error(1, errno
, "setsockopt rcvbuf");
138 if (setsockopt(fd
, SOL_SOCKET
, SO_REUSEPORT
, &val
, sizeof(val
)))
139 error(1, errno
, "setsockopt reuseport");
141 if (bind(fd
, (void *)&cfg_bind_addr
, cfg_alen
))
142 error(1, errno
, "bind");
147 if (listen(accept_fd
, 1))
148 error(1, errno
, "listen");
150 do_poll(accept_fd
, cfg_connect_timeout_ms
);
154 fd
= accept(accept_fd
, NULL
, NULL
);
156 error(1, errno
, "accept");
157 if (close(accept_fd
))
158 error(1, errno
, "close accept fd");
164 /* Flush all outstanding bytes for the tcp receive queue */
165 static void do_flush_tcp(int fd
)
170 /* MSG_TRUNC flushes up to len bytes */
171 ret
= recv(fd
, NULL
, 1 << 21, MSG_TRUNC
| MSG_DONTWAIT
);
172 if (ret
== -1 && errno
== EAGAIN
)
175 error(1, errno
, "flush");
177 /* client detached */
187 static char sanitized_char(char val
)
189 return (val
>= 'a' && val
<= 'z') ? val
: '.';
192 static void do_verify_udp(const char *data
, int len
)
197 /* verify contents */
198 if (cur
< 'a' || cur
> 'z')
199 error(1, 0, "data initial byte out of range");
201 for (i
= 1; i
< len
; i
++) {
208 error(1, 0, "data[%d]: len %d, %c(%hhu) != %c(%hhu)\n",
210 sanitized_char(data
[i
]), data
[i
],
211 sanitized_char(cur
), cur
);
215 static int recv_msg(int fd
, char *buf
, int len
, int *gso_size
)
217 char control
[CMSG_SPACE(sizeof(int))] = {0};
218 struct msghdr msg
= {0};
219 struct iovec iov
= {0};
220 struct cmsghdr
*cmsg
;
229 msg
.msg_control
= control
;
230 msg
.msg_controllen
= sizeof(control
);
233 ret
= recvmsg(fd
, &msg
, MSG_TRUNC
| MSG_DONTWAIT
);
235 for (cmsg
= CMSG_FIRSTHDR(&msg
); cmsg
!= NULL
;
236 cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
237 if (cmsg
->cmsg_level
== SOL_UDP
238 && cmsg
->cmsg_type
== UDP_GRO
) {
239 *gso_size
= *(int *)CMSG_DATA(cmsg
);
247 /* Flush all outstanding datagrams. Verify first few bytes of each. */
248 static void do_flush_udp(int fd
)
250 static char rbuf
[ETH_MAX_MTU
];
251 int ret
, len
, gso_size
= 0, budget
= 256;
253 len
= cfg_read_all
? sizeof(rbuf
) : 0;
255 /* MSG_TRUNC will make return value full datagram length */
256 if (!cfg_expected_gso_size
)
257 ret
= recv(fd
, rbuf
, len
, MSG_TRUNC
| MSG_DONTWAIT
);
259 ret
= recv_msg(fd
, rbuf
, len
, &gso_size
);
260 if (ret
== -1 && errno
== EAGAIN
)
263 error(1, errno
, "recv");
264 if (cfg_expected_pkt_len
&& ret
!= cfg_expected_pkt_len
)
265 error(1, 0, "recv: bad packet len, got %d,"
266 " expected %d\n", ret
, cfg_expected_pkt_len
);
267 if (len
&& cfg_verify
) {
269 error(1, errno
, "recv: 0 byte datagram\n");
271 do_verify_udp(rbuf
, ret
);
273 if (cfg_expected_gso_size
&& cfg_expected_gso_size
!= gso_size
)
274 error(1, 0, "recv: bad gso size, got %d, expected %d "
275 "(-1 == no gso cmsg))\n", gso_size
,
276 cfg_expected_gso_size
);
280 if (cfg_expected_pkt_nr
&& packets
>= cfg_expected_pkt_nr
)
285 static void usage(const char *filepath
)
287 error(1, 0, "Usage: %s [-C connect_timeout] [-Grtv] [-b addr] [-p port]"
288 " [-l pktlen] [-n packetnr] [-R rcv_timeout] [-S gsosize]",
292 static void parse_opts(int argc
, char **argv
)
294 const char *bind_addr
= NULL
;
297 while ((c
= getopt(argc
, argv
, "4b:C:Gl:n:p:rR:S:tv")) != -1) {
300 cfg_family
= PF_INET
;
301 cfg_alen
= sizeof(struct sockaddr_in
);
307 cfg_connect_timeout_ms
= strtoul(optarg
, NULL
, 0);
310 cfg_gro_segment
= true;
313 cfg_expected_pkt_len
= strtoul(optarg
, NULL
, 0);
316 cfg_expected_pkt_nr
= strtoul(optarg
, NULL
, 0);
319 cfg_port
= strtoul(optarg
, NULL
, 0);
325 cfg_rcv_timeout_ms
= strtoul(optarg
, NULL
, 0);
328 cfg_expected_gso_size
= strtol(optarg
, NULL
, 0);
343 bind_addr
= cfg_family
== PF_INET6
? "::" : "0.0.0.0";
345 setup_sockaddr(cfg_family
, bind_addr
, &cfg_bind_addr
);
350 if (cfg_tcp
&& cfg_verify
)
351 error(1, 0, "TODO: implement verify mode for tcp");
354 static void do_recv(void)
356 int timeout_ms
= cfg_tcp
? cfg_rcv_timeout_ms
: cfg_connect_timeout_ms
;
357 unsigned long tnow
, treport
;
360 fd
= do_socket(cfg_tcp
);
362 if (cfg_gro_segment
&& !cfg_tcp
) {
364 if (setsockopt(fd
, IPPROTO_UDP
, UDP_GRO
, &val
, sizeof(val
)))
365 error(1, errno
, "setsockopt UDP_GRO");
368 treport
= gettimeofday_ms() + 1000;
370 do_poll(fd
, timeout_ms
);
377 tnow
= gettimeofday_ms();
378 if (!cfg_expected_pkt_nr
&& tnow
> treport
) {
381 "%s rx: %6lu MB/s %8lu calls/s\n",
382 cfg_tcp
? "tcp" : "udp",
383 bytes
>> 20, packets
);
385 treport
= tnow
+ 1000;
388 timeout_ms
= cfg_rcv_timeout_ms
;
390 } while (!interrupted
);
392 if (cfg_expected_pkt_nr
&& (packets
!= cfg_expected_pkt_nr
))
393 error(1, 0, "wrong packet number! got %ld, expected %d\n",
394 packets
, cfg_expected_pkt_nr
);
397 error(1, errno
, "close");
400 int main(int argc
, char **argv
)
402 parse_opts(argc
, argv
);
404 signal(SIGINT
, sigint_handler
);