]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
f20a35cc | 2 | /*** |
810adae9 | 3 | Copyright © 2014 Intel Corporation. All rights reserved. |
f20a35cc PF |
4 | ***/ |
5 | ||
6 | #include <netinet/icmp6.h> | |
1e7a0e21 | 7 | #include <arpa/inet.h> |
ca78ad1d | 8 | #include <unistd.h> |
f20a35cc | 9 | |
07630cea | 10 | #include "sd-ndisc.h" |
f20a35cc | 11 | |
1e7a0e21 | 12 | #include "alloc-util.h" |
86d82cb8 | 13 | #include "fd-util.h" |
1e7a0e21 | 14 | #include "hexdecoct.h" |
690afe79 | 15 | #include "icmp6-util-unix.h" |
07630cea | 16 | #include "socket-util.h" |
1e7a0e21 | 17 | #include "strv.h" |
5a67ed24 | 18 | #include "ndisc-internal.h" |
6d7c4033 | 19 | #include "tests.h" |
f20a35cc PF |
20 | |
21 | static struct ether_addr mac_addr = { | |
22 | .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} | |
23 | }; | |
24 | ||
25 | static bool verbose = false; | |
5a67ed24 | 26 | static sd_ndisc *test_timeout_nd; |
f20a35cc | 27 | |
1e7a0e21 LP |
28 | static void router_dump(sd_ndisc_router *rt) { |
29 | struct in6_addr addr; | |
1e7a0e21 | 30 | uint8_t hop_limit; |
d4c8de21 | 31 | usec_t t, lifetime, retrans_time; |
6197db53 | 32 | uint64_t flags; |
1e7a0e21 | 33 | uint32_t mtu; |
1e7a0e21 LP |
34 | unsigned preference; |
35 | int r; | |
36 | ||
37 | assert_se(rt); | |
38 | ||
39 | log_info("--"); | |
4961f566 YW |
40 | assert_se(sd_ndisc_router_get_address(rt, &addr) >= 0); |
41 | log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr)); | |
1e7a0e21 LP |
42 | |
43 | assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
04f5c018 | 44 | log_info("Timestamp: %s", FORMAT_TIMESTAMP(t)); |
1e7a0e21 LP |
45 | |
46 | assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0); | |
47 | log_info("Monotonic: %" PRIu64, t); | |
48 | ||
49 | if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0) | |
50 | log_info("No hop limit set"); | |
51 | else | |
52 | log_info("Hop limit: %u", hop_limit); | |
53 | ||
54 | assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); | |
55 | log_info("Flags: <%s|%s>", | |
56 | flags & ND_RA_FLAG_OTHER ? "OTHER" : "", | |
57 | flags & ND_RA_FLAG_MANAGED ? "MANAGED" : ""); | |
58 | ||
59 | assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0); | |
60 | log_info("Preference: %s", | |
61 | preference == SD_NDISC_PREFERENCE_LOW ? "low" : | |
62 | preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium"); | |
63 | ||
64 | assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0); | |
6197db53 YW |
65 | assert_se(sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); |
66 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 | 67 | |
d4c8de21 MM |
68 | assert_se(sd_ndisc_router_get_retransmission_time(rt, &retrans_time) >= 0); |
69 | log_info("Retransmission Time: %s", FORMAT_TIMESPAN(retrans_time, USEC_PER_SEC)); | |
70 | ||
1e7a0e21 LP |
71 | if (sd_ndisc_router_get_mtu(rt, &mtu) < 0) |
72 | log_info("No MTU set"); | |
73 | else | |
74 | log_info("MTU: %" PRIu32, mtu); | |
75 | ||
76 | r = sd_ndisc_router_option_rewind(rt); | |
77 | for (;;) { | |
78 | uint8_t type; | |
79 | ||
80 | assert_se(r >= 0); | |
81 | ||
82 | if (r == 0) | |
83 | break; | |
84 | ||
85 | assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0); | |
86 | ||
87 | log_info(">> Option %u", type); | |
88 | ||
89 | switch (type) { | |
90 | ||
91 | case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: | |
92 | case SD_NDISC_OPTION_TARGET_LL_ADDRESS: { | |
93 | _cleanup_free_ char *c = NULL; | |
94 | const void *p; | |
95 | size_t n; | |
96 | ||
97 | assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0); | |
98 | assert_se(n > 2); | |
99 | assert_se(c = hexmem((uint8_t*) p + 2, n - 2)); | |
100 | ||
101 | log_info("Address: %s", c); | |
102 | break; | |
103 | } | |
104 | ||
105 | case SD_NDISC_OPTION_PREFIX_INFORMATION: { | |
1e7a0e21 LP |
106 | unsigned prefix_len; |
107 | uint8_t pfl; | |
108 | struct in6_addr a; | |
1e7a0e21 | 109 | |
6197db53 YW |
110 | assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime) >= 0); |
111 | assert_se(sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
112 | log_info("Valid Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 | 113 | |
6197db53 YW |
114 | assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime) >= 0); |
115 | assert_se(sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
116 | log_info("Preferred Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
117 | |
118 | assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0); | |
119 | log_info("Flags: <%s|%s>", | |
120 | pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "", | |
121 | pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : ""); | |
122 | ||
123 | assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0); | |
124 | log_info("Prefix Length: %u", prefix_len); | |
125 | ||
126 | assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0); | |
071e522e | 127 | log_info("Prefix: %s", IN6_ADDR_TO_STRING(&a)); |
1e7a0e21 LP |
128 | |
129 | break; | |
130 | } | |
131 | ||
132 | case SD_NDISC_OPTION_RDNSS: { | |
133 | const struct in6_addr *a; | |
1e7a0e21 LP |
134 | int n, i; |
135 | ||
136 | n = sd_ndisc_router_rdnss_get_addresses(rt, &a); | |
137 | assert_se(n > 0); | |
138 | ||
071e522e ZJS |
139 | for (i = 0; i < n; i++) |
140 | log_info("DNS: %s", IN6_ADDR_TO_STRING(a + i)); | |
1e7a0e21 | 141 | |
6197db53 YW |
142 | assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime) >= 0); |
143 | assert_se(sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
144 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
145 | break; |
146 | } | |
147 | ||
148 | case SD_NDISC_OPTION_DNSSL: { | |
149 | _cleanup_strv_free_ char **l = NULL; | |
1e7a0e21 LP |
150 | int n, i; |
151 | ||
152 | n = sd_ndisc_router_dnssl_get_domains(rt, &l); | |
153 | assert_se(n > 0); | |
154 | ||
155 | for (i = 0; i < n; i++) | |
156 | log_info("Domain: %s", l[i]); | |
157 | ||
6197db53 YW |
158 | assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime) >= 0); |
159 | assert_se(sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
160 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
161 | break; |
162 | }} | |
163 | ||
164 | r = sd_ndisc_router_option_next(rt); | |
165 | } | |
166 | } | |
167 | ||
f20a35cc PF |
168 | static int send_ra(uint8_t flags) { |
169 | uint8_t advertisement[] = { | |
38a80ba1 | 170 | /* struct nd_router_advert */ |
f20a35cc PF |
171 | 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, |
172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
38a80ba1 | 173 | /* type = 0x03 (SD_NDISC_OPTION_PREFIX_INFORMATION), length = 32 */ |
f20a35cc PF |
174 | 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, |
175 | 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, | |
176 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
177 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
38a80ba1 | 178 | /* type = 0x19 (SD_NDISC_OPTION_RDNSS), length = 24 */ |
f20a35cc PF |
179 | 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, |
180 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | |
38a80ba1 | 182 | /* type = 0x1f (SD_NDISC_OPTION_DNSSL), legnth = 24 */ |
f20a35cc PF |
183 | 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, |
184 | 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, | |
185 | 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
38a80ba1 | 186 | /* type = 0x01 (SD_NDISC_OPTION_SOURCE_LL_ADDRESS), legth = 8 */ |
f20a35cc PF |
187 | 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, |
188 | }; | |
189 | ||
190 | advertisement[5] = flags; | |
191 | ||
787784c4 | 192 | assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == |
c21ed681 | 193 | sizeof(advertisement)); |
f20a35cc PF |
194 | |
195 | if (verbose) | |
196 | printf(" sent RA with flag 0x%02x\n", flags); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
2324fd3a | 201 | static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata) { |
f20a35cc | 202 | sd_event *e = userdata; |
cb53894d | 203 | static unsigned idx = 0; |
1e7a0e21 | 204 | uint64_t flags_array[] = { |
9d96e6c3 TG |
205 | 0, |
206 | 0, | |
207 | 0, | |
208 | ND_RA_FLAG_OTHER, | |
209 | ND_RA_FLAG_MANAGED | |
f20a35cc | 210 | }; |
1e7a0e21 | 211 | uint64_t flags; |
8d7f2c6a | 212 | |
787784c4 | 213 | assert_se(nd); |
f20a35cc | 214 | |
1e7a0e21 LP |
215 | if (event != SD_NDISC_EVENT_ROUTER) |
216 | return; | |
217 | ||
218 | router_dump(rt); | |
219 | ||
220 | assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); | |
9d96e6c3 | 221 | assert_se(flags == flags_array[idx]); |
f20a35cc PF |
222 | idx++; |
223 | ||
224 | if (verbose) | |
1e7a0e21 | 225 | printf(" got event 0x%02" PRIx64 "\n", flags); |
f20a35cc | 226 | |
9d96e6c3 TG |
227 | if (idx < ELEMENTSOF(flags_array)) { |
228 | send_ra(flags_array[idx]); | |
8d7f2c6a PF |
229 | return; |
230 | } | |
231 | ||
8d7f2c6a | 232 | sd_event_exit(e, 0); |
f20a35cc PF |
233 | } |
234 | ||
68da8adf | 235 | TEST(rs) { |
86d82cb8 YW |
236 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
237 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; | |
f20a35cc | 238 | |
99af546d PF |
239 | send_ra_function = send_ra; |
240 | ||
241 | assert_se(sd_event_new(&e) >= 0); | |
242 | ||
4d7b83da | 243 | assert_se(sd_ndisc_new(&nd) >= 0); |
787784c4 | 244 | assert_se(nd); |
f20a35cc | 245 | |
4d7b83da | 246 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); |
f20a35cc | 247 | |
2f8e7633 | 248 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); |
4d7b83da | 249 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); |
1e7a0e21 | 250 | assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0); |
f20a35cc | 251 | |
ba4e0427 | 252 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, |
2e37084f YW |
253 | 30 * USEC_PER_SEC, 0, |
254 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
f20a35cc | 255 | |
4d7b83da | 256 | assert_se(sd_ndisc_stop(nd) >= 0); |
1e7a0e21 | 257 | assert_se(sd_ndisc_start(nd) >= 0); |
6b8a1aa6 | 258 | assert_se(sd_ndisc_start(nd) >= 0); |
4d7b83da | 259 | assert_se(sd_ndisc_stop(nd) >= 0); |
86d82cb8 | 260 | test_fd[1] = safe_close(test_fd[1]); |
836cf090 | 261 | |
1e7a0e21 | 262 | assert_se(sd_ndisc_start(nd) >= 0); |
f20a35cc | 263 | |
2e37084f | 264 | assert_se(sd_event_loop(e) >= 0); |
f20a35cc | 265 | |
86d82cb8 | 266 | test_fd[1] = safe_close(test_fd[1]); |
f20a35cc PF |
267 | } |
268 | ||
5a67ed24 PF |
269 | static int test_timeout_value(uint8_t flags) { |
270 | static int count = 0; | |
271 | static usec_t last = 0; | |
272 | sd_ndisc *nd = test_timeout_nd; | |
273 | usec_t min, max; | |
5a67ed24 PF |
274 | |
275 | assert_se(nd); | |
276 | assert_se(nd->event); | |
277 | ||
278 | if (++count >= 20) | |
279 | sd_event_exit(nd->event, 0); | |
280 | ||
281 | if (last == 0) { | |
282 | /* initial RT = IRT + RAND*IRT */ | |
283 | min = NDISC_ROUTER_SOLICITATION_INTERVAL - | |
284 | NDISC_ROUTER_SOLICITATION_INTERVAL / 10; | |
285 | max = NDISC_ROUTER_SOLICITATION_INTERVAL + | |
286 | NDISC_ROUTER_SOLICITATION_INTERVAL / 10; | |
287 | } else { | |
288 | /* next RT = 2*RTprev + RAND*RTprev */ | |
289 | min = 2 * last - last / 10; | |
290 | max = 2 * last + last / 10; | |
291 | } | |
292 | ||
293 | /* final RT > MRT */ | |
294 | if (last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL) { | |
295 | min = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL - | |
296 | NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; | |
297 | max = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL + | |
298 | NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; | |
299 | } | |
300 | ||
5a67ed24 PF |
301 | log_info("backoff timeout interval %2d %s%s <= %s <= %s", |
302 | count, | |
5291f26d ZJS |
303 | last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL ? "(max) ": "", |
304 | FORMAT_TIMESPAN(min, USEC_PER_MSEC), | |
305 | FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_MSEC), | |
306 | FORMAT_TIMESPAN(max, USEC_PER_MSEC)); | |
5a67ed24 PF |
307 | |
308 | assert_se(min <= nd->retransmit_time); | |
309 | assert_se(max >= nd->retransmit_time); | |
310 | ||
311 | last = nd->retransmit_time; | |
312 | ||
313 | assert_se(sd_event_source_set_time(nd->timeout_event_source, 0) >= 0); | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
68da8adf | 318 | TEST(timeout) { |
86d82cb8 YW |
319 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
320 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; | |
5a67ed24 | 321 | |
5a67ed24 PF |
322 | send_ra_function = test_timeout_value; |
323 | ||
324 | assert_se(sd_event_new(&e) >= 0); | |
325 | ||
326 | assert_se(sd_ndisc_new(&nd) >= 0); | |
327 | assert_se(nd); | |
328 | ||
329 | test_timeout_nd = nd; | |
330 | ||
331 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); | |
332 | ||
333 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); | |
334 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); | |
335 | ||
ba4e0427 | 336 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, |
2e37084f YW |
337 | 30 * USEC_PER_SEC, 0, |
338 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
5a67ed24 PF |
339 | |
340 | assert_se(sd_ndisc_start(nd) >= 0); | |
341 | ||
2e37084f | 342 | assert_se(sd_event_loop(e) >= 0); |
5a67ed24 | 343 | |
86d82cb8 | 344 | test_fd[1] = safe_close(test_fd[1]); |
5a67ed24 PF |
345 | } |
346 | ||
68da8adf | 347 | DEFINE_TEST_MAIN(LOG_DEBUG); |