1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Exercise UDP software checksums over multiple spliced pipe fragments. 4 * All traffic stays on loopback; no privileges or external services are needed. 5 * Exit status: 0 = pass, 1 = failure, 4 = all cases skipped. 6 */ 7 #define _GNU_SOURCE 8 9 #include <arpa/inet.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <poll.h> 13 #include <stdbool.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <sys/socket.h> 18 #include <time.h> 19 #include <unistd.h> 20 21 #include "kselftest.h" 22 23 #define TIMEOUT_MS 200 24 25 struct test_case { 26 const char *name; 27 const char *body; 28 const char *prefix; 29 size_t fragments[3]; 30 bool ordinary; 31 bool page_tail; 32 }; 33 34 static const struct test_case cases[] = { 35 { "ordinary-control", "abcDEFGH", "", { 3, 5 }, true, false }, 36 { "uniform-odd-fragments", "BBBBBBBB", "", { 3, 5 }, false, false }, 37 { "even-fragments", "abcdEFGH", "", { 4, 4 }, false, false }, 38 { "odd-fragments", "abcDEFGH", "", { 3, 5 }, false, false }, 39 { "odd-prefix-odd-fragments", "abcDEFGH", "P", { 3, 5 }, false, false }, 40 { "even-prefix-odd-fragments", "abcDEFGH", "PQ", { 3, 5 }, false, false }, 41 { "page-tail-odd-fragments", "abcDEFGH", "", { 3, 5 }, false, true }, 42 { "three-fragments", "abcDEFGhijkl", "", { 3, 4, 5 }, false, false }, 43 { "odd-prefix-three-fragments", 44 "abcDEFGhijkl", 45 "P", 46 { 3, 4, 5 }, 47 false, 48 false }, 49 }; 50 51 static const char *const families[] = { "IPv4", "IPv6", "IPv4-mapped" }; 52 53 static int error(const char *what) 54 { 55 ksft_perror(what); 56 return 1; 57 } 58 59 static bool unavailable(void) 60 { 61 return errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT || 62 errno == EADDRNOTAVAIL; 63 } 64 65 static long long now_ms(void) 66 { 67 struct timespec ts; 68 69 if (clock_gettime(CLOCK_MONOTONIC, &ts)) 70 ksft_exit_fail_perror("clock_gettime"); 71 return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; 72 } 73 74 static int receive_one(int fd, const char *expected, size_t length) 75 { 76 struct pollfd pfd = { .fd = fd, .events = POLLIN }; 77 long long deadline = now_ms() + TIMEOUT_MS; 78 int remaining, ret; 79 char received[64]; 80 ssize_t count; 81 82 for (;;) { 83 remaining = deadline - now_ms(); 84 if (remaining < 0) 85 remaining = 0; 86 ret = poll(&pfd, 1, remaining); 87 if (ret < 0) { 88 if (errno == EINTR) 89 continue; 90 return error("poll"); 91 } 92 if (!ret) { 93 ksft_print_msg("no datagram within %d ms after send\n", 94 TIMEOUT_MS); 95 return 1; 96 } 97 count = recv(fd, received, sizeof(received), 98 MSG_DONTWAIT | MSG_TRUNC); 99 if (count < 0) { 100 if (errno == EINTR || errno == EAGAIN) 101 continue; 102 return error("recv"); 103 } 104 break; 105 } 106 if (count != (ssize_t)length || memcmp(received, expected, length)) { 107 ksft_print_msg("received %zd bytes; expected %zu matching\n", 108 count, length); 109 return 1; 110 } 111 count = recv(fd, received, sizeof(received), MSG_DONTWAIT | MSG_TRUNC); 112 if (count >= 0) { 113 ksft_print_msg("unexpected extra datagram (%zd bytes)\n", 114 count); 115 return 1; 116 } 117 if (errno != EAGAIN) 118 return error("extra-datagram check"); 119 return 0; 120 } 121 122 /* Separate source pipes prevent adjacent small writes merging into one buffer. */ 123 static int append_fragment(int dest, const char *data, size_t length) 124 { 125 int source[2], ret = 1; 126 127 if (pipe2(source, O_NONBLOCK | O_CLOEXEC)) 128 return error("source pipe"); 129 if (write(source[1], data, length) != (ssize_t)length) { 130 error("source write"); 131 goto out; 132 } 133 if (splice(source[0], NULL, dest, NULL, length, SPLICE_F_NONBLOCK) != 134 (ssize_t)length) { 135 error("pipe-to-pipe splice"); 136 goto out; 137 } 138 ret = 0; 139 out: 140 close(source[0]); 141 close(source[1]); 142 return ret; 143 } 144 145 static int pipe_capacity(int fd, unsigned int slots, const char **skip_reason) 146 { 147 long required = slots * sysconf(_SC_PAGESIZE); 148 int capacity = fcntl(fd, F_GETPIPE_SZ); 149 150 if (capacity < 0) 151 return error("query pipe capacity"); 152 if (capacity >= required || 153 fcntl(fd, F_SETPIPE_SZ, required) >= required) 154 return 0; 155 ksft_print_msg("cannot reserve %u pipe slots: %s\n", slots, 156 strerror(errno)); 157 *skip_reason = "required pipe capacity unavailable"; 158 return KSFT_SKIP; 159 } 160 161 /* Leave three bytes at a page's end, followed by five bytes on the next page. */ 162 static int append_page_tail(int dest, const char *body, 163 const char **skip_reason) 164 { 165 long page = sysconf(_SC_PAGESIZE); 166 int source[2], ret = 1; 167 char *data; 168 169 if (page < 8) { 170 ksft_print_msg("invalid page size %ld\n", page); 171 return 1; 172 } 173 data = malloc(page + 5); 174 if (!data) 175 return error("malloc"); 176 memset(data, 'x', page - 3); 177 memcpy(data + page - 3, body, 8); 178 if (pipe2(source, O_NONBLOCK | O_CLOEXEC)) { 179 error("page-tail source pipe"); 180 goto free_data; 181 } 182 ret = pipe_capacity(source[1], 2, skip_reason); 183 if (ret) 184 goto out; 185 ret = 1; 186 if (write(source[1], data, page + 5) != page + 5) { 187 error("page-tail write"); 188 goto out; 189 } 190 if (read(source[0], data, page - 3) != page - 3) { 191 error("page-tail discard"); 192 goto out; 193 } 194 if (splice(source[0], NULL, dest, NULL, 8, SPLICE_F_NONBLOCK) != 8) { 195 error("page-tail pipe-to-pipe splice"); 196 goto out; 197 } 198 ret = 0; 199 out: 200 close(source[0]); 201 close(source[1]); 202 free_data: 203 free(data); 204 return ret; 205 } 206 207 static int run_case(unsigned int family, bool connected, 208 const struct test_case *test, const char **skip_reason) 209 { 210 size_t body_length = strlen(test->body), offset = 0; 211 int tx_family = family == 0 ? AF_INET : AF_INET6; 212 int rx_family = family == 1 ? AF_INET6 : AF_INET; 213 int rx = -1, tx = -1, pipefd[2] = { -1, -1 }; 214 size_t prefix_length = strlen(test->prefix); 215 struct sockaddr_storage address = { 0 }; 216 socklen_t address_length; 217 struct sockaddr_in6 *v6; 218 struct sockaddr_in *v4; 219 int ret = 1, zero = 0; 220 char expected[64]; 221 unsigned int i; 222 ssize_t count; 223 224 v4 = (struct sockaddr_in *)&address; 225 v6 = (struct sockaddr_in6 *)&address; 226 *skip_reason = "address family unavailable"; 227 memcpy(expected, test->prefix, prefix_length); 228 memcpy(expected + prefix_length, test->body, body_length); 229 rx = socket(rx_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); 230 if (rx < 0) { 231 ret = unavailable() ? KSFT_SKIP : error("receive socket"); 232 goto out; 233 } 234 if (rx_family == AF_INET) { 235 v4->sin_family = AF_INET; 236 v4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); 237 address_length = sizeof(*v4); 238 } else { 239 v6->sin6_family = AF_INET6; 240 v6->sin6_addr = in6addr_loopback; 241 address_length = sizeof(*v6); 242 } 243 if (bind(rx, (struct sockaddr *)&address, address_length)) { 244 ret = unavailable() ? KSFT_SKIP : error("bind loopback"); 245 goto out; 246 } 247 if (getsockname(rx, (struct sockaddr *)&address, &address_length)) { 248 error("getsockname"); 249 goto out; 250 } 251 if (family == 2) { 252 unsigned short port = v4->sin_port; 253 254 memset(&address, 0, sizeof(address)); 255 v6->sin6_family = AF_INET6; 256 v6->sin6_port = port; 257 inet_pton(AF_INET6, "::ffff:127.0.0.1", &v6->sin6_addr); 258 address_length = sizeof(*v6); 259 } 260 tx = socket(tx_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); 261 if (tx < 0) { 262 ret = unavailable() ? KSFT_SKIP : error("send socket"); 263 goto out; 264 } 265 if (family == 2 && 266 setsockopt(tx, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero))) { 267 error("disable IPV6_V6ONLY"); 268 goto out; 269 } 270 if (connected && 271 connect(tx, (struct sockaddr *)&address, address_length)) { 272 error("connect"); 273 goto out; 274 } 275 if (test->ordinary) { 276 count = sendto(tx, expected, prefix_length + body_length, 0, 277 (struct sockaddr *)&address, address_length); 278 if (count != (ssize_t)(prefix_length + body_length)) { 279 error("ordinary sendto"); 280 goto out; 281 } 282 } else { 283 if (pipe2(pipefd, O_NONBLOCK | O_CLOEXEC)) { 284 error("assembled pipe"); 285 goto out; 286 } 287 ret = pipe_capacity(pipefd[1], test->fragments[2] ? 3 : 2, 288 skip_reason); 289 if (ret) 290 goto out; 291 ret = 1; 292 if (test->page_tail) { 293 ret = append_page_tail(pipefd[1], test->body, 294 skip_reason); 295 if (ret) 296 goto out; 297 ret = 1; 298 } else { 299 for (i = 0; i < ARRAY_SIZE(test->fragments); i++) { 300 if (!test->fragments[i]) 301 break; 302 if (append_fragment(pipefd[1], 303 test->body + offset, 304 test->fragments[i])) 305 goto out; 306 offset += test->fragments[i]; 307 } 308 } 309 /* Even an empty prefix establishes the destination and UDP cork. */ 310 count = sendto(tx, test->prefix, prefix_length, MSG_MORE, 311 (struct sockaddr *)&address, address_length); 312 if (count != (ssize_t)prefix_length) { 313 error("prime sendto(MSG_MORE)"); 314 goto out; 315 } 316 count = splice(pipefd[0], NULL, tx, NULL, body_length, 317 SPLICE_F_NONBLOCK | SPLICE_F_MORE); 318 if (count != (ssize_t)body_length) { 319 ksft_print_msg("socket splice: %zd, expected %zu: %s\n", 320 count, body_length, 321 count < 0 ? strerror(errno) : 322 "short transfer"); 323 goto out; 324 } 325 if (sendto(tx, "", 0, 0, (struct sockaddr *)&address, 326 address_length)) { 327 error("commit sendto"); 328 goto out; 329 } 330 } 331 ret = receive_one(rx, expected, prefix_length + body_length); 332 out: 333 if (pipefd[0] >= 0) { 334 close(pipefd[0]); 335 close(pipefd[1]); 336 } 337 if (tx >= 0) 338 close(tx); 339 if (rx >= 0) 340 close(rx); 341 return ret; 342 } 343 344 int main(void) 345 { 346 unsigned int family, connected, i; 347 const char *connection, *skip; 348 int ret; 349 350 ksft_print_header(); 351 ksft_set_plan(ARRAY_SIZE(families) * 2 * ARRAY_SIZE(cases)); 352 for (family = 0; family < ARRAY_SIZE(families); family++) { 353 for (connected = 0; connected < 2; connected++) { 354 connection = connected ? "connected" : "unconnected"; 355 for (i = 0; i < ARRAY_SIZE(cases); i++) { 356 ret = run_case(family, connected, &cases[i], 357 &skip); 358 if (ret == KSFT_SKIP) 359 ksft_test_result_skip("%s %s %s: %s\n", 360 families[family], 361 connection, 362 cases[i].name, 363 skip); 364 else 365 ksft_test_result(!ret, "%s %s %s\n", 366 families[family], 367 connection, 368 cases[i].name); 369 } 370 } 371 } 372 if (!ksft_get_pass_cnt() && !ksft_get_fail_cnt()) { 373 /* ksft_exit_skip() would add a result beyond the plan. */ 374 ksft_print_cnts(); 375 return KSFT_SKIP; 376 } 377 ksft_finished(); 378 } 379