1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Quick test for getsockopt{_iter} tests. 4 * 5 * Each fixture targets one converted protocol and pins down the 6 * returned-length / errno semantics across buffer-size variations, 7 * an unknown optname and a bogus level. 8 * 9 * - netlink: NETLINK_PKTINFO covers the flag-style int path; the 10 * NETLINK_LIST_MEMBERSHIPS cases cover the size-discovery path 11 * that always reports the required buffer length back via optlen, 12 * even when the user buffer is too small to receive any group bits. 13 * - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path. 14 * - raw: ICMP_FILTER covers a fixed-size struct payload that clamps 15 * the length down on a short buffer instead of failing. 16 * 17 * Author: Breno Leitao <leitao@debian.org> 18 */ 19 20 #include <errno.h> 21 #include <stdint.h> 22 #include <stdio.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <linux/netlink.h> 26 #include <linux/rtnetlink.h> 27 #include <linux/time_types.h> 28 #include <linux/vm_sockets.h> 29 #include <linux/icmp.h> 30 #include <netinet/in.h> 31 #include <netinet/tcp.h> 32 #include <arpa/inet.h> 33 #include <sys/socket.h> 34 #include <linux/tls.h> 35 #include "kselftest_harness.h" 36 37 #ifndef AF_VSOCK 38 #define AF_VSOCK 40 39 #endif 40 #ifndef SOL_RAW 41 #define SOL_RAW 255 42 #endif 43 #ifndef ICMP_FILTER 44 #define ICMP_FILTER 1 45 #endif 46 #ifndef IPV6_HDRINCL 47 #define IPV6_HDRINCL 36 48 #endif 49 #ifndef IPV6_CHECKSUM 50 #define IPV6_CHECKSUM 7 51 #endif 52 #ifndef SOL_TLS 53 #define SOL_TLS 282 54 #endif 55 #ifndef TCP_ULP 56 #define TCP_ULP 31 57 #endif 58 59 /* ---------- netlink ---------- */ 60 61 FIXTURE(netlink) 62 { 63 int fd; 64 }; 65 66 FIXTURE_SETUP(netlink) 67 { 68 int group = RTNLGRP_LINK; 69 70 self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 71 if (self->fd < 0) 72 SKIP(return, "AF_NETLINK socket: %s", strerror(errno)); 73 74 /* Joining a multicast group grows nlk->ngroups so the 75 * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report. 76 */ 77 if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, 78 &group, sizeof(group)) < 0) 79 SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno)); 80 } 81 82 FIXTURE_TEARDOWN(netlink) 83 { 84 if (self->fd >= 0) 85 close(self->fd); 86 } 87 88 TEST_F(netlink, pktinfo_exact) 89 { 90 socklen_t optlen; 91 int val = -1; 92 93 optlen = sizeof(val); 94 95 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, 96 &val, &optlen)); 97 ASSERT_EQ(sizeof(int), optlen); 98 ASSERT_TRUE(val == 0 || val == 1); 99 } 100 101 TEST_F(netlink, pktinfo_oversize_clamped) 102 { 103 char buf[16] = {}; 104 socklen_t optlen; 105 106 optlen = sizeof(buf); 107 108 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, 109 buf, &optlen)); 110 ASSERT_EQ(sizeof(int), optlen); 111 } 112 113 TEST_F(netlink, pktinfo_undersize) 114 { 115 char buf[2] = {}; 116 socklen_t optlen; 117 118 optlen = sizeof(buf); 119 120 ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, 121 buf, &optlen)); 122 ASSERT_EQ(EINVAL, errno); 123 ASSERT_EQ(sizeof(buf), optlen); 124 } 125 126 TEST_F(netlink, list_memberships_size_discovery) 127 { 128 socklen_t optlen = 0; 129 char dummy; 130 131 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, 132 NETLINK_LIST_MEMBERSHIPS, 133 &dummy, &optlen)); 134 ASSERT_GT(optlen, 0); 135 ASSERT_EQ(0, optlen % sizeof(__u32)); 136 } 137 138 TEST_F(netlink, list_memberships_full_read) 139 { 140 __u32 buf[64] = {}; 141 socklen_t optlen; 142 143 optlen = sizeof(buf); 144 145 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, 146 NETLINK_LIST_MEMBERSHIPS, 147 buf, &optlen)); 148 ASSERT_GT(optlen, 0); 149 ASSERT_LE(optlen, sizeof(buf)); 150 ASSERT_EQ(0, optlen % sizeof(__u32)); 151 } 152 153 TEST_F(netlink, bad_level) 154 { 155 socklen_t optlen; 156 int val; 157 158 optlen = sizeof(val); 159 160 ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO, 161 &val, &optlen)); 162 ASSERT_EQ(ENOPROTOOPT, errno); 163 ASSERT_EQ(sizeof(val), optlen); 164 } 165 166 TEST_F(netlink, bad_optname) 167 { 168 socklen_t optlen; 169 int val; 170 171 optlen = sizeof(val); 172 173 ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff, 174 &val, &optlen)); 175 ASSERT_EQ(ENOPROTOOPT, errno); 176 ASSERT_EQ(sizeof(val), optlen); 177 } 178 179 /* ---------- vsock ---------- */ 180 181 FIXTURE(vsock) 182 { 183 int fd; 184 }; 185 186 FIXTURE_SETUP(vsock) 187 { 188 self->fd = socket(AF_VSOCK, SOCK_STREAM, 0); 189 if (self->fd < 0) 190 SKIP(return, "AF_VSOCK socket: %s", strerror(errno)); 191 } 192 193 FIXTURE_TEARDOWN(vsock) 194 { 195 if (self->fd >= 0) 196 close(self->fd); 197 } 198 199 TEST_F(vsock, buffer_size_exact) 200 { 201 socklen_t optlen; 202 uint64_t val = 0; 203 204 optlen = sizeof(val); 205 206 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, 207 SO_VM_SOCKETS_BUFFER_SIZE, 208 &val, &optlen)); 209 ASSERT_EQ(sizeof(uint64_t), optlen); 210 ASSERT_GT(val, 0); 211 } 212 213 TEST_F(vsock, buffer_size_oversize_clamped) 214 { 215 char buf[16] = {}; 216 socklen_t optlen; 217 218 optlen = sizeof(buf); 219 220 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, 221 SO_VM_SOCKETS_BUFFER_SIZE, 222 buf, &optlen)); 223 ASSERT_EQ(sizeof(uint64_t), optlen); 224 } 225 226 TEST_F(vsock, buffer_size_undersize) 227 { 228 char buf[4] = {}; 229 socklen_t optlen; 230 231 optlen = sizeof(buf); 232 233 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 234 SO_VM_SOCKETS_BUFFER_SIZE, 235 buf, &optlen)); 236 ASSERT_EQ(EINVAL, errno); 237 ASSERT_EQ(sizeof(buf), optlen); 238 } 239 240 TEST_F(vsock, bad_level) 241 { 242 socklen_t optlen; 243 uint64_t val; 244 245 optlen = sizeof(val); 246 247 ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, 248 SO_VM_SOCKETS_BUFFER_SIZE, 249 &val, &optlen)); 250 ASSERT_EQ(ENOPROTOOPT, errno); 251 ASSERT_EQ(sizeof(val), optlen); 252 } 253 254 TEST_F(vsock, bad_optname) 255 { 256 socklen_t optlen; 257 uint64_t val; 258 259 optlen = sizeof(val); 260 261 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff, 262 &val, &optlen)); 263 ASSERT_EQ(ENOPROTOOPT, errno); 264 ASSERT_EQ(sizeof(val), optlen); 265 } 266 267 /* SO_VM_SOCKETS_CONNECT_TIMEOUT_{NEW,OLD} return a sock_timeval-shaped 268 * payload, which is wider than u64 on 64-bit. They exercise the path 269 * where the protocol's reported lv (16 bytes) is larger than the 270 * common 8-byte u64 case covered above. 271 */ 272 TEST_F(vsock, connect_timeout_new_exact) 273 { 274 struct __kernel_sock_timeval tv = {}; 275 socklen_t optlen; 276 277 optlen = sizeof(tv); 278 279 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, 280 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, 281 &tv, &optlen)); 282 ASSERT_EQ(sizeof(tv), optlen); 283 } 284 285 TEST_F(vsock, connect_timeout_new_oversize_clamped) 286 { 287 char buf[sizeof(struct __kernel_sock_timeval) * 2] = {}; 288 socklen_t optlen; 289 290 optlen = sizeof(buf); 291 292 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, 293 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, 294 buf, &optlen)); 295 ASSERT_EQ(sizeof(struct __kernel_sock_timeval), optlen); 296 } 297 298 TEST_F(vsock, connect_timeout_new_undersize) 299 { 300 socklen_t optlen; 301 uint64_t val; 302 303 optlen = sizeof(val); 304 305 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 306 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW, 307 &val, &optlen)); 308 ASSERT_EQ(EINVAL, errno); 309 ASSERT_EQ(sizeof(val), optlen); 310 } 311 312 TEST_F(vsock, connect_timeout_old_exact) 313 { 314 struct __kernel_old_timeval tv = {}; 315 socklen_t optlen; 316 317 optlen = sizeof(tv); 318 319 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, 320 SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD, 321 &tv, &optlen)); 322 ASSERT_EQ(sizeof(tv), optlen); 323 } 324 325 /* ---------- raw (ipv4) ---------- */ 326 327 FIXTURE(raw) 328 { 329 int fd; 330 }; 331 332 FIXTURE_SETUP(raw) 333 { 334 struct icmp_filter filt = { .data = 0xdeadbeef }; 335 336 self->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 337 if (self->fd < 0) 338 SKIP(return, "SOCK_RAW/ICMP socket: %s", strerror(errno)); 339 340 if (setsockopt(self->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof(filt)) < 0) 341 SKIP(return, "set ICMP_FILTER: %s", strerror(errno)); 342 } 343 344 FIXTURE_TEARDOWN(raw) 345 { 346 if (self->fd >= 0) 347 close(self->fd); 348 } 349 350 TEST_F(raw, icmpfilter_exact) 351 { 352 struct icmp_filter filt = {}; 353 socklen_t optlen = sizeof(filt); 354 355 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER, 356 &filt, &optlen)); 357 ASSERT_EQ(sizeof(filt), optlen); 358 ASSERT_EQ(0xdeadbeef, filt.data); 359 } 360 361 TEST_F(raw, icmpfilter_oversize_clamped) 362 { 363 char buf[16] = {}; 364 socklen_t optlen = sizeof(buf); 365 366 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER, 367 buf, &optlen)); 368 ASSERT_EQ(sizeof(struct icmp_filter), optlen); 369 } 370 371 /* Unlike the int/u64 options above, ICMP_FILTER clamps the length down 372 * to the user buffer instead of returning EINVAL: a short buffer 373 * succeeds and reports the truncated length back via optlen. 374 */ 375 TEST_F(raw, icmpfilter_undersize_clamped) 376 { 377 char buf[2] = {}; 378 socklen_t optlen = sizeof(buf); 379 380 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER, 381 buf, &optlen)); 382 ASSERT_EQ(sizeof(buf), optlen); 383 } 384 385 TEST_F(raw, icmpfilter_wrong_proto) 386 { 387 struct icmp_filter filt; 388 socklen_t optlen = sizeof(filt); 389 int fd; 390 391 fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); 392 if (fd < 0) 393 SKIP(return, "SOCK_RAW/UDP socket: %s", strerror(errno)); 394 395 ASSERT_EQ(-1, getsockopt(fd, SOL_RAW, ICMP_FILTER, &filt, &optlen)); 396 ASSERT_EQ(EOPNOTSUPP, errno); 397 close(fd); 398 } 399 400 TEST_F(raw, bad_optname) 401 { 402 socklen_t optlen; 403 int val; 404 405 optlen = sizeof(val); 406 407 ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen)); 408 ASSERT_EQ(ENOPROTOOPT, errno); 409 ASSERT_EQ(sizeof(val), optlen); 410 } 411 412 /* ---------- raw (ipv6) ---------- */ 413 414 FIXTURE(rawv6) 415 { 416 int fd; 417 }; 418 419 FIXTURE_SETUP(rawv6) 420 { 421 self->fd = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP); 422 if (self->fd < 0) 423 SKIP(return, "SOCK_RAW/IPv6 socket: %s", strerror(errno)); 424 } 425 426 FIXTURE_TEARDOWN(rawv6) 427 { 428 if (self->fd >= 0) 429 close(self->fd); 430 } 431 432 TEST_F(rawv6, hdrincl_exact) 433 { 434 socklen_t optlen; 435 int val = -1; 436 437 optlen = sizeof(val); 438 439 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL, 440 &val, &optlen)); 441 ASSERT_EQ(sizeof(int), optlen); 442 ASSERT_TRUE(val == 0 || val == 1); 443 } 444 445 TEST_F(rawv6, hdrincl_oversize_clamped) 446 { 447 char buf[16] = {}; 448 socklen_t optlen = sizeof(buf); 449 450 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL, 451 buf, &optlen)); 452 ASSERT_EQ(sizeof(int), optlen); 453 } 454 455 /* Raw int options clamp the reported length down to the user buffer 456 * instead of returning EINVAL on a short buffer. 457 */ 458 TEST_F(rawv6, hdrincl_undersize_clamped) 459 { 460 socklen_t optlen = 2; 461 int val = 0; 462 463 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL, 464 &val, &optlen)); 465 ASSERT_EQ(2, optlen); 466 } 467 468 TEST_F(rawv6, checksum_default) 469 { 470 socklen_t optlen; 471 int val = 0; 472 473 optlen = sizeof(val); 474 475 /* A non-ICMPv6 raw socket has the checksum disabled, reported as -1. */ 476 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_CHECKSUM, 477 &val, &optlen)); 478 ASSERT_EQ(sizeof(int), optlen); 479 ASSERT_EQ(-1, val); 480 } 481 482 TEST_F(rawv6, bad_optname) 483 { 484 socklen_t optlen; 485 int val; 486 487 optlen = sizeof(val); 488 489 /* SOL_RAW reaches do_rawv6_getsockopt() directly. */ 490 ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen)); 491 ASSERT_EQ(ENOPROTOOPT, errno); 492 ASSERT_EQ(sizeof(val), optlen); 493 } 494 495 /* ---------- tls ---------- */ 496 497 FIXTURE(tls) 498 { 499 int fd; 500 int sfd; 501 }; 502 503 FIXTURE_SETUP(tls) 504 { 505 struct sockaddr_in a = { 506 .sin_family = AF_INET, 507 .sin_addr.s_addr = htonl(INADDR_LOOPBACK), 508 }; 509 socklen_t alen = sizeof(a); 510 int lfd; 511 512 self->fd = -1; 513 self->sfd = -1; 514 515 lfd = socket(AF_INET, SOCK_STREAM, 0); 516 if (lfd < 0) 517 SKIP(return, "TCP socket: %s", strerror(errno)); 518 if (bind(lfd, (struct sockaddr *)&a, sizeof(a)) || listen(lfd, 1) || 519 getsockname(lfd, (struct sockaddr *)&a, &alen)) { 520 close(lfd); 521 SKIP(return, "listener setup: %s", strerror(errno)); 522 } 523 self->fd = socket(AF_INET, SOCK_STREAM, 0); 524 if (self->fd < 0) { 525 close(lfd); 526 SKIP(return, "TCP socket: %s", strerror(errno)); 527 } 528 if (connect(self->fd, (struct sockaddr *)&a, sizeof(a))) { 529 close(lfd); 530 SKIP(return, "connect: %s", strerror(errno)); 531 } 532 self->sfd = accept(lfd, NULL, NULL); 533 close(lfd); 534 if (setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"))) 535 SKIP(return, "TCP_ULP=tls: %s (built without TLS?)", 536 strerror(errno)); 537 } 538 539 FIXTURE_TEARDOWN(tls) 540 { 541 if (self->fd >= 0) 542 close(self->fd); 543 if (self->sfd >= 0) 544 close(self->sfd); 545 } 546 547 /* do_tls_getsockopt_tx_zc(): fixed-size int, exact length required. */ 548 TEST_F(tls, tx_zerocopy_exact) 549 { 550 socklen_t optlen = sizeof(int); 551 int val = -1; 552 553 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO, 554 &val, &optlen)); 555 ASSERT_EQ(sizeof(int), optlen); 556 ASSERT_TRUE(val == 0 || val == 1); 557 } 558 559 TEST_F(tls, tx_zerocopy_wrong_len) 560 { 561 socklen_t optlen = 2; 562 int val; 563 564 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO, 565 &val, &optlen)); 566 ASSERT_EQ(EINVAL, errno); 567 } 568 569 /* do_tls_getsockopt_conf(): NULL optval still yields EINVAL -- the 570 * converted code tests opt->iter_out.ubuf in place of optval. 571 */ 572 TEST_F(tls, conf_null_optval) 573 { 574 socklen_t optlen = 64; 575 576 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, NULL, &optlen)); 577 ASSERT_EQ(EINVAL, errno); 578 } 579 580 TEST_F(tls, conf_short) 581 { 582 socklen_t optlen = 2; 583 char buf[2]; 584 585 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, buf, &optlen)); 586 ASSERT_EQ(EINVAL, errno); 587 } 588 589 /* TLS_TX before crypto is set reports not-ready. */ 590 TEST_F(tls, conf_not_ready) 591 { 592 struct tls_crypto_info info; 593 socklen_t optlen = sizeof(info); 594 595 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, &info, &optlen)); 596 ASSERT_EQ(EBUSY, errno); 597 } 598 599 /* Set TX crypto, then read it back at the base and full sizes, exercising 600 * both copy_to_iter() branches. SKIP if AES-GCM is unavailable. 601 */ 602 TEST_F(tls, conf_crypto_roundtrip) 603 { 604 struct tls12_crypto_info_aes_gcm_128 tx = { 605 .info.version = TLS_1_2_VERSION, 606 .info.cipher_type = TLS_CIPHER_AES_GCM_128, 607 }; 608 struct tls12_crypto_info_aes_gcm_128 full; 609 struct tls_crypto_info base; 610 socklen_t optlen; 611 612 if (setsockopt(self->fd, SOL_TLS, TLS_TX, &tx, sizeof(tx))) 613 SKIP(return, "set TLS_TX aes_gcm_128: %s", strerror(errno)); 614 615 optlen = sizeof(base); 616 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &base, &optlen)); 617 ASSERT_EQ(sizeof(base), optlen); 618 ASSERT_EQ(TLS_1_2_VERSION, base.version); 619 ASSERT_EQ(TLS_CIPHER_AES_GCM_128, base.cipher_type); 620 621 optlen = sizeof(full); 622 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &full, &optlen)); 623 ASSERT_EQ(sizeof(full), optlen); 624 ASSERT_EQ(TLS_CIPHER_AES_GCM_128, full.info.cipher_type); 625 } 626 627 TEST_F(tls, bad_optname) 628 { 629 socklen_t optlen = sizeof(int); 630 int val; 631 632 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, 0x7fff, &val, &optlen)); 633 ASSERT_EQ(ENOPROTOOPT, errno); 634 } 635 636 TEST_HARNESS_MAIN 637