1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Author: Brett A C Sheffield <bacs@librecast.net> 4 * 5 * Kernel selftest for the IPv6 fragmentation regression which affected stable 6 * kernels: 7 * 8 * https://lore.kernel.org/stable/aElivdUXqd1OqgMY@karahi.gladserv.com 9 * 10 * Commit: a18dfa9925b9 ("ipv6: save dontfrag in cork") was backported to stable 11 * without some prerequisite commits. 12 * 13 * This caused a regression when sending IPv6 UDP packets by preventing 14 * fragmentation and instead returning -1 (EMSGSIZE). 15 * 16 * This selftest demonstrates the issue by sending an IPv6 UDP packet to 17 * localhost (::1) on the loopback interface from the autoconfigured link-local 18 * address. 19 * 20 * sendmsg(2) returns bytes sent correctly on a working kernel, and returns -1 21 * (EMSGSIZE) when the regression is present. 22 * 23 * The regression was not present in the mainline kernel, but add this test to 24 * catch similar breakage in future. 25 */ 26 27 #define _GNU_SOURCE 28 29 #include <error.h> 30 #include <net/if.h> 31 #include <netinet/in.h> 32 #include <sched.h> 33 #include <stdio.h> 34 #include <sys/ioctl.h> 35 #include <sys/socket.h> 36 #include <unistd.h> 37 #include "../kselftest.h" 38 39 #define MTU 1500 40 #define LARGER_THAN_MTU 8192 41 42 static void setup(void) 43 { 44 struct ifreq ifr = { 45 .ifr_name = "lo" 46 }; 47 int ctl; 48 49 /* we need to set MTU, so do this in a namespace to play nicely */ 50 if (unshare(CLONE_NEWNET) == -1) 51 error(KSFT_FAIL, errno, "unshare"); 52 53 ctl = socket(AF_LOCAL, SOCK_STREAM, 0); 54 if (ctl == -1) 55 error(KSFT_FAIL, errno, "socket"); 56 57 /* ensure MTU is smaller than what we plan to send */ 58 ifr.ifr_mtu = MTU; 59 if (ioctl(ctl, SIOCSIFMTU, &ifr) == -1) 60 error(KSFT_FAIL, errno, "ioctl: set MTU"); 61 62 /* bring up interface */ 63 if (ioctl(ctl, SIOCGIFFLAGS, &ifr) == -1) 64 error(KSFT_FAIL, errno, "ioctl SIOCGIFFLAGS"); 65 ifr.ifr_flags = ifr.ifr_flags | IFF_UP; 66 if (ioctl(ctl, SIOCSIFFLAGS, &ifr) == -1) 67 error(KSFT_FAIL, errno, "ioctl: bring interface up"); 68 69 if (close(ctl) == -1) 70 error(KSFT_FAIL, errno, "close"); 71 } 72 73 int main(void) 74 { 75 struct in6_addr addr = { 76 .s6_addr[15] = 0x01, /* ::1 */ 77 }; 78 struct sockaddr_in6 sa = { 79 .sin6_family = AF_INET6, 80 .sin6_addr = addr, 81 .sin6_port = htons(9) /* port 9/udp (DISCARD) */ 82 }; 83 static char buf[LARGER_THAN_MTU] = {0}; 84 struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; 85 struct msghdr msg = { 86 .msg_iov = &iov, 87 .msg_iovlen = 1, 88 .msg_name = (struct sockaddr *)&sa, 89 .msg_namelen = sizeof(sa), 90 }; 91 ssize_t rc; 92 int s; 93 94 printf("Testing IPv6 fragmentation\n"); 95 setup(); 96 s = socket(AF_INET6, SOCK_DGRAM, 0); 97 send_again: 98 rc = sendmsg(s, &msg, 0); 99 if (rc == -1) { 100 /* if interface wasn't ready, try again */ 101 if (errno == EADDRNOTAVAIL) { 102 usleep(1000); 103 goto send_again; 104 } 105 error(KSFT_FAIL, errno, "sendmsg"); 106 } else if (rc != LARGER_THAN_MTU) { 107 error(KSFT_FAIL, errno, "sendmsg returned %zi, expected %i", 108 rc, LARGER_THAN_MTU); 109 } 110 printf("[PASS] sendmsg() returned %zi\n", rc); 111 if (close(s) == -1) 112 error(KSFT_FAIL, errno, "close"); 113 return KSFT_PASS; 114 } 115