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