1*3fb321fdSWillem de Bruijn // SPDX-License-Identifier: GPL-2.0 2*3fb321fdSWillem de Bruijn /* Test IPV6_FLOWINFO cmsg on send and recv */ 3*3fb321fdSWillem de Bruijn 4*3fb321fdSWillem de Bruijn #define _GNU_SOURCE 5*3fb321fdSWillem de Bruijn 6*3fb321fdSWillem de Bruijn #include <arpa/inet.h> 7*3fb321fdSWillem de Bruijn #include <asm/byteorder.h> 8*3fb321fdSWillem de Bruijn #include <error.h> 9*3fb321fdSWillem de Bruijn #include <errno.h> 10*3fb321fdSWillem de Bruijn #include <fcntl.h> 11*3fb321fdSWillem de Bruijn #include <limits.h> 12*3fb321fdSWillem de Bruijn #include <linux/in6.h> 13*3fb321fdSWillem de Bruijn #include <stdbool.h> 14*3fb321fdSWillem de Bruijn #include <stdio.h> 15*3fb321fdSWillem de Bruijn #include <stdint.h> 16*3fb321fdSWillem de Bruijn #include <stdlib.h> 17*3fb321fdSWillem de Bruijn #include <string.h> 18*3fb321fdSWillem de Bruijn #include <sys/socket.h> 19*3fb321fdSWillem de Bruijn #include <sys/stat.h> 20*3fb321fdSWillem de Bruijn #include <sys/time.h> 21*3fb321fdSWillem de Bruijn #include <sys/types.h> 22*3fb321fdSWillem de Bruijn #include <unistd.h> 23*3fb321fdSWillem de Bruijn 24*3fb321fdSWillem de Bruijn /* uapi/glibc weirdness may leave this undefined */ 25*3fb321fdSWillem de Bruijn #ifndef IPV6_FLOWINFO 26*3fb321fdSWillem de Bruijn #define IPV6_FLOWINFO 11 27*3fb321fdSWillem de Bruijn #endif 28*3fb321fdSWillem de Bruijn 29*3fb321fdSWillem de Bruijn #ifndef IPV6_FLOWLABEL_MGR 30*3fb321fdSWillem de Bruijn #define IPV6_FLOWLABEL_MGR 32 31*3fb321fdSWillem de Bruijn #endif 32*3fb321fdSWillem de Bruijn 33*3fb321fdSWillem de Bruijn #define FLOWLABEL_WILDCARD ((uint32_t) -1) 34*3fb321fdSWillem de Bruijn 35*3fb321fdSWillem de Bruijn static const char cfg_data[] = "a"; 36*3fb321fdSWillem de Bruijn static uint32_t cfg_label = 1; 37*3fb321fdSWillem de Bruijn 38*3fb321fdSWillem de Bruijn static void do_send(int fd, bool with_flowlabel, uint32_t flowlabel) 39*3fb321fdSWillem de Bruijn { 40*3fb321fdSWillem de Bruijn char control[CMSG_SPACE(sizeof(flowlabel))] = {0}; 41*3fb321fdSWillem de Bruijn struct msghdr msg = {0}; 42*3fb321fdSWillem de Bruijn struct iovec iov = {0}; 43*3fb321fdSWillem de Bruijn int ret; 44*3fb321fdSWillem de Bruijn 45*3fb321fdSWillem de Bruijn iov.iov_base = (char *)cfg_data; 46*3fb321fdSWillem de Bruijn iov.iov_len = sizeof(cfg_data); 47*3fb321fdSWillem de Bruijn 48*3fb321fdSWillem de Bruijn msg.msg_iov = &iov; 49*3fb321fdSWillem de Bruijn msg.msg_iovlen = 1; 50*3fb321fdSWillem de Bruijn 51*3fb321fdSWillem de Bruijn if (with_flowlabel) { 52*3fb321fdSWillem de Bruijn struct cmsghdr *cm; 53*3fb321fdSWillem de Bruijn 54*3fb321fdSWillem de Bruijn cm = (void *)control; 55*3fb321fdSWillem de Bruijn cm->cmsg_len = CMSG_LEN(sizeof(flowlabel)); 56*3fb321fdSWillem de Bruijn cm->cmsg_level = SOL_IPV6; 57*3fb321fdSWillem de Bruijn cm->cmsg_type = IPV6_FLOWINFO; 58*3fb321fdSWillem de Bruijn *(uint32_t *)CMSG_DATA(cm) = htonl(flowlabel); 59*3fb321fdSWillem de Bruijn 60*3fb321fdSWillem de Bruijn msg.msg_control = control; 61*3fb321fdSWillem de Bruijn msg.msg_controllen = sizeof(control); 62*3fb321fdSWillem de Bruijn } 63*3fb321fdSWillem de Bruijn 64*3fb321fdSWillem de Bruijn ret = sendmsg(fd, &msg, 0); 65*3fb321fdSWillem de Bruijn if (ret == -1) 66*3fb321fdSWillem de Bruijn error(1, errno, "send"); 67*3fb321fdSWillem de Bruijn 68*3fb321fdSWillem de Bruijn if (with_flowlabel) 69*3fb321fdSWillem de Bruijn fprintf(stderr, "sent with label %u\n", flowlabel); 70*3fb321fdSWillem de Bruijn else 71*3fb321fdSWillem de Bruijn fprintf(stderr, "sent without label\n"); 72*3fb321fdSWillem de Bruijn } 73*3fb321fdSWillem de Bruijn 74*3fb321fdSWillem de Bruijn static void do_recv(int fd, bool with_flowlabel, uint32_t expect) 75*3fb321fdSWillem de Bruijn { 76*3fb321fdSWillem de Bruijn char control[CMSG_SPACE(sizeof(expect))]; 77*3fb321fdSWillem de Bruijn char data[sizeof(cfg_data)]; 78*3fb321fdSWillem de Bruijn struct msghdr msg = {0}; 79*3fb321fdSWillem de Bruijn struct iovec iov = {0}; 80*3fb321fdSWillem de Bruijn struct cmsghdr *cm; 81*3fb321fdSWillem de Bruijn uint32_t flowlabel; 82*3fb321fdSWillem de Bruijn int ret; 83*3fb321fdSWillem de Bruijn 84*3fb321fdSWillem de Bruijn iov.iov_base = data; 85*3fb321fdSWillem de Bruijn iov.iov_len = sizeof(data); 86*3fb321fdSWillem de Bruijn 87*3fb321fdSWillem de Bruijn msg.msg_iov = &iov; 88*3fb321fdSWillem de Bruijn msg.msg_iovlen = 1; 89*3fb321fdSWillem de Bruijn 90*3fb321fdSWillem de Bruijn memset(control, 0, sizeof(control)); 91*3fb321fdSWillem de Bruijn msg.msg_control = control; 92*3fb321fdSWillem de Bruijn msg.msg_controllen = sizeof(control); 93*3fb321fdSWillem de Bruijn 94*3fb321fdSWillem de Bruijn ret = recvmsg(fd, &msg, 0); 95*3fb321fdSWillem de Bruijn if (ret == -1) 96*3fb321fdSWillem de Bruijn error(1, errno, "recv"); 97*3fb321fdSWillem de Bruijn if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) 98*3fb321fdSWillem de Bruijn error(1, 0, "recv: truncated"); 99*3fb321fdSWillem de Bruijn if (ret != sizeof(cfg_data)) 100*3fb321fdSWillem de Bruijn error(1, 0, "recv: length mismatch"); 101*3fb321fdSWillem de Bruijn if (memcmp(data, cfg_data, sizeof(data))) 102*3fb321fdSWillem de Bruijn error(1, 0, "recv: data mismatch"); 103*3fb321fdSWillem de Bruijn 104*3fb321fdSWillem de Bruijn cm = CMSG_FIRSTHDR(&msg); 105*3fb321fdSWillem de Bruijn if (with_flowlabel) { 106*3fb321fdSWillem de Bruijn if (!cm) 107*3fb321fdSWillem de Bruijn error(1, 0, "recv: missing cmsg"); 108*3fb321fdSWillem de Bruijn if (CMSG_NXTHDR(&msg, cm)) 109*3fb321fdSWillem de Bruijn error(1, 0, "recv: too many cmsg"); 110*3fb321fdSWillem de Bruijn if (cm->cmsg_level != SOL_IPV6 || 111*3fb321fdSWillem de Bruijn cm->cmsg_type != IPV6_FLOWINFO) 112*3fb321fdSWillem de Bruijn error(1, 0, "recv: unexpected cmsg level or type"); 113*3fb321fdSWillem de Bruijn 114*3fb321fdSWillem de Bruijn flowlabel = ntohl(*(uint32_t *)CMSG_DATA(cm)); 115*3fb321fdSWillem de Bruijn fprintf(stderr, "recv with label %u\n", flowlabel); 116*3fb321fdSWillem de Bruijn 117*3fb321fdSWillem de Bruijn if (expect != FLOWLABEL_WILDCARD && expect != flowlabel) 118*3fb321fdSWillem de Bruijn fprintf(stderr, "recv: incorrect flowlabel %u != %u\n", 119*3fb321fdSWillem de Bruijn flowlabel, expect); 120*3fb321fdSWillem de Bruijn 121*3fb321fdSWillem de Bruijn } else { 122*3fb321fdSWillem de Bruijn fprintf(stderr, "recv without label\n"); 123*3fb321fdSWillem de Bruijn } 124*3fb321fdSWillem de Bruijn } 125*3fb321fdSWillem de Bruijn 126*3fb321fdSWillem de Bruijn static bool get_autoflowlabel_enabled(void) 127*3fb321fdSWillem de Bruijn { 128*3fb321fdSWillem de Bruijn int fd, ret; 129*3fb321fdSWillem de Bruijn char val; 130*3fb321fdSWillem de Bruijn 131*3fb321fdSWillem de Bruijn fd = open("/proc/sys/net/ipv6/auto_flowlabels", O_RDONLY); 132*3fb321fdSWillem de Bruijn if (fd == -1) 133*3fb321fdSWillem de Bruijn error(1, errno, "open sysctl"); 134*3fb321fdSWillem de Bruijn 135*3fb321fdSWillem de Bruijn ret = read(fd, &val, 1); 136*3fb321fdSWillem de Bruijn if (ret == -1) 137*3fb321fdSWillem de Bruijn error(1, errno, "read sysctl"); 138*3fb321fdSWillem de Bruijn if (ret == 0) 139*3fb321fdSWillem de Bruijn error(1, 0, "read sysctl: 0"); 140*3fb321fdSWillem de Bruijn 141*3fb321fdSWillem de Bruijn if (close(fd)) 142*3fb321fdSWillem de Bruijn error(1, errno, "close sysctl"); 143*3fb321fdSWillem de Bruijn 144*3fb321fdSWillem de Bruijn return val == '1'; 145*3fb321fdSWillem de Bruijn } 146*3fb321fdSWillem de Bruijn 147*3fb321fdSWillem de Bruijn static void flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags) 148*3fb321fdSWillem de Bruijn { 149*3fb321fdSWillem de Bruijn struct in6_flowlabel_req req = { 150*3fb321fdSWillem de Bruijn .flr_action = IPV6_FL_A_GET, 151*3fb321fdSWillem de Bruijn .flr_label = htonl(label), 152*3fb321fdSWillem de Bruijn .flr_flags = flags, 153*3fb321fdSWillem de Bruijn .flr_share = share, 154*3fb321fdSWillem de Bruijn }; 155*3fb321fdSWillem de Bruijn 156*3fb321fdSWillem de Bruijn /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */ 157*3fb321fdSWillem de Bruijn req.flr_dst.s6_addr[0] = 0xfd; 158*3fb321fdSWillem de Bruijn req.flr_dst.s6_addr[15] = 0x1; 159*3fb321fdSWillem de Bruijn 160*3fb321fdSWillem de Bruijn if (setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req))) 161*3fb321fdSWillem de Bruijn error(1, errno, "setsockopt flowlabel get"); 162*3fb321fdSWillem de Bruijn } 163*3fb321fdSWillem de Bruijn 164*3fb321fdSWillem de Bruijn static void parse_opts(int argc, char **argv) 165*3fb321fdSWillem de Bruijn { 166*3fb321fdSWillem de Bruijn int c; 167*3fb321fdSWillem de Bruijn 168*3fb321fdSWillem de Bruijn while ((c = getopt(argc, argv, "l:")) != -1) { 169*3fb321fdSWillem de Bruijn switch (c) { 170*3fb321fdSWillem de Bruijn case 'l': 171*3fb321fdSWillem de Bruijn cfg_label = strtoul(optarg, NULL, 0); 172*3fb321fdSWillem de Bruijn break; 173*3fb321fdSWillem de Bruijn default: 174*3fb321fdSWillem de Bruijn error(1, 0, "%s: parse error", argv[0]); 175*3fb321fdSWillem de Bruijn } 176*3fb321fdSWillem de Bruijn } 177*3fb321fdSWillem de Bruijn } 178*3fb321fdSWillem de Bruijn 179*3fb321fdSWillem de Bruijn int main(int argc, char **argv) 180*3fb321fdSWillem de Bruijn { 181*3fb321fdSWillem de Bruijn struct sockaddr_in6 addr = { 182*3fb321fdSWillem de Bruijn .sin6_family = AF_INET6, 183*3fb321fdSWillem de Bruijn .sin6_port = htons(8000), 184*3fb321fdSWillem de Bruijn .sin6_addr = IN6ADDR_LOOPBACK_INIT, 185*3fb321fdSWillem de Bruijn }; 186*3fb321fdSWillem de Bruijn const int one = 1; 187*3fb321fdSWillem de Bruijn int fdt, fdr; 188*3fb321fdSWillem de Bruijn 189*3fb321fdSWillem de Bruijn parse_opts(argc, argv); 190*3fb321fdSWillem de Bruijn 191*3fb321fdSWillem de Bruijn fdt = socket(PF_INET6, SOCK_DGRAM, 0); 192*3fb321fdSWillem de Bruijn if (fdt == -1) 193*3fb321fdSWillem de Bruijn error(1, errno, "socket t"); 194*3fb321fdSWillem de Bruijn 195*3fb321fdSWillem de Bruijn fdr = socket(PF_INET6, SOCK_DGRAM, 0); 196*3fb321fdSWillem de Bruijn if (fdr == -1) 197*3fb321fdSWillem de Bruijn error(1, errno, "socket r"); 198*3fb321fdSWillem de Bruijn 199*3fb321fdSWillem de Bruijn if (connect(fdt, (void *)&addr, sizeof(addr))) 200*3fb321fdSWillem de Bruijn error(1, errno, "connect"); 201*3fb321fdSWillem de Bruijn if (bind(fdr, (void *)&addr, sizeof(addr))) 202*3fb321fdSWillem de Bruijn error(1, errno, "bind"); 203*3fb321fdSWillem de Bruijn 204*3fb321fdSWillem de Bruijn flowlabel_get(fdt, cfg_label, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE); 205*3fb321fdSWillem de Bruijn 206*3fb321fdSWillem de Bruijn if (setsockopt(fdr, SOL_IPV6, IPV6_FLOWINFO, &one, sizeof(one))) 207*3fb321fdSWillem de Bruijn error(1, errno, "setsockopt flowinfo"); 208*3fb321fdSWillem de Bruijn 209*3fb321fdSWillem de Bruijn if (get_autoflowlabel_enabled()) { 210*3fb321fdSWillem de Bruijn fprintf(stderr, "send no label: recv auto flowlabel\n"); 211*3fb321fdSWillem de Bruijn do_send(fdt, false, 0); 212*3fb321fdSWillem de Bruijn do_recv(fdr, true, FLOWLABEL_WILDCARD); 213*3fb321fdSWillem de Bruijn } else { 214*3fb321fdSWillem de Bruijn fprintf(stderr, "send no label: recv no label (auto off)\n"); 215*3fb321fdSWillem de Bruijn do_send(fdt, false, 0); 216*3fb321fdSWillem de Bruijn do_recv(fdr, false, 0); 217*3fb321fdSWillem de Bruijn } 218*3fb321fdSWillem de Bruijn 219*3fb321fdSWillem de Bruijn fprintf(stderr, "send label\n"); 220*3fb321fdSWillem de Bruijn do_send(fdt, true, cfg_label); 221*3fb321fdSWillem de Bruijn do_recv(fdr, true, cfg_label); 222*3fb321fdSWillem de Bruijn 223*3fb321fdSWillem de Bruijn if (close(fdr)) 224*3fb321fdSWillem de Bruijn error(1, errno, "close r"); 225*3fb321fdSWillem de Bruijn if (close(fdt)) 226*3fb321fdSWillem de Bruijn error(1, errno, "close t"); 227*3fb321fdSWillem de Bruijn 228*3fb321fdSWillem de Bruijn return 0; 229*3fb321fdSWillem de Bruijn } 230