1*c65b5bb2SDavid Wei // SPDX-License-Identifier: GPL-2.0
2*c65b5bb2SDavid Wei #include <error.h>
3*c65b5bb2SDavid Wei #include <fcntl.h>
4*c65b5bb2SDavid Wei #include <limits.h>
5*c65b5bb2SDavid Wei #include <stdbool.h>
6*c65b5bb2SDavid Wei #include <stdint.h>
7*c65b5bb2SDavid Wei #include <stdio.h>
8*c65b5bb2SDavid Wei #include <stdlib.h>
9*c65b5bb2SDavid Wei #include <string.h>
10*c65b5bb2SDavid Wei #include <unistd.h>
11*c65b5bb2SDavid Wei #include <arpa/inet.h>
12*c65b5bb2SDavid Wei #include <sys/socket.h>
13*c65b5bb2SDavid Wei #include <netinet/tcp.h>
14*c65b5bb2SDavid Wei #include <errno.h>
15*c65b5bb2SDavid Wei
16*c65b5bb2SDavid Wei static int cfg_server;
17*c65b5bb2SDavid Wei static int cfg_client;
18*c65b5bb2SDavid Wei static int cfg_port = 8000;
19*c65b5bb2SDavid Wei static struct sockaddr_in6 cfg_addr;
20*c65b5bb2SDavid Wei static char *cfg_outfile;
21*c65b5bb2SDavid Wei
parse_address(const char * str,int port,struct sockaddr_in6 * sin6)22*c65b5bb2SDavid Wei static int parse_address(const char *str, int port, struct sockaddr_in6 *sin6)
23*c65b5bb2SDavid Wei {
24*c65b5bb2SDavid Wei int ret;
25*c65b5bb2SDavid Wei
26*c65b5bb2SDavid Wei sin6->sin6_family = AF_INET6;
27*c65b5bb2SDavid Wei sin6->sin6_port = htons(port);
28*c65b5bb2SDavid Wei
29*c65b5bb2SDavid Wei ret = inet_pton(sin6->sin6_family, str, &sin6->sin6_addr);
30*c65b5bb2SDavid Wei if (ret != 1) {
31*c65b5bb2SDavid Wei /* fallback to plain IPv4 */
32*c65b5bb2SDavid Wei ret = inet_pton(AF_INET, str, &sin6->sin6_addr.s6_addr32[3]);
33*c65b5bb2SDavid Wei if (ret != 1)
34*c65b5bb2SDavid Wei return -1;
35*c65b5bb2SDavid Wei
36*c65b5bb2SDavid Wei /* add ::ffff prefix */
37*c65b5bb2SDavid Wei sin6->sin6_addr.s6_addr32[0] = 0;
38*c65b5bb2SDavid Wei sin6->sin6_addr.s6_addr32[1] = 0;
39*c65b5bb2SDavid Wei sin6->sin6_addr.s6_addr16[4] = 0;
40*c65b5bb2SDavid Wei sin6->sin6_addr.s6_addr16[5] = 0xffff;
41*c65b5bb2SDavid Wei }
42*c65b5bb2SDavid Wei
43*c65b5bb2SDavid Wei return 0;
44*c65b5bb2SDavid Wei }
45*c65b5bb2SDavid Wei
run_server(void)46*c65b5bb2SDavid Wei static void run_server(void)
47*c65b5bb2SDavid Wei {
48*c65b5bb2SDavid Wei unsigned long qlen = 32;
49*c65b5bb2SDavid Wei int fd, opt, connfd;
50*c65b5bb2SDavid Wei socklen_t len;
51*c65b5bb2SDavid Wei char buf[64];
52*c65b5bb2SDavid Wei FILE *outfile;
53*c65b5bb2SDavid Wei
54*c65b5bb2SDavid Wei outfile = fopen(cfg_outfile, "w");
55*c65b5bb2SDavid Wei if (!outfile)
56*c65b5bb2SDavid Wei error(1, errno, "fopen() outfile");
57*c65b5bb2SDavid Wei
58*c65b5bb2SDavid Wei fd = socket(AF_INET6, SOCK_STREAM, 0);
59*c65b5bb2SDavid Wei if (fd == -1)
60*c65b5bb2SDavid Wei error(1, errno, "socket()");
61*c65b5bb2SDavid Wei
62*c65b5bb2SDavid Wei opt = 1;
63*c65b5bb2SDavid Wei if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
64*c65b5bb2SDavid Wei error(1, errno, "setsockopt(SO_REUSEADDR)");
65*c65b5bb2SDavid Wei
66*c65b5bb2SDavid Wei if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)) < 0)
67*c65b5bb2SDavid Wei error(1, errno, "setsockopt(TCP_FASTOPEN)");
68*c65b5bb2SDavid Wei
69*c65b5bb2SDavid Wei if (bind(fd, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr)) < 0)
70*c65b5bb2SDavid Wei error(1, errno, "bind()");
71*c65b5bb2SDavid Wei
72*c65b5bb2SDavid Wei if (listen(fd, 5) < 0)
73*c65b5bb2SDavid Wei error(1, errno, "listen()");
74*c65b5bb2SDavid Wei
75*c65b5bb2SDavid Wei len = sizeof(cfg_addr);
76*c65b5bb2SDavid Wei connfd = accept(fd, (struct sockaddr *)&cfg_addr, &len);
77*c65b5bb2SDavid Wei if (connfd < 0)
78*c65b5bb2SDavid Wei error(1, errno, "accept()");
79*c65b5bb2SDavid Wei
80*c65b5bb2SDavid Wei len = sizeof(opt);
81*c65b5bb2SDavid Wei if (getsockopt(connfd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &opt, &len) < 0)
82*c65b5bb2SDavid Wei error(1, errno, "getsockopt(SO_INCOMING_NAPI_ID)");
83*c65b5bb2SDavid Wei
84*c65b5bb2SDavid Wei read(connfd, buf, 64);
85*c65b5bb2SDavid Wei fprintf(outfile, "%d\n", opt);
86*c65b5bb2SDavid Wei
87*c65b5bb2SDavid Wei fclose(outfile);
88*c65b5bb2SDavid Wei close(connfd);
89*c65b5bb2SDavid Wei close(fd);
90*c65b5bb2SDavid Wei }
91*c65b5bb2SDavid Wei
run_client(void)92*c65b5bb2SDavid Wei static void run_client(void)
93*c65b5bb2SDavid Wei {
94*c65b5bb2SDavid Wei int fd;
95*c65b5bb2SDavid Wei char *msg = "Hello, world!";
96*c65b5bb2SDavid Wei
97*c65b5bb2SDavid Wei fd = socket(AF_INET6, SOCK_STREAM, 0);
98*c65b5bb2SDavid Wei if (fd == -1)
99*c65b5bb2SDavid Wei error(1, errno, "socket()");
100*c65b5bb2SDavid Wei
101*c65b5bb2SDavid Wei sendto(fd, msg, strlen(msg), MSG_FASTOPEN, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr));
102*c65b5bb2SDavid Wei
103*c65b5bb2SDavid Wei close(fd);
104*c65b5bb2SDavid Wei }
105*c65b5bb2SDavid Wei
usage(const char * filepath)106*c65b5bb2SDavid Wei static void usage(const char *filepath)
107*c65b5bb2SDavid Wei {
108*c65b5bb2SDavid Wei error(1, 0, "Usage: %s (-s|-c) -h<server_ip> -p<port> -o<outfile> ", filepath);
109*c65b5bb2SDavid Wei }
110*c65b5bb2SDavid Wei
parse_opts(int argc,char ** argv)111*c65b5bb2SDavid Wei static void parse_opts(int argc, char **argv)
112*c65b5bb2SDavid Wei {
113*c65b5bb2SDavid Wei struct sockaddr_in6 *addr6 = (void *) &cfg_addr;
114*c65b5bb2SDavid Wei char *addr = NULL;
115*c65b5bb2SDavid Wei int ret;
116*c65b5bb2SDavid Wei int c;
117*c65b5bb2SDavid Wei
118*c65b5bb2SDavid Wei if (argc <= 1)
119*c65b5bb2SDavid Wei usage(argv[0]);
120*c65b5bb2SDavid Wei
121*c65b5bb2SDavid Wei while ((c = getopt(argc, argv, "sch:p:o:")) != -1) {
122*c65b5bb2SDavid Wei switch (c) {
123*c65b5bb2SDavid Wei case 's':
124*c65b5bb2SDavid Wei if (cfg_client)
125*c65b5bb2SDavid Wei error(1, 0, "Pass one of -s or -c");
126*c65b5bb2SDavid Wei cfg_server = 1;
127*c65b5bb2SDavid Wei break;
128*c65b5bb2SDavid Wei case 'c':
129*c65b5bb2SDavid Wei if (cfg_server)
130*c65b5bb2SDavid Wei error(1, 0, "Pass one of -s or -c");
131*c65b5bb2SDavid Wei cfg_client = 1;
132*c65b5bb2SDavid Wei break;
133*c65b5bb2SDavid Wei case 'h':
134*c65b5bb2SDavid Wei addr = optarg;
135*c65b5bb2SDavid Wei break;
136*c65b5bb2SDavid Wei case 'p':
137*c65b5bb2SDavid Wei cfg_port = strtoul(optarg, NULL, 0);
138*c65b5bb2SDavid Wei break;
139*c65b5bb2SDavid Wei case 'o':
140*c65b5bb2SDavid Wei cfg_outfile = strdup(optarg);
141*c65b5bb2SDavid Wei if (!cfg_outfile)
142*c65b5bb2SDavid Wei error(1, 0, "outfile invalid");
143*c65b5bb2SDavid Wei break;
144*c65b5bb2SDavid Wei }
145*c65b5bb2SDavid Wei }
146*c65b5bb2SDavid Wei
147*c65b5bb2SDavid Wei if (cfg_server && addr)
148*c65b5bb2SDavid Wei error(1, 0, "Server cannot have -h specified");
149*c65b5bb2SDavid Wei
150*c65b5bb2SDavid Wei memset(addr6, 0, sizeof(*addr6));
151*c65b5bb2SDavid Wei addr6->sin6_family = AF_INET6;
152*c65b5bb2SDavid Wei addr6->sin6_port = htons(cfg_port);
153*c65b5bb2SDavid Wei addr6->sin6_addr = in6addr_any;
154*c65b5bb2SDavid Wei if (addr) {
155*c65b5bb2SDavid Wei ret = parse_address(addr, cfg_port, addr6);
156*c65b5bb2SDavid Wei if (ret)
157*c65b5bb2SDavid Wei error(1, 0, "Client address parse error: %s", addr);
158*c65b5bb2SDavid Wei }
159*c65b5bb2SDavid Wei }
160*c65b5bb2SDavid Wei
main(int argc,char ** argv)161*c65b5bb2SDavid Wei int main(int argc, char **argv)
162*c65b5bb2SDavid Wei {
163*c65b5bb2SDavid Wei parse_opts(argc, argv);
164*c65b5bb2SDavid Wei
165*c65b5bb2SDavid Wei if (cfg_server)
166*c65b5bb2SDavid Wei run_server();
167*c65b5bb2SDavid Wei else if (cfg_client)
168*c65b5bb2SDavid Wei run_client();
169*c65b5bb2SDavid Wei
170*c65b5bb2SDavid Wei return 0;
171*c65b5bb2SDavid Wei }
172