File: | src/sbin/slaacd/engine.c |
Warning: | line 924, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: engine.c,v 1.86 2023/12/14 09:58:59 claudio Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2017 Florian Obser <florian@openbsd.org> | |||
5 | * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org> | |||
6 | * Copyright (c) 2004 Esben Norby <norby@openbsd.org> | |||
7 | * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> | |||
8 | * | |||
9 | * Permission to use, copy, modify, and distribute this software for any | |||
10 | * purpose with or without fee is hereby granted, provided that the above | |||
11 | * copyright notice and this permission notice appear in all copies. | |||
12 | * | |||
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
20 | */ | |||
21 | ||||
22 | /* | |||
23 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |||
24 | * All rights reserved. | |||
25 | * | |||
26 | * Redistribution and use in source and binary forms, with or without | |||
27 | * modification, are permitted provided that the following conditions | |||
28 | * are met: | |||
29 | * 1. Redistributions of source code must retain the above copyright | |||
30 | * notice, this list of conditions and the following disclaimer. | |||
31 | * 2. Redistributions in binary form must reproduce the above copyright | |||
32 | * notice, this list of conditions and the following disclaimer in the | |||
33 | * documentation and/or other materials provided with the distribution. | |||
34 | * 3. Neither the name of the project nor the names of its contributors | |||
35 | * may be used to endorse or promote products derived from this software | |||
36 | * without specific prior written permission. | |||
37 | * | |||
38 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |||
39 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
40 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
41 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |||
42 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
43 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
44 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
45 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
46 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
47 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
48 | * SUCH DAMAGE. | |||
49 | */ | |||
50 | ||||
51 | #include <sys/types.h> | |||
52 | #include <sys/queue.h> | |||
53 | #include <sys/socket.h> | |||
54 | #include <sys/syslog.h> | |||
55 | #include <sys/uio.h> | |||
56 | ||||
57 | #include <net/if.h> | |||
58 | #include <net/route.h> | |||
59 | #include <arpa/inet.h> | |||
60 | #include <netinet/in.h> | |||
61 | #include <netinet/if_ether.h> | |||
62 | #include <netinet/ip6.h> | |||
63 | #include <netinet6/ip6_var.h> | |||
64 | #include <netinet6/nd6.h> | |||
65 | #include <netinet/icmp6.h> | |||
66 | ||||
67 | #include <crypto/sha2.h> | |||
68 | ||||
69 | #include <errno(*__errno()).h> | |||
70 | #include <event.h> | |||
71 | #include <imsg.h> | |||
72 | #include <pwd.h> | |||
73 | #include <signal.h> | |||
74 | #include <stddef.h> | |||
75 | #include <stdlib.h> | |||
76 | #include <string.h> | |||
77 | #include <time.h> | |||
78 | #include <unistd.h> | |||
79 | ||||
80 | #include "log.h" | |||
81 | #include "slaacd.h" | |||
82 | #include "engine.h" | |||
83 | ||||
84 | #define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b)) | |||
85 | ||||
86 | #define MAX_RTR_SOLICITATION_DELAY1 1 | |||
87 | #define MAX_RTR_SOLICITATION_DELAY_USEC1 * 1000000 MAX_RTR_SOLICITATION_DELAY1 * 1000000 | |||
88 | #define RTR_SOLICITATION_INTERVAL4 4 | |||
89 | #define MAX_RTR_SOLICITATIONS3 3 | |||
90 | ||||
91 | /* | |||
92 | * Constants for RFC 8981 temporary address extensions | |||
93 | * | |||
94 | * PRIV_PREFERRED_LIFETIME > (PRIV_MAX_DESYNC_FACTOR + PRIV_REGEN_ADVANCE) | |||
95 | */ | |||
96 | #define PRIV_VALID_LIFETIME172800 172800 /* 2 days */ | |||
97 | #define PRIV_PREFERRED_LIFETIME86400 86400 /* 1 day */ | |||
98 | #define PRIV_MAX_DESYNC_FACTOR34560 34560 /* PRIV_PREFERRED_LIFETIME * 0.4 */ | |||
99 | #define PRIV_REGEN_ADVANCE5 5 /* 5 seconds */ | |||
100 | ||||
101 | enum if_state { | |||
102 | IF_DOWN, | |||
103 | IF_INIT, | |||
104 | IF_BOUND, | |||
105 | }; | |||
106 | ||||
107 | enum proposal_state { | |||
108 | PROPOSAL_IF_DOWN, | |||
109 | PROPOSAL_NOT_CONFIGURED, | |||
110 | PROPOSAL_CONFIGURED, | |||
111 | PROPOSAL_NEARLY_EXPIRED, | |||
112 | PROPOSAL_WITHDRAWN, | |||
113 | PROPOSAL_DUPLICATED, | |||
114 | PROPOSAL_STALE, | |||
115 | }; | |||
116 | ||||
117 | const char* rpref_name[] = { | |||
118 | "Low", | |||
119 | "Medium", | |||
120 | "High", | |||
121 | }; | |||
122 | ||||
123 | struct radv_prefix { | |||
124 | LIST_ENTRY(radv_prefix)struct { struct radv_prefix *le_next; struct radv_prefix **le_prev ; } entries; | |||
125 | struct in6_addr prefix; | |||
126 | uint8_t prefix_len; /*XXX int */ | |||
127 | int onlink; | |||
128 | int autonomous; | |||
129 | uint32_t vltime; | |||
130 | uint32_t pltime; | |||
131 | int dad_counter; | |||
132 | }; | |||
133 | ||||
134 | struct radv_rdns { | |||
135 | LIST_ENTRY(radv_rdns)struct { struct radv_rdns *le_next; struct radv_rdns **le_prev ; } entries; | |||
136 | struct in6_addr rdns; | |||
137 | }; | |||
138 | ||||
139 | struct radv { | |||
140 | LIST_ENTRY(radv)struct { struct radv *le_next; struct radv **le_prev; } entries; | |||
141 | struct sockaddr_in6 from; | |||
142 | struct timespec when; | |||
143 | struct timespec uptime; | |||
144 | struct event timer; | |||
145 | uint32_t min_lifetime; | |||
146 | uint8_t curhoplimit; | |||
147 | int managed; | |||
148 | int other; | |||
149 | enum rpref rpref; | |||
150 | uint16_t router_lifetime; /* in seconds */ | |||
151 | uint32_t reachable_time; /* in milliseconds */ | |||
152 | uint32_t retrans_time; /* in milliseconds */ | |||
153 | LIST_HEAD(, radv_prefix)struct { struct radv_prefix *lh_first; } prefixes; | |||
154 | uint32_t rdns_lifetime; | |||
155 | LIST_HEAD(, radv_rdns)struct { struct radv_rdns *lh_first; } rdns_servers; | |||
156 | uint32_t mtu; | |||
157 | }; | |||
158 | ||||
159 | struct address_proposal { | |||
160 | LIST_ENTRY(address_proposal)struct { struct address_proposal *le_next; struct address_proposal **le_prev; } entries; | |||
161 | struct event timer; | |||
162 | int64_t id; | |||
163 | enum proposal_state state; | |||
164 | struct timeval timo; | |||
165 | struct timespec created; | |||
166 | struct timespec when; | |||
167 | struct timespec uptime; | |||
168 | uint32_t if_index; | |||
169 | struct ether_addr hw_address; | |||
170 | struct sockaddr_in6 from; | |||
171 | struct sockaddr_in6 addr; | |||
172 | struct in6_addr mask; | |||
173 | struct in6_addr prefix; | |||
174 | int temporary; | |||
175 | uint8_t prefix_len; | |||
176 | uint32_t vltime; | |||
177 | uint32_t pltime; | |||
178 | uint32_t desync_factor; | |||
179 | uint8_t soiikey[SLAACD_SOIIKEY_LEN16]; | |||
180 | uint32_t mtu; | |||
181 | }; | |||
182 | ||||
183 | struct dfr_proposal { | |||
184 | LIST_ENTRY(dfr_proposal)struct { struct dfr_proposal *le_next; struct dfr_proposal ** le_prev; } entries; | |||
185 | struct event timer; | |||
186 | int64_t id; | |||
187 | enum proposal_state state; | |||
188 | struct timeval timo; | |||
189 | struct timespec when; | |||
190 | struct timespec uptime; | |||
191 | uint32_t if_index; | |||
192 | int rdomain; | |||
193 | struct sockaddr_in6 addr; | |||
194 | uint32_t router_lifetime; | |||
195 | enum rpref rpref; | |||
196 | }; | |||
197 | ||||
198 | struct rdns_proposal { | |||
199 | LIST_ENTRY(rdns_proposal)struct { struct rdns_proposal *le_next; struct rdns_proposal * *le_prev; } entries; | |||
200 | struct event timer; | |||
201 | int64_t id; | |||
202 | enum proposal_state state; | |||
203 | struct timeval timo; | |||
204 | struct timespec when; | |||
205 | struct timespec uptime; | |||
206 | uint32_t if_index; | |||
207 | int rdomain; | |||
208 | struct sockaddr_in6 from; | |||
209 | int rdns_count; | |||
210 | struct in6_addr rdns[MAX_RDNS_COUNT8]; | |||
211 | uint32_t rdns_lifetime; | |||
212 | }; | |||
213 | ||||
214 | struct slaacd_iface { | |||
215 | LIST_ENTRY(slaacd_iface)struct { struct slaacd_iface *le_next; struct slaacd_iface ** le_prev; } entries; | |||
216 | enum if_state state; | |||
217 | struct event timer; | |||
218 | struct timeval timo; | |||
219 | struct timespec last_sol; | |||
220 | int probes; | |||
221 | uint32_t if_index; | |||
222 | uint32_t rdomain; | |||
223 | int running; | |||
224 | int autoconf; | |||
225 | int temporary; | |||
226 | int soii; | |||
227 | struct ether_addr hw_address; | |||
228 | struct sockaddr_in6 ll_address; | |||
229 | uint8_t soiikey[SLAACD_SOIIKEY_LEN16]; | |||
230 | int link_state; | |||
231 | uint32_t cur_mtu; | |||
232 | LIST_HEAD(, radv)struct { struct radv *lh_first; } radvs; | |||
233 | LIST_HEAD(, address_proposal)struct { struct address_proposal *lh_first; } addr_proposals; | |||
234 | LIST_HEAD(, dfr_proposal)struct { struct dfr_proposal *lh_first; } dfr_proposals; | |||
235 | LIST_HEAD(, rdns_proposal)struct { struct rdns_proposal *lh_first; } rdns_proposals; | |||
236 | }; | |||
237 | ||||
238 | LIST_HEAD(, slaacd_iface)struct { struct slaacd_iface *lh_first; } slaacd_interfaces; | |||
239 | ||||
240 | __dead__attribute__((__noreturn__)) void engine_shutdown(void); | |||
241 | void engine_sig_handler(int sig, short, void *); | |||
242 | void engine_dispatch_frontend(int, short, void *); | |||
243 | void engine_dispatch_main(int, short, void *); | |||
244 | #ifndef SMALL | |||
245 | void send_interface_info(struct slaacd_iface *, pid_t); | |||
246 | void engine_showinfo_ctl(struct imsg *, uint32_t); | |||
247 | void debug_log_ra(struct imsg_ra *); | |||
248 | int in6_mask2prefixlen(struct in6_addr *); | |||
249 | #endif /* SMALL */ | |||
250 | struct slaacd_iface *get_slaacd_iface_by_id(uint32_t); | |||
251 | void remove_slaacd_iface(uint32_t); | |||
252 | void free_ra(struct radv *); | |||
253 | void iface_state_transition(struct slaacd_iface *, enum | |||
254 | if_state); | |||
255 | void addr_proposal_state_transition(struct | |||
256 | address_proposal *, enum proposal_state); | |||
257 | void dfr_proposal_state_transition(struct dfr_proposal *, | |||
258 | enum proposal_state); | |||
259 | void rdns_proposal_state_transition(struct rdns_proposal *, | |||
260 | enum proposal_state); | |||
261 | void engine_update_iface(struct imsg_ifinfo *); | |||
262 | void request_solicitation(struct slaacd_iface *); | |||
263 | void parse_ra(struct slaacd_iface *, struct imsg_ra *); | |||
264 | void gen_addr(struct slaacd_iface *, struct radv_prefix *, | |||
265 | struct address_proposal *, int); | |||
266 | void gen_address_proposal(struct slaacd_iface *, struct | |||
267 | radv *, struct radv_prefix *, int); | |||
268 | void free_address_proposal(struct address_proposal *); | |||
269 | void withdraw_addr(struct address_proposal *); | |||
270 | void configure_address(struct address_proposal *); | |||
271 | void in6_prefixlen2mask(struct in6_addr *, int len); | |||
272 | void gen_dfr_proposal(struct slaacd_iface *, struct | |||
273 | radv *); | |||
274 | void configure_dfr(struct dfr_proposal *); | |||
275 | void free_dfr_proposal(struct dfr_proposal *); | |||
276 | void withdraw_dfr(struct dfr_proposal *); | |||
277 | void update_iface_ra_rdns(struct slaacd_iface *, | |||
278 | struct radv *); | |||
279 | void gen_rdns_proposal(struct slaacd_iface *, struct | |||
280 | radv *); | |||
281 | void free_rdns_proposal(struct rdns_proposal *); | |||
282 | void withdraw_rdns(struct rdns_proposal *); | |||
283 | void compose_rdns_proposal(uint32_t, int); | |||
284 | void update_iface_ra(struct slaacd_iface *, struct radv *); | |||
285 | void update_iface_ra_dfr(struct slaacd_iface *, | |||
286 | struct radv *); | |||
287 | void update_iface_ra_prefix(struct slaacd_iface *, | |||
288 | struct radv *, struct radv_prefix *prefix); | |||
289 | void address_proposal_timeout(int, short, void *); | |||
290 | void dfr_proposal_timeout(int, short, void *); | |||
291 | void rdns_proposal_timeout(int, short, void *); | |||
292 | void iface_timeout(int, short, void *); | |||
293 | struct radv *find_ra(struct slaacd_iface *, struct sockaddr_in6 *); | |||
294 | struct address_proposal *find_address_proposal_by_addr(struct slaacd_iface *, | |||
295 | struct sockaddr_in6 *); | |||
296 | struct dfr_proposal *find_dfr_proposal_by_gw(struct slaacd_iface *, | |||
297 | struct sockaddr_in6 *); | |||
298 | struct rdns_proposal *find_rdns_proposal_by_gw(struct slaacd_iface *, | |||
299 | struct sockaddr_in6 *); | |||
300 | struct radv_prefix *find_prefix(struct radv *, struct in6_addr *, uint8_t); | |||
301 | int engine_imsg_compose_main(int, pid_t, void *, uint16_t); | |||
302 | uint32_t real_lifetime(struct timespec *, uint32_t); | |||
303 | void merge_dad_couters(struct radv *, struct radv *); | |||
304 | ||||
305 | static struct imsgev *iev_frontend; | |||
306 | static struct imsgev *iev_main; | |||
307 | int64_t proposal_id; | |||
308 | ||||
309 | ||||
310 | #define CASE(x)case x : return "x" case x : return #x | |||
311 | ||||
312 | #ifndef SMALL | |||
313 | static const char* | |||
314 | if_state_name(enum if_state ifs) | |||
315 | { | |||
316 | switch (ifs) { | |||
317 | CASE(IF_DOWN)case IF_DOWN : return "IF_DOWN"; | |||
318 | CASE(IF_INIT)case IF_INIT : return "IF_INIT"; | |||
319 | CASE(IF_BOUND)case IF_BOUND : return "IF_BOUND"; | |||
320 | } | |||
321 | } | |||
322 | ||||
323 | static const char* | |||
324 | proposal_state_name(enum proposal_state ps) | |||
325 | { | |||
326 | switch (ps) { | |||
327 | CASE(PROPOSAL_IF_DOWN)case PROPOSAL_IF_DOWN : return "PROPOSAL_IF_DOWN"; | |||
328 | CASE(PROPOSAL_NOT_CONFIGURED)case PROPOSAL_NOT_CONFIGURED : return "PROPOSAL_NOT_CONFIGURED"; | |||
329 | CASE(PROPOSAL_CONFIGURED)case PROPOSAL_CONFIGURED : return "PROPOSAL_CONFIGURED"; | |||
330 | CASE(PROPOSAL_NEARLY_EXPIRED)case PROPOSAL_NEARLY_EXPIRED : return "PROPOSAL_NEARLY_EXPIRED"; | |||
331 | CASE(PROPOSAL_WITHDRAWN)case PROPOSAL_WITHDRAWN : return "PROPOSAL_WITHDRAWN"; | |||
332 | CASE(PROPOSAL_DUPLICATED)case PROPOSAL_DUPLICATED : return "PROPOSAL_DUPLICATED"; | |||
333 | CASE(PROPOSAL_STALE)case PROPOSAL_STALE : return "PROPOSAL_STALE"; | |||
334 | } | |||
335 | } | |||
336 | #endif | |||
337 | ||||
338 | void | |||
339 | engine_sig_handler(int sig, short event, void *arg) | |||
340 | { | |||
341 | /* | |||
342 | * Normal signal handler rules don't apply because libevent | |||
343 | * decouples for us. | |||
344 | */ | |||
345 | ||||
346 | switch (sig) { | |||
347 | case SIGINT2: | |||
348 | case SIGTERM15: | |||
349 | engine_shutdown(); | |||
350 | default: | |||
351 | fatalx("unexpected signal"); | |||
352 | } | |||
353 | } | |||
354 | ||||
355 | void | |||
356 | engine(int debug, int verbose) | |||
357 | { | |||
358 | struct event ev_sigint, ev_sigterm; | |||
359 | struct passwd *pw; | |||
360 | ||||
361 | log_init(debug, LOG_DAEMON(3<<3)); | |||
362 | log_setverbose(verbose); | |||
363 | ||||
364 | if ((pw = getpwnam(SLAACD_USER"_slaacd")) == NULL((void *)0)) | |||
365 | fatal("getpwnam"); | |||
366 | ||||
367 | if (chdir("/") == -1) | |||
368 | fatal("chdir(\"/\")"); | |||
369 | ||||
370 | if (unveil("/", "") == -1) | |||
371 | fatal("unveil /"); | |||
372 | if (unveil(NULL((void *)0), NULL((void *)0)) == -1) | |||
373 | fatal("unveil"); | |||
374 | ||||
375 | setproctitle("%s", "engine"); | |||
376 | log_procinit("engine"); | |||
377 | ||||
378 | if (setgroups(1, &pw->pw_gid) || | |||
379 | setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || | |||
380 | setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) | |||
381 | fatal("can't drop privileges"); | |||
382 | ||||
383 | if (pledge("stdio recvfd", NULL((void *)0)) == -1) | |||
384 | fatal("pledge"); | |||
385 | ||||
386 | event_init(); | |||
387 | ||||
388 | /* Setup signal handler(s). */ | |||
389 | signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL)event_set(&ev_sigint, 2, 0x08|0x10, engine_sig_handler, ( (void *)0)); | |||
390 | signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL)event_set(&ev_sigterm, 15, 0x08|0x10, engine_sig_handler, ((void *)0)); | |||
391 | signal_add(&ev_sigint, NULL)event_add(&ev_sigint, ((void *)0)); | |||
392 | signal_add(&ev_sigterm, NULL)event_add(&ev_sigterm, ((void *)0)); | |||
393 | signal(SIGPIPE13, SIG_IGN(void (*)(int))1); | |||
394 | signal(SIGHUP1, SIG_IGN(void (*)(int))1); | |||
395 | ||||
396 | /* Setup pipe and event handler to the main process. */ | |||
397 | if ((iev_main = malloc(sizeof(struct imsgev))) == NULL((void *)0)) | |||
398 | fatal(NULL((void *)0)); | |||
399 | ||||
400 | imsg_init(&iev_main->ibuf, 3); | |||
401 | iev_main->handler = engine_dispatch_main; | |||
402 | ||||
403 | /* Setup event handlers. */ | |||
404 | iev_main->events = EV_READ0x02; | |||
405 | event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, | |||
406 | iev_main->handler, iev_main); | |||
407 | event_add(&iev_main->ev, NULL((void *)0)); | |||
408 | ||||
409 | LIST_INIT(&slaacd_interfaces)do { ((&slaacd_interfaces)->lh_first) = ((void *)0); } while (0); | |||
410 | ||||
411 | event_dispatch(); | |||
412 | ||||
413 | engine_shutdown(); | |||
414 | } | |||
415 | ||||
416 | __dead__attribute__((__noreturn__)) void | |||
417 | engine_shutdown(void) | |||
418 | { | |||
419 | /* Close pipes. */ | |||
420 | msgbuf_clear(&iev_frontend->ibuf.w); | |||
421 | close(iev_frontend->ibuf.fd); | |||
422 | msgbuf_clear(&iev_main->ibuf.w); | |||
423 | close(iev_main->ibuf.fd); | |||
424 | ||||
425 | free(iev_frontend); | |||
426 | free(iev_main); | |||
427 | ||||
428 | log_info("engine exiting"); | |||
429 | exit(0); | |||
430 | } | |||
431 | ||||
432 | int | |||
433 | engine_imsg_compose_frontend(int type, pid_t pid, void *data, | |||
434 | uint16_t datalen) | |||
435 | { | |||
436 | return (imsg_compose_event(iev_frontend, type, 0, pid, -1, | |||
437 | data, datalen)); | |||
438 | } | |||
439 | ||||
440 | int | |||
441 | engine_imsg_compose_main(int type, pid_t pid, void *data, | |||
442 | uint16_t datalen) | |||
443 | { | |||
444 | return (imsg_compose_event(iev_main, type, 0, pid, -1, | |||
445 | data, datalen)); | |||
446 | } | |||
447 | ||||
448 | void | |||
449 | engine_dispatch_frontend(int fd, short event, void *bula) | |||
450 | { | |||
451 | struct imsgev *iev = bula; | |||
452 | struct imsgbuf *ibuf = &iev->ibuf; | |||
453 | struct imsg imsg; | |||
454 | struct slaacd_iface *iface; | |||
455 | struct imsg_ra ra; | |||
456 | struct address_proposal *addr_proposal = NULL((void *)0); | |||
457 | struct dfr_proposal *dfr_proposal = NULL((void *)0); | |||
458 | struct imsg_del_addr del_addr; | |||
459 | struct imsg_del_route del_route; | |||
460 | struct imsg_dup_addr dup_addr; | |||
461 | ssize_t n; | |||
462 | int shut = 0; | |||
463 | #ifndef SMALL | |||
464 | int verbose; | |||
465 | #endif /* SMALL */ | |||
466 | uint32_t if_index; | |||
467 | ||||
468 | if (event & EV_READ0x02) { | |||
| ||||
469 | if ((n = imsg_read(ibuf)) == -1 && errno(*__errno()) != EAGAIN35) | |||
470 | fatal("imsg_read error"); | |||
471 | if (n == 0) /* Connection closed. */ | |||
472 | shut = 1; | |||
473 | } | |||
474 | if (event & EV_WRITE0x04) { | |||
475 | if ((n = msgbuf_write(&ibuf->w)) == -1 && errno(*__errno()) != EAGAIN35) | |||
476 | fatal("msgbuf_write"); | |||
477 | if (n == 0) /* Connection closed. */ | |||
478 | shut = 1; | |||
479 | } | |||
480 | ||||
481 | for (;;) { | |||
482 | if ((n = imsg_get(ibuf, &imsg)) == -1) | |||
483 | fatal("%s: imsg_get error", __func__); | |||
484 | if (n == 0) /* No more messages. */ | |||
485 | break; | |||
486 | ||||
487 | switch (imsg.hdr.type) { | |||
488 | #ifndef SMALL | |||
489 | case IMSG_CTL_LOG_VERBOSE: | |||
490 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(verbose)) | |||
491 | fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " | |||
492 | "%lu", __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
493 | memcpy(&verbose, imsg.data, sizeof(verbose)); | |||
494 | log_setverbose(verbose); | |||
495 | break; | |||
496 | case IMSG_CTL_SHOW_INTERFACE_INFO: | |||
497 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(if_index)) | |||
498 | fatalx("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong " | |||
499 | "length: %lu", __func__, | |||
500 | IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
501 | memcpy(&if_index, imsg.data, sizeof(if_index)); | |||
502 | engine_showinfo_ctl(&imsg, if_index); | |||
503 | break; | |||
504 | #endif /* SMALL */ | |||
505 | case IMSG_REMOVE_IF: | |||
506 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(if_index)) | |||
507 | fatalx("%s: IMSG_REMOVE_IF wrong length: %lu", | |||
508 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
509 | memcpy(&if_index, imsg.data, sizeof(if_index)); | |||
510 | remove_slaacd_iface(if_index); | |||
511 | break; | |||
512 | case IMSG_RA: | |||
513 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(ra)) | |||
514 | fatalx("%s: IMSG_RA wrong length: %lu", | |||
515 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
516 | memcpy(&ra, imsg.data, sizeof(ra)); | |||
517 | iface = get_slaacd_iface_by_id(ra.if_index); | |||
518 | ||||
519 | /* | |||
520 | * Ignore unsolicitated router advertisements | |||
521 | * if we think the interface is still down. | |||
522 | * Otherwise we confuse the state machine. | |||
523 | */ | |||
524 | if (iface != NULL((void *)0) && iface->state != IF_DOWN) | |||
525 | parse_ra(iface, &ra); | |||
526 | break; | |||
527 | case IMSG_CTL_SEND_SOLICITATION: | |||
528 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(if_index)) | |||
529 | fatalx("%s: IMSG_CTL_SEND_SOLICITATION wrong " | |||
530 | "length: %lu", __func__, | |||
531 | IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
532 | memcpy(&if_index, imsg.data, sizeof(if_index)); | |||
533 | iface = get_slaacd_iface_by_id(if_index); | |||
534 | if (iface == NULL((void *)0)) | |||
535 | log_warnx("requested to send solicitation on " | |||
536 | "non-autoconf interface: %u", if_index); | |||
537 | else { | |||
538 | iface->last_sol.tv_sec = 0; /* no rate limit */ | |||
539 | request_solicitation(iface); | |||
540 | } | |||
541 | break; | |||
542 | case IMSG_DEL_ADDRESS: | |||
543 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(del_addr)) | |||
544 | fatalx("%s: IMSG_DEL_ADDRESS wrong length: %lu", | |||
545 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
546 | memcpy(&del_addr, imsg.data, sizeof(del_addr)); | |||
547 | iface = get_slaacd_iface_by_id(del_addr.if_index); | |||
548 | if (iface == NULL((void *)0)) { | |||
549 | log_debug("IMSG_DEL_ADDRESS: unknown interface" | |||
550 | ", ignoring"); | |||
551 | break; | |||
552 | } | |||
553 | ||||
554 | addr_proposal = find_address_proposal_by_addr(iface, | |||
555 | &del_addr.addr); | |||
556 | /* | |||
557 | * If it's in state PROPOSAL_WITHDRAWN we just | |||
558 | * deleted it ourself but want to keep it around | |||
559 | * so we can renew it | |||
560 | */ | |||
561 | if (addr_proposal && addr_proposal->state != | |||
562 | PROPOSAL_WITHDRAWN) | |||
563 | free_address_proposal(addr_proposal); | |||
564 | break; | |||
565 | case IMSG_DEL_ROUTE: | |||
566 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(del_route)) | |||
567 | fatalx("%s: IMSG_DEL_ROUTE wrong length: %lu", | |||
568 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
569 | memcpy(&del_route, imsg.data, sizeof(del_route)); | |||
570 | iface = get_slaacd_iface_by_id(del_route.if_index); | |||
571 | if (iface == NULL((void *)0)) { | |||
572 | log_debug("IMSG_DEL_ROUTE: unknown interface" | |||
573 | ", ignoring"); | |||
574 | break; | |||
575 | } | |||
576 | ||||
577 | dfr_proposal = find_dfr_proposal_by_gw(iface, | |||
578 | &del_route.gw); | |||
579 | ||||
580 | if (dfr_proposal) { | |||
581 | dfr_proposal->state = PROPOSAL_WITHDRAWN; | |||
582 | free_dfr_proposal(dfr_proposal); | |||
583 | } | |||
584 | break; | |||
585 | case IMSG_DUP_ADDRESS: | |||
586 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(dup_addr)) | |||
587 | fatalx("%s: IMSG_DUP_ADDRESS wrong length: %lu", | |||
588 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
589 | memcpy(&dup_addr, imsg.data, sizeof(dup_addr)); | |||
590 | iface = get_slaacd_iface_by_id(dup_addr.if_index); | |||
591 | if (iface == NULL((void *)0)) { | |||
592 | log_debug("IMSG_DUP_ADDRESS: unknown interface" | |||
593 | ", ignoring"); | |||
594 | break; | |||
595 | } | |||
596 | ||||
597 | addr_proposal = find_address_proposal_by_addr(iface, | |||
598 | &dup_addr.addr); | |||
599 | ||||
600 | if (addr_proposal) | |||
601 | addr_proposal_state_transition(addr_proposal, | |||
602 | PROPOSAL_DUPLICATED); | |||
603 | break; | |||
604 | case IMSG_REPROPOSE_RDNS: | |||
605 | LIST_FOREACH (iface, &slaacd_interfaces, entries)for((iface) = ((&slaacd_interfaces)->lh_first); (iface )!= ((void *)0); (iface) = ((iface)->entries.le_next)) | |||
606 | compose_rdns_proposal(iface->if_index, | |||
607 | iface->rdomain); | |||
608 | break; | |||
609 | default: | |||
610 | log_debug("%s: unexpected imsg %d", __func__, | |||
611 | imsg.hdr.type); | |||
612 | break; | |||
613 | } | |||
614 | imsg_free(&imsg); | |||
615 | } | |||
616 | if (!shut) | |||
617 | imsg_event_add(iev); | |||
618 | else { | |||
619 | /* This pipe is dead. Remove its event handler. */ | |||
620 | event_del(&iev->ev); | |||
621 | event_loopexit(NULL((void *)0)); | |||
622 | } | |||
623 | } | |||
624 | ||||
625 | void | |||
626 | engine_dispatch_main(int fd, short event, void *bula) | |||
627 | { | |||
628 | struct imsg imsg; | |||
629 | struct imsgev *iev = bula; | |||
630 | struct imsgbuf *ibuf = &iev->ibuf; | |||
631 | struct imsg_ifinfo imsg_ifinfo; | |||
632 | ssize_t n; | |||
633 | int shut = 0; | |||
634 | ||||
635 | if (event & EV_READ0x02) { | |||
636 | if ((n = imsg_read(ibuf)) == -1 && errno(*__errno()) != EAGAIN35) | |||
637 | fatal("imsg_read error"); | |||
638 | if (n == 0) /* Connection closed. */ | |||
639 | shut = 1; | |||
640 | } | |||
641 | if (event & EV_WRITE0x04) { | |||
642 | if ((n = msgbuf_write(&ibuf->w)) == -1 && errno(*__errno()) != EAGAIN35) | |||
643 | fatal("msgbuf_write"); | |||
644 | if (n == 0) /* Connection closed. */ | |||
645 | shut = 1; | |||
646 | } | |||
647 | ||||
648 | for (;;) { | |||
649 | if ((n = imsg_get(ibuf, &imsg)) == -1) | |||
650 | fatal("%s: imsg_get error", __func__); | |||
651 | if (n == 0) /* No more messages. */ | |||
652 | break; | |||
653 | ||||
654 | switch (imsg.hdr.type) { | |||
655 | case IMSG_SOCKET_IPC: | |||
656 | /* | |||
657 | * Setup pipe and event handler to the frontend | |||
658 | * process. | |||
659 | */ | |||
660 | if (iev_frontend) | |||
661 | fatalx("%s: received unexpected imsg fd " | |||
662 | "to engine", __func__); | |||
663 | ||||
664 | if ((fd = imsg_get_fd(&imsg)) == -1) | |||
665 | fatalx("%s: expected to receive imsg fd to " | |||
666 | "engine but didn't receive any", __func__); | |||
667 | ||||
668 | iev_frontend = malloc(sizeof(struct imsgev)); | |||
669 | if (iev_frontend == NULL((void *)0)) | |||
670 | fatal(NULL((void *)0)); | |||
671 | ||||
672 | imsg_init(&iev_frontend->ibuf, fd); | |||
673 | iev_frontend->handler = engine_dispatch_frontend; | |||
674 | iev_frontend->events = EV_READ0x02; | |||
675 | ||||
676 | event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, | |||
677 | iev_frontend->events, iev_frontend->handler, | |||
678 | iev_frontend); | |||
679 | event_add(&iev_frontend->ev, NULL((void *)0)); | |||
680 | ||||
681 | if (pledge("stdio", NULL((void *)0)) == -1) | |||
682 | fatal("pledge"); | |||
683 | break; | |||
684 | case IMSG_UPDATE_IF: | |||
685 | if (IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr)) != sizeof(imsg_ifinfo)) | |||
686 | fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", | |||
687 | __func__, IMSG_DATA_SIZE(imsg)((imsg).hdr.len - sizeof(struct imsg_hdr))); | |||
688 | memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); | |||
689 | engine_update_iface(&imsg_ifinfo); | |||
690 | break; | |||
691 | default: | |||
692 | log_debug("%s: unexpected imsg %d", __func__, | |||
693 | imsg.hdr.type); | |||
694 | break; | |||
695 | } | |||
696 | imsg_free(&imsg); | |||
697 | } | |||
698 | if (!shut) | |||
699 | imsg_event_add(iev); | |||
700 | else { | |||
701 | /* This pipe is dead. Remove its event handler. */ | |||
702 | event_del(&iev->ev); | |||
703 | event_loopexit(NULL((void *)0)); | |||
704 | } | |||
705 | } | |||
706 | ||||
707 | #ifndef SMALL | |||
708 | void | |||
709 | send_interface_info(struct slaacd_iface *iface, pid_t pid) | |||
710 | { | |||
711 | struct ctl_engine_info cei; | |||
712 | struct ctl_engine_info_ra cei_ra; | |||
713 | struct ctl_engine_info_ra_prefix cei_ra_prefix; | |||
714 | struct ctl_engine_info_ra_rdns cei_ra_rdns; | |||
715 | struct ctl_engine_info_address_proposal cei_addr_proposal; | |||
716 | struct ctl_engine_info_dfr_proposal cei_dfr_proposal; | |||
717 | struct ctl_engine_info_rdns_proposal cei_rdns_proposal; | |||
718 | struct radv *ra; | |||
719 | struct radv_prefix *prefix; | |||
720 | struct radv_rdns *rdns; | |||
721 | struct address_proposal *addr_proposal; | |||
722 | struct dfr_proposal *dfr_proposal; | |||
723 | struct rdns_proposal *rdns_proposal; | |||
724 | ||||
725 | memset(&cei, 0, sizeof(cei)); | |||
726 | cei.if_index = iface->if_index; | |||
727 | cei.running = iface->running; | |||
728 | cei.autoconf = iface->autoconf; | |||
729 | cei.temporary = iface->temporary; | |||
730 | cei.soii = iface->soii; | |||
731 | memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr)); | |||
732 | memcpy(&cei.ll_address, &iface->ll_address, | |||
733 | sizeof(struct sockaddr_in6)); | |||
734 | engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, | |||
735 | sizeof(cei)); | |||
736 | LIST_FOREACH(ra, &iface->radvs, entries)for((ra) = ((&iface->radvs)->lh_first); (ra)!= ((void *)0); (ra) = ((ra)->entries.le_next)) { | |||
737 | memset(&cei_ra, 0, sizeof(cei_ra)); | |||
738 | memcpy(&cei_ra.from, &ra->from, sizeof(cei_ra.from)); | |||
739 | memcpy(&cei_ra.when, &ra->when, sizeof(cei_ra.when)); | |||
740 | memcpy(&cei_ra.uptime, &ra->uptime, sizeof(cei_ra.uptime)); | |||
741 | cei_ra.curhoplimit = ra->curhoplimit; | |||
742 | cei_ra.managed = ra->managed; | |||
743 | cei_ra.other = ra->other; | |||
744 | if (strlcpy(cei_ra.rpref, rpref_name[ra->rpref], sizeof( | |||
745 | cei_ra.rpref)) >= sizeof(cei_ra.rpref)) | |||
746 | log_warnx("truncated router preference"); | |||
747 | cei_ra.router_lifetime = ra->router_lifetime; | |||
748 | cei_ra.reachable_time = ra->reachable_time; | |||
749 | cei_ra.retrans_time = ra->retrans_time; | |||
750 | cei_ra.mtu = ra->mtu; | |||
751 | engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO_RA, | |||
752 | pid, &cei_ra, sizeof(cei_ra)); | |||
753 | ||||
754 | LIST_FOREACH(prefix, &ra->prefixes, entries)for((prefix) = ((&ra->prefixes)->lh_first); (prefix )!= ((void *)0); (prefix) = ((prefix)->entries.le_next)) { | |||
755 | memset(&cei_ra_prefix, 0, sizeof(cei_ra_prefix)); | |||
756 | ||||
757 | cei_ra_prefix.prefix = prefix->prefix; | |||
758 | cei_ra_prefix.prefix_len = prefix->prefix_len; | |||
759 | cei_ra_prefix.onlink = prefix->onlink; | |||
760 | cei_ra_prefix.autonomous = prefix->autonomous; | |||
761 | cei_ra_prefix.vltime = prefix->vltime; | |||
762 | cei_ra_prefix.pltime = prefix->pltime; | |||
763 | engine_imsg_compose_frontend( | |||
764 | IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX, pid, | |||
765 | &cei_ra_prefix, sizeof(cei_ra_prefix)); | |||
766 | } | |||
767 | ||||
768 | LIST_FOREACH(rdns, &ra->rdns_servers, entries)for((rdns) = ((&ra->rdns_servers)->lh_first); (rdns )!= ((void *)0); (rdns) = ((rdns)->entries.le_next)) { | |||
769 | memset(&cei_ra_rdns, 0, sizeof(cei_ra_rdns)); | |||
770 | memcpy(&cei_ra_rdns.rdns, &rdns->rdns, | |||
771 | sizeof(cei_ra_rdns.rdns)); | |||
772 | cei_ra_rdns.lifetime = ra->rdns_lifetime; | |||
773 | engine_imsg_compose_frontend( | |||
774 | IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS, pid, | |||
775 | &cei_ra_rdns, sizeof(cei_ra_rdns)); | |||
776 | } | |||
777 | } | |||
778 | ||||
779 | if (!LIST_EMPTY(&iface->addr_proposals)(((&iface->addr_proposals)->lh_first) == ((void *)0 ))) | |||
780 | engine_imsg_compose_frontend( | |||
781 | IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS, pid, NULL((void *)0), 0); | |||
782 | ||||
783 | LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries)for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) { | |||
784 | memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal)); | |||
785 | cei_addr_proposal.id = addr_proposal->id; | |||
786 | if(strlcpy(cei_addr_proposal.state, | |||
787 | proposal_state_name(addr_proposal->state), | |||
788 | sizeof(cei_addr_proposal.state)) >= | |||
789 | sizeof(cei_addr_proposal.state)) | |||
790 | log_warnx("truncated state name"); | |||
791 | cei_addr_proposal.next_timeout = addr_proposal->timo.tv_sec; | |||
792 | cei_addr_proposal.when = addr_proposal->when; | |||
793 | cei_addr_proposal.uptime = addr_proposal->uptime; | |||
794 | memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof( | |||
795 | cei_addr_proposal.addr)); | |||
796 | memcpy(&cei_addr_proposal.prefix, &addr_proposal->prefix, | |||
797 | sizeof(cei_addr_proposal.prefix)); | |||
798 | cei_addr_proposal.prefix_len = addr_proposal->prefix_len; | |||
799 | cei_addr_proposal.temporary = addr_proposal->temporary; | |||
800 | cei_addr_proposal.vltime = addr_proposal->vltime; | |||
801 | cei_addr_proposal.pltime = addr_proposal->pltime; | |||
802 | ||||
803 | engine_imsg_compose_frontend( | |||
804 | IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL, pid, | |||
805 | &cei_addr_proposal, sizeof(cei_addr_proposal)); | |||
806 | } | |||
807 | ||||
808 | if (!LIST_EMPTY(&iface->dfr_proposals)(((&iface->dfr_proposals)->lh_first) == ((void *)0) )) | |||
809 | engine_imsg_compose_frontend( | |||
810 | IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS, pid, NULL((void *)0), 0); | |||
811 | ||||
812 | LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries)for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) { | |||
813 | memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal)); | |||
814 | cei_dfr_proposal.id = dfr_proposal->id; | |||
815 | if(strlcpy(cei_dfr_proposal.state, | |||
816 | proposal_state_name(dfr_proposal->state), | |||
817 | sizeof(cei_dfr_proposal.state)) >= | |||
818 | sizeof(cei_dfr_proposal.state)) | |||
819 | log_warnx("truncated state name"); | |||
820 | cei_dfr_proposal.next_timeout = dfr_proposal->timo.tv_sec; | |||
821 | cei_dfr_proposal.when = dfr_proposal->when; | |||
822 | cei_dfr_proposal.uptime = dfr_proposal->uptime; | |||
823 | memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof( | |||
824 | cei_dfr_proposal.addr)); | |||
825 | cei_dfr_proposal.router_lifetime = | |||
826 | dfr_proposal->router_lifetime; | |||
827 | if(strlcpy(cei_dfr_proposal.rpref, | |||
828 | rpref_name[dfr_proposal->rpref], | |||
829 | sizeof(cei_dfr_proposal.rpref)) >= | |||
830 | sizeof(cei_dfr_proposal.rpref)) | |||
831 | log_warnx("truncated router preference"); | |||
832 | engine_imsg_compose_frontend( | |||
833 | IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL, pid, | |||
834 | &cei_dfr_proposal, sizeof(cei_dfr_proposal)); | |||
835 | } | |||
836 | ||||
837 | if (!LIST_EMPTY(&iface->rdns_proposals)(((&iface->rdns_proposals)->lh_first) == ((void *)0 ))) | |||
838 | engine_imsg_compose_frontend( | |||
839 | IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSALS, pid, NULL((void *)0), 0); | |||
840 | ||||
841 | LIST_FOREACH(rdns_proposal, &iface->rdns_proposals, entries)for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) { | |||
842 | memset(&cei_rdns_proposal, 0, sizeof(cei_rdns_proposal)); | |||
843 | cei_rdns_proposal.id = rdns_proposal->id; | |||
844 | if(strlcpy(cei_rdns_proposal.state, | |||
845 | proposal_state_name(rdns_proposal->state), | |||
846 | sizeof(cei_rdns_proposal.state)) >= | |||
847 | sizeof(cei_rdns_proposal.state)) | |||
848 | log_warnx("truncated state name"); | |||
849 | cei_rdns_proposal.next_timeout = rdns_proposal->timo.tv_sec; | |||
850 | cei_rdns_proposal.when = rdns_proposal->when; | |||
851 | cei_rdns_proposal.uptime = rdns_proposal->uptime; | |||
852 | memcpy(&cei_rdns_proposal.from, &rdns_proposal->from, sizeof( | |||
853 | cei_rdns_proposal.from)); | |||
854 | cei_rdns_proposal.rdns_count = rdns_proposal->rdns_count; | |||
855 | memcpy(&cei_rdns_proposal.rdns, | |||
856 | &rdns_proposal->rdns, sizeof(cei_rdns_proposal.rdns)); | |||
857 | cei_rdns_proposal.rdns_lifetime = | |||
858 | rdns_proposal->rdns_lifetime; | |||
859 | engine_imsg_compose_frontend( | |||
860 | IMSG_CTL_SHOW_INTERFACE_INFO_RDNS_PROPOSAL, pid, | |||
861 | &cei_rdns_proposal, sizeof(cei_rdns_proposal)); | |||
862 | } | |||
863 | } | |||
864 | ||||
865 | void | |||
866 | engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index) | |||
867 | { | |||
868 | struct slaacd_iface *iface; | |||
869 | ||||
870 | switch (imsg->hdr.type) { | |||
871 | case IMSG_CTL_SHOW_INTERFACE_INFO: | |||
872 | if (if_index == 0) { | |||
873 | LIST_FOREACH (iface, &slaacd_interfaces, entries)for((iface) = ((&slaacd_interfaces)->lh_first); (iface )!= ((void *)0); (iface) = ((iface)->entries.le_next)) | |||
874 | send_interface_info(iface, imsg->hdr.pid); | |||
875 | } else { | |||
876 | if ((iface = get_slaacd_iface_by_id(if_index)) != NULL((void *)0)) | |||
877 | send_interface_info(iface, imsg->hdr.pid); | |||
878 | } | |||
879 | engine_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL((void *)0), | |||
880 | 0); | |||
881 | break; | |||
882 | default: | |||
883 | log_debug("%s: error handling imsg", __func__); | |||
884 | break; | |||
885 | } | |||
886 | } | |||
887 | ||||
888 | #endif /* SMALL */ | |||
889 | ||||
890 | struct slaacd_iface* | |||
891 | get_slaacd_iface_by_id(uint32_t if_index) | |||
892 | { | |||
893 | struct slaacd_iface *iface; | |||
894 | LIST_FOREACH (iface, &slaacd_interfaces, entries)for((iface) = ((&slaacd_interfaces)->lh_first); (iface )!= ((void *)0); (iface) = ((iface)->entries.le_next)) { | |||
895 | if (iface->if_index == if_index) | |||
896 | return (iface); | |||
897 | } | |||
898 | ||||
899 | return (NULL((void *)0)); | |||
900 | } | |||
901 | ||||
902 | void | |||
903 | remove_slaacd_iface(uint32_t if_index) | |||
904 | { | |||
905 | struct slaacd_iface *iface; | |||
906 | struct radv *ra; | |||
907 | struct address_proposal *addr_proposal; | |||
908 | struct dfr_proposal *dfr_proposal; | |||
909 | struct rdns_proposal *rdns_proposal; | |||
910 | ||||
911 | iface = get_slaacd_iface_by_id(if_index); | |||
912 | ||||
913 | if (iface == NULL((void *)0)) | |||
914 | return; | |||
915 | ||||
916 | LIST_REMOVE(iface, entries)do { if ((iface)->entries.le_next != ((void *)0)) (iface)-> entries.le_next->entries.le_prev = (iface)->entries.le_prev ; *(iface)->entries.le_prev = (iface)->entries.le_next; ; ; } while (0); | |||
917 | while(!LIST_EMPTY(&iface->radvs)(((&iface->radvs)->lh_first) == ((void *)0))) { | |||
918 | ra = LIST_FIRST(&iface->radvs)((&iface->radvs)->lh_first); | |||
919 | LIST_REMOVE(ra, entries)do { if ((ra)->entries.le_next != ((void *)0)) (ra)->entries .le_next->entries.le_prev = (ra)->entries.le_prev; *(ra )->entries.le_prev = (ra)->entries.le_next; ; ; } while (0); | |||
920 | free_ra(ra); | |||
921 | } | |||
922 | while(!LIST_EMPTY(&iface->addr_proposals)(((&iface->addr_proposals)->lh_first) == ((void *)0 ))) { | |||
923 | addr_proposal = LIST_FIRST(&iface->addr_proposals)((&iface->addr_proposals)->lh_first); | |||
924 | free_address_proposal(addr_proposal); | |||
| ||||
925 | } | |||
926 | while(!LIST_EMPTY(&iface->dfr_proposals)(((&iface->dfr_proposals)->lh_first) == ((void *)0) )) { | |||
927 | dfr_proposal = LIST_FIRST(&iface->dfr_proposals)((&iface->dfr_proposals)->lh_first); | |||
928 | free_dfr_proposal(dfr_proposal); | |||
929 | } | |||
930 | while(!LIST_EMPTY(&iface->rdns_proposals)(((&iface->rdns_proposals)->lh_first) == ((void *)0 ))) { | |||
931 | rdns_proposal = LIST_FIRST(&iface->rdns_proposals)((&iface->rdns_proposals)->lh_first); | |||
932 | free_rdns_proposal(rdns_proposal); | |||
933 | } | |||
934 | compose_rdns_proposal(iface->if_index, iface->rdomain); | |||
935 | evtimer_del(&iface->timer)event_del(&iface->timer); | |||
936 | free(iface); | |||
937 | } | |||
938 | ||||
939 | void | |||
940 | free_ra(struct radv *ra) | |||
941 | { | |||
942 | struct radv_prefix *prefix; | |||
943 | struct radv_rdns *rdns; | |||
944 | ||||
945 | if (ra == NULL((void *)0)) | |||
946 | return; | |||
947 | ||||
948 | evtimer_del(&ra->timer)event_del(&ra->timer); | |||
949 | ||||
950 | while (!LIST_EMPTY(&ra->prefixes)(((&ra->prefixes)->lh_first) == ((void *)0))) { | |||
951 | prefix = LIST_FIRST(&ra->prefixes)((&ra->prefixes)->lh_first); | |||
952 | LIST_REMOVE(prefix, entries)do { if ((prefix)->entries.le_next != ((void *)0)) (prefix )->entries.le_next->entries.le_prev = (prefix)->entries .le_prev; *(prefix)->entries.le_prev = (prefix)->entries .le_next; ; ; } while (0); | |||
953 | free(prefix); | |||
954 | } | |||
955 | ||||
956 | while (!LIST_EMPTY(&ra->rdns_servers)(((&ra->rdns_servers)->lh_first) == ((void *)0))) { | |||
957 | rdns = LIST_FIRST(&ra->rdns_servers)((&ra->rdns_servers)->lh_first); | |||
958 | LIST_REMOVE(rdns, entries)do { if ((rdns)->entries.le_next != ((void *)0)) (rdns)-> entries.le_next->entries.le_prev = (rdns)->entries.le_prev ; *(rdns)->entries.le_prev = (rdns)->entries.le_next; ; ; } while (0); | |||
959 | free(rdns); | |||
960 | } | |||
961 | ||||
962 | free(ra); | |||
963 | } | |||
964 | ||||
965 | void | |||
966 | iface_state_transition(struct slaacd_iface *iface, enum if_state new_state) | |||
967 | { | |||
968 | enum if_state old_state = iface->state; | |||
969 | struct address_proposal *addr_proposal; | |||
970 | struct dfr_proposal *dfr_proposal; | |||
971 | struct rdns_proposal *rdns_proposal; | |||
972 | char ifnamebuf[IF_NAMESIZE16], *if_name; | |||
973 | ||||
974 | iface->state = new_state; | |||
975 | ||||
976 | switch (new_state) { | |||
977 | case IF_DOWN: | |||
978 | if (old_state != IF_DOWN) { | |||
979 | LIST_FOREACH (addr_proposal, &iface->addr_proposals,for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) | |||
980 | entries)for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) | |||
981 | addr_proposal_state_transition(addr_proposal, | |||
982 | PROPOSAL_IF_DOWN); | |||
983 | LIST_FOREACH (dfr_proposal, &iface->dfr_proposals,for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) | |||
984 | entries)for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) | |||
985 | dfr_proposal_state_transition(dfr_proposal, | |||
986 | PROPOSAL_IF_DOWN); | |||
987 | LIST_FOREACH (rdns_proposal, &iface->rdns_proposals,for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) | |||
988 | entries)for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) | |||
989 | rdns_proposal_state_transition(rdns_proposal, | |||
990 | PROPOSAL_IF_DOWN); | |||
991 | } | |||
992 | ||||
993 | /* nothing else to do until interface comes back up */ | |||
994 | iface->timo.tv_sec = -1; | |||
995 | break; | |||
996 | case IF_INIT: | |||
997 | switch (old_state) { | |||
998 | case IF_INIT: | |||
999 | iface->probes++; | |||
1000 | break; | |||
1001 | case IF_DOWN: | |||
1002 | LIST_FOREACH (addr_proposal, &iface->addr_proposals,for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) | |||
1003 | entries)for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) | |||
1004 | addr_proposal_state_transition(addr_proposal, | |||
1005 | PROPOSAL_WITHDRAWN); | |||
1006 | LIST_FOREACH (dfr_proposal, &iface->dfr_proposals,for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) | |||
1007 | entries)for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) | |||
1008 | dfr_proposal_state_transition(dfr_proposal, | |||
1009 | PROPOSAL_WITHDRAWN); | |||
1010 | LIST_FOREACH (rdns_proposal, &iface->rdns_proposals,for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) | |||
1011 | entries)for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) | |||
1012 | rdns_proposal_state_transition(rdns_proposal, | |||
1013 | PROPOSAL_WITHDRAWN); | |||
1014 | default: | |||
1015 | iface->probes = 0; | |||
1016 | } | |||
1017 | if (iface->probes < MAX_RTR_SOLICITATIONS3) { | |||
1018 | iface->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1019 | request_solicitation(iface); | |||
1020 | } else | |||
1021 | /* no router available, stop probing */ | |||
1022 | iface->timo.tv_sec = -1; | |||
1023 | break; | |||
1024 | case IF_BOUND: | |||
1025 | iface->timo.tv_sec = -1; | |||
1026 | break; | |||
1027 | } | |||
1028 | ||||
1029 | if_name = if_indextoname(iface->if_index, ifnamebuf); | |||
1030 | log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL((void *)0) ? | |||
1031 | "?" : if_name, if_state_name(old_state), if_state_name(new_state), | |||
1032 | iface->timo.tv_sec); | |||
1033 | ||||
1034 | if (iface->timo.tv_sec == -1) { | |||
1035 | if (evtimer_pending(&iface->timer, NULL)event_pending(&iface->timer, 0x01, ((void *)0))) | |||
1036 | evtimer_del(&iface->timer)event_del(&iface->timer); | |||
1037 | } else | |||
1038 | evtimer_add(&iface->timer, &iface->timo)event_add(&iface->timer, &iface->timo); | |||
1039 | } | |||
1040 | ||||
1041 | void addr_proposal_state_transition(struct address_proposal *addr_proposal, | |||
1042 | enum proposal_state new_state) | |||
1043 | { | |||
1044 | enum proposal_state old_state = addr_proposal->state; | |||
1045 | struct slaacd_iface *iface; | |||
1046 | uint32_t lifetime; | |||
1047 | char ifnamebuf[IF_NAMESIZE16], *if_name; | |||
1048 | ||||
1049 | addr_proposal->state = new_state; | |||
1050 | ||||
1051 | if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) == NULL((void *)0)) | |||
1052 | return; | |||
1053 | ||||
1054 | switch (addr_proposal->state) { | |||
1055 | case PROPOSAL_IF_DOWN: | |||
1056 | if (old_state == PROPOSAL_IF_DOWN) { | |||
1057 | withdraw_addr(addr_proposal); | |||
1058 | addr_proposal->timo.tv_sec = -1; | |||
1059 | } else { | |||
1060 | addr_proposal->timo.tv_sec = | |||
1061 | real_lifetime(&addr_proposal->uptime, | |||
1062 | addr_proposal->vltime); | |||
1063 | } | |||
1064 | break; | |||
1065 | case PROPOSAL_NOT_CONFIGURED: | |||
1066 | break; | |||
1067 | case PROPOSAL_CONFIGURED: | |||
1068 | lifetime = real_lifetime(&addr_proposal->uptime, | |||
1069 | addr_proposal->pltime); | |||
1070 | if (lifetime == 0) | |||
1071 | lifetime = real_lifetime(&addr_proposal->uptime, | |||
1072 | addr_proposal->vltime); | |||
1073 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1074 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1075 | addr_proposal->timo.tv_sec = lifetime - | |||
1076 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1077 | else | |||
1078 | addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1079 | break; | |||
1080 | case PROPOSAL_NEARLY_EXPIRED: | |||
1081 | lifetime = real_lifetime(&addr_proposal->uptime, | |||
1082 | addr_proposal->pltime); | |||
1083 | if (lifetime == 0) | |||
1084 | lifetime = real_lifetime(&addr_proposal->uptime, | |||
1085 | addr_proposal->vltime); | |||
1086 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1087 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1088 | addr_proposal->timo.tv_sec = lifetime - | |||
1089 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1090 | else | |||
1091 | addr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1092 | request_solicitation(iface); | |||
1093 | break; | |||
1094 | case PROPOSAL_WITHDRAWN: | |||
1095 | withdraw_addr(addr_proposal); | |||
1096 | addr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS3 * | |||
1097 | RTR_SOLICITATION_INTERVAL4; | |||
1098 | break; | |||
1099 | case PROPOSAL_DUPLICATED: | |||
1100 | addr_proposal->timo.tv_sec = 0; | |||
1101 | break; | |||
1102 | case PROPOSAL_STALE: | |||
1103 | addr_proposal->timo.tv_sec = 0; /* remove immediately */ | |||
1104 | break; | |||
1105 | } | |||
1106 | ||||
1107 | if_name = if_indextoname(addr_proposal->if_index, ifnamebuf); | |||
1108 | log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL((void *)0) ? | |||
1109 | "?" : if_name, proposal_state_name(old_state), | |||
1110 | proposal_state_name(new_state), | |||
1111 | addr_proposal->timo.tv_sec); | |||
1112 | ||||
1113 | if (addr_proposal->timo.tv_sec == -1) { | |||
1114 | if (evtimer_pending(&addr_proposal->timer, NULL)event_pending(&addr_proposal->timer, 0x01, ((void *)0) )) | |||
1115 | evtimer_del(&addr_proposal->timer)event_del(&addr_proposal->timer); | |||
1116 | } else | |||
1117 | evtimer_add(&addr_proposal->timer, &addr_proposal->timo)event_add(&addr_proposal->timer, &addr_proposal-> timo); | |||
1118 | } | |||
1119 | ||||
1120 | void dfr_proposal_state_transition(struct dfr_proposal *dfr_proposal, | |||
1121 | enum proposal_state new_state) | |||
1122 | { | |||
1123 | enum proposal_state old_state = dfr_proposal->state; | |||
1124 | struct slaacd_iface *iface; | |||
1125 | uint32_t lifetime; | |||
1126 | char ifnamebuf[IF_NAMESIZE16], *if_name; | |||
1127 | ||||
1128 | dfr_proposal->state = new_state; | |||
1129 | ||||
1130 | if ((iface = get_slaacd_iface_by_id(dfr_proposal->if_index)) == NULL((void *)0)) | |||
1131 | return; | |||
1132 | ||||
1133 | switch (dfr_proposal->state) { | |||
1134 | case PROPOSAL_IF_DOWN: | |||
1135 | if (old_state == PROPOSAL_IF_DOWN) { | |||
1136 | withdraw_dfr(dfr_proposal); | |||
1137 | dfr_proposal->timo.tv_sec = -1; | |||
1138 | } else { | |||
1139 | dfr_proposal->timo.tv_sec = | |||
1140 | real_lifetime(&dfr_proposal->uptime, | |||
1141 | dfr_proposal->router_lifetime); | |||
1142 | } | |||
1143 | break; | |||
1144 | case PROPOSAL_NOT_CONFIGURED: | |||
1145 | break; | |||
1146 | case PROPOSAL_CONFIGURED: | |||
1147 | lifetime = real_lifetime(&dfr_proposal->uptime, | |||
1148 | dfr_proposal->router_lifetime); | |||
1149 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1150 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1151 | dfr_proposal->timo.tv_sec = lifetime - | |||
1152 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1153 | else | |||
1154 | dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1155 | break; | |||
1156 | case PROPOSAL_NEARLY_EXPIRED: | |||
1157 | lifetime = real_lifetime(&dfr_proposal->uptime, | |||
1158 | dfr_proposal->router_lifetime); | |||
1159 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1160 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1161 | dfr_proposal->timo.tv_sec = lifetime - | |||
1162 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1163 | else | |||
1164 | dfr_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1165 | request_solicitation(iface); | |||
1166 | break; | |||
1167 | case PROPOSAL_WITHDRAWN: | |||
1168 | withdraw_dfr(dfr_proposal); | |||
1169 | dfr_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS3 * | |||
1170 | RTR_SOLICITATION_INTERVAL4; | |||
1171 | break; | |||
1172 | case PROPOSAL_STALE: | |||
1173 | dfr_proposal->timo.tv_sec = 0; /* remove immediately */ | |||
1174 | break; | |||
1175 | case PROPOSAL_DUPLICATED: | |||
1176 | fatalx("invalid dfr state: PROPOSAL_DUPLICATED"); | |||
1177 | break; | |||
1178 | } | |||
1179 | ||||
1180 | if_name = if_indextoname(dfr_proposal->if_index, ifnamebuf); | |||
1181 | log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL((void *)0) ? | |||
1182 | "?" : if_name, proposal_state_name(old_state), | |||
1183 | proposal_state_name(new_state), | |||
1184 | dfr_proposal->timo.tv_sec); | |||
1185 | ||||
1186 | if (dfr_proposal->timo.tv_sec == -1) { | |||
1187 | if (evtimer_pending(&dfr_proposal->timer, NULL)event_pending(&dfr_proposal->timer, 0x01, ((void *)0))) | |||
1188 | evtimer_del(&dfr_proposal->timer)event_del(&dfr_proposal->timer); | |||
1189 | } else | |||
1190 | evtimer_add(&dfr_proposal->timer, &dfr_proposal->timo)event_add(&dfr_proposal->timer, &dfr_proposal-> timo); | |||
1191 | ||||
1192 | } | |||
1193 | ||||
1194 | void rdns_proposal_state_transition(struct rdns_proposal *rdns_proposal, | |||
1195 | enum proposal_state new_state) | |||
1196 | { | |||
1197 | enum proposal_state old_state = rdns_proposal->state; | |||
1198 | struct slaacd_iface *iface; | |||
1199 | uint32_t lifetime; | |||
1200 | char ifnamebuf[IF_NAMESIZE16], *if_name; | |||
1201 | ||||
1202 | rdns_proposal->state = new_state; | |||
1203 | ||||
1204 | if ((iface = get_slaacd_iface_by_id(rdns_proposal->if_index)) == NULL((void *)0)) | |||
1205 | return; | |||
1206 | ||||
1207 | switch (rdns_proposal->state) { | |||
1208 | case PROPOSAL_IF_DOWN: | |||
1209 | if (old_state == PROPOSAL_IF_DOWN) { | |||
1210 | withdraw_rdns(rdns_proposal); | |||
1211 | rdns_proposal->timo.tv_sec = -1; | |||
1212 | } else { | |||
1213 | rdns_proposal->timo.tv_sec = | |||
1214 | real_lifetime(&rdns_proposal->uptime, | |||
1215 | rdns_proposal->rdns_lifetime); | |||
1216 | } | |||
1217 | break; | |||
1218 | case PROPOSAL_NOT_CONFIGURED: | |||
1219 | break; | |||
1220 | case PROPOSAL_CONFIGURED: | |||
1221 | lifetime = real_lifetime(&rdns_proposal->uptime, | |||
1222 | rdns_proposal->rdns_lifetime); | |||
1223 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1224 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1225 | rdns_proposal->timo.tv_sec = lifetime - | |||
1226 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1227 | else | |||
1228 | rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1229 | break; | |||
1230 | case PROPOSAL_NEARLY_EXPIRED: | |||
1231 | lifetime = real_lifetime(&rdns_proposal->uptime, | |||
1232 | rdns_proposal->rdns_lifetime); | |||
1233 | if (lifetime > MAX_RTR_SOLICITATIONS3 * | |||
1234 | (RTR_SOLICITATION_INTERVAL4 + 1)) | |||
1235 | rdns_proposal->timo.tv_sec = lifetime - | |||
1236 | MAX_RTR_SOLICITATIONS3 * RTR_SOLICITATION_INTERVAL4; | |||
1237 | else | |||
1238 | rdns_proposal->timo.tv_sec = RTR_SOLICITATION_INTERVAL4; | |||
1239 | request_solicitation(iface); | |||
1240 | break; | |||
1241 | case PROPOSAL_WITHDRAWN: | |||
1242 | withdraw_rdns(rdns_proposal); | |||
1243 | rdns_proposal->timo.tv_sec = MAX_RTR_SOLICITATIONS3 * | |||
1244 | RTR_SOLICITATION_INTERVAL4; | |||
1245 | break; | |||
1246 | case PROPOSAL_STALE: | |||
1247 | rdns_proposal->timo.tv_sec = 0; /* remove immediately */ | |||
1248 | break; | |||
1249 | case PROPOSAL_DUPLICATED: | |||
1250 | fatalx("invalid rdns state: PROPOSAL_DUPLICATED"); | |||
1251 | break; | |||
1252 | } | |||
1253 | ||||
1254 | if_name = if_indextoname(rdns_proposal->if_index, ifnamebuf); | |||
1255 | log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL((void *)0) ? | |||
1256 | "?" : if_name, proposal_state_name(old_state), | |||
1257 | proposal_state_name(new_state), | |||
1258 | rdns_proposal->timo.tv_sec); | |||
1259 | ||||
1260 | if (rdns_proposal->timo.tv_sec == -1) { | |||
1261 | if (evtimer_pending(&rdns_proposal->timer, NULL)event_pending(&rdns_proposal->timer, 0x01, ((void *)0) )) | |||
1262 | evtimer_del(&rdns_proposal->timer)event_del(&rdns_proposal->timer); | |||
1263 | } else | |||
1264 | evtimer_add(&rdns_proposal->timer, &rdns_proposal->timo)event_add(&rdns_proposal->timer, &rdns_proposal-> timo); | |||
1265 | } | |||
1266 | ||||
1267 | void | |||
1268 | request_solicitation(struct slaacd_iface *iface) | |||
1269 | { | |||
1270 | struct timespec now, diff, sol_delay = {RTR_SOLICITATION_INTERVAL4, 0}; | |||
1271 | ||||
1272 | clock_gettime(CLOCK_MONOTONIC3, &now); | |||
1273 | timespecsub(&now, &iface->last_sol, &diff)do { (&diff)->tv_sec = (&now)->tv_sec - (&iface ->last_sol)->tv_sec; (&diff)->tv_nsec = (&now )->tv_nsec - (&iface->last_sol)->tv_nsec; if ((& diff)->tv_nsec < 0) { (&diff)->tv_sec--; (&diff )->tv_nsec += 1000000000L; } } while (0); | |||
1274 | if (timespeccmp(&diff, &sol_delay, <)(((&diff)->tv_sec == (&sol_delay)->tv_sec) ? (( &diff)->tv_nsec < (&sol_delay)->tv_nsec) : ( (&diff)->tv_sec < (&sol_delay)->tv_sec))) { | |||
1275 | log_warnx("last solicitation less then %d seconds ago", | |||
1276 | RTR_SOLICITATION_INTERVAL4); | |||
1277 | return; | |||
1278 | } | |||
1279 | ||||
1280 | iface->last_sol = now; | |||
1281 | engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, 0, | |||
1282 | &iface->if_index, sizeof(iface->if_index)); | |||
1283 | } | |||
1284 | ||||
1285 | void | |||
1286 | engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) | |||
1287 | { | |||
1288 | struct slaacd_iface *iface; | |||
1289 | int need_refresh = 0; | |||
1290 | ||||
1291 | iface = get_slaacd_iface_by_id(imsg_ifinfo->if_index); | |||
1292 | if (iface == NULL((void *)0)) { | |||
1293 | if ((iface = calloc(1, sizeof(*iface))) == NULL((void *)0)) | |||
1294 | fatal("calloc"); | |||
1295 | iface->state = IF_DOWN; | |||
1296 | iface->timo.tv_usec = arc4random_uniform(1000000); | |||
1297 | evtimer_set(&iface->timer, iface_timeout, iface)event_set(&iface->timer, -1, 0, iface_timeout, iface); | |||
1298 | iface->if_index = imsg_ifinfo->if_index; | |||
1299 | iface->rdomain = imsg_ifinfo->rdomain; | |||
1300 | iface->running = imsg_ifinfo->running; | |||
1301 | iface->link_state = imsg_ifinfo->link_state; | |||
1302 | iface->autoconf = imsg_ifinfo->autoconf; | |||
1303 | iface->temporary = imsg_ifinfo->temporary; | |||
1304 | iface->soii = imsg_ifinfo->soii; | |||
1305 | memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, | |||
1306 | sizeof(struct ether_addr)); | |||
1307 | memcpy(&iface->ll_address, &imsg_ifinfo->ll_address, | |||
1308 | sizeof(struct sockaddr_in6)); | |||
1309 | memcpy(iface->soiikey, imsg_ifinfo->soiikey, | |||
1310 | sizeof(iface->soiikey)); | |||
1311 | LIST_INIT(&iface->radvs)do { ((&iface->radvs)->lh_first) = ((void *)0); } while (0); | |||
1312 | LIST_INSERT_HEAD(&slaacd_interfaces, iface, entries)do { if (((iface)->entries.le_next = (&slaacd_interfaces )->lh_first) != ((void *)0)) (&slaacd_interfaces)-> lh_first->entries.le_prev = &(iface)->entries.le_next ; (&slaacd_interfaces)->lh_first = (iface); (iface)-> entries.le_prev = &(&slaacd_interfaces)->lh_first; } while (0); | |||
1313 | LIST_INIT(&iface->addr_proposals)do { ((&iface->addr_proposals)->lh_first) = ((void * )0); } while (0); | |||
1314 | LIST_INIT(&iface->dfr_proposals)do { ((&iface->dfr_proposals)->lh_first) = ((void * )0); } while (0); | |||
1315 | LIST_INIT(&iface->rdns_proposals)do { ((&iface->rdns_proposals)->lh_first) = ((void * )0); } while (0); | |||
1316 | need_refresh = 1; | |||
1317 | } else { | |||
1318 | memcpy(&iface->ll_address, &imsg_ifinfo->ll_address, | |||
1319 | sizeof(struct sockaddr_in6)); | |||
1320 | ||||
1321 | if (iface->autoconf != imsg_ifinfo->autoconf) { | |||
1322 | iface->autoconf = imsg_ifinfo->autoconf; | |||
1323 | need_refresh = 1; | |||
1324 | } | |||
1325 | ||||
1326 | if (iface->temporary != imsg_ifinfo->temporary) { | |||
1327 | iface->temporary = imsg_ifinfo->temporary; | |||
1328 | need_refresh = 1; | |||
1329 | } | |||
1330 | ||||
1331 | if (iface->soii != imsg_ifinfo->soii) { | |||
1332 | iface->soii = imsg_ifinfo->soii; | |||
1333 | need_refresh = 1; | |||
1334 | } | |||
1335 | ||||
1336 | if (memcmp(&iface->hw_address, &imsg_ifinfo->hw_address, | |||
1337 | sizeof(struct ether_addr)) != 0) { | |||
1338 | memcpy(&iface->hw_address, &imsg_ifinfo->hw_address, | |||
1339 | sizeof(struct ether_addr)); | |||
1340 | need_refresh = 1; | |||
1341 | } | |||
1342 | ||||
1343 | if (memcmp(iface->soiikey, imsg_ifinfo->soiikey, | |||
1344 | sizeof(iface->soiikey)) != 0) { | |||
1345 | memcpy(iface->soiikey, imsg_ifinfo->soiikey, | |||
1346 | sizeof(iface->soiikey)); | |||
1347 | need_refresh = 1; | |||
1348 | } | |||
1349 | ||||
1350 | if (imsg_ifinfo->running != iface->running) { | |||
1351 | iface->running = imsg_ifinfo->running; | |||
1352 | need_refresh = 1; | |||
1353 | } | |||
1354 | if (imsg_ifinfo->link_state != iface->link_state) { | |||
1355 | iface->link_state = imsg_ifinfo->link_state; | |||
1356 | need_refresh = 1; | |||
1357 | } | |||
1358 | } | |||
1359 | ||||
1360 | if (!need_refresh) | |||
1361 | return; | |||
1362 | ||||
1363 | if (iface->running && LINK_STATE_IS_UP(iface->link_state)((iface->link_state) >= 4 || (iface->link_state) == 0 )) | |||
1364 | iface_state_transition(iface, IF_INIT); | |||
1365 | ||||
1366 | else | |||
1367 | iface_state_transition(iface, IF_DOWN); | |||
1368 | } | |||
1369 | ||||
1370 | void | |||
1371 | parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra) | |||
1372 | { | |||
1373 | struct icmp6_hdr *icmp6_hdr; | |||
1374 | struct nd_router_advert *nd_ra; | |||
1375 | struct radv *radv; | |||
1376 | struct radv_prefix *prefix; | |||
1377 | struct radv_rdns *rdns; | |||
1378 | ssize_t len = ra->len; | |||
1379 | const char *hbuf; | |||
1380 | uint8_t *p; | |||
1381 | ||||
1382 | #ifndef SMALL | |||
1383 | if (log_getverbose() > 1) | |||
1384 | debug_log_ra(ra); | |||
1385 | #endif /* SMALL */ | |||
1386 | ||||
1387 | hbuf = sin6_to_str(&ra->from); | |||
1388 | if ((size_t)len < sizeof(struct icmp6_hdr)) { | |||
1389 | log_warnx("received too short message (%ld) from %s", len, | |||
1390 | hbuf); | |||
1391 | return; | |||
1392 | } | |||
1393 | ||||
1394 | p = ra->packet; | |||
1395 | icmp6_hdr = (struct icmp6_hdr *)p; | |||
1396 | if (icmp6_hdr->icmp6_type != ND_ROUTER_ADVERT134) | |||
1397 | return; | |||
1398 | ||||
1399 | if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)(((&ra->from.sin6_addr)->__u6_addr.__u6_addr8[0] == 0xfe) && (((&ra->from.sin6_addr)->__u6_addr .__u6_addr8[1] & 0xc0) == 0x80))) { | |||
1400 | log_debug("RA from non link local address %s", hbuf); | |||
1401 | return; | |||
1402 | } | |||
1403 | ||||
1404 | if ((size_t)len < sizeof(struct nd_router_advert)) { | |||
1405 | log_warnx("received too short message (%ld) from %s", len, | |||
1406 | hbuf); | |||
1407 | return; | |||
1408 | } | |||
1409 | ||||
1410 | if ((radv = calloc(1, sizeof(*radv))) == NULL((void *)0)) | |||
1411 | fatal("calloc"); | |||
1412 | ||||
1413 | LIST_INIT(&radv->prefixes)do { ((&radv->prefixes)->lh_first) = ((void *)0); } while (0); | |||
1414 | LIST_INIT(&radv->rdns_servers)do { ((&radv->rdns_servers)->lh_first) = ((void *)0 ); } while (0); | |||
1415 | ||||
1416 | radv->min_lifetime = UINT32_MAX0xffffffffU; | |||
1417 | ||||
1418 | nd_ra = (struct nd_router_advert *)p; | |||
1419 | len -= sizeof(struct nd_router_advert); | |||
1420 | p += sizeof(struct nd_router_advert); | |||
1421 | ||||
1422 | log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", | |||
1423 | nd_ra->nd_ra_typend_ra_hdr.icmp6_type, nd_ra->nd_ra_codend_ra_hdr.icmp6_code, hbuf, len); | |||
1424 | ||||
1425 | if (nd_ra->nd_ra_codend_ra_hdr.icmp6_code != 0) { | |||
1426 | log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_codend_ra_hdr.icmp6_code, | |||
1427 | hbuf); | |||
1428 | goto err; | |||
1429 | } | |||
1430 | ||||
1431 | memcpy(&radv->from, &ra->from, sizeof(ra->from)); | |||
1432 | ||||
1433 | if (clock_gettime(CLOCK_REALTIME0, &radv->when)) | |||
1434 | fatal("clock_gettime"); | |||
1435 | if (clock_gettime(CLOCK_MONOTONIC3, &radv->uptime)) | |||
1436 | fatal("clock_gettime"); | |||
1437 | ||||
1438 | radv->curhoplimit = nd_ra->nd_ra_curhoplimitnd_ra_hdr.icmp6_dataun.icmp6_un_data8[0]; | |||
1439 | radv->managed = nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_MANAGED0x80; | |||
1440 | radv->other = nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_OTHER0x40; | |||
1441 | ||||
1442 | switch (nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_RTPREF_MASK0x18) { | |||
1443 | case ND_RA_FLAG_RTPREF_HIGH0x08: | |||
1444 | radv->rpref=HIGH; | |||
1445 | break; | |||
1446 | case ND_RA_FLAG_RTPREF_LOW0x18: | |||
1447 | radv->rpref=LOW; | |||
1448 | break; | |||
1449 | case ND_RA_FLAG_RTPREF_MEDIUM0x00: | |||
1450 | /* fallthrough */ | |||
1451 | default: | |||
1452 | radv->rpref=MEDIUM; | |||
1453 | break; | |||
1454 | } | |||
1455 | radv->router_lifetime = ntohs(nd_ra->nd_ra_router_lifetime)(__uint16_t)(__builtin_constant_p(nd_ra->nd_ra_hdr.icmp6_dataun .icmp6_un_data16[1]) ? (__uint16_t)(((__uint16_t)(nd_ra->nd_ra_hdr .icmp6_dataun.icmp6_un_data16[1]) & 0xffU) << 8 | ( (__uint16_t)(nd_ra->nd_ra_hdr.icmp6_dataun.icmp6_un_data16 [1]) & 0xff00U) >> 8) : __swap16md(nd_ra->nd_ra_hdr .icmp6_dataun.icmp6_un_data16[1])); | |||
1456 | if (radv->router_lifetime != 0) | |||
1457 | radv->min_lifetime = radv->router_lifetime; | |||
1458 | radv->reachable_time = ntohl(nd_ra->nd_ra_reachable)(__uint32_t)(__builtin_constant_p(nd_ra->nd_ra_reachable) ? (__uint32_t)(((__uint32_t)(nd_ra->nd_ra_reachable) & 0xff ) << 24 | ((__uint32_t)(nd_ra->nd_ra_reachable) & 0xff00) << 8 | ((__uint32_t)(nd_ra->nd_ra_reachable ) & 0xff0000) >> 8 | ((__uint32_t)(nd_ra->nd_ra_reachable ) & 0xff000000) >> 24) : __swap32md(nd_ra->nd_ra_reachable )); | |||
1459 | radv->retrans_time = ntohl(nd_ra->nd_ra_retransmit)(__uint32_t)(__builtin_constant_p(nd_ra->nd_ra_retransmit) ? (__uint32_t)(((__uint32_t)(nd_ra->nd_ra_retransmit) & 0xff) << 24 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff00) << 8 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff0000) >> 8 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff000000) >> 24) : __swap32md(nd_ra->nd_ra_retransmit )); | |||
1460 | ||||
1461 | while ((size_t)len >= sizeof(struct nd_opt_hdr)) { | |||
1462 | struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; | |||
1463 | struct nd_opt_prefix_info *prf; | |||
1464 | struct nd_opt_rdnss *rdnss; | |||
1465 | struct nd_opt_mtu *mtu; | |||
1466 | struct in6_addr *in6; | |||
1467 | int i; | |||
1468 | ||||
1469 | len -= sizeof(struct nd_opt_hdr); | |||
1470 | p += sizeof(struct nd_opt_hdr); | |||
1471 | ||||
1472 | if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { | |||
1473 | log_warnx("invalid option len: %u > %ld", | |||
1474 | nd_opt_hdr->nd_opt_len, len); | |||
1475 | goto err; | |||
1476 | } | |||
1477 | ||||
1478 | switch (nd_opt_hdr->nd_opt_type) { | |||
1479 | case ND_OPT_PREFIX_INFORMATION3: | |||
1480 | if (nd_opt_hdr->nd_opt_len != 4) { | |||
1481 | log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " | |||
1482 | "len != 4"); | |||
1483 | goto err; | |||
1484 | } | |||
1485 | ||||
1486 | if ((prefix = calloc(1, sizeof(*prefix))) == NULL((void *)0)) | |||
1487 | fatal("calloc"); | |||
1488 | ||||
1489 | prf = (struct nd_opt_prefix_info*) nd_opt_hdr; | |||
1490 | prefix->prefix = prf->nd_opt_pi_prefix; | |||
1491 | prefix->prefix_len = prf->nd_opt_pi_prefix_len; | |||
1492 | prefix->onlink = prf->nd_opt_pi_flags_reserved & | |||
1493 | ND_OPT_PI_FLAG_ONLINK0x80; | |||
1494 | prefix->autonomous = prf->nd_opt_pi_flags_reserved & | |||
1495 | ND_OPT_PI_FLAG_AUTO0x40; | |||
1496 | prefix->vltime = ntohl(prf->nd_opt_pi_valid_time)(__uint32_t)(__builtin_constant_p(prf->nd_opt_pi_valid_time ) ? (__uint32_t)(((__uint32_t)(prf->nd_opt_pi_valid_time) & 0xff) << 24 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff00) << 8 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff0000) >> 8 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff000000) >> 24) : __swap32md(prf->nd_opt_pi_valid_time )); | |||
1497 | prefix->pltime = ntohl(prf->nd_opt_pi_preferred_time)(__uint32_t)(__builtin_constant_p(prf->nd_opt_pi_preferred_time ) ? (__uint32_t)(((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff) << 24 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff00) << 8 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff0000) >> 8 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff000000) >> 24) : __swap32md(prf->nd_opt_pi_preferred_time )); | |||
1498 | if (radv->min_lifetime > prefix->pltime) | |||
1499 | radv->min_lifetime = prefix->pltime; | |||
1500 | ||||
1501 | LIST_INSERT_HEAD(&radv->prefixes, prefix, entries)do { if (((prefix)->entries.le_next = (&radv->prefixes )->lh_first) != ((void *)0)) (&radv->prefixes)-> lh_first->entries.le_prev = &(prefix)->entries.le_next ; (&radv->prefixes)->lh_first = (prefix); (prefix)-> entries.le_prev = &(&radv->prefixes)->lh_first; } while (0); | |||
1502 | ||||
1503 | break; | |||
1504 | ||||
1505 | case ND_OPT_RDNSS25: | |||
1506 | if (nd_opt_hdr->nd_opt_len < 3) { | |||
1507 | log_warnx("invalid ND_OPT_RDNSS: len < 24"); | |||
1508 | goto err; | |||
1509 | } | |||
1510 | ||||
1511 | if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { | |||
1512 | log_warnx("invalid ND_OPT_RDNSS: length with" | |||
1513 | "out header is not multiply of 16: %d", | |||
1514 | (nd_opt_hdr->nd_opt_len - 1) * 8); | |||
1515 | goto err; | |||
1516 | } | |||
1517 | ||||
1518 | rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; | |||
1519 | ||||
1520 | radv->rdns_lifetime = ntohl((__uint32_t)(__builtin_constant_p(rdnss->nd_opt_rdnss_lifetime ) ? (__uint32_t)(((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff) << 24 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff00) << 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff0000) >> 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff000000) >> 24) : __swap32md(rdnss->nd_opt_rdnss_lifetime )) | |||
1521 | rdnss->nd_opt_rdnss_lifetime)(__uint32_t)(__builtin_constant_p(rdnss->nd_opt_rdnss_lifetime ) ? (__uint32_t)(((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff) << 24 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff00) << 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff0000) >> 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff000000) >> 24) : __swap32md(rdnss->nd_opt_rdnss_lifetime )); | |||
1522 | if (radv->min_lifetime > radv->rdns_lifetime) | |||
1523 | radv->min_lifetime = radv->rdns_lifetime; | |||
1524 | ||||
1525 | in6 = (struct in6_addr*) (p + 6); | |||
1526 | for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, | |||
1527 | in6++) { | |||
1528 | if((rdns = calloc(1, sizeof(*rdns))) == NULL((void *)0)) | |||
1529 | fatal("calloc"); | |||
1530 | memcpy(&rdns->rdns, in6, sizeof(rdns->rdns)); | |||
1531 | LIST_INSERT_HEAD(&radv->rdns_servers, rdns,do { if (((rdns)->entries.le_next = (&radv->rdns_servers )->lh_first) != ((void *)0)) (&radv->rdns_servers)-> lh_first->entries.le_prev = &(rdns)->entries.le_next ; (&radv->rdns_servers)->lh_first = (rdns); (rdns)-> entries.le_prev = &(&radv->rdns_servers)->lh_first ; } while (0) | |||
1532 | entries)do { if (((rdns)->entries.le_next = (&radv->rdns_servers )->lh_first) != ((void *)0)) (&radv->rdns_servers)-> lh_first->entries.le_prev = &(rdns)->entries.le_next ; (&radv->rdns_servers)->lh_first = (rdns); (rdns)-> entries.le_prev = &(&radv->rdns_servers)->lh_first ; } while (0); | |||
1533 | } | |||
1534 | break; | |||
1535 | case ND_OPT_MTU5: | |||
1536 | if (nd_opt_hdr->nd_opt_len != 1) { | |||
1537 | log_warnx("invalid ND_OPT_MTU: len != 1"); | |||
1538 | goto err; | |||
1539 | } | |||
1540 | mtu = (struct nd_opt_mtu*) nd_opt_hdr; | |||
1541 | radv->mtu = ntohl(mtu->nd_opt_mtu_mtu)(__uint32_t)(__builtin_constant_p(mtu->nd_opt_mtu_mtu) ? ( __uint32_t)(((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff) << 24 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff00 ) << 8 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff0000 ) >> 8 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff000000 ) >> 24) : __swap32md(mtu->nd_opt_mtu_mtu)); | |||
1542 | ||||
1543 | /* path MTU cannot be less than IPV6_MMTU */ | |||
1544 | if (radv->mtu < IPV6_MMTU1280) { | |||
1545 | radv->mtu = 0; | |||
1546 | log_warnx("invalid advertised MTU"); | |||
1547 | } | |||
1548 | ||||
1549 | break; | |||
1550 | case ND_OPT_DNSSL31: | |||
1551 | case ND_OPT_REDIRECTED_HEADER4: | |||
1552 | case ND_OPT_SOURCE_LINKADDR1: | |||
1553 | case ND_OPT_TARGET_LINKADDR2: | |||
1554 | case ND_OPT_ROUTE_INFO24: | |||
1555 | #if 0 | |||
1556 | log_debug("\tOption: %u (len: %u) not implemented", | |||
1557 | nd_opt_hdr->nd_opt_type, nd_opt_hdr->nd_opt_len * | |||
1558 | 8); | |||
1559 | #endif | |||
1560 | break; | |||
1561 | default: | |||
1562 | log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); | |||
1563 | break; | |||
1564 | ||||
1565 | } | |||
1566 | len -= nd_opt_hdr->nd_opt_len * 8 - 2; | |||
1567 | p += nd_opt_hdr->nd_opt_len * 8 - 2; | |||
1568 | } | |||
1569 | update_iface_ra(iface, radv); | |||
1570 | return; | |||
1571 | ||||
1572 | err: | |||
1573 | free_ra(radv); | |||
1574 | } | |||
1575 | ||||
1576 | void | |||
1577 | gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct | |||
1578 | address_proposal *addr_proposal, int temporary) | |||
1579 | { | |||
1580 | SHA2_CTX ctx; | |||
1581 | struct in6_addr iid; | |||
1582 | int i; | |||
1583 | u_int8_t digest[SHA512_DIGEST_LENGTH64]; | |||
1584 | ||||
1585 | memset(&iid, 0, sizeof(iid)); | |||
1586 | ||||
1587 | /* from in6_ifadd() in nd6_rtr.c */ | |||
1588 | /* XXX from in6.h, guarded by #ifdef _KERNEL XXX nonstandard */ | |||
1589 | #define s6_addr32 __u6_addr.__u6_addr32 | |||
1590 | ||||
1591 | in6_prefixlen2mask(&addr_proposal->mask, addr_proposal->prefix_len); | |||
1592 | ||||
1593 | memset(&addr_proposal->addr, 0, sizeof(addr_proposal->addr)); | |||
1594 | ||||
1595 | addr_proposal->addr.sin6_family = AF_INET624; | |||
1596 | addr_proposal->addr.sin6_len = sizeof(addr_proposal->addr); | |||
1597 | ||||
1598 | memcpy(&addr_proposal->addr.sin6_addr, &prefix->prefix, | |||
1599 | sizeof(addr_proposal->addr.sin6_addr)); | |||
1600 | ||||
1601 | for (i = 0; i < 4; i++) | |||
1602 | addr_proposal->addr.sin6_addr.s6_addr32[i] &= | |||
1603 | addr_proposal->mask.s6_addr32[i]; | |||
1604 | ||||
1605 | if (temporary) { | |||
1606 | arc4random_buf(&iid.s6_addr__u6_addr.__u6_addr8, sizeof(iid.s6_addr__u6_addr.__u6_addr8)); | |||
1607 | } else if (iface->soii) { | |||
1608 | SHA512Init(&ctx); | |||
1609 | SHA512Update(&ctx, &prefix->prefix, | |||
1610 | sizeof(prefix->prefix)); | |||
1611 | SHA512Update(&ctx, &iface->hw_address, | |||
1612 | sizeof(iface->hw_address)); | |||
1613 | SHA512Update(&ctx, &prefix->dad_counter, | |||
1614 | sizeof(prefix->dad_counter)); | |||
1615 | SHA512Update(&ctx, addr_proposal->soiikey, | |||
1616 | sizeof(addr_proposal->soiikey)); | |||
1617 | SHA512Final(digest, &ctx); | |||
1618 | ||||
1619 | memcpy(&iid.s6_addr__u6_addr.__u6_addr8, digest + (sizeof(digest) - | |||
1620 | sizeof(iid.s6_addr__u6_addr.__u6_addr8)), sizeof(iid.s6_addr__u6_addr.__u6_addr8)); | |||
1621 | } else { | |||
1622 | /* This is safe, because we have a 64 prefix len */ | |||
1623 | memcpy(&iid.s6_addr__u6_addr.__u6_addr8, &iface->ll_address.sin6_addr, | |||
1624 | sizeof(iid.s6_addr__u6_addr.__u6_addr8)); | |||
1625 | } | |||
1626 | ||||
1627 | for (i = 0; i < 4; i++) | |||
1628 | addr_proposal->addr.sin6_addr.s6_addr32[i] |= | |||
1629 | (iid.s6_addr32[i] & ~addr_proposal->mask.s6_addr32[i]); | |||
1630 | #undef s6_addr32 | |||
1631 | } | |||
1632 | ||||
1633 | /* from sys/netinet6/in6.c */ | |||
1634 | void | |||
1635 | in6_prefixlen2mask(struct in6_addr *maskp, int len) | |||
1636 | { | |||
1637 | u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; | |||
1638 | int bytelen, bitlen, i; | |||
1639 | ||||
1640 | if (0 > len || len > 128) | |||
1641 | fatalx("%s: invalid prefix length(%d)\n", __func__, len); | |||
1642 | ||||
1643 | bzero(maskp, sizeof(*maskp)); | |||
1644 | bytelen = len / 8; | |||
1645 | bitlen = len % 8; | |||
1646 | for (i = 0; i < bytelen; i++) | |||
1647 | maskp->s6_addr__u6_addr.__u6_addr8[i] = 0xff; | |||
1648 | /* len == 128 is ok because bitlen == 0 then */ | |||
1649 | if (bitlen) | |||
1650 | maskp->s6_addr__u6_addr.__u6_addr8[bytelen] = maskarray[bitlen - 1]; | |||
1651 | } | |||
1652 | ||||
1653 | #ifndef SMALL | |||
1654 | /* from kame via ifconfig, where it's called prefix() */ | |||
1655 | int | |||
1656 | in6_mask2prefixlen(struct in6_addr *in6) | |||
1657 | { | |||
1658 | u_char *nam = (u_char *)in6; | |||
1659 | int byte, bit, plen = 0, size = sizeof(struct in6_addr); | |||
1660 | ||||
1661 | for (byte = 0; byte < size; byte++, plen += 8) | |||
1662 | if (nam[byte] != 0xff) | |||
1663 | break; | |||
1664 | if (byte == size) | |||
1665 | return (plen); | |||
1666 | for (bit = 7; bit != 0; bit--, plen++) | |||
1667 | if (!(nam[byte] & (1 << bit))) | |||
1668 | break; | |||
1669 | for (; bit != 0; bit--) | |||
1670 | if (nam[byte] & (1 << bit)) | |||
1671 | return (0); | |||
1672 | byte++; | |||
1673 | for (; byte < size; byte++) | |||
1674 | if (nam[byte]) | |||
1675 | return (0); | |||
1676 | return (plen); | |||
1677 | } | |||
1678 | ||||
1679 | void | |||
1680 | debug_log_ra(struct imsg_ra *ra) | |||
1681 | { | |||
1682 | struct nd_router_advert *nd_ra; | |||
1683 | ssize_t len = ra->len; | |||
1684 | char ntopbuf[INET6_ADDRSTRLEN46]; | |||
1685 | const char *hbuf; | |||
1686 | uint8_t *p; | |||
1687 | ||||
1688 | hbuf = sin6_to_str(&ra->from); | |||
1689 | ||||
1690 | if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)(((&ra->from.sin6_addr)->__u6_addr.__u6_addr8[0] == 0xfe) && (((&ra->from.sin6_addr)->__u6_addr .__u6_addr8[1] & 0xc0) == 0x80))) { | |||
1691 | log_warnx("RA from non link local address %s", hbuf); | |||
1692 | return; | |||
1693 | } | |||
1694 | ||||
1695 | if ((size_t)len < sizeof(struct nd_router_advert)) { | |||
1696 | log_warnx("received too short message (%ld) from %s", len, | |||
1697 | hbuf); | |||
1698 | return; | |||
1699 | } | |||
1700 | ||||
1701 | p = ra->packet; | |||
1702 | nd_ra = (struct nd_router_advert *)p; | |||
1703 | len -= sizeof(struct nd_router_advert); | |||
1704 | p += sizeof(struct nd_router_advert); | |||
1705 | ||||
1706 | log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", | |||
1707 | nd_ra->nd_ra_typend_ra_hdr.icmp6_type, nd_ra->nd_ra_codend_ra_hdr.icmp6_code, hbuf, len); | |||
1708 | ||||
1709 | if (nd_ra->nd_ra_typend_ra_hdr.icmp6_type != ND_ROUTER_ADVERT134) { | |||
1710 | log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_typend_ra_hdr.icmp6_type, | |||
1711 | hbuf); | |||
1712 | return; | |||
1713 | } | |||
1714 | ||||
1715 | if (nd_ra->nd_ra_codend_ra_hdr.icmp6_code != 0) { | |||
1716 | log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_codend_ra_hdr.icmp6_code, | |||
1717 | hbuf); | |||
1718 | return; | |||
1719 | } | |||
1720 | ||||
1721 | log_debug("---"); | |||
1722 | log_debug("RA from %s", hbuf); | |||
1723 | log_debug("\tCur Hop Limit: %u", nd_ra->nd_ra_curhoplimitnd_ra_hdr.icmp6_dataun.icmp6_un_data8[0]); | |||
1724 | log_debug("\tManaged address configuration: %d", | |||
1725 | (nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_MANAGED0x80) ? 1 : 0); | |||
1726 | log_debug("\tOther configuration: %d", | |||
1727 | (nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_OTHER0x40) ? 1 : 0); | |||
1728 | switch (nd_ra->nd_ra_flags_reservednd_ra_hdr.icmp6_dataun.icmp6_un_data8[1] & ND_RA_FLAG_RTPREF_MASK0x18) { | |||
1729 | case ND_RA_FLAG_RTPREF_HIGH0x08: | |||
1730 | log_debug("\tRouter Preference: high"); | |||
1731 | break; | |||
1732 | case ND_RA_FLAG_RTPREF_MEDIUM0x00: | |||
1733 | log_debug("\tRouter Preference: medium"); | |||
1734 | break; | |||
1735 | case ND_RA_FLAG_RTPREF_LOW0x18: | |||
1736 | log_debug("\tRouter Preference: low"); | |||
1737 | break; | |||
1738 | case ND_RA_FLAG_RTPREF_RSV0x10: | |||
1739 | log_debug("\tRouter Preference: reserved"); | |||
1740 | break; | |||
1741 | } | |||
1742 | log_debug("\tRouter Lifetime: %hds", | |||
1743 | ntohs(nd_ra->nd_ra_router_lifetime)(__uint16_t)(__builtin_constant_p(nd_ra->nd_ra_hdr.icmp6_dataun .icmp6_un_data16[1]) ? (__uint16_t)(((__uint16_t)(nd_ra->nd_ra_hdr .icmp6_dataun.icmp6_un_data16[1]) & 0xffU) << 8 | ( (__uint16_t)(nd_ra->nd_ra_hdr.icmp6_dataun.icmp6_un_data16 [1]) & 0xff00U) >> 8) : __swap16md(nd_ra->nd_ra_hdr .icmp6_dataun.icmp6_un_data16[1]))); | |||
1744 | log_debug("\tReachable Time: %ums", ntohl(nd_ra->nd_ra_reachable)(__uint32_t)(__builtin_constant_p(nd_ra->nd_ra_reachable) ? (__uint32_t)(((__uint32_t)(nd_ra->nd_ra_reachable) & 0xff ) << 24 | ((__uint32_t)(nd_ra->nd_ra_reachable) & 0xff00) << 8 | ((__uint32_t)(nd_ra->nd_ra_reachable ) & 0xff0000) >> 8 | ((__uint32_t)(nd_ra->nd_ra_reachable ) & 0xff000000) >> 24) : __swap32md(nd_ra->nd_ra_reachable ))); | |||
1745 | log_debug("\tRetrans Timer: %ums", ntohl(nd_ra->nd_ra_retransmit)(__uint32_t)(__builtin_constant_p(nd_ra->nd_ra_retransmit) ? (__uint32_t)(((__uint32_t)(nd_ra->nd_ra_retransmit) & 0xff) << 24 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff00) << 8 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff0000) >> 8 | ((__uint32_t)(nd_ra->nd_ra_retransmit ) & 0xff000000) >> 24) : __swap32md(nd_ra->nd_ra_retransmit ))); | |||
1746 | ||||
1747 | while ((size_t)len >= sizeof(struct nd_opt_hdr)) { | |||
1748 | struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; | |||
1749 | struct nd_opt_mtu *mtu; | |||
1750 | struct nd_opt_prefix_info *prf; | |||
1751 | struct nd_opt_rdnss *rdnss; | |||
1752 | struct in6_addr *in6; | |||
1753 | int i; | |||
1754 | ||||
1755 | len -= sizeof(struct nd_opt_hdr); | |||
1756 | p += sizeof(struct nd_opt_hdr); | |||
1757 | if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { | |||
1758 | log_warnx("invalid option len: %u > %ld", | |||
1759 | nd_opt_hdr->nd_opt_len, len); | |||
1760 | return; | |||
1761 | } | |||
1762 | log_debug("\tOption: %u (len: %u)", nd_opt_hdr->nd_opt_type, | |||
1763 | nd_opt_hdr->nd_opt_len * 8); | |||
1764 | switch (nd_opt_hdr->nd_opt_type) { | |||
1765 | case ND_OPT_SOURCE_LINKADDR1: | |||
1766 | if (nd_opt_hdr->nd_opt_len == 1) | |||
1767 | log_debug("\t\tND_OPT_SOURCE_LINKADDR: " | |||
1768 | "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", | |||
1769 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], | |||
1770 | p[7]); | |||
1771 | else | |||
1772 | log_debug("\t\tND_OPT_SOURCE_LINKADDR"); | |||
1773 | break; | |||
1774 | case ND_OPT_TARGET_LINKADDR2: | |||
1775 | if (nd_opt_hdr->nd_opt_len == 1) | |||
1776 | log_debug("\t\tND_OPT_TARGET_LINKADDR: " | |||
1777 | "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", | |||
1778 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], | |||
1779 | p[7]); | |||
1780 | else | |||
1781 | log_debug("\t\tND_OPT_TARGET_LINKADDR"); | |||
1782 | break; | |||
1783 | case ND_OPT_PREFIX_INFORMATION3: | |||
1784 | if (nd_opt_hdr->nd_opt_len != 4) { | |||
1785 | log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " | |||
1786 | "len != 4"); | |||
1787 | return; | |||
1788 | } | |||
1789 | prf = (struct nd_opt_prefix_info*) nd_opt_hdr; | |||
1790 | ||||
1791 | log_debug("\t\tND_OPT_PREFIX_INFORMATION: %s/%u", | |||
1792 | inet_ntop(AF_INET624, &prf->nd_opt_pi_prefix, | |||
1793 | ntopbuf, INET6_ADDRSTRLEN46), | |||
1794 | prf->nd_opt_pi_prefix_len); | |||
1795 | log_debug("\t\t\tOn-link: %d", | |||
1796 | prf->nd_opt_pi_flags_reserved & | |||
1797 | ND_OPT_PI_FLAG_ONLINK0x80 ? 1:0); | |||
1798 | log_debug("\t\t\tAutonomous address-configuration: %d", | |||
1799 | prf->nd_opt_pi_flags_reserved & | |||
1800 | ND_OPT_PI_FLAG_AUTO0x40 ? 1 : 0); | |||
1801 | log_debug("\t\t\tvltime: %u", | |||
1802 | ntohl(prf->nd_opt_pi_valid_time)(__uint32_t)(__builtin_constant_p(prf->nd_opt_pi_valid_time ) ? (__uint32_t)(((__uint32_t)(prf->nd_opt_pi_valid_time) & 0xff) << 24 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff00) << 8 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff0000) >> 8 | ((__uint32_t)(prf->nd_opt_pi_valid_time ) & 0xff000000) >> 24) : __swap32md(prf->nd_opt_pi_valid_time ))); | |||
1803 | log_debug("\t\t\tpltime: %u", | |||
1804 | ntohl(prf->nd_opt_pi_preferred_time)(__uint32_t)(__builtin_constant_p(prf->nd_opt_pi_preferred_time ) ? (__uint32_t)(((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff) << 24 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff00) << 8 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff0000) >> 8 | ((__uint32_t)(prf->nd_opt_pi_preferred_time ) & 0xff000000) >> 24) : __swap32md(prf->nd_opt_pi_preferred_time ))); | |||
1805 | break; | |||
1806 | case ND_OPT_REDIRECTED_HEADER4: | |||
1807 | log_debug("\t\tND_OPT_REDIRECTED_HEADER"); | |||
1808 | break; | |||
1809 | case ND_OPT_MTU5: | |||
1810 | if (nd_opt_hdr->nd_opt_len != 1) { | |||
1811 | log_warnx("invalid ND_OPT_MTU: len != 1"); | |||
1812 | return; | |||
1813 | } | |||
1814 | mtu = (struct nd_opt_mtu*) nd_opt_hdr; | |||
1815 | log_debug("\t\tND_OPT_MTU: %u", | |||
1816 | ntohl(mtu->nd_opt_mtu_mtu)(__uint32_t)(__builtin_constant_p(mtu->nd_opt_mtu_mtu) ? ( __uint32_t)(((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff) << 24 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff00 ) << 8 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff0000 ) >> 8 | ((__uint32_t)(mtu->nd_opt_mtu_mtu) & 0xff000000 ) >> 24) : __swap32md(mtu->nd_opt_mtu_mtu))); | |||
1817 | break; | |||
1818 | case ND_OPT_ROUTE_INFO24: | |||
1819 | log_debug("\t\tND_OPT_ROUTE_INFO"); | |||
1820 | break; | |||
1821 | case ND_OPT_RDNSS25: | |||
1822 | if (nd_opt_hdr->nd_opt_len < 3) { | |||
1823 | log_warnx("invalid ND_OPT_RDNSS: len < 24"); | |||
1824 | return; | |||
1825 | } | |||
1826 | if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { | |||
1827 | log_warnx("invalid ND_OPT_RDNSS: length with" | |||
1828 | "out header is not multiply of 16: %d", | |||
1829 | (nd_opt_hdr->nd_opt_len - 1) * 8); | |||
1830 | return; | |||
1831 | } | |||
1832 | rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; | |||
1833 | log_debug("\t\tND_OPT_RDNSS: lifetime: %u", ntohl((__uint32_t)(__builtin_constant_p(rdnss->nd_opt_rdnss_lifetime ) ? (__uint32_t)(((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff) << 24 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff00) << 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff0000) >> 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff000000) >> 24) : __swap32md(rdnss->nd_opt_rdnss_lifetime )) | |||
1834 | rdnss->nd_opt_rdnss_lifetime)(__uint32_t)(__builtin_constant_p(rdnss->nd_opt_rdnss_lifetime ) ? (__uint32_t)(((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff) << 24 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff00) << 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff0000) >> 8 | ((__uint32_t)(rdnss->nd_opt_rdnss_lifetime ) & 0xff000000) >> 24) : __swap32md(rdnss->nd_opt_rdnss_lifetime ))); | |||
1835 | in6 = (struct in6_addr*) (p + 6); | |||
1836 | for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, | |||
1837 | in6++) { | |||
1838 | log_debug("\t\t\t%s", inet_ntop(AF_INET624, in6, | |||
1839 | ntopbuf, INET6_ADDRSTRLEN46)); | |||
1840 | } | |||
1841 | break; | |||
1842 | default: | |||
1843 | log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); | |||
1844 | break; | |||
1845 | ||||
1846 | } | |||
1847 | len -= nd_opt_hdr->nd_opt_len * 8 - 2; | |||
1848 | p += nd_opt_hdr->nd_opt_len * 8 - 2; | |||
1849 | } | |||
1850 | } | |||
1851 | #endif /* SMALL */ | |||
1852 | ||||
1853 | void update_iface_ra(struct slaacd_iface *iface, struct radv *ra) | |||
1854 | { | |||
1855 | struct radv *old_ra; | |||
1856 | struct radv_prefix *prefix; | |||
1857 | ||||
1858 | if ((old_ra = find_ra(iface, &ra->from)) == NULL((void *)0)) | |||
1859 | LIST_INSERT_HEAD(&iface->radvs, ra, entries)do { if (((ra)->entries.le_next = (&iface->radvs)-> lh_first) != ((void *)0)) (&iface->radvs)->lh_first ->entries.le_prev = &(ra)->entries.le_next; (&iface ->radvs)->lh_first = (ra); (ra)->entries.le_prev = & (&iface->radvs)->lh_first; } while (0); | |||
1860 | else { | |||
1861 | LIST_REPLACE(old_ra, ra, entries)do { if (((ra)->entries.le_next = (old_ra)->entries.le_next ) != ((void *)0)) (ra)->entries.le_next->entries.le_prev = &(ra)->entries.le_next; (ra)->entries.le_prev = ( old_ra)->entries.le_prev; *(ra)->entries.le_prev = (ra) ; ; ; } while (0); | |||
1862 | merge_dad_couters(old_ra, ra); | |||
1863 | free_ra(old_ra); | |||
1864 | } | |||
1865 | ||||
1866 | update_iface_ra_dfr(iface, ra); | |||
1867 | ||||
1868 | LIST_FOREACH(prefix, &ra->prefixes, entries)for((prefix) = ((&ra->prefixes)->lh_first); (prefix )!= ((void *)0); (prefix) = ((prefix)->entries.le_next)) { | |||
1869 | if (!prefix->autonomous || prefix->vltime == 0 || | |||
1870 | prefix->pltime > prefix->vltime || | |||
1871 | IN6_IS_ADDR_LINKLOCAL(&prefix->prefix)(((&prefix->prefix)->__u6_addr.__u6_addr8[0] == 0xfe ) && (((&prefix->prefix)->__u6_addr.__u6_addr8 [1] & 0xc0) == 0x80))) | |||
1872 | continue; | |||
1873 | update_iface_ra_prefix(iface, ra, prefix); | |||
1874 | } | |||
1875 | ||||
1876 | update_iface_ra_rdns(iface, ra); | |||
1877 | } | |||
1878 | ||||
1879 | void | |||
1880 | update_iface_ra_dfr(struct slaacd_iface *iface, struct radv *ra) | |||
1881 | { | |||
1882 | struct dfr_proposal *dfr_proposal; | |||
1883 | ||||
1884 | dfr_proposal = find_dfr_proposal_by_gw(iface, &ra->from); | |||
1885 | ||||
1886 | if (ra->router_lifetime == 0) { | |||
1887 | free_dfr_proposal(dfr_proposal); | |||
1888 | return; | |||
1889 | } | |||
1890 | ||||
1891 | if (!dfr_proposal) { | |||
1892 | /* new proposal */ | |||
1893 | gen_dfr_proposal(iface, ra); | |||
1894 | return; | |||
1895 | } | |||
1896 | ||||
1897 | dfr_proposal->when = ra->when; | |||
1898 | dfr_proposal->uptime = ra->uptime; | |||
1899 | dfr_proposal->router_lifetime = ra->router_lifetime; | |||
1900 | ||||
1901 | log_debug("%s, dfr state: %s, rl: %d", __func__, | |||
1902 | proposal_state_name(dfr_proposal->state), | |||
1903 | real_lifetime(&dfr_proposal->uptime, | |||
1904 | dfr_proposal->router_lifetime)); | |||
1905 | ||||
1906 | switch (dfr_proposal->state) { | |||
1907 | case PROPOSAL_CONFIGURED: | |||
1908 | case PROPOSAL_NEARLY_EXPIRED: | |||
1909 | /* routes do not expire in the kernel, update timeout */ | |||
1910 | dfr_proposal_state_transition(dfr_proposal, | |||
1911 | PROPOSAL_CONFIGURED); | |||
1912 | break; | |||
1913 | case PROPOSAL_IF_DOWN: | |||
1914 | case PROPOSAL_WITHDRAWN: | |||
1915 | log_debug("updating dfr"); | |||
1916 | configure_dfr(dfr_proposal); | |||
1917 | break; | |||
1918 | default: | |||
1919 | log_debug("%s: iface %d: %s", __func__, iface->if_index, | |||
1920 | sin6_to_str(&dfr_proposal->addr)); | |||
1921 | break; | |||
1922 | } | |||
1923 | } | |||
1924 | ||||
1925 | void | |||
1926 | update_iface_ra_prefix(struct slaacd_iface *iface, struct radv *ra, | |||
1927 | struct radv_prefix *prefix) | |||
1928 | { | |||
1929 | struct address_proposal *addr_proposal; | |||
1930 | uint32_t pltime, vltime; | |||
1931 | int found, found_temporary, duplicate_found; | |||
1932 | ||||
1933 | found = found_temporary = duplicate_found = 0; | |||
1934 | ||||
1935 | if (!!iface->autoconf != !!iface->temporary) { | |||
1936 | struct address_proposal *tmp; | |||
1937 | /* | |||
1938 | * If only the autoconf or temporary flag is set, check if we | |||
1939 | * have the "other kind" of address configured and delete it. | |||
1940 | */ | |||
1941 | LIST_FOREACH_SAFE (addr_proposal, &iface->addr_proposals,for ((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal) && ((tmp) = ((addr_proposal)->entries .le_next), 1); (addr_proposal) = (tmp)) | |||
1942 | entries, tmp)for ((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal) && ((tmp) = ((addr_proposal)->entries .le_next), 1); (addr_proposal) = (tmp)) { | |||
1943 | if ((!addr_proposal->temporary && !iface->autoconf) || | |||
1944 | (addr_proposal->temporary && !iface->temporary)) | |||
1945 | free_address_proposal(addr_proposal); | |||
1946 | } | |||
1947 | } | |||
1948 | ||||
1949 | LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries)for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) { | |||
1950 | if (prefix->prefix_len == addr_proposal-> prefix_len && | |||
1951 | memcmp(&prefix->prefix, &addr_proposal->prefix, | |||
1952 | sizeof(struct in6_addr)) != 0) | |||
1953 | continue; | |||
1954 | ||||
1955 | if (memcmp(&addr_proposal->hw_address, | |||
1956 | &iface->hw_address, | |||
1957 | sizeof(addr_proposal->hw_address)) != 0) | |||
1958 | continue; | |||
1959 | ||||
1960 | if (memcmp(&addr_proposal->soiikey, &iface->soiikey, | |||
1961 | sizeof(addr_proposal->soiikey)) != 0) | |||
1962 | continue; | |||
1963 | ||||
1964 | if (addr_proposal->state == PROPOSAL_DUPLICATED) { | |||
1965 | duplicate_found = 1; | |||
1966 | continue; | |||
1967 | } | |||
1968 | ||||
1969 | vltime = prefix->vltime; | |||
1970 | ||||
1971 | if (addr_proposal->temporary) { | |||
1972 | struct timespec now; | |||
1973 | int64_t ltime, mtime; | |||
1974 | ||||
1975 | if (clock_gettime(CLOCK_MONOTONIC3, &now)) | |||
1976 | fatal("clock_gettime"); | |||
1977 | ||||
1978 | mtime = addr_proposal->created.tv_sec + | |||
1979 | PRIV_PREFERRED_LIFETIME86400 - | |||
1980 | addr_proposal->desync_factor; | |||
1981 | ||||
1982 | ltime = MINIMUM(mtime, now.tv_sec + prefix->pltime)(((mtime) < (now.tv_sec + prefix->pltime)) ? (mtime) : ( now.tv_sec + prefix->pltime)) - | |||
1983 | now.tv_sec; | |||
1984 | ||||
1985 | pltime = ltime > 0 ? ltime : 0; | |||
1986 | ||||
1987 | ltime = MINIMUM(addr_proposal->created.tv_sec +(((addr_proposal->created.tv_sec + 172800) < (now.tv_sec + vltime)) ? (addr_proposal->created.tv_sec + 172800) : ( now.tv_sec + vltime)) | |||
1988 | PRIV_VALID_LIFETIME, now.tv_sec + vltime)(((addr_proposal->created.tv_sec + 172800) < (now.tv_sec + vltime)) ? (addr_proposal->created.tv_sec + 172800) : ( now.tv_sec + vltime)) - | |||
1989 | now.tv_sec; | |||
1990 | vltime = ltime > 0 ? ltime : 0; | |||
1991 | ||||
1992 | if ((mtime - now.tv_sec) > PRIV_REGEN_ADVANCE5) | |||
1993 | found_temporary = 1; | |||
1994 | } else { | |||
1995 | pltime = prefix->pltime; | |||
1996 | found = 1; | |||
1997 | } | |||
1998 | ||||
1999 | addr_proposal->from = ra->from; | |||
2000 | addr_proposal->when = ra->when; | |||
2001 | addr_proposal->uptime = ra->uptime; | |||
2002 | ||||
2003 | addr_proposal->vltime = vltime; | |||
2004 | addr_proposal->pltime = pltime; | |||
2005 | ||||
2006 | if (ra->mtu == iface->cur_mtu) | |||
2007 | addr_proposal->mtu = 0; | |||
2008 | else { | |||
2009 | addr_proposal->mtu = ra->mtu; | |||
2010 | iface->cur_mtu = ra->mtu; | |||
2011 | } | |||
2012 | ||||
2013 | log_debug("%s, addr state: %s", __func__, | |||
2014 | proposal_state_name(addr_proposal->state)); | |||
2015 | ||||
2016 | switch (addr_proposal->state) { | |||
2017 | case PROPOSAL_CONFIGURED: | |||
2018 | case PROPOSAL_NEARLY_EXPIRED: | |||
2019 | case PROPOSAL_IF_DOWN: | |||
2020 | case PROPOSAL_WITHDRAWN: | |||
2021 | log_debug("updating address"); | |||
2022 | configure_address(addr_proposal); | |||
2023 | break; | |||
2024 | default: | |||
2025 | log_debug("%s: iface %d: %s", __func__, iface->if_index, | |||
2026 | sin6_to_str(&addr_proposal->addr)); | |||
2027 | break; | |||
2028 | } | |||
2029 | } | |||
2030 | ||||
2031 | if (!found && iface->autoconf && duplicate_found && iface->soii) { | |||
2032 | prefix->dad_counter++; | |||
2033 | log_debug("%s dad_counter: %d", __func__, prefix->dad_counter); | |||
2034 | gen_address_proposal(iface, ra, prefix, 0); | |||
2035 | } else if (!found && iface->autoconf && (iface->soii || | |||
2036 | prefix->prefix_len <= 64)) | |||
2037 | /* new proposal */ | |||
2038 | gen_address_proposal(iface, ra, prefix, 0); | |||
2039 | ||||
2040 | /* temporary addresses do not depend on eui64 */ | |||
2041 | if (!found_temporary && iface->temporary) { | |||
2042 | if (prefix->pltime >= PRIV_REGEN_ADVANCE5) { | |||
2043 | /* new temporary proposal */ | |||
2044 | gen_address_proposal(iface, ra, prefix, 1); | |||
2045 | } else if (prefix->pltime > 0) { | |||
2046 | log_warnx("%s: pltime from %s is too small: %d < %d; " | |||
2047 | "not generating temporary address", __func__, | |||
2048 | sin6_to_str(&ra->from), prefix->pltime, | |||
2049 | PRIV_REGEN_ADVANCE5); | |||
2050 | } | |||
2051 | } | |||
2052 | } | |||
2053 | ||||
2054 | void | |||
2055 | update_iface_ra_rdns(struct slaacd_iface *iface, struct radv *ra) | |||
2056 | { | |||
2057 | struct rdns_proposal *rdns_proposal; | |||
2058 | struct radv_rdns *radv_rdns; | |||
2059 | struct in6_addr rdns[MAX_RDNS_COUNT8]; | |||
2060 | int rdns_count; | |||
2061 | ||||
2062 | rdns_proposal = find_rdns_proposal_by_gw(iface, &ra->from); | |||
2063 | ||||
2064 | if (!rdns_proposal) { | |||
2065 | /* new proposal */ | |||
2066 | if (!LIST_EMPTY(&ra->rdns_servers)(((&ra->rdns_servers)->lh_first) == ((void *)0))) | |||
2067 | gen_rdns_proposal(iface, ra); | |||
2068 | return; | |||
2069 | } | |||
2070 | ||||
2071 | rdns_count = 0; | |||
2072 | memset(&rdns, 0, sizeof(rdns)); | |||
2073 | LIST_FOREACH(radv_rdns, &ra->rdns_servers, entries)for((radv_rdns) = ((&ra->rdns_servers)->lh_first); ( radv_rdns)!= ((void *)0); (radv_rdns) = ((radv_rdns)->entries .le_next)) { | |||
2074 | memcpy(&rdns[rdns_count++], | |||
2075 | &radv_rdns->rdns, sizeof(struct in6_addr)); | |||
2076 | if (rdns_proposal->rdns_count == MAX_RDNS_COUNT8) | |||
2077 | break; | |||
2078 | } | |||
2079 | ||||
2080 | if (rdns_count == 0) { | |||
2081 | free_rdns_proposal(rdns_proposal); | |||
2082 | return; | |||
2083 | } | |||
2084 | ||||
2085 | if (rdns_proposal->rdns_count != rdns_count || | |||
2086 | memcmp(&rdns_proposal->rdns, &rdns, sizeof(rdns)) != 0) { | |||
2087 | memcpy(&rdns_proposal->rdns, &rdns, sizeof(rdns)); | |||
2088 | rdns_proposal->rdns_count = rdns_count; | |||
2089 | rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; | |||
2090 | } | |||
2091 | rdns_proposal->when = ra->when; | |||
2092 | rdns_proposal->uptime = ra->uptime; | |||
2093 | rdns_proposal->rdns_lifetime = ra->rdns_lifetime; | |||
2094 | ||||
2095 | log_debug("%s, rdns state: %s, rl: %d", __func__, | |||
2096 | proposal_state_name(rdns_proposal->state), | |||
2097 | real_lifetime(&rdns_proposal->uptime, | |||
2098 | rdns_proposal->rdns_lifetime)); | |||
2099 | ||||
2100 | switch (rdns_proposal->state) { | |||
2101 | case PROPOSAL_CONFIGURED: | |||
2102 | case PROPOSAL_NEARLY_EXPIRED: | |||
2103 | /* rdns are not expired by the kernel, update timeout */ | |||
2104 | rdns_proposal_state_transition(rdns_proposal, | |||
2105 | PROPOSAL_CONFIGURED); | |||
2106 | break; | |||
2107 | case PROPOSAL_IF_DOWN: | |||
2108 | case PROPOSAL_WITHDRAWN: | |||
2109 | case PROPOSAL_NOT_CONFIGURED: | |||
2110 | log_debug("updating rdns"); | |||
2111 | rdns_proposal_state_transition(rdns_proposal, | |||
2112 | PROPOSAL_CONFIGURED); | |||
2113 | compose_rdns_proposal(rdns_proposal->if_index, | |||
2114 | rdns_proposal->rdomain); | |||
2115 | break; | |||
2116 | default: | |||
2117 | log_debug("%s: iface %d: %s", __func__, iface->if_index, | |||
2118 | sin6_to_str(&rdns_proposal->from)); | |||
2119 | break; | |||
2120 | } | |||
2121 | } | |||
2122 | ||||
2123 | ||||
2124 | void | |||
2125 | configure_address(struct address_proposal *addr_proposal) | |||
2126 | { | |||
2127 | struct imsg_configure_address address; | |||
2128 | struct slaacd_iface *iface; | |||
2129 | ||||
2130 | log_debug("%s: %d", __func__, addr_proposal->if_index); | |||
2131 | ||||
2132 | address.if_index = addr_proposal->if_index; | |||
2133 | memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr)); | |||
2134 | memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask)); | |||
2135 | address.vltime = addr_proposal->vltime; | |||
2136 | address.pltime = addr_proposal->pltime; | |||
2137 | address.temporary = addr_proposal->temporary; | |||
2138 | address.mtu = addr_proposal->mtu; | |||
2139 | ||||
2140 | engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, | |||
2141 | sizeof(address)); | |||
2142 | ||||
2143 | if ((iface = get_slaacd_iface_by_id(addr_proposal->if_index)) != NULL((void *)0)) | |||
2144 | iface_state_transition(iface, IF_BOUND); | |||
2145 | addr_proposal_state_transition(addr_proposal, PROPOSAL_CONFIGURED); | |||
2146 | } | |||
2147 | ||||
2148 | void | |||
2149 | gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct | |||
2150 | radv_prefix *prefix, int temporary) | |||
2151 | { | |||
2152 | struct address_proposal *addr_proposal; | |||
2153 | const char *hbuf; | |||
2154 | ||||
2155 | if ((addr_proposal = calloc(1, sizeof(*addr_proposal))) == NULL((void *)0)) | |||
2156 | fatal("calloc"); | |||
2157 | addr_proposal->id = ++proposal_id; | |||
2158 | evtimer_set(&addr_proposal->timer, address_proposal_timeout,event_set(&addr_proposal->timer, -1, 0, address_proposal_timeout , addr_proposal) | |||
2159 | addr_proposal)event_set(&addr_proposal->timer, -1, 0, address_proposal_timeout , addr_proposal); | |||
2160 | addr_proposal->timo.tv_sec = 1; | |||
2161 | addr_proposal->timo.tv_usec = arc4random_uniform(1000000); | |||
2162 | addr_proposal->state = PROPOSAL_NOT_CONFIGURED; | |||
2163 | if (clock_gettime(CLOCK_MONOTONIC3, &addr_proposal->created)) | |||
2164 | fatal("clock_gettime"); | |||
2165 | addr_proposal->when = ra->when; | |||
2166 | addr_proposal->uptime = ra->uptime; | |||
2167 | addr_proposal->if_index = iface->if_index; | |||
2168 | memcpy(&addr_proposal->from, &ra->from, | |||
2169 | sizeof(addr_proposal->from)); | |||
2170 | memcpy(&addr_proposal->hw_address, &iface->hw_address, | |||
2171 | sizeof(addr_proposal->hw_address)); | |||
2172 | memcpy(&addr_proposal->soiikey, &iface->soiikey, | |||
2173 | sizeof(addr_proposal->soiikey)); | |||
2174 | addr_proposal->temporary = temporary; | |||
2175 | memcpy(&addr_proposal->prefix, &prefix->prefix, | |||
2176 | sizeof(addr_proposal->prefix)); | |||
2177 | addr_proposal->prefix_len = prefix->prefix_len; | |||
2178 | ||||
2179 | if (temporary) { | |||
2180 | addr_proposal->vltime = MINIMUM(prefix->vltime,(((prefix->vltime) < (172800)) ? (prefix->vltime) : ( 172800)) | |||
2181 | PRIV_VALID_LIFETIME)(((prefix->vltime) < (172800)) ? (prefix->vltime) : ( 172800)); | |||
2182 | addr_proposal->desync_factor = | |||
2183 | arc4random_uniform(PRIV_MAX_DESYNC_FACTOR34560); | |||
2184 | ||||
2185 | addr_proposal->pltime = MINIMUM(prefix->pltime,(((prefix->pltime) < (86400 - addr_proposal->desync_factor )) ? (prefix->pltime) : (86400 - addr_proposal->desync_factor )) | |||
2186 | PRIV_PREFERRED_LIFETIME - addr_proposal->desync_factor)(((prefix->pltime) < (86400 - addr_proposal->desync_factor )) ? (prefix->pltime) : (86400 - addr_proposal->desync_factor )); | |||
2187 | } else { | |||
2188 | addr_proposal->vltime = prefix->vltime; | |||
2189 | addr_proposal->pltime = prefix->pltime; | |||
2190 | } | |||
2191 | ||||
2192 | if (ra->mtu == iface->cur_mtu) | |||
2193 | addr_proposal->mtu = 0; | |||
2194 | else { | |||
2195 | addr_proposal->mtu = ra->mtu; | |||
2196 | iface->cur_mtu = ra->mtu; | |||
2197 | } | |||
2198 | ||||
2199 | gen_addr(iface, prefix, addr_proposal, temporary); | |||
2200 | ||||
2201 | LIST_INSERT_HEAD(&iface->addr_proposals, addr_proposal, entries)do { if (((addr_proposal)->entries.le_next = (&iface-> addr_proposals)->lh_first) != ((void *)0)) (&iface-> addr_proposals)->lh_first->entries.le_prev = &(addr_proposal )->entries.le_next; (&iface->addr_proposals)->lh_first = (addr_proposal); (addr_proposal)->entries.le_prev = & (&iface->addr_proposals)->lh_first; } while (0); | |||
2202 | configure_address(addr_proposal); | |||
2203 | ||||
2204 | hbuf = sin6_to_str(&addr_proposal->addr); | |||
2205 | log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); | |||
2206 | } | |||
2207 | ||||
2208 | void | |||
2209 | free_address_proposal(struct address_proposal *addr_proposal) | |||
2210 | { | |||
2211 | if (addr_proposal
| |||
2212 | return; | |||
2213 | ||||
2214 | LIST_REMOVE(addr_proposal, entries)do { if ((addr_proposal)->entries.le_next != ((void *)0)) ( addr_proposal)->entries.le_next->entries.le_prev = (addr_proposal )->entries.le_prev; *(addr_proposal)->entries.le_prev = (addr_proposal)->entries.le_next; ; ; } while (0); | |||
2215 | evtimer_del(&addr_proposal->timer)event_del(&addr_proposal->timer); | |||
2216 | switch (addr_proposal->state) { | |||
2217 | case PROPOSAL_CONFIGURED: | |||
2218 | case PROPOSAL_NEARLY_EXPIRED: | |||
2219 | case PROPOSAL_STALE: | |||
2220 | withdraw_addr(addr_proposal); | |||
2221 | break; | |||
2222 | default: | |||
2223 | break; | |||
2224 | } | |||
2225 | free(addr_proposal); | |||
2226 | } | |||
2227 | ||||
2228 | void | |||
2229 | withdraw_addr(struct address_proposal *addr_proposal) | |||
2230 | { | |||
2231 | struct imsg_configure_address address; | |||
2232 | ||||
2233 | log_debug("%s: %d", __func__, addr_proposal->if_index); | |||
2234 | memset(&address, 0, sizeof(address)); | |||
2235 | address.if_index = addr_proposal->if_index; | |||
2236 | memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr)); | |||
2237 | ||||
2238 | engine_imsg_compose_main(IMSG_WITHDRAW_ADDRESS, 0, &address, | |||
2239 | sizeof(address)); | |||
2240 | } | |||
2241 | ||||
2242 | void | |||
2243 | gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra) | |||
2244 | { | |||
2245 | struct dfr_proposal *dfr_proposal; | |||
2246 | const char *hbuf; | |||
2247 | ||||
2248 | if ((dfr_proposal = calloc(1, sizeof(*dfr_proposal))) == NULL((void *)0)) | |||
2249 | fatal("calloc"); | |||
2250 | dfr_proposal->id = ++proposal_id; | |||
2251 | evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout,event_set(&dfr_proposal->timer, -1, 0, dfr_proposal_timeout , dfr_proposal) | |||
2252 | dfr_proposal)event_set(&dfr_proposal->timer, -1, 0, dfr_proposal_timeout , dfr_proposal); | |||
2253 | dfr_proposal->timo.tv_sec = 1; | |||
2254 | dfr_proposal->timo.tv_usec = arc4random_uniform(1000000); | |||
2255 | dfr_proposal->state = PROPOSAL_NOT_CONFIGURED; | |||
2256 | dfr_proposal->when = ra->when; | |||
2257 | dfr_proposal->uptime = ra->uptime; | |||
2258 | dfr_proposal->if_index = iface->if_index; | |||
2259 | dfr_proposal->rdomain = iface->rdomain; | |||
2260 | memcpy(&dfr_proposal->addr, &ra->from, | |||
2261 | sizeof(dfr_proposal->addr)); | |||
2262 | dfr_proposal->router_lifetime = ra->router_lifetime; | |||
2263 | dfr_proposal->rpref = ra->rpref; | |||
2264 | ||||
2265 | LIST_INSERT_HEAD(&iface->dfr_proposals, dfr_proposal, entries)do { if (((dfr_proposal)->entries.le_next = (&iface-> dfr_proposals)->lh_first) != ((void *)0)) (&iface-> dfr_proposals)->lh_first->entries.le_prev = &(dfr_proposal )->entries.le_next; (&iface->dfr_proposals)->lh_first = (dfr_proposal); (dfr_proposal)->entries.le_prev = & (&iface->dfr_proposals)->lh_first; } while (0); | |||
2266 | configure_dfr(dfr_proposal); | |||
2267 | ||||
2268 | hbuf = sin6_to_str(&dfr_proposal->addr); | |||
2269 | log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); | |||
2270 | } | |||
2271 | ||||
2272 | void | |||
2273 | configure_dfr(struct dfr_proposal *dfr_proposal) | |||
2274 | { | |||
2275 | struct imsg_configure_dfr dfr; | |||
2276 | ||||
2277 | log_debug("%s: %d", __func__, dfr_proposal->if_index); | |||
2278 | ||||
2279 | dfr.if_index = dfr_proposal->if_index; | |||
2280 | dfr.rdomain = dfr_proposal->rdomain; | |||
2281 | memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); | |||
2282 | dfr.router_lifetime = dfr_proposal->router_lifetime; | |||
2283 | ||||
2284 | engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr)); | |||
2285 | ||||
2286 | dfr_proposal_state_transition(dfr_proposal, PROPOSAL_CONFIGURED); | |||
2287 | } | |||
2288 | ||||
2289 | void | |||
2290 | withdraw_dfr(struct dfr_proposal *dfr_proposal) | |||
2291 | { | |||
2292 | struct imsg_configure_dfr dfr; | |||
2293 | ||||
2294 | log_debug("%s: %d", __func__, dfr_proposal->if_index); | |||
2295 | ||||
2296 | dfr.if_index = dfr_proposal->if_index; | |||
2297 | dfr.rdomain = dfr_proposal->rdomain; | |||
2298 | memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); | |||
2299 | dfr.router_lifetime = dfr_proposal->router_lifetime; | |||
2300 | ||||
2301 | engine_imsg_compose_main(IMSG_WITHDRAW_DFR, 0, &dfr, sizeof(dfr)); | |||
2302 | } | |||
2303 | ||||
2304 | void | |||
2305 | free_dfr_proposal(struct dfr_proposal *dfr_proposal) | |||
2306 | { | |||
2307 | if (dfr_proposal == NULL((void *)0)) | |||
2308 | return; | |||
2309 | ||||
2310 | LIST_REMOVE(dfr_proposal, entries)do { if ((dfr_proposal)->entries.le_next != ((void *)0)) ( dfr_proposal)->entries.le_next->entries.le_prev = (dfr_proposal )->entries.le_prev; *(dfr_proposal)->entries.le_prev = ( dfr_proposal)->entries.le_next; ; ; } while (0); | |||
2311 | evtimer_del(&dfr_proposal->timer)event_del(&dfr_proposal->timer); | |||
2312 | switch (dfr_proposal->state) { | |||
2313 | case PROPOSAL_CONFIGURED: | |||
2314 | case PROPOSAL_NEARLY_EXPIRED: | |||
2315 | case PROPOSAL_STALE: | |||
2316 | withdraw_dfr(dfr_proposal); | |||
2317 | break; | |||
2318 | default: | |||
2319 | break; | |||
2320 | } | |||
2321 | free(dfr_proposal); | |||
2322 | } | |||
2323 | ||||
2324 | void | |||
2325 | gen_rdns_proposal(struct slaacd_iface *iface, struct radv *ra) | |||
2326 | { | |||
2327 | struct rdns_proposal *rdns_proposal; | |||
2328 | struct radv_rdns *rdns; | |||
2329 | const char *hbuf; | |||
2330 | ||||
2331 | if ((rdns_proposal = calloc(1, sizeof(*rdns_proposal))) == NULL((void *)0)) | |||
2332 | fatal("calloc"); | |||
2333 | rdns_proposal->id = ++proposal_id; | |||
2334 | evtimer_set(&rdns_proposal->timer, rdns_proposal_timeout,event_set(&rdns_proposal->timer, -1, 0, rdns_proposal_timeout , rdns_proposal) | |||
2335 | rdns_proposal)event_set(&rdns_proposal->timer, -1, 0, rdns_proposal_timeout , rdns_proposal); | |||
2336 | rdns_proposal->timo.tv_sec = 1; | |||
2337 | rdns_proposal->timo.tv_usec = arc4random_uniform(1000000); | |||
2338 | rdns_proposal->state = PROPOSAL_NOT_CONFIGURED; | |||
2339 | rdns_proposal->when = ra->when; | |||
2340 | rdns_proposal->uptime = ra->uptime; | |||
2341 | rdns_proposal->if_index = iface->if_index; | |||
2342 | rdns_proposal->rdomain = iface->rdomain; | |||
2343 | memcpy(&rdns_proposal->from, &ra->from, | |||
2344 | sizeof(rdns_proposal->from)); | |||
2345 | rdns_proposal->rdns_lifetime = ra->rdns_lifetime; | |||
2346 | LIST_FOREACH(rdns, &ra->rdns_servers, entries)for((rdns) = ((&ra->rdns_servers)->lh_first); (rdns )!= ((void *)0); (rdns) = ((rdns)->entries.le_next)) { | |||
2347 | memcpy(&rdns_proposal->rdns[rdns_proposal->rdns_count++], | |||
2348 | &rdns->rdns, sizeof(struct in6_addr)); | |||
2349 | if (rdns_proposal->rdns_count == MAX_RDNS_COUNT8) | |||
2350 | break; | |||
2351 | } | |||
2352 | ||||
2353 | LIST_INSERT_HEAD(&iface->rdns_proposals, rdns_proposal, entries)do { if (((rdns_proposal)->entries.le_next = (&iface-> rdns_proposals)->lh_first) != ((void *)0)) (&iface-> rdns_proposals)->lh_first->entries.le_prev = &(rdns_proposal )->entries.le_next; (&iface->rdns_proposals)->lh_first = (rdns_proposal); (rdns_proposal)->entries.le_prev = & (&iface->rdns_proposals)->lh_first; } while (0); | |||
2354 | compose_rdns_proposal(iface->if_index, iface->rdomain); | |||
2355 | ||||
2356 | hbuf = sin6_to_str(&rdns_proposal->from); | |||
2357 | log_debug("%s: iface %d: %s", __func__, iface->if_index, hbuf); | |||
2358 | } | |||
2359 | ||||
2360 | void | |||
2361 | compose_rdns_proposal(uint32_t if_index, int rdomain) | |||
2362 | { | |||
2363 | struct imsg_propose_rdns rdns; | |||
2364 | struct slaacd_iface *iface; | |||
2365 | struct rdns_proposal *rdns_proposal; | |||
2366 | int i; | |||
2367 | ||||
2368 | memset(&rdns, 0, sizeof(rdns)); | |||
2369 | rdns.if_index = if_index; | |||
2370 | rdns.rdomain = rdomain; | |||
2371 | ||||
2372 | if ((iface = get_slaacd_iface_by_id(if_index)) != NULL((void *)0)) { | |||
2373 | LIST_FOREACH(rdns_proposal, &iface->rdns_proposals, entries)for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) { | |||
2374 | if (rdns_proposal->state == PROPOSAL_WITHDRAWN || | |||
2375 | rdns_proposal->state == PROPOSAL_STALE) | |||
2376 | continue; | |||
2377 | rdns_proposal_state_transition(rdns_proposal, | |||
2378 | PROPOSAL_CONFIGURED); | |||
2379 | for (i = 0; i < rdns_proposal->rdns_count && | |||
2380 | rdns.rdns_count < MAX_RDNS_COUNT8; i++) { | |||
2381 | rdns.rdns[rdns.rdns_count++] = | |||
2382 | rdns_proposal->rdns[i]; | |||
2383 | } | |||
2384 | } | |||
2385 | } | |||
2386 | ||||
2387 | engine_imsg_compose_main(IMSG_PROPOSE_RDNS, 0, &rdns, sizeof(rdns)); | |||
2388 | } | |||
2389 | ||||
2390 | void | |||
2391 | free_rdns_proposal(struct rdns_proposal *rdns_proposal) | |||
2392 | { | |||
2393 | if (rdns_proposal == NULL((void *)0)) | |||
2394 | return; | |||
2395 | ||||
2396 | LIST_REMOVE(rdns_proposal, entries)do { if ((rdns_proposal)->entries.le_next != ((void *)0)) ( rdns_proposal)->entries.le_next->entries.le_prev = (rdns_proposal )->entries.le_prev; *(rdns_proposal)->entries.le_prev = (rdns_proposal)->entries.le_next; ; ; } while (0); | |||
2397 | evtimer_del(&rdns_proposal->timer)event_del(&rdns_proposal->timer); | |||
2398 | switch (rdns_proposal->state) { | |||
2399 | case PROPOSAL_CONFIGURED: | |||
2400 | case PROPOSAL_NEARLY_EXPIRED: | |||
2401 | case PROPOSAL_STALE: | |||
2402 | withdraw_rdns(rdns_proposal); | |||
2403 | break; | |||
2404 | default: | |||
2405 | break; | |||
2406 | } | |||
2407 | free(rdns_proposal); | |||
2408 | } | |||
2409 | ||||
2410 | void | |||
2411 | withdraw_rdns(struct rdns_proposal *rdns_proposal) | |||
2412 | { | |||
2413 | log_debug("%s: %d", __func__, rdns_proposal->if_index); | |||
2414 | ||||
2415 | rdns_proposal->state = PROPOSAL_WITHDRAWN; | |||
2416 | ||||
2417 | /* we have to re-propose all rdns servers, minus one */ | |||
2418 | compose_rdns_proposal(rdns_proposal->if_index, rdns_proposal->rdomain); | |||
2419 | } | |||
2420 | ||||
2421 | void | |||
2422 | address_proposal_timeout(int fd, short events, void *arg) | |||
2423 | { | |||
2424 | struct address_proposal *addr_proposal; | |||
2425 | struct slaacd_iface *iface = NULL((void *)0); | |||
2426 | struct radv *ra = NULL((void *)0); | |||
2427 | struct radv_prefix *prefix = NULL((void *)0); | |||
2428 | const char *hbuf; | |||
2429 | ||||
2430 | addr_proposal = (struct address_proposal *)arg; | |||
2431 | ||||
2432 | hbuf = sin6_to_str(&addr_proposal->addr); | |||
2433 | log_debug("%s: iface %d: %s [%s], priv: %s", __func__, | |||
2434 | addr_proposal->if_index, hbuf, | |||
2435 | proposal_state_name(addr_proposal->state), | |||
2436 | addr_proposal->temporary ? "y" : "n"); | |||
2437 | ||||
2438 | switch (addr_proposal->state) { | |||
2439 | case PROPOSAL_IF_DOWN: | |||
2440 | addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); | |||
2441 | break; | |||
2442 | case PROPOSAL_CONFIGURED: | |||
2443 | addr_proposal_state_transition(addr_proposal, | |||
2444 | PROPOSAL_NEARLY_EXPIRED); | |||
2445 | break; | |||
2446 | case PROPOSAL_NEARLY_EXPIRED: | |||
2447 | if (real_lifetime(&addr_proposal->uptime, | |||
2448 | addr_proposal->vltime) > 0) | |||
2449 | addr_proposal_state_transition(addr_proposal, | |||
2450 | PROPOSAL_NEARLY_EXPIRED); | |||
2451 | else | |||
2452 | addr_proposal_state_transition(addr_proposal, | |||
2453 | PROPOSAL_STALE); | |||
2454 | break; | |||
2455 | case PROPOSAL_DUPLICATED: | |||
2456 | iface = get_slaacd_iface_by_id(addr_proposal->if_index); | |||
2457 | if (iface != NULL((void *)0)) | |||
2458 | ra = find_ra(iface, &addr_proposal->from); | |||
2459 | if (ra != NULL((void *)0)) | |||
2460 | prefix = find_prefix(ra, &addr_proposal->prefix, | |||
2461 | addr_proposal->prefix_len); | |||
2462 | if (prefix != NULL((void *)0)) { | |||
2463 | if (!addr_proposal->temporary) { | |||
2464 | prefix->dad_counter++; | |||
2465 | gen_address_proposal(iface, ra, prefix, 0); | |||
2466 | } else | |||
2467 | gen_address_proposal(iface, ra, prefix, 1); | |||
2468 | } | |||
2469 | addr_proposal_state_transition(addr_proposal, PROPOSAL_STALE); | |||
2470 | break; | |||
2471 | case PROPOSAL_STALE: | |||
2472 | free_address_proposal(addr_proposal); | |||
2473 | addr_proposal = NULL((void *)0); | |||
2474 | break; | |||
2475 | case PROPOSAL_WITHDRAWN: | |||
2476 | free_address_proposal(addr_proposal); | |||
2477 | addr_proposal = NULL((void *)0); | |||
2478 | break; | |||
2479 | default: | |||
2480 | log_debug("%s: unhandled state: %s", __func__, | |||
2481 | proposal_state_name(addr_proposal->state)); | |||
2482 | } | |||
2483 | } | |||
2484 | ||||
2485 | void | |||
2486 | dfr_proposal_timeout(int fd, short events, void *arg) | |||
2487 | { | |||
2488 | struct dfr_proposal *dfr_proposal; | |||
2489 | const char *hbuf; | |||
2490 | ||||
2491 | dfr_proposal = (struct dfr_proposal *)arg; | |||
2492 | ||||
2493 | hbuf = sin6_to_str(&dfr_proposal->addr); | |||
2494 | log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index, | |||
2495 | hbuf, proposal_state_name(dfr_proposal->state)); | |||
2496 | ||||
2497 | switch (dfr_proposal->state) { | |||
2498 | case PROPOSAL_IF_DOWN: | |||
2499 | dfr_proposal_state_transition(dfr_proposal, PROPOSAL_STALE); | |||
2500 | break; | |||
2501 | case PROPOSAL_CONFIGURED: | |||
2502 | dfr_proposal_state_transition(dfr_proposal, | |||
2503 | PROPOSAL_NEARLY_EXPIRED); | |||
2504 | break; | |||
2505 | case PROPOSAL_NEARLY_EXPIRED: | |||
2506 | if (real_lifetime(&dfr_proposal->uptime, | |||
2507 | dfr_proposal->router_lifetime) > 0) | |||
2508 | dfr_proposal_state_transition(dfr_proposal, | |||
2509 | PROPOSAL_NEARLY_EXPIRED); | |||
2510 | else | |||
2511 | dfr_proposal_state_transition(dfr_proposal, | |||
2512 | PROPOSAL_STALE); | |||
2513 | break; | |||
2514 | case PROPOSAL_STALE: | |||
2515 | free_dfr_proposal(dfr_proposal); | |||
2516 | dfr_proposal = NULL((void *)0); | |||
2517 | break; | |||
2518 | case PROPOSAL_WITHDRAWN: | |||
2519 | free_dfr_proposal(dfr_proposal); | |||
2520 | dfr_proposal = NULL((void *)0); | |||
2521 | break; | |||
2522 | ||||
2523 | default: | |||
2524 | log_debug("%s: unhandled state: %s", __func__, | |||
2525 | proposal_state_name(dfr_proposal->state)); | |||
2526 | } | |||
2527 | } | |||
2528 | ||||
2529 | void | |||
2530 | rdns_proposal_timeout(int fd, short events, void *arg) | |||
2531 | { | |||
2532 | struct rdns_proposal *rdns_proposal; | |||
2533 | const char *hbuf; | |||
2534 | ||||
2535 | rdns_proposal = (struct rdns_proposal *)arg; | |||
2536 | ||||
2537 | hbuf = sin6_to_str(&rdns_proposal->from); | |||
2538 | log_debug("%s: iface %d: %s [%s]", __func__, rdns_proposal->if_index, | |||
2539 | hbuf, proposal_state_name(rdns_proposal->state)); | |||
2540 | ||||
2541 | switch (rdns_proposal->state) { | |||
2542 | case PROPOSAL_IF_DOWN: | |||
2543 | rdns_proposal_state_transition(rdns_proposal, PROPOSAL_STALE); | |||
2544 | break; | |||
2545 | case PROPOSAL_CONFIGURED: | |||
2546 | rdns_proposal_state_transition(rdns_proposal, | |||
2547 | PROPOSAL_NEARLY_EXPIRED); | |||
2548 | break; | |||
2549 | case PROPOSAL_NEARLY_EXPIRED: | |||
2550 | if (real_lifetime(&rdns_proposal->uptime, | |||
2551 | rdns_proposal->rdns_lifetime) > 0) | |||
2552 | rdns_proposal_state_transition(rdns_proposal, | |||
2553 | PROPOSAL_NEARLY_EXPIRED); | |||
2554 | else | |||
2555 | rdns_proposal_state_transition(rdns_proposal, | |||
2556 | PROPOSAL_STALE); | |||
2557 | break; | |||
2558 | case PROPOSAL_STALE: | |||
2559 | free_rdns_proposal(rdns_proposal); | |||
2560 | rdns_proposal = NULL((void *)0); | |||
2561 | break; | |||
2562 | case PROPOSAL_WITHDRAWN: | |||
2563 | free_rdns_proposal(rdns_proposal); | |||
2564 | rdns_proposal = NULL((void *)0); | |||
2565 | break; | |||
2566 | ||||
2567 | default: | |||
2568 | log_debug("%s: unhandled state: %s", __func__, | |||
2569 | proposal_state_name(rdns_proposal->state)); | |||
2570 | } | |||
2571 | } | |||
2572 | ||||
2573 | void | |||
2574 | iface_timeout(int fd, short events, void *arg) | |||
2575 | { | |||
2576 | struct slaacd_iface *iface = (struct slaacd_iface *)arg; | |||
2577 | ||||
2578 | log_debug("%s[%d]: %s", __func__, iface->if_index, | |||
2579 | if_state_name(iface->state)); | |||
2580 | ||||
2581 | switch (iface->state) { | |||
2582 | case IF_DOWN: | |||
2583 | fatalx("%s: timeout in wrong state IF_DOWN", __func__); | |||
2584 | break; | |||
2585 | case IF_INIT: | |||
2586 | iface_state_transition(iface, IF_INIT); | |||
2587 | break; | |||
2588 | default: | |||
2589 | break; | |||
2590 | } | |||
2591 | } | |||
2592 | ||||
2593 | struct radv* | |||
2594 | find_ra(struct slaacd_iface *iface, struct sockaddr_in6 *from) | |||
2595 | { | |||
2596 | struct radv *ra; | |||
2597 | ||||
2598 | LIST_FOREACH (ra, &iface->radvs, entries)for((ra) = ((&iface->radvs)->lh_first); (ra)!= ((void *)0); (ra) = ((ra)->entries.le_next)) { | |||
2599 | if (memcmp(&ra->from.sin6_addr, &from->sin6_addr, | |||
2600 | sizeof(from->sin6_addr)) == 0) | |||
2601 | return (ra); | |||
2602 | } | |||
2603 | ||||
2604 | return (NULL((void *)0)); | |||
2605 | } | |||
2606 | ||||
2607 | struct address_proposal* | |||
2608 | find_address_proposal_by_addr(struct slaacd_iface *iface, struct sockaddr_in6 | |||
2609 | *addr) | |||
2610 | { | |||
2611 | struct address_proposal *addr_proposal; | |||
2612 | ||||
2613 | LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries)for((addr_proposal) = ((&iface->addr_proposals)->lh_first ); (addr_proposal)!= ((void *)0); (addr_proposal) = ((addr_proposal )->entries.le_next)) { | |||
2614 | if (memcmp(&addr_proposal->addr, addr, sizeof(*addr)) == 0) | |||
2615 | return (addr_proposal); | |||
2616 | } | |||
2617 | ||||
2618 | return (NULL((void *)0)); | |||
2619 | } | |||
2620 | ||||
2621 | struct dfr_proposal* | |||
2622 | find_dfr_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6 | |||
2623 | *addr) | |||
2624 | { | |||
2625 | struct dfr_proposal *dfr_proposal; | |||
2626 | ||||
2627 | LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, entries)for((dfr_proposal) = ((&iface->dfr_proposals)->lh_first ); (dfr_proposal)!= ((void *)0); (dfr_proposal) = ((dfr_proposal )->entries.le_next)) { | |||
2628 | if (memcmp(&dfr_proposal->addr, addr, sizeof(*addr)) == 0) | |||
2629 | return (dfr_proposal); | |||
2630 | } | |||
2631 | ||||
2632 | return (NULL((void *)0)); | |||
2633 | } | |||
2634 | ||||
2635 | struct rdns_proposal* | |||
2636 | find_rdns_proposal_by_gw(struct slaacd_iface *iface, struct sockaddr_in6 | |||
2637 | *from) | |||
2638 | { | |||
2639 | struct rdns_proposal *rdns_proposal; | |||
2640 | ||||
2641 | LIST_FOREACH (rdns_proposal, &iface->rdns_proposals, entries)for((rdns_proposal) = ((&iface->rdns_proposals)->lh_first ); (rdns_proposal)!= ((void *)0); (rdns_proposal) = ((rdns_proposal )->entries.le_next)) { | |||
2642 | if (memcmp(&rdns_proposal->from, from, sizeof(*from)) == 0) | |||
2643 | return (rdns_proposal); | |||
2644 | } | |||
2645 | ||||
2646 | return (NULL((void *)0)); | |||
2647 | } | |||
2648 | ||||
2649 | struct radv_prefix * | |||
2650 | find_prefix(struct radv *ra, struct in6_addr *prefix, uint8_t prefix_len) | |||
2651 | { | |||
2652 | struct radv_prefix *result; | |||
2653 | ||||
2654 | ||||
2655 | LIST_FOREACH(result, &ra->prefixes, entries)for((result) = ((&ra->prefixes)->lh_first); (result )!= ((void *)0); (result) = ((result)->entries.le_next)) { | |||
2656 | if (memcmp(&result->prefix, prefix, | |||
2657 | sizeof(result->prefix)) == 0 && result->prefix_len == | |||
2658 | prefix_len) | |||
2659 | return (result); | |||
2660 | } | |||
2661 | return (NULL((void *)0)); | |||
2662 | } | |||
2663 | ||||
2664 | uint32_t | |||
2665 | real_lifetime(struct timespec *received_uptime, uint32_t ltime) | |||
2666 | { | |||
2667 | struct timespec now, diff; | |||
2668 | int64_t remaining; | |||
2669 | ||||
2670 | if (clock_gettime(CLOCK_MONOTONIC3, &now)) | |||
2671 | fatal("clock_gettime"); | |||
2672 | ||||
2673 | timespecsub(&now, received_uptime, &diff)do { (&diff)->tv_sec = (&now)->tv_sec - (received_uptime )->tv_sec; (&diff)->tv_nsec = (&now)->tv_nsec - (received_uptime)->tv_nsec; if ((&diff)->tv_nsec < 0) { (&diff)->tv_sec--; (&diff)->tv_nsec += 1000000000L; } } while (0); | |||
2674 | ||||
2675 | remaining = ((int64_t)ltime) - diff.tv_sec; | |||
2676 | ||||
2677 | if (remaining < 0) | |||
2678 | remaining = 0; | |||
2679 | ||||
2680 | return (remaining); | |||
2681 | } | |||
2682 | ||||
2683 | void | |||
2684 | merge_dad_couters(struct radv *old_ra, struct radv *new_ra) | |||
2685 | { | |||
2686 | ||||
2687 | struct radv_prefix *old_prefix, *new_prefix; | |||
2688 | ||||
2689 | LIST_FOREACH(old_prefix, &old_ra->prefixes, entries)for((old_prefix) = ((&old_ra->prefixes)->lh_first); (old_prefix)!= ((void *)0); (old_prefix) = ((old_prefix)-> entries.le_next)) { | |||
2690 | if (!old_prefix->dad_counter) | |||
2691 | continue; | |||
2692 | if ((new_prefix = find_prefix(new_ra, &old_prefix->prefix, | |||
2693 | old_prefix->prefix_len)) != NULL((void *)0)) | |||
2694 | new_prefix->dad_counter = old_prefix->dad_counter; | |||
2695 | } | |||
2696 | } |