1 // SPDX-License-Identifier: GPL-2.0
2 /* Test IPV6_FLOWINFO cmsg on send and recv */
7 #include <asm/byteorder.h>
12 #include <linux/icmpv6.h>
13 #include <linux/in6.h>
19 #include <sys/socket.h>
22 #include <sys/types.h>
25 /* uapi/glibc weirdness may leave this undefined */
27 #define IPV6_FLOWINFO 11
30 #ifndef IPV6_FLOWLABEL_MGR
31 #define IPV6_FLOWLABEL_MGR 32
33 #ifndef IPV6_FLOWINFO_SEND
34 #define IPV6_FLOWINFO_SEND 33
37 #define FLOWLABEL_WILDCARD ((uint32_t) -1)
39 static const char cfg_data
[] = "a";
40 static uint32_t cfg_label
= 1;
42 static bool use_flowinfo_send
;
44 static struct icmp6hdr icmp6
= {
45 .icmp6_type
= ICMPV6_ECHO_REQUEST
48 static struct sockaddr_in6 addr
= {
49 .sin6_family
= AF_INET6
,
50 .sin6_addr
= IN6ADDR_LOOPBACK_INIT
,
53 static void do_send(int fd
, bool with_flowlabel
, uint32_t flowlabel
)
55 char control
[CMSG_SPACE(sizeof(flowlabel
))] = {0};
56 struct msghdr msg
= {0};
58 .iov_base
= (char *)cfg_data
,
59 .iov_len
= sizeof(cfg_data
)
64 iov
.iov_base
= &icmp6
;
65 iov
.iov_len
= sizeof(icmp6
);
71 if (use_flowinfo_send
) {
73 msg
.msg_namelen
= sizeof(addr
);
74 } else if (with_flowlabel
) {
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
);
83 msg
.msg_control
= control
;
84 msg
.msg_controllen
= sizeof(control
);
87 ret
= sendmsg(fd
, &msg
, 0);
89 error(1, errno
, "send");
92 fprintf(stderr
, "sent with label %u\n", flowlabel
);
94 fprintf(stderr
, "sent without label\n");
97 static void do_recv(int fd
, bool with_flowlabel
, uint32_t expect
)
99 char control
[CMSG_SPACE(sizeof(expect
))];
100 char data
[sizeof(cfg_data
)];
101 struct msghdr msg
= {0};
102 struct iovec iov
= {0};
108 iov
.iov_len
= sizeof(data
);
113 memset(control
, 0, sizeof(control
));
114 msg
.msg_control
= control
;
115 msg
.msg_controllen
= sizeof(control
);
117 ret
= recvmsg(fd
, &msg
, 0);
119 error(1, errno
, "recv");
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");
130 cm
= CMSG_FIRSTHDR(&msg
);
131 if (with_flowlabel
) {
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");
140 flowlabel
= ntohl(*(uint32_t *)CMSG_DATA(cm
));
141 fprintf(stderr
, "recv with label %u\n", flowlabel
);
143 if (expect
!= FLOWLABEL_WILDCARD
&& expect
!= flowlabel
) {
144 fprintf(stderr
, "recv: incorrect flowlabel %u != %u\n",
146 error(1, 0, "recv: flowlabel is wrong");
150 fprintf(stderr
, "recv without label\n");
154 static bool get_autoflowlabel_enabled(void)
159 fd
= open("/proc/sys/net/ipv6/auto_flowlabels", O_RDONLY
);
161 error(1, errno
, "open sysctl");
163 ret
= read(fd
, &val
, 1);
165 error(1, errno
, "read sysctl");
167 error(1, 0, "read sysctl: 0");
170 error(1, errno
, "close sysctl");
175 static void flowlabel_get(int fd
, uint32_t label
, uint8_t share
, uint16_t flags
)
177 struct in6_flowlabel_req req
= {
178 .flr_action
= IPV6_FL_A_GET
,
179 .flr_label
= htonl(label
),
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;
188 if (setsockopt(fd
, SOL_IPV6
, IPV6_FLOWLABEL_MGR
, &req
, sizeof(req
)))
189 error(1, errno
, "setsockopt flowlabel get");
192 static void parse_opts(int argc
, char **argv
)
196 while ((c
= getopt(argc
, argv
, "l:ps")) != -1) {
199 cfg_label
= strtoul(optarg
, NULL
, 0);
205 use_flowinfo_send
= true;
208 error(1, 0, "%s: parse error", argv
[0]);
213 int main(int argc
, char **argv
)
219 addr
.sin6_port
= htons(8000);
221 parse_opts(argc
, argv
);
224 fprintf(stderr
, "attempting to use ping sockets\n");
225 prot
= IPPROTO_ICMPV6
;
228 fdt
= socket(PF_INET6
, SOCK_DGRAM
, prot
);
230 error(1, errno
, "socket t");
232 fdr
= use_ping
? fdt
: socket(PF_INET6
, SOCK_DGRAM
, 0);
234 error(1, errno
, "socket r");
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");
241 flowlabel_get(fdt
, cfg_label
, IPV6_FL_S_EXCL
, IPV6_FL_F_CREATE
);
243 if (setsockopt(fdr
, SOL_IPV6
, IPV6_FLOWINFO
, &one
, sizeof(one
)))
244 error(1, errno
, "setsockopt flowinfo");
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
);
251 fprintf(stderr
, "send no label: recv no label (auto off)\n");
252 do_send(fdt
, false, 0);
253 do_recv(fdr
, false, 0);
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
,
261 error(1, errno
, "setsockopt flowinfo_send");
264 fprintf(stderr
, "send label\n");
265 do_send(fdt
, true, cfg_label
);
266 do_recv(fdr
, true, cfg_label
);
269 error(1, errno
, "close r");
270 if (!use_ping
&& close(fdt
))
271 error(1, errno
, "close t");