1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <arpa/inet.h> 4 #include <error.h> 5 #include <linux/errqueue.h> 6 #include <linux/icmp.h> 7 #include <linux/icmpv6.h> 8 #include <linux/in6.h> 9 #include <linux/ip.h> 10 #include <linux/ipv6.h> 11 #include <netinet/in.h> 12 #include <netinet/udp.h> 13 #include <poll.h> 14 #include <sched.h> 15 #include <stdbool.h> 16 #include <stdint.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <sys/ioctl.h> 20 #include <sys/socket.h> 21 22 #include "../kselftest_harness.h" 23 24 static const unsigned short src_port = 44444; 25 static const unsigned short dst_port = 55555; 26 static const int min_orig_dgram_len = 128; 27 static const int min_payload_len_v4 = 28 min_orig_dgram_len - sizeof(struct iphdr) - sizeof(struct udphdr); 29 static const int min_payload_len_v6 = 30 min_orig_dgram_len - sizeof(struct ipv6hdr) - sizeof(struct udphdr); 31 static const uint8_t orig_payload_byte = 0xAA; 32 33 struct sockaddr_inet { 34 union { 35 struct sockaddr_in6 v6; 36 struct sockaddr_in v4; 37 struct sockaddr sa; 38 }; 39 socklen_t len; 40 }; 41 42 struct ip_case_info { 43 int domain; 44 int level; 45 int opt1; 46 int opt2; 47 int proto; 48 int (*build_func)(uint8_t *buf, ssize_t buflen, bool with_ext, 49 int payload_len, bool bad_csum, bool bad_len, 50 bool smaller_len); 51 int min_payload; 52 }; 53 54 static int bringup_loopback(void) 55 { 56 struct ifreq ifr = { 57 .ifr_name = "lo" 58 }; 59 int fd; 60 61 fd = socket(AF_INET, SOCK_DGRAM, 0); 62 if (fd < 0) 63 return -1; 64 65 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) 66 goto err; 67 68 ifr.ifr_flags = ifr.ifr_flags | IFF_UP; 69 70 if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) 71 goto err; 72 73 close(fd); 74 return 0; 75 76 err: 77 close(fd); 78 return -1; 79 } 80 81 static uint16_t csum(const void *buf, size_t len) 82 { 83 const uint8_t *data = buf; 84 uint32_t sum = 0; 85 86 while (len > 1) { 87 sum += (data[0] << 8) | data[1]; 88 data += 2; 89 len -= 2; 90 } 91 92 if (len == 1) 93 sum += data[0] << 8; 94 95 while (sum >> 16) 96 sum = (sum & 0xFFFF) + (sum >> 16); 97 98 return ~sum & 0xFFFF; 99 } 100 101 static int poll_err(int fd) 102 { 103 struct pollfd pfd; 104 105 memset(&pfd, 0, sizeof(pfd)); 106 pfd.fd = fd; 107 108 if (poll(&pfd, 1, 5000) != 1 || pfd.revents != POLLERR) 109 return -1; 110 111 return 0; 112 } 113 114 static void set_addr(struct sockaddr_inet *addr, int domain, 115 unsigned short port) 116 { 117 memset(addr, 0, sizeof(*addr)); 118 119 switch (domain) { 120 case AF_INET: 121 addr->v4.sin_family = AF_INET; 122 addr->v4.sin_port = htons(port); 123 addr->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 124 addr->len = sizeof(addr->v4); 125 break; 126 case AF_INET6: 127 addr->v6.sin6_family = AF_INET6; 128 addr->v6.sin6_port = htons(port); 129 addr->v6.sin6_addr = in6addr_loopback; 130 addr->len = sizeof(addr->v6); 131 break; 132 } 133 } 134 135 static int bind_and_setsockopt(int fd, const struct ip_case_info *info) 136 { 137 struct sockaddr_inet addr; 138 int opt = 1; 139 140 set_addr(&addr, info->domain, src_port); 141 142 if (setsockopt(fd, info->level, info->opt1, &opt, sizeof(opt)) < 0) 143 return -1; 144 145 if (setsockopt(fd, info->level, info->opt2, &opt, sizeof(opt)) < 0) 146 return -1; 147 148 return bind(fd, &addr.sa, addr.len); 149 } 150 151 static int build_rfc4884_ext(uint8_t *buf, size_t buflen, bool bad_csum, 152 bool bad_len, bool smaller_len) 153 { 154 struct icmp_extobj_hdr *objh; 155 struct icmp_ext_hdr *exthdr; 156 size_t obj_len, ext_len; 157 uint16_t sum; 158 159 /* Use an object payload of 4 bytes */ 160 obj_len = sizeof(*objh) + sizeof(uint32_t); 161 ext_len = sizeof(*exthdr) + obj_len; 162 163 if (ext_len > buflen) 164 return -EINVAL; 165 166 exthdr = (struct icmp_ext_hdr *)buf; 167 objh = (struct icmp_extobj_hdr *)(buf + sizeof(*exthdr)); 168 169 exthdr->version = 2; 170 /* When encoding a bad object length, either encode a length too small 171 * to fit the object header or too big to fit in the packet. 172 */ 173 if (bad_len) 174 obj_len = smaller_len ? sizeof(*objh) - 1 : obj_len * 2; 175 objh->length = htons(obj_len); 176 177 sum = csum(buf, ext_len); 178 exthdr->checksum = htons(bad_csum ? sum - 1 : sum); 179 180 return ext_len; 181 } 182 183 static int build_orig_dgram_v4(uint8_t *buf, ssize_t buflen, int payload_len) 184 { 185 struct udphdr *udph; 186 struct iphdr *iph; 187 size_t len = 0; 188 189 len = sizeof(*iph) + sizeof(*udph) + payload_len; 190 if (len > buflen) 191 return -EINVAL; 192 193 iph = (struct iphdr *)buf; 194 udph = (struct udphdr *)(buf + sizeof(*iph)); 195 196 iph->version = 4; 197 iph->ihl = 5; 198 iph->protocol = IPPROTO_UDP; 199 iph->saddr = htonl(INADDR_LOOPBACK); 200 iph->daddr = htonl(INADDR_LOOPBACK); 201 iph->tot_len = htons(len); 202 iph->check = htons(csum(iph, sizeof(*iph))); 203 204 udph->source = htons(src_port); 205 udph->dest = htons(dst_port); 206 udph->len = htons(sizeof(*udph) + payload_len); 207 208 memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte, 209 payload_len); 210 211 return len; 212 } 213 214 static int build_orig_dgram_v6(uint8_t *buf, ssize_t buflen, int payload_len) 215 { 216 struct udphdr *udph; 217 struct ipv6hdr *iph; 218 size_t len = 0; 219 220 len = sizeof(*iph) + sizeof(*udph) + payload_len; 221 if (len > buflen) 222 return -EINVAL; 223 224 iph = (struct ipv6hdr *)buf; 225 udph = (struct udphdr *)(buf + sizeof(*iph)); 226 227 iph->version = 6; 228 iph->payload_len = htons(sizeof(*udph) + payload_len); 229 iph->nexthdr = IPPROTO_UDP; 230 iph->saddr = in6addr_loopback; 231 iph->daddr = in6addr_loopback; 232 233 udph->source = htons(src_port); 234 udph->dest = htons(dst_port); 235 udph->len = htons(sizeof(*udph) + payload_len); 236 237 memset(buf + sizeof(*iph) + sizeof(*udph), orig_payload_byte, 238 payload_len); 239 240 return len; 241 } 242 243 static int build_icmpv4_pkt(uint8_t *buf, ssize_t buflen, bool with_ext, 244 int payload_len, bool bad_csum, bool bad_len, 245 bool smaller_len) 246 { 247 struct icmphdr *icmph; 248 int len, ret; 249 250 len = sizeof(*icmph); 251 memset(buf, 0, buflen); 252 253 icmph = (struct icmphdr *)buf; 254 icmph->type = ICMP_DEST_UNREACH; 255 icmph->code = ICMP_PORT_UNREACH; 256 icmph->checksum = 0; 257 258 ret = build_orig_dgram_v4(buf + len, buflen - len, payload_len); 259 if (ret < 0) 260 return ret; 261 262 len += ret; 263 264 icmph->un.reserved[1] = (len - sizeof(*icmph)) / sizeof(uint32_t); 265 266 if (with_ext) { 267 ret = build_rfc4884_ext(buf + len, buflen - len, 268 bad_csum, bad_len, smaller_len); 269 if (ret < 0) 270 return ret; 271 272 len += ret; 273 } 274 275 icmph->checksum = htons(csum(icmph, len)); 276 return len; 277 } 278 279 static int build_icmpv6_pkt(uint8_t *buf, ssize_t buflen, bool with_ext, 280 int payload_len, bool bad_csum, bool bad_len, 281 bool smaller_len) 282 { 283 struct icmp6hdr *icmph; 284 int len, ret; 285 286 len = sizeof(*icmph); 287 memset(buf, 0, buflen); 288 289 icmph = (struct icmp6hdr *)buf; 290 icmph->icmp6_type = ICMPV6_DEST_UNREACH; 291 icmph->icmp6_code = ICMPV6_PORT_UNREACH; 292 icmph->icmp6_cksum = 0; 293 294 ret = build_orig_dgram_v6(buf + len, buflen - len, payload_len); 295 if (ret < 0) 296 return ret; 297 298 len += ret; 299 300 icmph->icmp6_datagram_len = (len - sizeof(*icmph)) / sizeof(uint64_t); 301 302 if (with_ext) { 303 ret = build_rfc4884_ext(buf + len, buflen - len, 304 bad_csum, bad_len, smaller_len); 305 if (ret < 0) 306 return ret; 307 308 len += ret; 309 } 310 311 icmph->icmp6_cksum = htons(csum(icmph, len)); 312 return len; 313 } 314 315 FIXTURE(rfc4884) {}; 316 317 FIXTURE_SETUP(rfc4884) 318 { 319 int ret; 320 321 ret = unshare(CLONE_NEWNET); 322 ASSERT_EQ(ret, 0) { 323 TH_LOG("unshare(CLONE_NEWNET) failed: %s", strerror(errno)); 324 } 325 326 ret = bringup_loopback(); 327 ASSERT_EQ(ret, 0) TH_LOG("Failed to bring up loopback interface"); 328 } 329 330 FIXTURE_TEARDOWN(rfc4884) 331 { 332 } 333 334 const struct ip_case_info ipv4_info = { 335 .domain = AF_INET, 336 .level = SOL_IP, 337 .opt1 = IP_RECVERR, 338 .opt2 = IP_RECVERR_RFC4884, 339 .proto = IPPROTO_ICMP, 340 .build_func = build_icmpv4_pkt, 341 .min_payload = min_payload_len_v4, 342 }; 343 344 const struct ip_case_info ipv6_info = { 345 .domain = AF_INET6, 346 .level = SOL_IPV6, 347 .opt1 = IPV6_RECVERR, 348 .opt2 = IPV6_RECVERR_RFC4884, 349 .proto = IPPROTO_ICMPV6, 350 .build_func = build_icmpv6_pkt, 351 .min_payload = min_payload_len_v6, 352 }; 353 354 FIXTURE_VARIANT(rfc4884) { 355 /* IPv4/v6 related information */ 356 struct ip_case_info info; 357 /* Whether to append an ICMP extension or not */ 358 bool with_ext; 359 /* UDP payload length */ 360 int payload_len; 361 /* Whether to generate a bad checksum in the ICMP extension structure */ 362 bool bad_csum; 363 /* Whether to generate a bad length in the ICMP object header */ 364 bool bad_len; 365 /* Whether it is too small to fit the object header or too big to fit 366 * in the packet 367 */ 368 bool smaller_len; 369 }; 370 371 /* Tests that a valid ICMPv4 error message with extension and the original 372 * datagram is smaller than 128 bytes, generates an error with zero offset, 373 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 374 */ 375 FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_small_payload) { 376 .info = ipv4_info, 377 .with_ext = true, 378 .payload_len = 64, 379 .bad_csum = false, 380 .bad_len = false, 381 }; 382 383 /* Tests that a valid ICMPv4 error message with extension and 128 bytes original 384 * datagram, generates an error with the expected offset, and does not raise the 385 * SO_EE_RFC4884_FLAG_INVALID flag. 386 */ 387 FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext) { 388 .info = ipv4_info, 389 .with_ext = true, 390 .payload_len = min_payload_len_v4, 391 .bad_csum = false, 392 .bad_len = false, 393 }; 394 395 /* Tests that a valid ICMPv4 error message with extension and the original 396 * datagram is larger than 128 bytes, generates an error with the expected 397 * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 398 */ 399 FIXTURE_VARIANT_ADD(rfc4884, ipv4_ext_large_payload) { 400 .info = ipv4_info, 401 .with_ext = true, 402 .payload_len = 256, 403 .bad_csum = false, 404 .bad_len = false, 405 }; 406 407 /* Tests that a valid ICMPv4 error message without extension and the original 408 * datagram is smaller than 128 bytes, generates an error with zero offset, 409 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 410 */ 411 FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_small_payload) { 412 .info = ipv4_info, 413 .with_ext = false, 414 .payload_len = 64, 415 .bad_csum = false, 416 .bad_len = false, 417 }; 418 419 /* Tests that a valid ICMPv4 error message without extension and 128 bytes 420 * original datagram, generates an error with zero offset, and does not raise 421 * the SO_EE_RFC4884_FLAG_INVALID flag. 422 */ 423 FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_min_payload) { 424 .info = ipv4_info, 425 .with_ext = false, 426 .payload_len = min_payload_len_v4, 427 .bad_csum = false, 428 .bad_len = false, 429 }; 430 431 /* Tests that a valid ICMPv4 error message without extension and the original 432 * datagram is larger than 128 bytes, generates an error with zero offset, 433 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 434 */ 435 FIXTURE_VARIANT_ADD(rfc4884, ipv4_no_ext_large_payload) { 436 .info = ipv4_info, 437 .with_ext = false, 438 .payload_len = 256, 439 .bad_csum = false, 440 .bad_len = false, 441 }; 442 443 /* Tests that an ICMPv4 error message with extension and an invalid checksum, 444 * generates an error with the expected offset, and raises the 445 * SO_EE_RFC4884_FLAG_INVALID flag. 446 */ 447 FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_checksum) { 448 .info = ipv4_info, 449 .with_ext = true, 450 .payload_len = min_payload_len_v4, 451 .bad_csum = true, 452 .bad_len = false, 453 }; 454 455 /* Tests that an ICMPv4 error message with extension and an object length 456 * smaller than the object header, generates an error with the expected offset, 457 * and raises the SO_EE_RFC4884_FLAG_INVALID flag. 458 */ 459 FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_small) { 460 .info = ipv4_info, 461 .with_ext = true, 462 .payload_len = min_payload_len_v4, 463 .bad_csum = false, 464 .bad_len = true, 465 .smaller_len = true, 466 }; 467 468 /* Tests that an ICMPv4 error message with extension and an object length that 469 * is too big to fit in the packet, generates an error with the expected offset, 470 * and raises the SO_EE_RFC4884_FLAG_INVALID flag. 471 */ 472 FIXTURE_VARIANT_ADD(rfc4884, ipv4_invalid_ext_length_large) { 473 .info = ipv4_info, 474 .with_ext = true, 475 .payload_len = min_payload_len_v4, 476 .bad_csum = false, 477 .bad_len = true, 478 .smaller_len = false, 479 }; 480 481 /* Tests that a valid ICMPv6 error message with extension and the original 482 * datagram is smaller than 128 bytes, generates an error with zero offset, 483 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 484 */ 485 FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_small_payload) { 486 .info = ipv6_info, 487 .with_ext = true, 488 .payload_len = 64, 489 .bad_csum = false, 490 .bad_len = false, 491 }; 492 493 /* Tests that a valid ICMPv6 error message with extension and 128 bytes original 494 * datagram, generates an error with the expected offset, and does not raise the 495 * SO_EE_RFC4884_FLAG_INVALID flag. 496 */ 497 FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext) { 498 .info = ipv6_info, 499 .with_ext = true, 500 .payload_len = min_payload_len_v6, 501 .bad_csum = false, 502 .bad_len = false, 503 }; 504 505 /* Tests that a valid ICMPv6 error message with extension and the original 506 * datagram is larger than 128 bytes, generates an error with the expected 507 * offset, and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 508 */ 509 FIXTURE_VARIANT_ADD(rfc4884, ipv6_ext_large_payload) { 510 .info = ipv6_info, 511 .with_ext = true, 512 .payload_len = 256, 513 .bad_csum = false, 514 .bad_len = false, 515 }; 516 /* Tests that a valid ICMPv6 error message without extension and the original 517 * datagram is smaller than 128 bytes, generates an error with zero offset, 518 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 519 */ 520 FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_small_payload) { 521 .info = ipv6_info, 522 .with_ext = false, 523 .payload_len = 64, 524 .bad_csum = false, 525 .bad_len = false, 526 }; 527 528 /* Tests that a valid ICMPv6 error message without extension and 128 bytes 529 * original datagram, generates an error with zero offset, and does not 530 * raise the SO_EE_RFC4884_FLAG_INVALID flag. 531 */ 532 FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_min_payload) { 533 .info = ipv6_info, 534 .with_ext = false, 535 .payload_len = min_payload_len_v6, 536 .bad_csum = false, 537 .bad_len = false, 538 }; 539 540 /* Tests that a valid ICMPv6 error message without extension and the original 541 * datagram is larger than 128 bytes, generates an error with zero offset, 542 * and does not raise the SO_EE_RFC4884_FLAG_INVALID flag. 543 */ 544 FIXTURE_VARIANT_ADD(rfc4884, ipv6_no_ext_large_payload) { 545 .info = ipv6_info, 546 .with_ext = false, 547 .payload_len = 256, 548 .bad_csum = false, 549 .bad_len = false, 550 }; 551 552 /* Tests that an ICMPv6 error message with extension and an invalid checksum, 553 * generates an error with the expected offset, and raises the 554 * SO_EE_RFC4884_FLAG_INVALID flag. 555 */ 556 FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_checksum) { 557 .info = ipv6_info, 558 .with_ext = true, 559 .payload_len = min_payload_len_v6, 560 .bad_csum = true, 561 .bad_len = false, 562 }; 563 564 /* Tests that an ICMPv6 error message with extension and an object length 565 * smaller than the object header, generates an error with the expected offset, 566 * and raises the SO_EE_RFC4884_FLAG_INVALID flag. 567 */ 568 FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_small) { 569 .info = ipv6_info, 570 .with_ext = true, 571 .payload_len = min_payload_len_v6, 572 .bad_csum = false, 573 .bad_len = true, 574 .smaller_len = true, 575 }; 576 577 /* Tests that an ICMPv6 error message with extension and an object length that 578 * is too big to fit in the packet, generates an error with the expected offset, 579 * and raises the SO_EE_RFC4884_FLAG_INVALID flag. 580 */ 581 FIXTURE_VARIANT_ADD(rfc4884, ipv6_invalid_ext_length_large) { 582 .info = ipv6_info, 583 .with_ext = true, 584 .payload_len = min_payload_len_v6, 585 .bad_csum = false, 586 .bad_len = true, 587 .smaller_len = false, 588 }; 589 590 static void 591 check_rfc4884_offset(struct __test_metadata *_metadata, int sock, 592 const FIXTURE_VARIANT(rfc4884) *v) 593 { 594 char rxbuf[1024]; 595 char ctrl[1024]; 596 struct iovec iov = { 597 .iov_base = rxbuf, 598 .iov_len = sizeof(rxbuf) 599 }; 600 struct msghdr msg = { 601 .msg_iov = &iov, 602 .msg_iovlen = 1, 603 .msg_control = ctrl, 604 .msg_controllen = sizeof(ctrl), 605 }; 606 struct cmsghdr *cmsg; 607 int recv; 608 609 ASSERT_EQ(poll_err(sock), 0); 610 611 recv = recvmsg(sock, &msg, MSG_ERRQUEUE); 612 ASSERT_GE(recv, 0) TH_LOG("recvmsg(MSG_ERRQUEUE) failed"); 613 614 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 615 bool is_invalid, expected_invalid; 616 struct sock_extended_err *ee; 617 int expected_off; 618 uint16_t off; 619 620 if (cmsg->cmsg_level != v->info.level || 621 cmsg->cmsg_type != v->info.opt1) { 622 TH_LOG("Unrelated cmsgs were encountered in recvmsg()"); 623 continue; 624 } 625 626 ee = (struct sock_extended_err *)CMSG_DATA(cmsg); 627 off = ee->ee_rfc4884.len; 628 is_invalid = ee->ee_rfc4884.flags & SO_EE_RFC4884_FLAG_INVALID; 629 630 expected_invalid = v->bad_csum || v->bad_len; 631 ASSERT_EQ(is_invalid, expected_invalid) { 632 TH_LOG("Expected invalidity flag to be %d, but got %d", 633 expected_invalid, is_invalid); 634 } 635 636 expected_off = 637 (v->with_ext && v->payload_len >= v->info.min_payload) ? 638 v->payload_len : 0; 639 ASSERT_EQ(off, expected_off) { 640 TH_LOG("Expected RFC4884 offset %u, got %u", 641 expected_off, off); 642 } 643 break; 644 } 645 } 646 647 TEST_F(rfc4884, rfc4884) 648 { 649 const typeof(variant) v = variant; 650 struct sockaddr_inet addr; 651 uint8_t pkt[1024]; 652 int dgram, raw; 653 int len, sent; 654 int err; 655 656 dgram = socket(v->info.domain, SOCK_DGRAM, 0); 657 ASSERT_GE(dgram, 0) TH_LOG("Opening datagram socket failed"); 658 659 err = bind_and_setsockopt(dgram, &v->info); 660 ASSERT_EQ(err, 0) TH_LOG("Bind failed"); 661 662 raw = socket(v->info.domain, SOCK_RAW, v->info.proto); 663 ASSERT_GE(raw, 0) TH_LOG("Opening raw socket failed"); 664 665 len = v->info.build_func(pkt, sizeof(pkt), v->with_ext, v->payload_len, 666 v->bad_csum, v->bad_len, v->smaller_len); 667 ASSERT_GT(len, 0) TH_LOG("Building packet failed"); 668 669 set_addr(&addr, v->info.domain, 0); 670 sent = sendto(raw, pkt, len, 0, &addr.sa, addr.len); 671 ASSERT_EQ(len, sent) TH_LOG("Sending packet failed"); 672 673 check_rfc4884_offset(_metadata, dgram, v); 674 675 close(dgram); 676 close(raw); 677 } 678 679 TEST_HARNESS_MAIN 680