1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Test IPV6_FLOWINFO_MGR */ |
3 | |
4 | #define _GNU_SOURCE |
5 | |
6 | #include <arpa/inet.h> |
7 | #include <error.h> |
8 | #include <errno.h> |
9 | #include <limits.h> |
10 | #include <linux/in6.h> |
11 | #include <stdbool.h> |
12 | #include <stdio.h> |
13 | #include <stdint.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | #include <sys/socket.h> |
17 | #include <sys/stat.h> |
18 | #include <sys/time.h> |
19 | #include <sys/types.h> |
20 | #include <sys/wait.h> |
21 | #include <unistd.h> |
22 | |
23 | /* uapi/glibc weirdness may leave this undefined */ |
24 | #ifndef IPV6_FLOWLABEL_MGR |
25 | #define IPV6_FLOWLABEL_MGR 32 |
26 | #endif |
27 | |
28 | /* from net/ipv6/ip6_flowlabel.c */ |
29 | #define FL_MIN_LINGER 6 |
30 | |
31 | #define explain(x) \ |
32 | do { if (cfg_verbose) fprintf(stderr, " " x "\n"); } while (0) |
33 | |
34 | #define __expect(x) \ |
35 | do { \ |
36 | if (!(x)) \ |
37 | fprintf(stderr, "[OK] " #x "\n"); \ |
38 | else \ |
39 | error(1, 0, "[ERR] " #x " (line %d)", __LINE__); \ |
40 | } while (0) |
41 | |
42 | #define expect_pass(x) __expect(x) |
43 | #define expect_fail(x) __expect(!(x)) |
44 | |
45 | static bool cfg_long_running; |
46 | static bool cfg_verbose; |
47 | |
48 | static int flowlabel_get(int fd, uint32_t label, uint8_t share, uint16_t flags) |
49 | { |
50 | struct in6_flowlabel_req req = { |
51 | .flr_action = IPV6_FL_A_GET, |
52 | .flr_label = htonl(label), |
53 | .flr_flags = flags, |
54 | .flr_share = share, |
55 | }; |
56 | |
57 | /* do not pass IPV6_ADDR_ANY or IPV6_ADDR_MAPPED */ |
58 | req.flr_dst.s6_addr[0] = 0xfd; |
59 | req.flr_dst.s6_addr[15] = 0x1; |
60 | |
61 | return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); |
62 | } |
63 | |
64 | static int flowlabel_put(int fd, uint32_t label) |
65 | { |
66 | struct in6_flowlabel_req req = { |
67 | .flr_action = IPV6_FL_A_PUT, |
68 | .flr_label = htonl(label), |
69 | }; |
70 | |
71 | return setsockopt(fd, SOL_IPV6, IPV6_FLOWLABEL_MGR, &req, sizeof(req)); |
72 | } |
73 | |
74 | static void run_tests(int fd) |
75 | { |
76 | int wstatus; |
77 | pid_t pid; |
78 | |
79 | explain("cannot get non-existent label" ); |
80 | expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); |
81 | |
82 | explain("cannot put non-existent label" ); |
83 | expect_fail(flowlabel_put(fd, 1)); |
84 | |
85 | explain("cannot create label greater than 20 bits" ); |
86 | expect_fail(flowlabel_get(fd, 0x1FFFFF, IPV6_FL_S_ANY, |
87 | IPV6_FL_F_CREATE)); |
88 | |
89 | explain("create a new label (FL_F_CREATE)" ); |
90 | expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); |
91 | explain("can get the label (without FL_F_CREATE)" ); |
92 | expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, 0)); |
93 | explain("can get it again with create flag set, too" ); |
94 | expect_pass(flowlabel_get(fd, 1, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); |
95 | explain("cannot get it again with the exclusive (FL_FL_EXCL) flag" ); |
96 | expect_fail(flowlabel_get(fd, 1, IPV6_FL_S_ANY, |
97 | IPV6_FL_F_CREATE | IPV6_FL_F_EXCL)); |
98 | explain("can now put exactly three references" ); |
99 | expect_pass(flowlabel_put(fd, 1)); |
100 | expect_pass(flowlabel_put(fd, 1)); |
101 | expect_pass(flowlabel_put(fd, 1)); |
102 | expect_fail(flowlabel_put(fd, 1)); |
103 | |
104 | explain("create a new exclusive label (FL_S_EXCL)" ); |
105 | expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); |
106 | explain("cannot get it again in non-exclusive mode" ); |
107 | expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, IPV6_FL_F_CREATE)); |
108 | explain("cannot get it again in exclusive mode either" ); |
109 | expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_EXCL, IPV6_FL_F_CREATE)); |
110 | expect_pass(flowlabel_put(fd, 2)); |
111 | |
112 | if (cfg_long_running) { |
113 | explain("cannot reuse the label, due to linger" ); |
114 | expect_fail(flowlabel_get(fd, 2, IPV6_FL_S_ANY, |
115 | IPV6_FL_F_CREATE)); |
116 | explain("after sleep, can reuse" ); |
117 | sleep(FL_MIN_LINGER * 2 + 1); |
118 | expect_pass(flowlabel_get(fd, 2, IPV6_FL_S_ANY, |
119 | IPV6_FL_F_CREATE)); |
120 | } |
121 | |
122 | explain("create a new user-private label (FL_S_USER)" ); |
123 | expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, IPV6_FL_F_CREATE)); |
124 | explain("cannot get it again in non-exclusive mode" ); |
125 | expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_ANY, 0)); |
126 | explain("cannot get it again in exclusive mode" ); |
127 | expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_EXCL, 0)); |
128 | explain("can get it again in user mode" ); |
129 | expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); |
130 | explain("child process can get it too, but not after setuid(nobody)" ); |
131 | pid = fork(); |
132 | if (pid == -1) |
133 | error(1, errno, "fork" ); |
134 | if (!pid) { |
135 | expect_pass(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); |
136 | if (setuid(USHRT_MAX)) |
137 | fprintf(stderr, "[INFO] skip setuid child test\n" ); |
138 | else |
139 | expect_fail(flowlabel_get(fd, 3, IPV6_FL_S_USER, 0)); |
140 | exit(0); |
141 | } |
142 | if (wait(&wstatus) == -1) |
143 | error(1, errno, "wait" ); |
144 | if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) |
145 | error(1, errno, "wait: unexpected child result" ); |
146 | |
147 | explain("create a new process-private label (FL_S_PROCESS)" ); |
148 | expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, IPV6_FL_F_CREATE)); |
149 | explain("can get it again" ); |
150 | expect_pass(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); |
151 | explain("child process cannot can get it" ); |
152 | pid = fork(); |
153 | if (pid == -1) |
154 | error(1, errno, "fork" ); |
155 | if (!pid) { |
156 | expect_fail(flowlabel_get(fd, 4, IPV6_FL_S_PROCESS, 0)); |
157 | exit(0); |
158 | } |
159 | if (wait(&wstatus) == -1) |
160 | error(1, errno, "wait" ); |
161 | if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) |
162 | error(1, errno, "wait: unexpected child result" ); |
163 | } |
164 | |
165 | static void parse_opts(int argc, char **argv) |
166 | { |
167 | int c; |
168 | |
169 | while ((c = getopt(argc, argv, "lv" )) != -1) { |
170 | switch (c) { |
171 | case 'l': |
172 | cfg_long_running = true; |
173 | break; |
174 | case 'v': |
175 | cfg_verbose = true; |
176 | break; |
177 | default: |
178 | error(1, 0, "%s: parse error" , argv[0]); |
179 | } |
180 | } |
181 | } |
182 | |
183 | int main(int argc, char **argv) |
184 | { |
185 | int fd; |
186 | |
187 | parse_opts(argc, argv); |
188 | |
189 | fd = socket(PF_INET6, SOCK_DGRAM, 0); |
190 | if (fd == -1) |
191 | error(1, errno, "socket" ); |
192 | |
193 | run_tests(fd); |
194 | |
195 | if (close(fd)) |
196 | error(1, errno, "close" ); |
197 | |
198 | return 0; |
199 | } |
200 | |