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