selftests/net: change shebang to bash to support "source"
[platform/kernel/linux-starfive.git] / tools / testing / selftests / net / ipv6_flowlabel.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Test IPV6_FLOWINFO cmsg on send and recv */
3
4 #define _GNU_SOURCE
5
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_FLOWINFO
27 #define IPV6_FLOWINFO 11
28 #endif
29
30 #ifndef IPV6_FLOWLABEL_MGR
31 #define IPV6_FLOWLABEL_MGR 32
32 #endif
33 #ifndef IPV6_FLOWINFO_SEND
34 #define IPV6_FLOWINFO_SEND 33
35 #endif
36
37 #define FLOWLABEL_WILDCARD      ((uint32_t) -1)
38
39 static const char cfg_data[]    = "a";
40 static uint32_t cfg_label       = 1;
41 static bool use_ping;
42 static bool use_flowinfo_send;
43
44 static struct icmp6hdr icmp6 = {
45         .icmp6_type = ICMPV6_ECHO_REQUEST
46 };
47
48 static struct sockaddr_in6 addr = {
49         .sin6_family = AF_INET6,
50         .sin6_addr = IN6ADDR_LOOPBACK_INIT,
51 };
52
53 static 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         else
94                 fprintf(stderr, "sent without label\n");
95 }
96
97 static 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
129 parse_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
154 static 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
175 static 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
192 static 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
213 int 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 }