]>
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 | ||
1cf40697 | 6 | #include <netinet/icmp6.h> |
ca78ad1d | 7 | #include <unistd.h> |
f20a35cc | 8 | |
07630cea | 9 | #include "sd-ndisc.h" |
f20a35cc | 10 | |
1e7a0e21 | 11 | #include "alloc-util.h" |
86d82cb8 | 12 | #include "fd-util.h" |
1e7a0e21 | 13 | #include "hexdecoct.h" |
8430c4d9 | 14 | #include "icmp6-packet.h" |
8e41e460 | 15 | #include "icmp6-test-util.h" |
5cdf13c7 | 16 | #include "in-addr-util.h" |
1cf40697 | 17 | #include "ndisc-internal.h" |
1e7a0e21 | 18 | #include "strv.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; | |
f20a35cc | 26 | |
1e7a0e21 LP |
27 | static void router_dump(sd_ndisc_router *rt) { |
28 | struct in6_addr addr; | |
1e7a0e21 | 29 | uint8_t hop_limit; |
d4c8de21 | 30 | usec_t t, lifetime, retrans_time; |
6197db53 | 31 | uint64_t flags; |
1e7a0e21 | 32 | uint32_t mtu; |
9ca04752 | 33 | uint8_t preference; |
1e7a0e21 LP |
34 | int r; |
35 | ||
36 | assert_se(rt); | |
37 | ||
38 | log_info("--"); | |
9ca04752 | 39 | assert_se(sd_ndisc_router_get_sender_address(rt, &addr) >= 0); |
4961f566 | 40 | log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr)); |
1e7a0e21 LP |
41 | |
42 | assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
04f5c018 | 43 | log_info("Timestamp: %s", FORMAT_TIMESTAMP(t)); |
1e7a0e21 LP |
44 | |
45 | assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &t) >= 0); | |
46 | log_info("Monotonic: %" PRIu64, t); | |
47 | ||
48 | if (sd_ndisc_router_get_hop_limit(rt, &hop_limit) < 0) | |
49 | log_info("No hop limit set"); | |
50 | else | |
51 | log_info("Hop limit: %u", hop_limit); | |
52 | ||
53 | assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); | |
54 | log_info("Flags: <%s|%s>", | |
55 | flags & ND_RA_FLAG_OTHER ? "OTHER" : "", | |
56 | flags & ND_RA_FLAG_MANAGED ? "MANAGED" : ""); | |
57 | ||
58 | assert_se(sd_ndisc_router_get_preference(rt, &preference) >= 0); | |
59 | log_info("Preference: %s", | |
60 | preference == SD_NDISC_PREFERENCE_LOW ? "low" : | |
61 | preference == SD_NDISC_PREFERENCE_HIGH ? "high" : "medium"); | |
62 | ||
63 | assert_se(sd_ndisc_router_get_lifetime(rt, &lifetime) >= 0); | |
6197db53 YW |
64 | assert_se(sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); |
65 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 | 66 | |
d4c8de21 MM |
67 | assert_se(sd_ndisc_router_get_retransmission_time(rt, &retrans_time) >= 0); |
68 | log_info("Retransmission Time: %s", FORMAT_TIMESPAN(retrans_time, USEC_PER_SEC)); | |
69 | ||
1e7a0e21 LP |
70 | if (sd_ndisc_router_get_mtu(rt, &mtu) < 0) |
71 | log_info("No MTU set"); | |
72 | else | |
73 | log_info("MTU: %" PRIu32, mtu); | |
74 | ||
75 | r = sd_ndisc_router_option_rewind(rt); | |
76 | for (;;) { | |
77 | uint8_t type; | |
78 | ||
79 | assert_se(r >= 0); | |
80 | ||
81 | if (r == 0) | |
82 | break; | |
83 | ||
84 | assert_se(sd_ndisc_router_option_get_type(rt, &type) >= 0); | |
85 | ||
86 | log_info(">> Option %u", type); | |
87 | ||
88 | switch (type) { | |
89 | ||
90 | case SD_NDISC_OPTION_SOURCE_LL_ADDRESS: | |
91 | case SD_NDISC_OPTION_TARGET_LL_ADDRESS: { | |
92 | _cleanup_free_ char *c = NULL; | |
9ca04752 | 93 | const uint8_t *p; |
1e7a0e21 LP |
94 | size_t n; |
95 | ||
96 | assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0); | |
97 | assert_se(n > 2); | |
9ca04752 | 98 | assert_se(c = hexmem(p + 2, n - 2)); |
1e7a0e21 LP |
99 | |
100 | log_info("Address: %s", c); | |
101 | break; | |
102 | } | |
103 | ||
104 | case SD_NDISC_OPTION_PREFIX_INFORMATION: { | |
9ca04752 | 105 | uint8_t prefix_len, pfl; |
1e7a0e21 | 106 | struct in6_addr a; |
1e7a0e21 | 107 | |
6197db53 YW |
108 | assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime) >= 0); |
109 | assert_se(sd_ndisc_router_prefix_get_valid_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
110 | log_info("Valid Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 | 111 | |
6197db53 YW |
112 | assert_se(sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime) >= 0); |
113 | assert_se(sd_ndisc_router_prefix_get_preferred_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
114 | log_info("Preferred Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
115 | |
116 | assert_se(sd_ndisc_router_prefix_get_flags(rt, &pfl) >= 0); | |
117 | log_info("Flags: <%s|%s>", | |
118 | pfl & ND_OPT_PI_FLAG_ONLINK ? "ONLINK" : "", | |
119 | pfl & ND_OPT_PI_FLAG_AUTO ? "AUTO" : ""); | |
120 | ||
121 | assert_se(sd_ndisc_router_prefix_get_prefixlen(rt, &prefix_len) >= 0); | |
122 | log_info("Prefix Length: %u", prefix_len); | |
123 | ||
124 | assert_se(sd_ndisc_router_prefix_get_address(rt, &a) >= 0); | |
071e522e | 125 | log_info("Prefix: %s", IN6_ADDR_TO_STRING(&a)); |
1e7a0e21 LP |
126 | |
127 | break; | |
128 | } | |
129 | ||
130 | case SD_NDISC_OPTION_RDNSS: { | |
131 | const struct in6_addr *a; | |
1e7a0e21 LP |
132 | int n, i; |
133 | ||
134 | n = sd_ndisc_router_rdnss_get_addresses(rt, &a); | |
135 | assert_se(n > 0); | |
136 | ||
071e522e ZJS |
137 | for (i = 0; i < n; i++) |
138 | log_info("DNS: %s", IN6_ADDR_TO_STRING(a + i)); | |
1e7a0e21 | 139 | |
6197db53 YW |
140 | assert_se(sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime) >= 0); |
141 | assert_se(sd_ndisc_router_rdnss_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
142 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
143 | break; |
144 | } | |
145 | ||
146 | case SD_NDISC_OPTION_DNSSL: { | |
9ca04752 | 147 | char **l; |
1e7a0e21 | 148 | |
9ca04752 | 149 | assert_se(sd_ndisc_router_dnssl_get_domains(rt, &l) >= 0); |
1e7a0e21 | 150 | |
9ca04752 YW |
151 | STRV_FOREACH(s, l) |
152 | log_info("Domain: %s", *s); | |
1e7a0e21 | 153 | |
6197db53 YW |
154 | assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime) >= 0); |
155 | assert_se(sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0); | |
156 | log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t)); | |
1e7a0e21 LP |
157 | break; |
158 | }} | |
159 | ||
160 | r = sd_ndisc_router_option_next(rt); | |
161 | } | |
162 | } | |
163 | ||
f20a35cc PF |
164 | static int send_ra(uint8_t flags) { |
165 | uint8_t advertisement[] = { | |
38a80ba1 | 166 | /* struct nd_router_advert */ |
f20a35cc PF |
167 | 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, |
168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
38a80ba1 | 169 | /* type = 0x03 (SD_NDISC_OPTION_PREFIX_INFORMATION), length = 32 */ |
f20a35cc PF |
170 | 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, |
171 | 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, | |
172 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
38a80ba1 | 174 | /* type = 0x19 (SD_NDISC_OPTION_RDNSS), length = 24 */ |
f20a35cc PF |
175 | 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, |
176 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
177 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | |
d9b8acda | 178 | /* type = 0x1f (SD_NDISC_OPTION_DNSSL), length = 24 */ |
f20a35cc PF |
179 | 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, |
180 | 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, | |
181 | 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
d9b8acda | 182 | /* type = 0x01 (SD_NDISC_OPTION_SOURCE_LL_ADDRESS), length = 8 */ |
f20a35cc PF |
183 | 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, |
184 | }; | |
185 | ||
186 | advertisement[5] = flags; | |
187 | ||
787784c4 | 188 | assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == |
c21ed681 | 189 | sizeof(advertisement)); |
f20a35cc PF |
190 | |
191 | if (verbose) | |
192 | printf(" sent RA with flag 0x%02x\n", flags); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
ecab9b60 | 197 | static void test_callback_ra(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) { |
f20a35cc | 198 | sd_event *e = userdata; |
cb53894d | 199 | static unsigned idx = 0; |
1e7a0e21 | 200 | uint64_t flags_array[] = { |
9d96e6c3 TG |
201 | 0, |
202 | 0, | |
203 | 0, | |
204 | ND_RA_FLAG_OTHER, | |
205 | ND_RA_FLAG_MANAGED | |
f20a35cc | 206 | }; |
1e7a0e21 | 207 | uint64_t flags; |
8d7f2c6a | 208 | |
787784c4 | 209 | assert_se(nd); |
f20a35cc | 210 | |
1e7a0e21 LP |
211 | if (event != SD_NDISC_EVENT_ROUTER) |
212 | return; | |
213 | ||
28eef158 YW |
214 | sd_ndisc_router *rt = ASSERT_PTR(message); |
215 | ||
1e7a0e21 LP |
216 | router_dump(rt); |
217 | ||
218 | assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0); | |
9d96e6c3 | 219 | assert_se(flags == flags_array[idx]); |
f20a35cc PF |
220 | idx++; |
221 | ||
222 | if (verbose) | |
1e7a0e21 | 223 | printf(" got event 0x%02" PRIx64 "\n", flags); |
f20a35cc | 224 | |
9d96e6c3 TG |
225 | if (idx < ELEMENTSOF(flags_array)) { |
226 | send_ra(flags_array[idx]); | |
8d7f2c6a PF |
227 | return; |
228 | } | |
229 | ||
a39d8396 | 230 | idx = 0; |
8d7f2c6a | 231 | sd_event_exit(e, 0); |
f20a35cc PF |
232 | } |
233 | ||
8430c4d9 YW |
234 | static int on_recv_rs(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
235 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; | |
236 | assert_se(icmp6_packet_receive(fd, &packet) >= 0); | |
237 | ||
238 | return send_ra(0); | |
239 | } | |
240 | ||
68da8adf | 241 | TEST(rs) { |
86d82cb8 | 242 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
8430c4d9 | 243 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
86d82cb8 | 244 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; |
f20a35cc | 245 | |
99af546d PF |
246 | assert_se(sd_event_new(&e) >= 0); |
247 | ||
4d7b83da | 248 | assert_se(sd_ndisc_new(&nd) >= 0); |
787784c4 | 249 | assert_se(nd); |
f20a35cc | 250 | |
4d7b83da | 251 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); |
f20a35cc | 252 | |
2f8e7633 | 253 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); |
4d7b83da | 254 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); |
ecab9b60 | 255 | assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0); |
f20a35cc | 256 | |
ba4e0427 | 257 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, |
2e37084f YW |
258 | 30 * USEC_PER_SEC, 0, |
259 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
f20a35cc | 260 | |
4d7b83da | 261 | assert_se(sd_ndisc_stop(nd) >= 0); |
1e7a0e21 | 262 | assert_se(sd_ndisc_start(nd) >= 0); |
6b8a1aa6 | 263 | assert_se(sd_ndisc_start(nd) >= 0); |
4d7b83da | 264 | assert_se(sd_ndisc_stop(nd) >= 0); |
86d82cb8 | 265 | test_fd[1] = safe_close(test_fd[1]); |
836cf090 | 266 | |
1e7a0e21 | 267 | assert_se(sd_ndisc_start(nd) >= 0); |
f20a35cc | 268 | |
8430c4d9 YW |
269 | assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs, nd) >= 0); |
270 | assert_se(sd_event_source_set_io_fd_own(s, true) >= 0); | |
271 | ||
2e37084f | 272 | assert_se(sd_event_loop(e) >= 0); |
f20a35cc | 273 | |
8430c4d9 | 274 | test_fd[1] = -EBADF; |
f20a35cc PF |
275 | } |
276 | ||
a39d8396 YW |
277 | static int send_ra_invalid_domain(uint8_t flags) { |
278 | uint8_t advertisement[] = { | |
279 | /* struct nd_router_advert */ | |
280 | 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, | |
281 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
282 | /* type = 0x03 (SD_NDISC_OPTION_PREFIX_INFORMATION), length = 32 */ | |
283 | 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, | |
284 | 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, | |
285 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
286 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
287 | /* type = 0x19 (SD_NDISC_OPTION_RDNSS), length = 24 */ | |
288 | 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, | |
289 | 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, | |
290 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | |
d9b8acda | 291 | /* type = 0x1f (SD_NDISC_OPTION_DNSSL), length = 112 */ |
a39d8396 YW |
292 | 0x1f, 0x0e, 0xee, 0x68, 0xb0, 0xf4, 0x36, 0x39, |
293 | 0x2c, 0xbc, 0x0b, 0xbc, 0xa9, 0x97, 0x71, 0x37, | |
294 | 0xad, 0x86, 0x80, 0x14, 0x2e, 0x58, 0xaa, 0x8a, | |
295 | 0xb7, 0xa1, 0xbe, 0x91, 0x59, 0x00, 0xc4, 0xe8, | |
296 | 0xdd, 0xd8, 0x6d, 0xe5, 0x4a, 0x7a, 0x71, 0x42, | |
297 | 0x74, 0x45, 0x9e, 0x2e, 0xfd, 0x9d, 0x71, 0x1d, | |
298 | 0xd0, 0xc0, 0x54, 0x0c, 0x4d, 0x1f, 0xbf, 0x90, | |
299 | 0xd9, 0x79, 0x58, 0xc0, 0x1d, 0xa3, 0x39, 0xcf, | |
300 | 0xb8, 0xec, 0xd2, 0xe4, 0xcd, 0xb6, 0x13, 0x2f, | |
301 | 0xc0, 0x46, 0xe8, 0x07, 0x3f, 0xaa, 0x28, 0xa5, | |
302 | 0x23, 0xf1, 0xf0, 0xca, 0xd3, 0x19, 0x3f, 0xfa, | |
303 | 0x6c, 0x7c, 0xec, 0x1b, 0xcf, 0x71, 0xeb, 0xba, | |
304 | 0x68, 0x1b, 0x8e, 0x7d, 0x93, 0x7e, 0x0b, 0x9f, | |
305 | 0xdb, 0x12, 0x9c, 0x75, 0x22, 0x5f, 0x12, 0x00, | |
d9b8acda | 306 | /* type = 0x01 (SD_NDISC_OPTION_SOURCE_LL_ADDRESS), length = 8 */ |
a39d8396 YW |
307 | 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, |
308 | }; | |
309 | ||
310 | advertisement[5] = flags; | |
311 | ||
312 | printf("sizeof(nd_router_advert)=%zu\n", sizeof(struct nd_router_advert)); | |
313 | ||
314 | assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == | |
315 | sizeof(advertisement)); | |
316 | ||
317 | if (verbose) | |
318 | printf(" sent RA with flag 0x%02x\n", flags); | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
8430c4d9 YW |
323 | static int on_recv_rs_invalid_domain(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
324 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; | |
325 | assert_se(icmp6_packet_receive(fd, &packet) >= 0); | |
326 | ||
327 | return send_ra_invalid_domain(0); | |
328 | } | |
329 | ||
a39d8396 YW |
330 | TEST(invalid_domain) { |
331 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; | |
8430c4d9 | 332 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
a39d8396 YW |
333 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; |
334 | ||
a39d8396 YW |
335 | assert_se(sd_event_new(&e) >= 0); |
336 | ||
337 | assert_se(sd_ndisc_new(&nd) >= 0); | |
338 | assert_se(nd); | |
339 | ||
340 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); | |
341 | ||
342 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); | |
343 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); | |
ecab9b60 | 344 | assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0); |
a39d8396 YW |
345 | |
346 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, | |
347 | 30 * USEC_PER_SEC, 0, | |
348 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
349 | ||
350 | assert_se(sd_ndisc_start(nd) >= 0); | |
351 | ||
8430c4d9 YW |
352 | assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_invalid_domain, nd) >= 0); |
353 | assert_se(sd_event_source_set_io_fd_own(s, true) >= 0); | |
354 | ||
a39d8396 YW |
355 | assert_se(sd_event_loop(e) >= 0); |
356 | ||
8430c4d9 | 357 | test_fd[1] = -EBADF; |
a39d8396 YW |
358 | } |
359 | ||
ecab9b60 YW |
360 | static void neighbor_dump(sd_ndisc_neighbor *na) { |
361 | struct in6_addr addr; | |
362 | uint32_t flags; | |
363 | ||
364 | assert_se(na); | |
365 | ||
366 | log_info("--"); | |
367 | assert_se(sd_ndisc_neighbor_get_sender_address(na, &addr) >= 0); | |
368 | log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr)); | |
369 | ||
370 | assert_se(sd_ndisc_neighbor_get_flags(na, &flags) >= 0); | |
371 | log_info("Flags: Router:%s, Solicited:%s, Override: %s", | |
372 | yes_no(flags & ND_NA_FLAG_ROUTER), | |
373 | yes_no(flags & ND_NA_FLAG_SOLICITED), | |
374 | yes_no(flags & ND_NA_FLAG_OVERRIDE)); | |
375 | ||
376 | assert_se(sd_ndisc_neighbor_is_router(na) == FLAGS_SET(flags, ND_NA_FLAG_ROUTER)); | |
377 | assert_se(sd_ndisc_neighbor_is_solicited(na) == FLAGS_SET(flags, ND_NA_FLAG_SOLICITED)); | |
378 | assert_se(sd_ndisc_neighbor_is_override(na) == FLAGS_SET(flags, ND_NA_FLAG_OVERRIDE)); | |
379 | } | |
380 | ||
381 | static int send_na(uint32_t flags) { | |
382 | uint8_t advertisement[] = { | |
383 | /* struct nd_neighbor_advert */ | |
384 | 0x88, 0x00, 0xde, 0x83, 0x00, 0x00, 0x00, 0x00, | |
385 | 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
386 | 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, | |
387 | /* type = 0x02 (SD_NDISC_OPTION_TARGET_LL_ADDRESS), length = 8 */ | |
388 | 0x01, 0x01, 'A', 'B', 'C', '1', '2', '3', | |
389 | }; | |
390 | ||
391 | ((struct nd_neighbor_advert*) advertisement)->nd_na_flags_reserved = flags; | |
392 | ||
393 | assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement)); | |
394 | if (verbose) | |
395 | printf(" sent NA with flag 0x%02x\n", flags); | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | static void test_callback_na(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) { | |
401 | sd_event *e = userdata; | |
402 | static unsigned idx = 0; | |
403 | uint32_t flags_array[] = { | |
404 | 0, | |
405 | 0, | |
406 | ND_NA_FLAG_ROUTER, | |
407 | ND_NA_FLAG_SOLICITED, | |
408 | ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE, | |
409 | }; | |
410 | uint32_t flags; | |
411 | ||
412 | assert_se(nd); | |
413 | ||
414 | if (event != SD_NDISC_EVENT_NEIGHBOR) | |
415 | return; | |
416 | ||
417 | sd_ndisc_neighbor *rt = ASSERT_PTR(message); | |
418 | ||
419 | neighbor_dump(rt); | |
420 | ||
421 | assert_se(sd_ndisc_neighbor_get_flags(rt, &flags) >= 0); | |
422 | assert_se(flags == flags_array[idx]); | |
423 | idx++; | |
424 | ||
425 | if (verbose) | |
426 | printf(" got event 0x%02" PRIx32 "\n", flags); | |
427 | ||
428 | if (idx < ELEMENTSOF(flags_array)) { | |
429 | send_na(flags_array[idx]); | |
430 | return; | |
431 | } | |
432 | ||
433 | idx = 0; | |
434 | sd_event_exit(e, 0); | |
435 | } | |
436 | ||
437 | static int on_recv_rs_na(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |
438 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; | |
439 | assert_se(icmp6_packet_receive(fd, &packet) >= 0); | |
440 | ||
441 | return send_na(0); | |
442 | } | |
443 | ||
444 | TEST(na) { | |
445 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; | |
446 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; | |
447 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; | |
448 | ||
449 | assert_se(sd_event_new(&e) >= 0); | |
450 | ||
451 | assert_se(sd_ndisc_new(&nd) >= 0); | |
452 | assert_se(nd); | |
453 | ||
454 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); | |
455 | ||
456 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); | |
457 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); | |
458 | assert_se(sd_ndisc_set_callback(nd, test_callback_na, e) >= 0); | |
459 | ||
460 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, | |
461 | 30 * USEC_PER_SEC, 0, | |
462 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
463 | ||
464 | assert_se(sd_ndisc_start(nd) >= 0); | |
465 | ||
466 | assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_na, nd) >= 0); | |
467 | assert_se(sd_event_source_set_io_fd_own(s, true) >= 0); | |
468 | ||
469 | assert_se(sd_event_loop(e) >= 0); | |
470 | ||
471 | test_fd[1] = -EBADF; | |
472 | } | |
473 | ||
8430c4d9 YW |
474 | static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) { |
475 | _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; | |
476 | sd_ndisc *nd = ASSERT_PTR(userdata); | |
5a67ed24 PF |
477 | static int count = 0; |
478 | static usec_t last = 0; | |
5a67ed24 | 479 | usec_t min, max; |
5a67ed24 | 480 | |
8430c4d9 | 481 | assert_se(icmp6_packet_receive(fd, &packet) >= 0); |
5a67ed24 PF |
482 | |
483 | if (++count >= 20) | |
484 | sd_event_exit(nd->event, 0); | |
485 | ||
486 | if (last == 0) { | |
487 | /* initial RT = IRT + RAND*IRT */ | |
488 | min = NDISC_ROUTER_SOLICITATION_INTERVAL - | |
489 | NDISC_ROUTER_SOLICITATION_INTERVAL / 10; | |
490 | max = NDISC_ROUTER_SOLICITATION_INTERVAL + | |
491 | NDISC_ROUTER_SOLICITATION_INTERVAL / 10; | |
492 | } else { | |
493 | /* next RT = 2*RTprev + RAND*RTprev */ | |
494 | min = 2 * last - last / 10; | |
495 | max = 2 * last + last / 10; | |
496 | } | |
497 | ||
498 | /* final RT > MRT */ | |
499 | if (last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL) { | |
500 | min = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL - | |
501 | NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; | |
502 | max = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL + | |
503 | NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; | |
504 | } | |
505 | ||
5a67ed24 PF |
506 | log_info("backoff timeout interval %2d %s%s <= %s <= %s", |
507 | count, | |
5291f26d ZJS |
508 | last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL ? "(max) ": "", |
509 | FORMAT_TIMESPAN(min, USEC_PER_MSEC), | |
510 | FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_MSEC), | |
511 | FORMAT_TIMESPAN(max, USEC_PER_MSEC)); | |
5a67ed24 PF |
512 | |
513 | assert_se(min <= nd->retransmit_time); | |
514 | assert_se(max >= nd->retransmit_time); | |
515 | ||
516 | last = nd->retransmit_time; | |
517 | ||
518 | assert_se(sd_event_source_set_time(nd->timeout_event_source, 0) >= 0); | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
68da8adf | 523 | TEST(timeout) { |
86d82cb8 | 524 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
8430c4d9 | 525 | _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; |
86d82cb8 | 526 | _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; |
5a67ed24 | 527 | |
5a67ed24 PF |
528 | assert_se(sd_event_new(&e) >= 0); |
529 | ||
530 | assert_se(sd_ndisc_new(&nd) >= 0); | |
531 | assert_se(nd); | |
532 | ||
5a67ed24 PF |
533 | assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); |
534 | ||
535 | assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); | |
536 | assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); | |
537 | ||
ba4e0427 | 538 | assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, |
2e37084f YW |
539 | 30 * USEC_PER_SEC, 0, |
540 | NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); | |
5a67ed24 PF |
541 | |
542 | assert_se(sd_ndisc_start(nd) >= 0); | |
543 | ||
8430c4d9 YW |
544 | assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_timeout, nd) >= 0); |
545 | assert_se(sd_event_source_set_io_fd_own(s, true) >= 0); | |
546 | ||
2e37084f | 547 | assert_se(sd_event_loop(e) >= 0); |
5a67ed24 | 548 | |
8430c4d9 | 549 | test_fd[1] = -EBADF; |
5a67ed24 PF |
550 | } |
551 | ||
68da8adf | 552 | DEFINE_TEST_MAIN(LOG_DEBUG); |