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 if (read(connfd, buf, 64) < 0) 85 perror("read()"); 86 fprintf(outfile, "%d\n", opt); 87 88 fclose(outfile); 89 close(connfd); 90 close(fd); 91 } 92 93 static void run_client(void) 94 { 95 int fd; 96 char *msg = "Hello, world!"; 97 98 fd = socket(AF_INET6, SOCK_STREAM, 0); 99 if (fd == -1) 100 error(1, errno, "socket()"); 101 102 sendto(fd, msg, strlen(msg), MSG_FASTOPEN, (struct sockaddr *)&cfg_addr, sizeof(cfg_addr)); 103 104 close(fd); 105 } 106 107 static void usage(const char *filepath) 108 { 109 error(1, 0, "Usage: %s (-s|-c) -h<server_ip> -p<port> -o<outfile> ", filepath); 110 } 111 112 static void parse_opts(int argc, char **argv) 113 { 114 struct sockaddr_in6 *addr6 = (void *) &cfg_addr; 115 char *addr = NULL; 116 int ret; 117 int c; 118 119 if (argc <= 1) 120 usage(argv[0]); 121 122 while ((c = getopt(argc, argv, "sch:p:o:")) != -1) { 123 switch (c) { 124 case 's': 125 if (cfg_client) 126 error(1, 0, "Pass one of -s or -c"); 127 cfg_server = 1; 128 break; 129 case 'c': 130 if (cfg_server) 131 error(1, 0, "Pass one of -s or -c"); 132 cfg_client = 1; 133 break; 134 case 'h': 135 addr = optarg; 136 break; 137 case 'p': 138 cfg_port = strtoul(optarg, NULL, 0); 139 break; 140 case 'o': 141 cfg_outfile = strdup(optarg); 142 if (!cfg_outfile) 143 error(1, 0, "outfile invalid"); 144 break; 145 } 146 } 147 148 if (cfg_server && addr) 149 error(1, 0, "Server cannot have -h specified"); 150 151 memset(addr6, 0, sizeof(*addr6)); 152 addr6->sin6_family = AF_INET6; 153 addr6->sin6_port = htons(cfg_port); 154 addr6->sin6_addr = in6addr_any; 155 if (addr) { 156 ret = parse_address(addr, cfg_port, addr6); 157 if (ret) 158 error(1, 0, "Client address parse error: %s", addr); 159 } 160 } 161 162 int main(int argc, char **argv) 163 { 164 parse_opts(argc, argv); 165 166 if (cfg_server) 167 run_server(); 168 else if (cfg_client) 169 run_client(); 170 171 return 0; 172 } 173