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