1 |
|
|
/* $OpenBSD: engine.c,v 1.18 2017/08/23 15:49:08 florian 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 <errno.h> |
68 |
|
|
#include <event.h> |
69 |
|
|
#include <imsg.h> |
70 |
|
|
#include <pwd.h> |
71 |
|
|
#include <signal.h> |
72 |
|
|
#include <stddef.h> |
73 |
|
|
#include <stdlib.h> |
74 |
|
|
#include <string.h> |
75 |
|
|
#include <time.h> |
76 |
|
|
#include <unistd.h> |
77 |
|
|
|
78 |
|
|
#include "log.h" |
79 |
|
|
#include "slaacd.h" |
80 |
|
|
#include "engine.h" |
81 |
|
|
|
82 |
|
|
#define MAX_RTR_SOLICITATION_DELAY 1 |
83 |
|
|
#define MAX_RTR_SOLICITATION_DELAY_USEC MAX_RTR_SOLICITATION_DELAY * 1000000 |
84 |
|
|
#define RTR_SOLICITATION_INTERVAL 4 |
85 |
|
|
#define MAX_RTR_SOLICITATIONS 3 |
86 |
|
|
|
87 |
|
|
enum if_state { |
88 |
|
|
IF_DOWN, |
89 |
|
|
IF_DELAY, |
90 |
|
|
IF_PROBE, |
91 |
|
|
IF_IDLE, |
92 |
|
|
}; |
93 |
|
|
|
94 |
|
|
const char* if_state_name[] = { |
95 |
|
|
"IF_DOWN", |
96 |
|
|
"IF_DELAY", |
97 |
|
|
"IF_PROBE", |
98 |
|
|
"IF_IDLE", |
99 |
|
|
}; |
100 |
|
|
|
101 |
|
|
enum proposal_state { |
102 |
|
|
PROPOSAL_NOT_CONFIGURED, |
103 |
|
|
PROPOSAL_SENT, |
104 |
|
|
PROPOSAL_CONFIGURED, |
105 |
|
|
PROPOSAL_NEARLY_EXPIRED, |
106 |
|
|
PROPOSAL_WITHDRAWN, |
107 |
|
|
}; |
108 |
|
|
|
109 |
|
|
const char* proposal_state_name[] = { |
110 |
|
|
"NOT_CONFIGURED", |
111 |
|
|
"SENT", |
112 |
|
|
"CONFIGURED", |
113 |
|
|
"NEARLY_EXPIRED", |
114 |
|
|
"WITHDRAWN", |
115 |
|
|
}; |
116 |
|
|
|
117 |
|
|
const char* rpref_name[] = { |
118 |
|
|
"Low", |
119 |
|
|
"Medium", |
120 |
|
|
"High", |
121 |
|
|
}; |
122 |
|
|
|
123 |
|
|
struct radv_prefix { |
124 |
|
|
LIST_ENTRY(radv_prefix) 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 |
|
|
}; |
132 |
|
|
|
133 |
|
|
struct radv_rdns { |
134 |
|
|
LIST_ENTRY(radv_rdns) entries; |
135 |
|
|
struct in6_addr rdns; |
136 |
|
|
}; |
137 |
|
|
|
138 |
|
|
struct radv_dnssl { |
139 |
|
|
LIST_ENTRY(radv_dnssl) entries; |
140 |
|
|
char dnssl[SLAACD_MAX_DNSSL]; |
141 |
|
|
}; |
142 |
|
|
|
143 |
|
|
struct radv { |
144 |
|
|
LIST_ENTRY(radv) entries; |
145 |
|
|
struct sockaddr_in6 from; |
146 |
|
|
struct timespec when; |
147 |
|
|
struct timespec uptime; |
148 |
|
|
struct event timer; |
149 |
|
|
uint32_t min_lifetime; |
150 |
|
|
uint8_t curhoplimit; |
151 |
|
|
int managed; |
152 |
|
|
int other; |
153 |
|
|
enum rpref rpref; |
154 |
|
|
uint16_t router_lifetime; /* in seconds */ |
155 |
|
|
uint32_t reachable_time; /* in milliseconds */ |
156 |
|
|
uint32_t retrans_time; /* in milliseconds */ |
157 |
|
|
LIST_HEAD(, radv_prefix) prefixes; |
158 |
|
|
uint32_t rdns_lifetime; |
159 |
|
|
LIST_HEAD(, radv_rdns) rdns_servers; |
160 |
|
|
uint32_t dnssl_lifetime; |
161 |
|
|
LIST_HEAD(, radv_dnssl) dnssls; |
162 |
|
|
}; |
163 |
|
|
|
164 |
|
|
struct address_proposal { |
165 |
|
|
LIST_ENTRY(address_proposal) entries; |
166 |
|
|
struct event timer; |
167 |
|
|
int64_t id; |
168 |
|
|
enum proposal_state state; |
169 |
|
|
time_t next_timeout; |
170 |
|
|
int timeout_count; |
171 |
|
|
struct timespec when; |
172 |
|
|
struct timespec uptime; |
173 |
|
|
uint32_t if_index; |
174 |
|
|
struct ether_addr hw_address; |
175 |
|
|
struct sockaddr_in6 addr; |
176 |
|
|
struct in6_addr mask; |
177 |
|
|
struct in6_addr prefix; |
178 |
|
|
int privacy; |
179 |
|
|
uint8_t prefix_len; |
180 |
|
|
uint32_t vltime; |
181 |
|
|
uint32_t pltime; |
182 |
|
|
}; |
183 |
|
|
|
184 |
|
|
struct dfr_proposal { |
185 |
|
|
LIST_ENTRY(dfr_proposal) entries; |
186 |
|
|
struct event timer; |
187 |
|
|
int64_t id; |
188 |
|
|
enum proposal_state state; |
189 |
|
|
time_t next_timeout; |
190 |
|
|
int timeout_count; |
191 |
|
|
struct timespec when; |
192 |
|
|
struct timespec uptime; |
193 |
|
|
uint32_t if_index; |
194 |
|
|
struct sockaddr_in6 addr; |
195 |
|
|
uint32_t router_lifetime; |
196 |
|
|
enum rpref rpref; |
197 |
|
|
}; |
198 |
|
|
|
199 |
|
|
struct slaacd_iface { |
200 |
|
|
LIST_ENTRY(slaacd_iface) entries; |
201 |
|
|
enum if_state state; |
202 |
|
|
struct event timer; |
203 |
|
|
int probes; |
204 |
|
|
uint32_t if_index; |
205 |
|
|
int running; |
206 |
|
|
int autoconfprivacy; |
207 |
|
|
struct ether_addr hw_address; |
208 |
|
|
struct sockaddr_in6 ll_address; |
209 |
|
|
LIST_HEAD(, radv) radvs; |
210 |
|
|
LIST_HEAD(, address_proposal) addr_proposals; |
211 |
|
|
LIST_HEAD(, dfr_proposal) dfr_proposals; |
212 |
|
|
}; |
213 |
|
|
|
214 |
|
|
LIST_HEAD(, slaacd_iface) slaacd_interfaces; |
215 |
|
|
|
216 |
|
|
__dead void engine_shutdown(void); |
217 |
|
|
void engine_sig_handler(int sig, short, void *); |
218 |
|
|
void engine_dispatch_frontend(int, short, void *); |
219 |
|
|
void engine_dispatch_main(int, short, void *); |
220 |
|
|
#ifndef SMALL |
221 |
|
|
void send_interface_info(struct slaacd_iface *, pid_t); |
222 |
|
|
void engine_showinfo_ctl(struct imsg *, uint32_t); |
223 |
|
|
void debug_log_ra(struct imsg_ra *); |
224 |
|
|
int in6_mask2prefixlen(struct in6_addr *); |
225 |
|
|
#endif /* SMALL */ |
226 |
|
|
struct slaacd_iface *get_slaacd_iface_by_id(uint32_t); |
227 |
|
|
void remove_slaacd_iface(uint32_t); |
228 |
|
|
void free_ra(struct radv *); |
229 |
|
|
void parse_ra(struct slaacd_iface *, struct imsg_ra *); |
230 |
|
|
void gen_addr(struct slaacd_iface *, struct radv_prefix *, |
231 |
|
|
struct address_proposal *, int); |
232 |
|
|
void gen_address_proposal(struct slaacd_iface *, struct |
233 |
|
|
radv *, struct radv_prefix *, int); |
234 |
|
|
void free_address_proposal(struct address_proposal *); |
235 |
|
|
void timeout_from_lifetime(struct address_proposal *); |
236 |
|
|
void configure_address(struct address_proposal *); |
237 |
|
|
void in6_prefixlen2mask(struct in6_addr *, int len); |
238 |
|
|
void gen_dfr_proposal(struct slaacd_iface *, struct |
239 |
|
|
radv *); |
240 |
|
|
void configure_dfr(struct dfr_proposal *); |
241 |
|
|
void free_dfr_proposal(struct dfr_proposal *); |
242 |
|
|
void withdraw_dfr(struct dfr_proposal *); |
243 |
|
|
char *parse_dnssl(char *, int); |
244 |
|
|
void update_iface_ra(struct slaacd_iface *, struct radv *); |
245 |
|
|
void send_proposal(struct imsg_proposal *); |
246 |
|
|
void start_probe(struct slaacd_iface *); |
247 |
|
|
void address_proposal_timeout(int, short, void *); |
248 |
|
|
void dfr_proposal_timeout(int, short, void *); |
249 |
|
|
void iface_timeout(int, short, void *); |
250 |
|
|
struct radv *find_ra(struct slaacd_iface *, struct sockaddr_in6 *); |
251 |
|
|
struct address_proposal *find_address_proposal_by_id(struct slaacd_iface *, |
252 |
|
|
int64_t); |
253 |
|
|
struct address_proposal *find_address_proposal_by_addr(struct slaacd_iface *, |
254 |
|
|
struct sockaddr_in6 *); |
255 |
|
|
struct dfr_proposal *find_dfr_proposal_by_id(struct slaacd_iface *, |
256 |
|
|
int64_t); |
257 |
|
|
void find_prefix(struct slaacd_iface *, struct |
258 |
|
|
address_proposal *, struct radv **, struct |
259 |
|
|
radv_prefix **); |
260 |
|
|
int engine_imsg_compose_main(int, pid_t, void *, uint16_t); |
261 |
|
|
uint32_t real_lifetime(struct timespec *, uint32_t); |
262 |
|
|
|
263 |
|
|
struct imsgev *iev_frontend; |
264 |
|
|
struct imsgev *iev_main; |
265 |
|
|
int64_t proposal_id; |
266 |
|
|
|
267 |
|
|
void |
268 |
|
|
engine_sig_handler(int sig, short event, void *arg) |
269 |
|
|
{ |
270 |
|
|
/* |
271 |
|
|
* Normal signal handler rules don't apply because libevent |
272 |
|
|
* decouples for us. |
273 |
|
|
*/ |
274 |
|
|
|
275 |
|
|
switch (sig) { |
276 |
|
|
case SIGINT: |
277 |
|
|
case SIGTERM: |
278 |
|
|
engine_shutdown(); |
279 |
|
|
default: |
280 |
|
|
fatalx("unexpected signal"); |
281 |
|
|
} |
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
void |
285 |
|
|
engine(int debug, int verbose) |
286 |
|
|
{ |
287 |
|
|
struct event ev_sigint, ev_sigterm; |
288 |
|
|
struct passwd *pw; |
289 |
|
|
|
290 |
|
|
log_init(debug, LOG_DAEMON); |
291 |
|
|
log_setverbose(verbose); |
292 |
|
|
|
293 |
|
|
if ((pw = getpwnam(SLAACD_USER)) == NULL) |
294 |
|
|
fatal("getpwnam"); |
295 |
|
|
|
296 |
|
|
if (chroot(pw->pw_dir) == -1) |
297 |
|
|
fatal("chroot"); |
298 |
|
|
if (chdir("/") == -1) |
299 |
|
|
fatal("chdir(\"/\")"); |
300 |
|
|
|
301 |
|
|
slaacd_process = PROC_ENGINE; |
302 |
|
|
setproctitle("%s", log_procnames[slaacd_process]); |
303 |
|
|
log_procinit(log_procnames[slaacd_process]); |
304 |
|
|
|
305 |
|
|
if (setgroups(1, &pw->pw_gid) || |
306 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
307 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
308 |
|
|
fatal("can't drop privileges"); |
309 |
|
|
|
310 |
|
|
if (pledge("stdio recvfd flock rpath cpath wpath", NULL) == -1) |
311 |
|
|
fatal("pledge"); |
312 |
|
|
|
313 |
|
|
event_init(); |
314 |
|
|
|
315 |
|
|
/* Setup signal handler(s). */ |
316 |
|
|
signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL); |
317 |
|
|
signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL); |
318 |
|
|
signal_add(&ev_sigint, NULL); |
319 |
|
|
signal_add(&ev_sigterm, NULL); |
320 |
|
|
signal(SIGPIPE, SIG_IGN); |
321 |
|
|
signal(SIGHUP, SIG_IGN); |
322 |
|
|
|
323 |
|
|
/* Setup pipe and event handler to the main process. */ |
324 |
|
|
if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) |
325 |
|
|
fatal(NULL); |
326 |
|
|
|
327 |
|
|
imsg_init(&iev_main->ibuf, 3); |
328 |
|
|
iev_main->handler = engine_dispatch_main; |
329 |
|
|
|
330 |
|
|
/* Setup event handlers. */ |
331 |
|
|
iev_main->events = EV_READ; |
332 |
|
|
event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, |
333 |
|
|
iev_main->handler, iev_main); |
334 |
|
|
event_add(&iev_main->ev, NULL); |
335 |
|
|
|
336 |
|
|
LIST_INIT(&slaacd_interfaces); |
337 |
|
|
|
338 |
|
|
event_dispatch(); |
339 |
|
|
|
340 |
|
|
engine_shutdown(); |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
__dead void |
344 |
|
|
engine_shutdown(void) |
345 |
|
|
{ |
346 |
|
|
/* Close pipes. */ |
347 |
|
|
msgbuf_clear(&iev_frontend->ibuf.w); |
348 |
|
|
close(iev_frontend->ibuf.fd); |
349 |
|
|
msgbuf_clear(&iev_main->ibuf.w); |
350 |
|
|
close(iev_main->ibuf.fd); |
351 |
|
|
|
352 |
|
|
free(iev_frontend); |
353 |
|
|
free(iev_main); |
354 |
|
|
|
355 |
|
|
log_info("engine exiting"); |
356 |
|
|
exit(0); |
357 |
|
|
} |
358 |
|
|
|
359 |
|
|
int |
360 |
|
|
engine_imsg_compose_frontend(int type, pid_t pid, void *data, |
361 |
|
|
uint16_t datalen) |
362 |
|
|
{ |
363 |
|
|
return (imsg_compose_event(iev_frontend, type, 0, pid, -1, |
364 |
|
|
data, datalen)); |
365 |
|
|
} |
366 |
|
|
|
367 |
|
|
int |
368 |
|
|
engine_imsg_compose_main(int type, pid_t pid, void *data, |
369 |
|
|
uint16_t datalen) |
370 |
|
|
{ |
371 |
|
|
return (imsg_compose_event(iev_main, type, 0, pid, -1, |
372 |
|
|
data, datalen)); |
373 |
|
|
} |
374 |
|
|
|
375 |
|
|
void |
376 |
|
|
engine_dispatch_frontend(int fd, short event, void *bula) |
377 |
|
|
{ |
378 |
|
|
struct imsgev *iev = bula; |
379 |
|
|
struct imsgbuf *ibuf = &iev->ibuf; |
380 |
|
|
struct imsg imsg; |
381 |
|
|
struct slaacd_iface *iface; |
382 |
|
|
struct imsg_ra ra; |
383 |
|
|
struct imsg_proposal_ack proposal_ack; |
384 |
|
|
struct address_proposal *addr_proposal = NULL; |
385 |
|
|
struct dfr_proposal *dfr_proposal = NULL; |
386 |
|
|
struct imsg_del_addr del_addr; |
387 |
|
|
ssize_t n; |
388 |
|
|
int shut = 0; |
389 |
|
|
#ifndef SMALL |
390 |
|
|
int verbose; |
391 |
|
|
#endif /* SMALL */ |
392 |
|
|
uint32_t if_index; |
393 |
|
|
|
394 |
|
|
if (event & EV_READ) { |
395 |
|
|
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) |
396 |
|
|
fatal("imsg_read error"); |
397 |
|
|
if (n == 0) /* Connection closed. */ |
398 |
|
|
shut = 1; |
399 |
|
|
} |
400 |
|
|
if (event & EV_WRITE) { |
401 |
|
|
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) |
402 |
|
|
fatal("msgbuf_write"); |
403 |
|
|
if (n == 0) /* Connection closed. */ |
404 |
|
|
shut = 1; |
405 |
|
|
} |
406 |
|
|
|
407 |
|
|
for (;;) { |
408 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
409 |
|
|
fatal("%s: imsg_get error", __func__); |
410 |
|
|
if (n == 0) /* No more messages. */ |
411 |
|
|
break; |
412 |
|
|
|
413 |
|
|
switch (imsg.hdr.type) { |
414 |
|
|
#ifndef SMALL |
415 |
|
|
case IMSG_CTL_LOG_VERBOSE: |
416 |
|
|
/* Already checked by frontend. */ |
417 |
|
|
memcpy(&verbose, imsg.data, sizeof(verbose)); |
418 |
|
|
log_setverbose(verbose); |
419 |
|
|
break; |
420 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO: |
421 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index)) |
422 |
|
|
fatal("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong " |
423 |
|
|
"length: %d", __func__, imsg.hdr.len); |
424 |
|
|
memcpy(&if_index, imsg.data, sizeof(if_index)); |
425 |
|
|
engine_showinfo_ctl(&imsg, if_index); |
426 |
|
|
break; |
427 |
|
|
#endif /* SMALL */ |
428 |
|
|
case IMSG_REMOVE_IF: |
429 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index)) |
430 |
|
|
fatal("%s: IMSG_REMOVE_IF wrong length: %d", |
431 |
|
|
__func__, imsg.hdr.len); |
432 |
|
|
memcpy(&if_index, imsg.data, sizeof(if_index)); |
433 |
|
|
remove_slaacd_iface(if_index); |
434 |
|
|
break; |
435 |
|
|
case IMSG_RA: |
436 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(ra)) |
437 |
|
|
fatal("%s: IMSG_RA wrong length: %d", |
438 |
|
|
__func__, imsg.hdr.len); |
439 |
|
|
memcpy(&ra, imsg.data, sizeof(ra)); |
440 |
|
|
iface = get_slaacd_iface_by_id(ra.if_index); |
441 |
|
|
if (iface != NULL) |
442 |
|
|
parse_ra(iface, &ra); |
443 |
|
|
break; |
444 |
|
|
case IMSG_CTL_SEND_SOLICITATION: |
445 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(if_index)) |
446 |
|
|
fatal("%s: IMSG_CTL_SEND_SOLICITATION wrong " |
447 |
|
|
"length: %d", __func__, imsg.hdr.len); |
448 |
|
|
memcpy(&if_index, imsg.data, sizeof(if_index)); |
449 |
|
|
iface = get_slaacd_iface_by_id(if_index); |
450 |
|
|
if (iface == NULL) |
451 |
|
|
log_warnx("requested to send solicitation on " |
452 |
|
|
"non-autoconf interface: %u", if_index); |
453 |
|
|
else |
454 |
|
|
engine_imsg_compose_frontend( |
455 |
|
|
IMSG_CTL_SEND_SOLICITATION, imsg.hdr.pid, |
456 |
|
|
&iface->if_index, sizeof(iface->if_index)); |
457 |
|
|
break; |
458 |
|
|
case IMSG_PROPOSAL_ACK: |
459 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
460 |
|
|
sizeof(proposal_ack)) |
461 |
|
|
fatal("%s: IMSG_PROPOSAL_ACK wrong length: %d", |
462 |
|
|
__func__, imsg.hdr.len); |
463 |
|
|
memcpy(&proposal_ack, imsg.data, sizeof(proposal_ack)); |
464 |
|
|
log_debug("%s: IMSG_PROPOSAL_ACK: %lld - %d", __func__, |
465 |
|
|
proposal_ack.id, proposal_ack.pid); |
466 |
|
|
if (proposal_ack.pid != getpid()) { |
467 |
|
|
log_debug("IMSG_PROPOSAL_ACK: wrong pid, " |
468 |
|
|
"ignoring"); |
469 |
|
|
break; |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
iface = get_slaacd_iface_by_id(proposal_ack.if_index); |
473 |
|
|
if (iface == NULL) { |
474 |
|
|
log_debug("IMSG_PROPOSAL_ACK: unknown interface" |
475 |
|
|
", ignoring"); |
476 |
|
|
break; |
477 |
|
|
} |
478 |
|
|
|
479 |
|
|
addr_proposal = find_address_proposal_by_id(iface, |
480 |
|
|
proposal_ack.id); |
481 |
|
|
if (addr_proposal == NULL) { |
482 |
|
|
dfr_proposal = find_dfr_proposal_by_id(iface, |
483 |
|
|
proposal_ack.id); |
484 |
|
|
if (dfr_proposal == NULL) { |
485 |
|
|
log_debug("IMSG_PROPOSAL_ACK: cannot " |
486 |
|
|
"find proposal, ignoring"); |
487 |
|
|
break; |
488 |
|
|
} |
489 |
|
|
} |
490 |
|
|
if (addr_proposal != NULL) |
491 |
|
|
configure_address(addr_proposal); |
492 |
|
|
else if (dfr_proposal != NULL) |
493 |
|
|
configure_dfr(dfr_proposal); |
494 |
|
|
|
495 |
|
|
break; |
496 |
|
|
case IMSG_DEL_ADDRESS: |
497 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
498 |
|
|
sizeof(del_addr)) |
499 |
|
|
fatal("%s: IMSG_DEL_ADDRESS wrong length: %d", |
500 |
|
|
__func__, imsg.hdr.len); |
501 |
|
|
memcpy(&del_addr, imsg.data, sizeof(del_addr)); |
502 |
|
|
iface = get_slaacd_iface_by_id(del_addr.if_index); |
503 |
|
|
if (iface == NULL) { |
504 |
|
|
log_debug("IMSG_DEL_ADDRESS: unknown interface" |
505 |
|
|
", ignoring"); |
506 |
|
|
break; |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
addr_proposal = find_address_proposal_by_addr(iface, |
510 |
|
|
&del_addr.addr); |
511 |
|
|
|
512 |
|
|
if (addr_proposal) { |
513 |
|
|
/* XXX should we inform netcfgd? */ |
514 |
|
|
LIST_REMOVE(addr_proposal, entries); |
515 |
|
|
free_address_proposal(addr_proposal); |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
break; |
519 |
|
|
default: |
520 |
|
|
log_debug("%s: unexpected imsg %d", __func__, |
521 |
|
|
imsg.hdr.type); |
522 |
|
|
break; |
523 |
|
|
} |
524 |
|
|
imsg_free(&imsg); |
525 |
|
|
} |
526 |
|
|
if (!shut) |
527 |
|
|
imsg_event_add(iev); |
528 |
|
|
else { |
529 |
|
|
/* This pipe is dead. Remove its event handler. */ |
530 |
|
|
event_del(&iev->ev); |
531 |
|
|
event_loopexit(NULL); |
532 |
|
|
} |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
void |
536 |
|
|
engine_dispatch_main(int fd, short event, void *bula) |
537 |
|
|
{ |
538 |
|
|
struct imsg imsg; |
539 |
|
|
struct imsgev *iev = bula; |
540 |
|
|
struct imsgbuf *ibuf = &iev->ibuf; |
541 |
|
|
struct imsg_ifinfo imsg_ifinfo; |
542 |
|
|
struct slaacd_iface *iface; |
543 |
|
|
ssize_t n; |
544 |
|
|
int shut = 0; |
545 |
|
|
#ifndef SMALL |
546 |
|
|
struct imsg_addrinfo imsg_addrinfo; |
547 |
|
|
struct address_proposal *addr_proposal = NULL; |
548 |
|
|
size_t i; |
549 |
|
|
#endif /* SMALL */ |
550 |
|
|
|
551 |
|
|
if (event & EV_READ) { |
552 |
|
|
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) |
553 |
|
|
fatal("imsg_read error"); |
554 |
|
|
if (n == 0) /* Connection closed. */ |
555 |
|
|
shut = 1; |
556 |
|
|
} |
557 |
|
|
if (event & EV_WRITE) { |
558 |
|
|
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) |
559 |
|
|
fatal("msgbuf_write"); |
560 |
|
|
if (n == 0) /* Connection closed. */ |
561 |
|
|
shut = 1; |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
for (;;) { |
565 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
566 |
|
|
fatal("%s: imsg_get error", __func__); |
567 |
|
|
if (n == 0) /* No more messages. */ |
568 |
|
|
break; |
569 |
|
|
|
570 |
|
|
switch (imsg.hdr.type) { |
571 |
|
|
case IMSG_SOCKET_IPC: |
572 |
|
|
/* |
573 |
|
|
* Setup pipe and event handler to the frontend |
574 |
|
|
* process. |
575 |
|
|
*/ |
576 |
|
|
if (iev_frontend) { |
577 |
|
|
log_warnx("%s: received unexpected imsg fd " |
578 |
|
|
"to engine", __func__); |
579 |
|
|
break; |
580 |
|
|
} |
581 |
|
|
if ((fd = imsg.fd) == -1) { |
582 |
|
|
log_warnx("%s: expected to receive imsg fd to " |
583 |
|
|
"engine but didn't receive any", __func__); |
584 |
|
|
break; |
585 |
|
|
} |
586 |
|
|
|
587 |
|
|
iev_frontend = malloc(sizeof(struct imsgev)); |
588 |
|
|
if (iev_frontend == NULL) |
589 |
|
|
fatal(NULL); |
590 |
|
|
|
591 |
|
|
imsg_init(&iev_frontend->ibuf, fd); |
592 |
|
|
iev_frontend->handler = engine_dispatch_frontend; |
593 |
|
|
iev_frontend->events = EV_READ; |
594 |
|
|
|
595 |
|
|
event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, |
596 |
|
|
iev_frontend->events, iev_frontend->handler, |
597 |
|
|
iev_frontend); |
598 |
|
|
event_add(&iev_frontend->ev, NULL); |
599 |
|
|
|
600 |
|
|
if (pledge("stdio flock rpath cpath wpath", NULL) == -1) |
601 |
|
|
fatal("pledge"); |
602 |
|
|
break; |
603 |
|
|
case IMSG_UPDATE_IF: |
604 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
605 |
|
|
sizeof(imsg_ifinfo)) |
606 |
|
|
fatal("%s: IMSG_UPDATE_IF wrong length: %d", |
607 |
|
|
__func__, imsg.hdr.len); |
608 |
|
|
memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); |
609 |
|
|
|
610 |
|
|
iface = get_slaacd_iface_by_id(imsg_ifinfo.if_index); |
611 |
|
|
if (iface == NULL) { |
612 |
|
|
if ((iface = calloc(1, sizeof(*iface))) == NULL) |
613 |
|
|
fatal("calloc"); |
614 |
|
|
evtimer_set(&iface->timer, iface_timeout, |
615 |
|
|
iface); |
616 |
|
|
iface->if_index = imsg_ifinfo.if_index; |
617 |
|
|
iface->running = imsg_ifinfo.running; |
618 |
|
|
if (iface->running) |
619 |
|
|
start_probe(iface); |
620 |
|
|
else |
621 |
|
|
iface->state = IF_DOWN; |
622 |
|
|
iface->autoconfprivacy = |
623 |
|
|
imsg_ifinfo.autoconfprivacy; |
624 |
|
|
memcpy(&iface->hw_address, |
625 |
|
|
&imsg_ifinfo.hw_address, |
626 |
|
|
sizeof(struct ether_addr)); |
627 |
|
|
memcpy(&iface->ll_address, |
628 |
|
|
&imsg_ifinfo.ll_address, |
629 |
|
|
sizeof(struct sockaddr_in6)); |
630 |
|
|
LIST_INIT(&iface->radvs); |
631 |
|
|
LIST_INSERT_HEAD(&slaacd_interfaces, |
632 |
|
|
iface, entries); |
633 |
|
|
LIST_INIT(&iface->addr_proposals); |
634 |
|
|
LIST_INIT(&iface->dfr_proposals); |
635 |
|
|
} else { |
636 |
|
|
int need_refresh = 0; |
637 |
|
|
|
638 |
|
|
if (iface->autoconfprivacy != |
639 |
|
|
imsg_ifinfo.autoconfprivacy) { |
640 |
|
|
iface->autoconfprivacy = |
641 |
|
|
imsg_ifinfo.autoconfprivacy; |
642 |
|
|
need_refresh = 1; |
643 |
|
|
} |
644 |
|
|
if (memcmp(&iface->hw_address, |
645 |
|
|
&imsg_ifinfo.hw_address, |
646 |
|
|
sizeof(struct ether_addr)) != 0) { |
647 |
|
|
memcpy(&iface->hw_address, |
648 |
|
|
&imsg_ifinfo.hw_address, |
649 |
|
|
sizeof(struct ether_addr)); |
650 |
|
|
need_refresh = 1; |
651 |
|
|
} |
652 |
|
|
|
653 |
|
|
if (iface->state != IF_DOWN && |
654 |
|
|
imsg_ifinfo.running && need_refresh) |
655 |
|
|
start_probe(iface); |
656 |
|
|
|
657 |
|
|
iface->running = imsg_ifinfo.running; |
658 |
|
|
if (!iface->running) { |
659 |
|
|
iface->state = IF_DOWN; |
660 |
|
|
if (evtimer_pending(&iface->timer, |
661 |
|
|
NULL)) |
662 |
|
|
evtimer_del(&iface->timer); |
663 |
|
|
} |
664 |
|
|
|
665 |
|
|
memcpy(&iface->ll_address, |
666 |
|
|
&imsg_ifinfo.ll_address, |
667 |
|
|
sizeof(struct sockaddr_in6)); |
668 |
|
|
} |
669 |
|
|
break; |
670 |
|
|
#ifndef SMALL |
671 |
|
|
case IMSG_UPDATE_ADDRESS: |
672 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + |
673 |
|
|
sizeof(imsg_addrinfo)) |
674 |
|
|
fatal("%s: IMSG_UPDATE_ADDRESS wrong length: " |
675 |
|
|
"%d", __func__, imsg.hdr.len); |
676 |
|
|
|
677 |
|
|
memcpy(&imsg_addrinfo, imsg.data, |
678 |
|
|
sizeof(imsg_addrinfo)); |
679 |
|
|
|
680 |
|
|
iface = get_slaacd_iface_by_id(imsg_addrinfo.if_index); |
681 |
|
|
if (iface == NULL) |
682 |
|
|
break; |
683 |
|
|
|
684 |
|
|
log_debug("%s: IMSG_UPDATE_ADDRESS", __func__); |
685 |
|
|
|
686 |
|
|
addr_proposal = find_address_proposal_by_addr(iface, |
687 |
|
|
&imsg_addrinfo.addr); |
688 |
|
|
if (addr_proposal) |
689 |
|
|
break; |
690 |
|
|
|
691 |
|
|
if ((addr_proposal = calloc(1, |
692 |
|
|
sizeof(*addr_proposal))) == NULL) |
693 |
|
|
fatal("calloc"); |
694 |
|
|
evtimer_set(&addr_proposal->timer, |
695 |
|
|
address_proposal_timeout, addr_proposal); |
696 |
|
|
addr_proposal->id = ++proposal_id; |
697 |
|
|
addr_proposal->state = PROPOSAL_CONFIGURED; |
698 |
|
|
addr_proposal->vltime = imsg_addrinfo.vltime; |
699 |
|
|
addr_proposal->pltime = imsg_addrinfo.pltime; |
700 |
|
|
addr_proposal->timeout_count = 0; |
701 |
|
|
|
702 |
|
|
timeout_from_lifetime(addr_proposal); |
703 |
|
|
|
704 |
|
|
if (clock_gettime(CLOCK_REALTIME, &addr_proposal->when)) |
705 |
|
|
fatal("clock_gettime"); |
706 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, |
707 |
|
|
&addr_proposal->uptime)) |
708 |
|
|
fatal("clock_gettime"); |
709 |
|
|
addr_proposal->if_index = imsg_addrinfo.if_index; |
710 |
|
|
memcpy(&addr_proposal->hw_address, |
711 |
|
|
&imsg_addrinfo.hw_address, |
712 |
|
|
sizeof(addr_proposal->hw_address)); |
713 |
|
|
addr_proposal->addr = imsg_addrinfo.addr; |
714 |
|
|
addr_proposal->mask = imsg_addrinfo.mask; |
715 |
|
|
addr_proposal->prefix = addr_proposal->addr.sin6_addr; |
716 |
|
|
|
717 |
|
|
for (i = 0; i < sizeof(addr_proposal->prefix.s6_addr) / |
718 |
|
|
sizeof(addr_proposal->prefix.s6_addr[0]); i++) |
719 |
|
|
addr_proposal->prefix.s6_addr[i] &= |
720 |
|
|
addr_proposal->mask.s6_addr[i]; |
721 |
|
|
|
722 |
|
|
addr_proposal->privacy = imsg_addrinfo.privacy; |
723 |
|
|
addr_proposal->prefix_len = |
724 |
|
|
in6_mask2prefixlen(&addr_proposal->mask); |
725 |
|
|
|
726 |
|
|
LIST_INSERT_HEAD(&iface->addr_proposals, |
727 |
|
|
addr_proposal, entries); |
728 |
|
|
|
729 |
|
|
break; |
730 |
|
|
#endif /* SMALL */ |
731 |
|
|
default: |
732 |
|
|
log_debug("%s: unexpected imsg %d", __func__, |
733 |
|
|
imsg.hdr.type); |
734 |
|
|
break; |
735 |
|
|
} |
736 |
|
|
imsg_free(&imsg); |
737 |
|
|
} |
738 |
|
|
if (!shut) |
739 |
|
|
imsg_event_add(iev); |
740 |
|
|
else { |
741 |
|
|
/* This pipe is dead. Remove its event handler. */ |
742 |
|
|
event_del(&iev->ev); |
743 |
|
|
event_loopexit(NULL); |
744 |
|
|
} |
745 |
|
|
} |
746 |
|
|
|
747 |
|
|
#ifndef SMALL |
748 |
|
|
void |
749 |
|
|
send_interface_info(struct slaacd_iface *iface, pid_t pid) |
750 |
|
|
{ |
751 |
|
|
struct ctl_engine_info cei; |
752 |
|
|
struct ctl_engine_info_ra cei_ra; |
753 |
|
|
struct ctl_engine_info_ra_prefix cei_ra_prefix; |
754 |
|
|
struct ctl_engine_info_ra_rdns cei_ra_rdns; |
755 |
|
|
struct ctl_engine_info_ra_dnssl cei_ra_dnssl; |
756 |
|
|
struct ctl_engine_info_address_proposal cei_addr_proposal; |
757 |
|
|
struct ctl_engine_info_dfr_proposal cei_dfr_proposal; |
758 |
|
|
struct radv *ra; |
759 |
|
|
struct radv_prefix *prefix; |
760 |
|
|
struct radv_rdns *rdns; |
761 |
|
|
struct radv_dnssl *dnssl; |
762 |
|
|
struct address_proposal *addr_proposal; |
763 |
|
|
struct dfr_proposal *dfr_proposal; |
764 |
|
|
|
765 |
|
|
memset(&cei, 0, sizeof(cei)); |
766 |
|
|
cei.if_index = iface->if_index; |
767 |
|
|
cei.running = iface->running; |
768 |
|
|
cei.autoconfprivacy = iface->autoconfprivacy; |
769 |
|
|
memcpy(&cei.hw_address, &iface->hw_address, sizeof(struct ether_addr)); |
770 |
|
|
memcpy(&cei.ll_address, &iface->ll_address, |
771 |
|
|
sizeof(struct sockaddr_in6)); |
772 |
|
|
engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, |
773 |
|
|
sizeof(cei)); |
774 |
|
|
LIST_FOREACH(ra, &iface->radvs, entries) { |
775 |
|
|
memset(&cei_ra, 0, sizeof(cei_ra)); |
776 |
|
|
memcpy(&cei_ra.from, &ra->from, sizeof(cei_ra.from)); |
777 |
|
|
memcpy(&cei_ra.when, &ra->when, sizeof(cei_ra.when)); |
778 |
|
|
memcpy(&cei_ra.uptime, &ra->uptime, sizeof(cei_ra.uptime)); |
779 |
|
|
cei_ra.curhoplimit = ra->curhoplimit; |
780 |
|
|
cei_ra.managed = ra->managed; |
781 |
|
|
cei_ra.other = ra->other; |
782 |
|
|
if (strlcpy(cei_ra.rpref, rpref_name[ra->rpref], sizeof( |
783 |
|
|
cei_ra.rpref)) >= sizeof(cei_ra.rpref)) |
784 |
|
|
log_warnx("truncated router preference"); |
785 |
|
|
cei_ra.router_lifetime = ra->router_lifetime; |
786 |
|
|
cei_ra.reachable_time = ra->reachable_time; |
787 |
|
|
cei_ra.retrans_time = ra->retrans_time; |
788 |
|
|
engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO_RA, |
789 |
|
|
pid, &cei_ra, sizeof(cei_ra)); |
790 |
|
|
|
791 |
|
|
LIST_FOREACH(prefix, &ra->prefixes, entries) { |
792 |
|
|
memset(&cei_ra_prefix, 0, sizeof(cei_ra_prefix)); |
793 |
|
|
|
794 |
|
|
cei_ra_prefix.prefix = prefix->prefix; |
795 |
|
|
cei_ra_prefix.prefix_len = prefix->prefix_len; |
796 |
|
|
cei_ra_prefix.onlink = prefix->onlink; |
797 |
|
|
cei_ra_prefix.autonomous = prefix->autonomous; |
798 |
|
|
cei_ra_prefix.vltime = prefix->vltime; |
799 |
|
|
cei_ra_prefix.pltime = prefix->pltime; |
800 |
|
|
engine_imsg_compose_frontend( |
801 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_RA_PREFIX, pid, |
802 |
|
|
&cei_ra_prefix, sizeof(cei_ra_prefix)); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
LIST_FOREACH(rdns, &ra->rdns_servers, entries) { |
806 |
|
|
memset(&cei_ra_rdns, 0, sizeof(cei_ra_rdns)); |
807 |
|
|
memcpy(&cei_ra_rdns.rdns, &rdns->rdns, |
808 |
|
|
sizeof(cei_ra_rdns.rdns)); |
809 |
|
|
cei_ra_rdns.lifetime = ra->rdns_lifetime; |
810 |
|
|
engine_imsg_compose_frontend( |
811 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_RA_RDNS, pid, |
812 |
|
|
&cei_ra_rdns, sizeof(cei_ra_rdns)); |
813 |
|
|
} |
814 |
|
|
|
815 |
|
|
LIST_FOREACH(dnssl, &ra->dnssls, entries) { |
816 |
|
|
memset(&cei_ra_dnssl, 0, sizeof(cei_ra_dnssl)); |
817 |
|
|
memcpy(&cei_ra_dnssl.dnssl, &dnssl->dnssl, |
818 |
|
|
sizeof(cei_ra_dnssl.dnssl)); |
819 |
|
|
cei_ra_dnssl.lifetime = ra->dnssl_lifetime; |
820 |
|
|
engine_imsg_compose_frontend( |
821 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_RA_DNSSL, pid, |
822 |
|
|
&cei_ra_dnssl, sizeof(cei_ra_dnssl)); |
823 |
|
|
} |
824 |
|
|
} |
825 |
|
|
|
826 |
|
|
if (!LIST_EMPTY(&iface->addr_proposals)) |
827 |
|
|
engine_imsg_compose_frontend( |
828 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSALS, pid, NULL, 0); |
829 |
|
|
|
830 |
|
|
LIST_FOREACH(addr_proposal, &iface->addr_proposals, entries) { |
831 |
|
|
memset(&cei_addr_proposal, 0, sizeof(cei_addr_proposal)); |
832 |
|
|
cei_addr_proposal.id = addr_proposal->id; |
833 |
|
|
if(strlcpy(cei_addr_proposal.state, |
834 |
|
|
proposal_state_name[addr_proposal->state], |
835 |
|
|
sizeof(cei_addr_proposal.state)) >= |
836 |
|
|
sizeof(cei_addr_proposal.state)) |
837 |
|
|
log_warnx("truncated state name"); |
838 |
|
|
cei_addr_proposal.next_timeout = addr_proposal->next_timeout; |
839 |
|
|
cei_addr_proposal.timeout_count = addr_proposal->timeout_count; |
840 |
|
|
cei_addr_proposal.when = addr_proposal->when; |
841 |
|
|
cei_addr_proposal.uptime = addr_proposal->uptime; |
842 |
|
|
memcpy(&cei_addr_proposal.addr, &addr_proposal->addr, sizeof( |
843 |
|
|
cei_addr_proposal.addr)); |
844 |
|
|
memcpy(&cei_addr_proposal.prefix, &addr_proposal->prefix, |
845 |
|
|
sizeof(cei_addr_proposal.prefix)); |
846 |
|
|
cei_addr_proposal.prefix_len = addr_proposal->prefix_len; |
847 |
|
|
cei_addr_proposal.privacy = addr_proposal->privacy; |
848 |
|
|
cei_addr_proposal.vltime = addr_proposal->vltime; |
849 |
|
|
cei_addr_proposal.pltime = addr_proposal->pltime; |
850 |
|
|
|
851 |
|
|
engine_imsg_compose_frontend( |
852 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_ADDR_PROPOSAL, pid, |
853 |
|
|
&cei_addr_proposal, sizeof(cei_addr_proposal)); |
854 |
|
|
} |
855 |
|
|
|
856 |
|
|
if (!LIST_EMPTY(&iface->dfr_proposals)) |
857 |
|
|
engine_imsg_compose_frontend( |
858 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSALS, pid, NULL, 0); |
859 |
|
|
|
860 |
|
|
LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) { |
861 |
|
|
memset(&cei_dfr_proposal, 0, sizeof(cei_dfr_proposal)); |
862 |
|
|
cei_dfr_proposal.id = dfr_proposal->id; |
863 |
|
|
if(strlcpy(cei_dfr_proposal.state, |
864 |
|
|
proposal_state_name[dfr_proposal->state], |
865 |
|
|
sizeof(cei_dfr_proposal.state)) >= |
866 |
|
|
sizeof(cei_dfr_proposal.state)) |
867 |
|
|
log_warnx("truncated state name"); |
868 |
|
|
cei_dfr_proposal.next_timeout = dfr_proposal->next_timeout; |
869 |
|
|
cei_dfr_proposal.timeout_count = dfr_proposal->timeout_count; |
870 |
|
|
cei_dfr_proposal.when = dfr_proposal->when; |
871 |
|
|
cei_dfr_proposal.uptime = dfr_proposal->uptime; |
872 |
|
|
memcpy(&cei_dfr_proposal.addr, &dfr_proposal->addr, sizeof( |
873 |
|
|
cei_dfr_proposal.addr)); |
874 |
|
|
cei_dfr_proposal.router_lifetime = |
875 |
|
|
dfr_proposal->router_lifetime; |
876 |
|
|
if(strlcpy(cei_dfr_proposal.rpref, |
877 |
|
|
rpref_name[dfr_proposal->rpref], |
878 |
|
|
sizeof(cei_dfr_proposal.rpref)) >= |
879 |
|
|
sizeof(cei_dfr_proposal.rpref)) |
880 |
|
|
log_warnx("truncated router preference"); |
881 |
|
|
engine_imsg_compose_frontend( |
882 |
|
|
IMSG_CTL_SHOW_INTERFACE_INFO_DFR_PROPOSAL, pid, |
883 |
|
|
&cei_dfr_proposal, sizeof(cei_dfr_proposal)); |
884 |
|
|
} |
885 |
|
|
} |
886 |
|
|
|
887 |
|
|
void |
888 |
|
|
engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index) |
889 |
|
|
{ |
890 |
|
|
struct slaacd_iface *iface; |
891 |
|
|
|
892 |
|
|
switch (imsg->hdr.type) { |
893 |
|
|
case IMSG_CTL_SHOW_INTERFACE_INFO: |
894 |
|
|
if (if_index == 0) { |
895 |
|
|
LIST_FOREACH (iface, &slaacd_interfaces, entries) |
896 |
|
|
send_interface_info(iface, imsg->hdr.pid); |
897 |
|
|
} else { |
898 |
|
|
if ((iface = get_slaacd_iface_by_id(if_index)) != NULL) |
899 |
|
|
send_interface_info(iface, imsg->hdr.pid); |
900 |
|
|
} |
901 |
|
|
engine_imsg_compose_frontend(IMSG_CTL_END, imsg->hdr.pid, NULL, |
902 |
|
|
0); |
903 |
|
|
break; |
904 |
|
|
default: |
905 |
|
|
log_debug("%s: error handling imsg", __func__); |
906 |
|
|
break; |
907 |
|
|
} |
908 |
|
|
} |
909 |
|
|
#endif /* SMALL */ |
910 |
|
|
|
911 |
|
|
struct slaacd_iface* |
912 |
|
|
get_slaacd_iface_by_id(uint32_t if_index) |
913 |
|
|
{ |
914 |
|
|
struct slaacd_iface *iface; |
915 |
|
|
LIST_FOREACH (iface, &slaacd_interfaces, entries) { |
916 |
|
|
if (iface->if_index == if_index) |
917 |
|
|
return (iface); |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
return (NULL); |
921 |
|
|
} |
922 |
|
|
|
923 |
|
|
void |
924 |
|
|
remove_slaacd_iface(uint32_t if_index) |
925 |
|
|
{ |
926 |
|
|
struct slaacd_iface *iface; |
927 |
|
|
struct radv *ra; |
928 |
|
|
struct address_proposal *addr_proposal; |
929 |
|
|
struct dfr_proposal *dfr_proposal; |
930 |
|
|
|
931 |
|
|
iface = get_slaacd_iface_by_id(if_index); |
932 |
|
|
|
933 |
|
|
if (iface == NULL) |
934 |
|
|
return; |
935 |
|
|
|
936 |
|
|
LIST_REMOVE(iface, entries); |
937 |
|
|
while(!LIST_EMPTY(&iface->radvs)) { |
938 |
|
|
ra = LIST_FIRST(&iface->radvs); |
939 |
|
|
LIST_REMOVE(ra, entries); |
940 |
|
|
free_ra(ra); |
941 |
|
|
} |
942 |
|
|
/* XXX inform netcfgd? */ |
943 |
|
|
while(!LIST_EMPTY(&iface->addr_proposals)) { |
944 |
|
|
addr_proposal = LIST_FIRST(&iface->addr_proposals); |
945 |
|
|
LIST_REMOVE(addr_proposal, entries); |
946 |
|
|
free_address_proposal(addr_proposal); |
947 |
|
|
} |
948 |
|
|
while(!LIST_EMPTY(&iface->dfr_proposals)) { |
949 |
|
|
dfr_proposal = LIST_FIRST(&iface->dfr_proposals); |
950 |
|
|
LIST_REMOVE(dfr_proposal, entries); |
951 |
|
|
free_dfr_proposal(dfr_proposal); |
952 |
|
|
} |
953 |
|
|
evtimer_del(&iface->timer); |
954 |
|
|
free(iface); |
955 |
|
|
} |
956 |
|
|
|
957 |
|
|
void |
958 |
|
|
free_ra(struct radv *ra) |
959 |
|
|
{ |
960 |
|
|
struct radv_prefix *prefix; |
961 |
|
|
struct radv_rdns *rdns; |
962 |
|
|
struct radv_dnssl *dnssl; |
963 |
|
|
|
964 |
|
|
if (ra == NULL) |
965 |
|
|
return; |
966 |
|
|
|
967 |
|
|
evtimer_del(&ra->timer); |
968 |
|
|
|
969 |
|
|
while (!LIST_EMPTY(&ra->prefixes)) { |
970 |
|
|
prefix = LIST_FIRST(&ra->prefixes); |
971 |
|
|
LIST_REMOVE(prefix, entries); |
972 |
|
|
free(prefix); |
973 |
|
|
} |
974 |
|
|
|
975 |
|
|
while (!LIST_EMPTY(&ra->rdns_servers)) { |
976 |
|
|
rdns = LIST_FIRST(&ra->rdns_servers); |
977 |
|
|
LIST_REMOVE(rdns, entries); |
978 |
|
|
free(rdns); |
979 |
|
|
} |
980 |
|
|
|
981 |
|
|
while (!LIST_EMPTY(&ra->dnssls)) { |
982 |
|
|
dnssl = LIST_FIRST(&ra->dnssls); |
983 |
|
|
LIST_REMOVE(dnssl, entries); |
984 |
|
|
free(dnssl); |
985 |
|
|
} |
986 |
|
|
|
987 |
|
|
free(ra); |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
void |
991 |
|
|
parse_ra(struct slaacd_iface *iface, struct imsg_ra *ra) |
992 |
|
|
{ |
993 |
|
|
struct nd_router_advert *nd_ra; |
994 |
|
|
struct radv *radv; |
995 |
|
|
struct radv_prefix *prefix; |
996 |
|
|
struct radv_rdns *rdns; |
997 |
|
|
struct radv_dnssl *ra_dnssl; |
998 |
|
|
ssize_t len = ra->len; |
999 |
|
|
const char *hbuf; |
1000 |
|
|
uint8_t *p; |
1001 |
|
|
|
1002 |
|
|
#ifndef SMALL |
1003 |
|
|
if (log_getverbose() > 1) |
1004 |
|
|
debug_log_ra(ra); |
1005 |
|
|
#endif /* SMALL */ |
1006 |
|
|
|
1007 |
|
|
hbuf = sin6_to_str(&ra->from); |
1008 |
|
|
if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) { |
1009 |
|
|
log_warnx("RA from non link local address %s", hbuf); |
1010 |
|
|
return; |
1011 |
|
|
} |
1012 |
|
|
|
1013 |
|
|
if ((size_t)len < sizeof(struct nd_router_advert)) { |
1014 |
|
|
log_warnx("received too short message (%ld) from %s", len, |
1015 |
|
|
hbuf); |
1016 |
|
|
return; |
1017 |
|
|
} |
1018 |
|
|
|
1019 |
|
|
if ((radv = calloc(1, sizeof(*radv))) == NULL) |
1020 |
|
|
fatal("calloc"); |
1021 |
|
|
|
1022 |
|
|
LIST_INIT(&radv->prefixes); |
1023 |
|
|
LIST_INIT(&radv->rdns_servers); |
1024 |
|
|
LIST_INIT(&radv->dnssls); |
1025 |
|
|
|
1026 |
|
|
radv->min_lifetime = UINT32_MAX; |
1027 |
|
|
|
1028 |
|
|
p = ra->packet; |
1029 |
|
|
nd_ra = (struct nd_router_advert *)p; |
1030 |
|
|
len -= sizeof(struct nd_router_advert); |
1031 |
|
|
p += sizeof(struct nd_router_advert); |
1032 |
|
|
|
1033 |
|
|
log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", |
1034 |
|
|
nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len); |
1035 |
|
|
|
1036 |
|
|
if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) { |
1037 |
|
|
log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type, |
1038 |
|
|
hbuf); |
1039 |
|
|
goto err; |
1040 |
|
|
} |
1041 |
|
|
|
1042 |
|
|
if (nd_ra->nd_ra_code != 0) { |
1043 |
|
|
log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code, |
1044 |
|
|
hbuf); |
1045 |
|
|
goto err; |
1046 |
|
|
} |
1047 |
|
|
|
1048 |
|
|
memcpy(&radv->from, &ra->from, sizeof(ra->from)); |
1049 |
|
|
|
1050 |
|
|
if (clock_gettime(CLOCK_REALTIME, &radv->when)) |
1051 |
|
|
fatal("clock_gettime"); |
1052 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &radv->uptime)) |
1053 |
|
|
fatal("clock_gettime"); |
1054 |
|
|
|
1055 |
|
|
radv->curhoplimit = nd_ra->nd_ra_curhoplimit; |
1056 |
|
|
radv->managed = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED; |
1057 |
|
|
radv->other = nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER; |
1058 |
|
|
|
1059 |
|
|
switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) { |
1060 |
|
|
case ND_RA_FLAG_RTPREF_HIGH: |
1061 |
|
|
radv->rpref=HIGH; |
1062 |
|
|
break; |
1063 |
|
|
case ND_RA_FLAG_RTPREF_LOW: |
1064 |
|
|
radv->rpref=LOW; |
1065 |
|
|
break; |
1066 |
|
|
case ND_RA_FLAG_RTPREF_MEDIUM: |
1067 |
|
|
/* fallthrough */ |
1068 |
|
|
default: |
1069 |
|
|
radv->rpref=MEDIUM; |
1070 |
|
|
break; |
1071 |
|
|
} |
1072 |
|
|
radv->router_lifetime = ntohs(nd_ra->nd_ra_router_lifetime); |
1073 |
|
|
if (radv->router_lifetime != 0) |
1074 |
|
|
radv->min_lifetime = radv->router_lifetime; |
1075 |
|
|
radv->reachable_time = ntohl(nd_ra->nd_ra_reachable); |
1076 |
|
|
radv->retrans_time = ntohl(nd_ra->nd_ra_retransmit); |
1077 |
|
|
|
1078 |
|
|
while ((size_t)len >= sizeof(struct nd_opt_hdr)) { |
1079 |
|
|
struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; |
1080 |
|
|
struct nd_opt_prefix_info *prf; |
1081 |
|
|
struct nd_opt_rdnss *rdnss; |
1082 |
|
|
struct nd_opt_dnssl *dnssl; |
1083 |
|
|
struct in6_addr *in6; |
1084 |
|
|
int i; |
1085 |
|
|
char *nssl; |
1086 |
|
|
|
1087 |
|
|
len -= sizeof(struct nd_opt_hdr); |
1088 |
|
|
p += sizeof(struct nd_opt_hdr); |
1089 |
|
|
|
1090 |
|
|
if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { |
1091 |
|
|
log_warnx("invalid option len: %u > %ld", |
1092 |
|
|
nd_opt_hdr->nd_opt_len, len); |
1093 |
|
|
goto err; |
1094 |
|
|
} |
1095 |
|
|
|
1096 |
|
|
switch (nd_opt_hdr->nd_opt_type) { |
1097 |
|
|
case ND_OPT_PREFIX_INFORMATION: |
1098 |
|
|
if (nd_opt_hdr->nd_opt_len != 4) { |
1099 |
|
|
log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " |
1100 |
|
|
"len != 4"); |
1101 |
|
|
goto err; |
1102 |
|
|
} |
1103 |
|
|
|
1104 |
|
|
if ((prefix = calloc(1, sizeof(*prefix))) == NULL) |
1105 |
|
|
fatal("calloc"); |
1106 |
|
|
|
1107 |
|
|
prf = (struct nd_opt_prefix_info*) nd_opt_hdr; |
1108 |
|
|
prefix->prefix = prf->nd_opt_pi_prefix; |
1109 |
|
|
prefix->prefix_len = prf->nd_opt_pi_prefix_len; |
1110 |
|
|
prefix->onlink = prf->nd_opt_pi_flags_reserved & |
1111 |
|
|
ND_OPT_PI_FLAG_ONLINK; |
1112 |
|
|
prefix->autonomous = prf->nd_opt_pi_flags_reserved & |
1113 |
|
|
ND_OPT_PI_FLAG_AUTO; |
1114 |
|
|
prefix->vltime = ntohl(prf->nd_opt_pi_valid_time); |
1115 |
|
|
prefix->pltime = ntohl(prf->nd_opt_pi_preferred_time); |
1116 |
|
|
if (radv->min_lifetime > prefix->pltime) |
1117 |
|
|
radv->min_lifetime = prefix->pltime; |
1118 |
|
|
|
1119 |
|
|
LIST_INSERT_HEAD(&radv->prefixes, prefix, entries); |
1120 |
|
|
|
1121 |
|
|
break; |
1122 |
|
|
|
1123 |
|
|
case ND_OPT_RDNSS: |
1124 |
|
|
if (nd_opt_hdr->nd_opt_len < 3) { |
1125 |
|
|
log_warnx("invalid ND_OPT_RDNSS: len < 24"); |
1126 |
|
|
goto err; |
1127 |
|
|
} |
1128 |
|
|
|
1129 |
|
|
if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { |
1130 |
|
|
log_warnx("invalid ND_OPT_RDNSS: length with" |
1131 |
|
|
"out header is not multiply of 16: %d", |
1132 |
|
|
(nd_opt_hdr->nd_opt_len - 1) * 8); |
1133 |
|
|
goto err; |
1134 |
|
|
} |
1135 |
|
|
|
1136 |
|
|
rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; |
1137 |
|
|
|
1138 |
|
|
radv->rdns_lifetime = ntohl( |
1139 |
|
|
rdnss->nd_opt_rdnss_lifetime); |
1140 |
|
|
if (radv->min_lifetime > radv->rdns_lifetime) |
1141 |
|
|
radv->min_lifetime = radv->rdns_lifetime; |
1142 |
|
|
|
1143 |
|
|
in6 = (struct in6_addr*) (p + 6); |
1144 |
|
|
for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, |
1145 |
|
|
in6++) { |
1146 |
|
|
if((rdns = calloc(1, sizeof(*rdns))) == NULL) |
1147 |
|
|
fatal("calloc"); |
1148 |
|
|
memcpy(&rdns->rdns, in6, sizeof(rdns->rdns)); |
1149 |
|
|
LIST_INSERT_HEAD(&radv->rdns_servers, rdns, |
1150 |
|
|
entries); |
1151 |
|
|
} |
1152 |
|
|
break; |
1153 |
|
|
case ND_OPT_DNSSL: |
1154 |
|
|
if (nd_opt_hdr->nd_opt_len < 2) { |
1155 |
|
|
log_warnx("invalid ND_OPT_DNSSL: len < 16"); |
1156 |
|
|
goto err; |
1157 |
|
|
} |
1158 |
|
|
|
1159 |
|
|
dnssl = (struct nd_opt_dnssl*) nd_opt_hdr; |
1160 |
|
|
|
1161 |
|
|
if ((nssl = parse_dnssl(p + 6, |
1162 |
|
|
(nd_opt_hdr->nd_opt_len - 1) * 8)) == NULL) |
1163 |
|
|
goto err; /* error logging in parse_dnssl */ |
1164 |
|
|
|
1165 |
|
|
if((ra_dnssl = calloc(1, sizeof(*ra_dnssl))) == NULL) |
1166 |
|
|
fatal("calloc"); |
1167 |
|
|
|
1168 |
|
|
radv->dnssl_lifetime = ntohl( |
1169 |
|
|
dnssl->nd_opt_dnssl_lifetime); |
1170 |
|
|
if (radv->min_lifetime > radv->dnssl_lifetime) |
1171 |
|
|
radv->min_lifetime = radv->dnssl_lifetime; |
1172 |
|
|
|
1173 |
|
|
if (strlcpy(ra_dnssl->dnssl, nssl, |
1174 |
|
|
sizeof(ra_dnssl->dnssl)) >= |
1175 |
|
|
sizeof(ra_dnssl->dnssl)) { |
1176 |
|
|
log_warnx("dnssl too long"); |
1177 |
|
|
goto err; |
1178 |
|
|
} |
1179 |
|
|
free(nssl); |
1180 |
|
|
|
1181 |
|
|
LIST_INSERT_HEAD(&radv->dnssls, ra_dnssl, entries); |
1182 |
|
|
|
1183 |
|
|
break; |
1184 |
|
|
case ND_OPT_REDIRECTED_HEADER: |
1185 |
|
|
case ND_OPT_SOURCE_LINKADDR: |
1186 |
|
|
case ND_OPT_TARGET_LINKADDR: |
1187 |
|
|
case ND_OPT_MTU: |
1188 |
|
|
case ND_OPT_ROUTE_INFO: |
1189 |
|
|
#if 0 |
1190 |
|
|
log_debug("\tOption: %u (len: %u) not implemented", |
1191 |
|
|
nd_opt_hdr->nd_opt_type, nd_opt_hdr->nd_opt_len * |
1192 |
|
|
8); |
1193 |
|
|
#endif |
1194 |
|
|
break; |
1195 |
|
|
default: |
1196 |
|
|
log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); |
1197 |
|
|
break; |
1198 |
|
|
|
1199 |
|
|
} |
1200 |
|
|
len -= nd_opt_hdr->nd_opt_len * 8 - 2; |
1201 |
|
|
p += nd_opt_hdr->nd_opt_len * 8 - 2; |
1202 |
|
|
} |
1203 |
|
|
update_iface_ra(iface, radv); |
1204 |
|
|
iface->state = IF_IDLE; |
1205 |
|
|
return; |
1206 |
|
|
|
1207 |
|
|
err: |
1208 |
|
|
free_ra(radv); |
1209 |
|
|
} |
1210 |
|
|
|
1211 |
|
|
void |
1212 |
|
|
gen_addr(struct slaacd_iface *iface, struct radv_prefix *prefix, struct |
1213 |
|
|
address_proposal *addr_proposal, int privacy) |
1214 |
|
|
{ |
1215 |
|
|
struct in6_addr priv_in6; |
1216 |
|
|
|
1217 |
|
|
/* from in6_ifadd() in nd6_rtr.c */ |
1218 |
|
|
/* XXX from in6.h, guarded by #ifdef _KERNEL XXX nonstandard */ |
1219 |
|
|
#define s6_addr32 __u6_addr.__u6_addr32 |
1220 |
|
|
|
1221 |
|
|
/* XXX from in6_ifattach.c */ |
1222 |
|
|
#define EUI64_GBIT 0x01 |
1223 |
|
|
#define EUI64_UBIT 0x02 |
1224 |
|
|
|
1225 |
|
|
if (privacy) { |
1226 |
|
|
arc4random_buf(&priv_in6.s6_addr32[2], 8); |
1227 |
|
|
priv_in6.s6_addr[8] &= ~EUI64_GBIT; /* g bit to "individual" */ |
1228 |
|
|
priv_in6.s6_addr[8] |= EUI64_UBIT; /* u bit to "local" */ |
1229 |
|
|
/* convert EUI64 into IPv6 interface identifier */ |
1230 |
|
|
priv_in6.s6_addr[8] ^= EUI64_UBIT; |
1231 |
|
|
} |
1232 |
|
|
|
1233 |
|
|
in6_prefixlen2mask(&addr_proposal->mask, addr_proposal->prefix_len); |
1234 |
|
|
|
1235 |
|
|
memset(&addr_proposal->addr, 0, sizeof(addr_proposal->addr)); |
1236 |
|
|
|
1237 |
|
|
addr_proposal->addr.sin6_family = AF_INET6; |
1238 |
|
|
addr_proposal->addr.sin6_len = sizeof(addr_proposal->addr); |
1239 |
|
|
|
1240 |
|
|
memcpy(&addr_proposal->addr.sin6_addr, &prefix->prefix, |
1241 |
|
|
sizeof(addr_proposal->addr.sin6_addr)); |
1242 |
|
|
|
1243 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[0] &= |
1244 |
|
|
addr_proposal->mask.s6_addr32[0]; |
1245 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[1] &= |
1246 |
|
|
addr_proposal->mask.s6_addr32[1]; |
1247 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[2] &= |
1248 |
|
|
addr_proposal->mask.s6_addr32[2]; |
1249 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[3] &= |
1250 |
|
|
addr_proposal->mask.s6_addr32[3]; |
1251 |
|
|
|
1252 |
|
|
if (privacy) { |
1253 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[0] |= |
1254 |
|
|
(priv_in6.s6_addr32[0] & ~addr_proposal->mask.s6_addr32[0]); |
1255 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[1] |= |
1256 |
|
|
(priv_in6.s6_addr32[1] & ~addr_proposal->mask.s6_addr32[1]); |
1257 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[2] |= |
1258 |
|
|
(priv_in6.s6_addr32[2] & ~addr_proposal->mask.s6_addr32[2]); |
1259 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[3] |= |
1260 |
|
|
(priv_in6.s6_addr32[3] & ~addr_proposal->mask.s6_addr32[3]); |
1261 |
|
|
} else { |
1262 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[0] |= |
1263 |
|
|
(iface->ll_address.sin6_addr.s6_addr32[0] & |
1264 |
|
|
~addr_proposal->mask.s6_addr32[0]); |
1265 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[1] |= |
1266 |
|
|
(iface->ll_address.sin6_addr.s6_addr32[1] & |
1267 |
|
|
~addr_proposal->mask.s6_addr32[1]); |
1268 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[2] |= |
1269 |
|
|
(iface->ll_address.sin6_addr.s6_addr32[2] & |
1270 |
|
|
~addr_proposal->mask.s6_addr32[2]); |
1271 |
|
|
addr_proposal->addr.sin6_addr.s6_addr32[3] |= |
1272 |
|
|
(iface->ll_address.sin6_addr.s6_addr32[3] & |
1273 |
|
|
~addr_proposal->mask.s6_addr32[3]); |
1274 |
|
|
} |
1275 |
|
|
|
1276 |
|
|
#undef s6_addr32 |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
|
/* from sys/netinet6/in6.c */ |
1280 |
|
|
void |
1281 |
|
|
in6_prefixlen2mask(struct in6_addr *maskp, int len) |
1282 |
|
|
{ |
1283 |
|
|
u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; |
1284 |
|
|
int bytelen, bitlen, i; |
1285 |
|
|
|
1286 |
|
|
if (0 > len || len > 128) |
1287 |
|
|
fatal("%s: invalid prefix length(%d)\n", __func__, len); |
1288 |
|
|
|
1289 |
|
|
bzero(maskp, sizeof(*maskp)); |
1290 |
|
|
bytelen = len / 8; |
1291 |
|
|
bitlen = len % 8; |
1292 |
|
|
for (i = 0; i < bytelen; i++) |
1293 |
|
|
maskp->s6_addr[i] = 0xff; |
1294 |
|
|
/* len == 128 is ok because bitlen == 0 then */ |
1295 |
|
|
if (bitlen) |
1296 |
|
|
maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; |
1297 |
|
|
} |
1298 |
|
|
|
1299 |
|
|
#ifndef SMALL |
1300 |
|
|
/* from kame via ifconfig, where it's called prefix() */ |
1301 |
|
|
int |
1302 |
|
|
in6_mask2prefixlen(struct in6_addr *in6) |
1303 |
|
|
{ |
1304 |
|
|
u_char *nam = (u_char *)in6; |
1305 |
|
|
int byte, bit, plen = 0, size = sizeof(struct in6_addr); |
1306 |
|
|
|
1307 |
|
|
for (byte = 0; byte < size; byte++, plen += 8) |
1308 |
|
|
if (nam[byte] != 0xff) |
1309 |
|
|
break; |
1310 |
|
|
if (byte == size) |
1311 |
|
|
return (plen); |
1312 |
|
|
for (bit = 7; bit != 0; bit--, plen++) |
1313 |
|
|
if (!(nam[byte] & (1 << bit))) |
1314 |
|
|
break; |
1315 |
|
|
for (; bit != 0; bit--) |
1316 |
|
|
if (nam[byte] & (1 << bit)) |
1317 |
|
|
return (0); |
1318 |
|
|
byte++; |
1319 |
|
|
for (; byte < size; byte++) |
1320 |
|
|
if (nam[byte]) |
1321 |
|
|
return (0); |
1322 |
|
|
return (plen); |
1323 |
|
|
} |
1324 |
|
|
|
1325 |
|
|
void |
1326 |
|
|
debug_log_ra(struct imsg_ra *ra) |
1327 |
|
|
{ |
1328 |
|
|
struct nd_router_advert *nd_ra; |
1329 |
|
|
ssize_t len = ra->len; |
1330 |
|
|
char ntopbuf[INET6_ADDRSTRLEN]; |
1331 |
|
|
const char *hbuf; |
1332 |
|
|
uint8_t *p; |
1333 |
|
|
|
1334 |
|
|
hbuf = sin6_to_str(&ra->from); |
1335 |
|
|
|
1336 |
|
|
if (!IN6_IS_ADDR_LINKLOCAL(&ra->from.sin6_addr)) { |
1337 |
|
|
log_warnx("RA from non link local address %s", hbuf); |
1338 |
|
|
return; |
1339 |
|
|
} |
1340 |
|
|
|
1341 |
|
|
if ((size_t)len < sizeof(struct nd_router_advert)) { |
1342 |
|
|
log_warnx("received too short message (%ld) from %s", len, |
1343 |
|
|
hbuf); |
1344 |
|
|
return; |
1345 |
|
|
} |
1346 |
|
|
|
1347 |
|
|
p = ra->packet; |
1348 |
|
|
nd_ra = (struct nd_router_advert *)p; |
1349 |
|
|
len -= sizeof(struct nd_router_advert); |
1350 |
|
|
p += sizeof(struct nd_router_advert); |
1351 |
|
|
|
1352 |
|
|
log_debug("ICMPv6 type(%d), code(%d) from %s of length %ld", |
1353 |
|
|
nd_ra->nd_ra_type, nd_ra->nd_ra_code, hbuf, len); |
1354 |
|
|
|
1355 |
|
|
if (nd_ra->nd_ra_type != ND_ROUTER_ADVERT) { |
1356 |
|
|
log_warnx("invalid ICMPv6 type (%d) from %s", nd_ra->nd_ra_type, |
1357 |
|
|
hbuf); |
1358 |
|
|
return; |
1359 |
|
|
} |
1360 |
|
|
|
1361 |
|
|
if (nd_ra->nd_ra_code != 0) { |
1362 |
|
|
log_warnx("invalid ICMPv6 code (%d) from %s", nd_ra->nd_ra_code, |
1363 |
|
|
hbuf); |
1364 |
|
|
return; |
1365 |
|
|
} |
1366 |
|
|
|
1367 |
|
|
log_debug("---"); |
1368 |
|
|
log_debug("RA from %s", hbuf); |
1369 |
|
|
log_debug("\tCur Hop Limit: %u", nd_ra->nd_ra_curhoplimit); |
1370 |
|
|
log_debug("\tManaged address configuration: %d", |
1371 |
|
|
(nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) ? 1 : 0); |
1372 |
|
|
log_debug("\tOther configuration: %d", |
1373 |
|
|
(nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) ? 1 : 0); |
1374 |
|
|
switch (nd_ra->nd_ra_flags_reserved & ND_RA_FLAG_RTPREF_MASK) { |
1375 |
|
|
case ND_RA_FLAG_RTPREF_HIGH: |
1376 |
|
|
log_debug("\tRouter Preference: high"); |
1377 |
|
|
break; |
1378 |
|
|
case ND_RA_FLAG_RTPREF_MEDIUM: |
1379 |
|
|
log_debug("\tRouter Preference: medium"); |
1380 |
|
|
break; |
1381 |
|
|
case ND_RA_FLAG_RTPREF_LOW: |
1382 |
|
|
log_debug("\tRouter Preference: low"); |
1383 |
|
|
break; |
1384 |
|
|
case ND_RA_FLAG_RTPREF_RSV: |
1385 |
|
|
log_debug("\tRouter Preference: reserved"); |
1386 |
|
|
break; |
1387 |
|
|
} |
1388 |
|
|
log_debug("\tRouter Lifetime: %hds", |
1389 |
|
|
ntohs(nd_ra->nd_ra_router_lifetime)); |
1390 |
|
|
log_debug("\tReachable Time: %ums", ntohl(nd_ra->nd_ra_reachable)); |
1391 |
|
|
log_debug("\tRetrans Timer: %ums", ntohl(nd_ra->nd_ra_retransmit)); |
1392 |
|
|
|
1393 |
|
|
while ((size_t)len >= sizeof(struct nd_opt_hdr)) { |
1394 |
|
|
struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p; |
1395 |
|
|
struct nd_opt_mtu *mtu; |
1396 |
|
|
struct nd_opt_prefix_info *prf; |
1397 |
|
|
struct nd_opt_rdnss *rdnss; |
1398 |
|
|
struct nd_opt_dnssl *dnssl; |
1399 |
|
|
struct in6_addr *in6; |
1400 |
|
|
int i; |
1401 |
|
|
char *nssl; |
1402 |
|
|
|
1403 |
|
|
len -= sizeof(struct nd_opt_hdr); |
1404 |
|
|
p += sizeof(struct nd_opt_hdr); |
1405 |
|
|
if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) { |
1406 |
|
|
log_warnx("invalid option len: %u > %ld", |
1407 |
|
|
nd_opt_hdr->nd_opt_len, len); |
1408 |
|
|
return; |
1409 |
|
|
} |
1410 |
|
|
log_debug("\tOption: %u (len: %u)", nd_opt_hdr->nd_opt_type, |
1411 |
|
|
nd_opt_hdr->nd_opt_len * 8); |
1412 |
|
|
switch (nd_opt_hdr->nd_opt_type) { |
1413 |
|
|
case ND_OPT_SOURCE_LINKADDR: |
1414 |
|
|
if (nd_opt_hdr->nd_opt_len == 1) |
1415 |
|
|
log_debug("\t\tND_OPT_SOURCE_LINKADDR: " |
1416 |
|
|
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", |
1417 |
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], |
1418 |
|
|
p[7]); |
1419 |
|
|
else |
1420 |
|
|
log_debug("\t\tND_OPT_SOURCE_LINKADDR"); |
1421 |
|
|
break; |
1422 |
|
|
case ND_OPT_TARGET_LINKADDR: |
1423 |
|
|
if (nd_opt_hdr->nd_opt_len == 1) |
1424 |
|
|
log_debug("\t\tND_OPT_TARGET_LINKADDR: " |
1425 |
|
|
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", |
1426 |
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], |
1427 |
|
|
p[7]); |
1428 |
|
|
else |
1429 |
|
|
log_debug("\t\tND_OPT_TARGET_LINKADDR"); |
1430 |
|
|
break; |
1431 |
|
|
case ND_OPT_PREFIX_INFORMATION: |
1432 |
|
|
if (nd_opt_hdr->nd_opt_len != 4) { |
1433 |
|
|
log_warnx("invalid ND_OPT_PREFIX_INFORMATION: " |
1434 |
|
|
"len != 4"); |
1435 |
|
|
return; |
1436 |
|
|
} |
1437 |
|
|
prf = (struct nd_opt_prefix_info*) nd_opt_hdr; |
1438 |
|
|
|
1439 |
|
|
log_debug("\t\tND_OPT_PREFIX_INFORMATION: %s/%u", |
1440 |
|
|
inet_ntop(AF_INET6, &prf->nd_opt_pi_prefix, |
1441 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
1442 |
|
|
prf->nd_opt_pi_prefix_len); |
1443 |
|
|
log_debug("\t\t\tOn-link: %d", |
1444 |
|
|
prf->nd_opt_pi_flags_reserved & |
1445 |
|
|
ND_OPT_PI_FLAG_ONLINK ? 1:0); |
1446 |
|
|
log_debug("\t\t\tAutonomous address-configuration: %d", |
1447 |
|
|
prf->nd_opt_pi_flags_reserved & |
1448 |
|
|
ND_OPT_PI_FLAG_AUTO ? 1 : 0); |
1449 |
|
|
log_debug("\t\t\tvltime: %u", |
1450 |
|
|
ntohl(prf->nd_opt_pi_valid_time)); |
1451 |
|
|
log_debug("\t\t\tpltime: %u", |
1452 |
|
|
ntohl(prf->nd_opt_pi_preferred_time)); |
1453 |
|
|
break; |
1454 |
|
|
case ND_OPT_REDIRECTED_HEADER: |
1455 |
|
|
log_debug("\t\tND_OPT_REDIRECTED_HEADER"); |
1456 |
|
|
break; |
1457 |
|
|
case ND_OPT_MTU: |
1458 |
|
|
if (nd_opt_hdr->nd_opt_len != 1) { |
1459 |
|
|
log_warnx("invalid ND_OPT_MTU: len != 1"); |
1460 |
|
|
return; |
1461 |
|
|
} |
1462 |
|
|
mtu = (struct nd_opt_mtu*) nd_opt_hdr; |
1463 |
|
|
log_debug("\t\tND_OPT_MTU: %u", |
1464 |
|
|
ntohl(mtu->nd_opt_mtu_mtu)); |
1465 |
|
|
break; |
1466 |
|
|
case ND_OPT_ROUTE_INFO: |
1467 |
|
|
log_debug("\t\tND_OPT_ROUTE_INFO"); |
1468 |
|
|
break; |
1469 |
|
|
case ND_OPT_RDNSS: |
1470 |
|
|
if (nd_opt_hdr->nd_opt_len < 3) { |
1471 |
|
|
log_warnx("invalid ND_OPT_RDNSS: len < 24"); |
1472 |
|
|
return; |
1473 |
|
|
} |
1474 |
|
|
if ((nd_opt_hdr->nd_opt_len - 1) % 2 != 0) { |
1475 |
|
|
log_warnx("invalid ND_OPT_RDNSS: length with" |
1476 |
|
|
"out header is not multiply of 16: %d", |
1477 |
|
|
(nd_opt_hdr->nd_opt_len - 1) * 8); |
1478 |
|
|
return; |
1479 |
|
|
} |
1480 |
|
|
rdnss = (struct nd_opt_rdnss*) nd_opt_hdr; |
1481 |
|
|
log_debug("\t\tND_OPT_RDNSS: lifetime: %u", ntohl( |
1482 |
|
|
rdnss->nd_opt_rdnss_lifetime)); |
1483 |
|
|
in6 = (struct in6_addr*) (p + 6); |
1484 |
|
|
for (i=0; i < (nd_opt_hdr->nd_opt_len - 1)/2; i++, |
1485 |
|
|
in6++) { |
1486 |
|
|
log_debug("\t\t\t%s", inet_ntop(AF_INET6, in6, |
1487 |
|
|
ntopbuf, INET6_ADDRSTRLEN)); |
1488 |
|
|
} |
1489 |
|
|
break; |
1490 |
|
|
case ND_OPT_DNSSL: |
1491 |
|
|
if (nd_opt_hdr->nd_opt_len < 2) { |
1492 |
|
|
log_warnx("invalid ND_OPT_DNSSL: len < 16"); |
1493 |
|
|
return; |
1494 |
|
|
} |
1495 |
|
|
dnssl = (struct nd_opt_dnssl*) nd_opt_hdr; |
1496 |
|
|
nssl = parse_dnssl(p + 6, (nd_opt_hdr->nd_opt_len - 1) |
1497 |
|
|
* 8); |
1498 |
|
|
|
1499 |
|
|
if (nssl == NULL) |
1500 |
|
|
return; |
1501 |
|
|
|
1502 |
|
|
log_debug("\t\tND_OPT_DNSSL: lifetime: %u", ntohl( |
1503 |
|
|
dnssl->nd_opt_dnssl_lifetime)); |
1504 |
|
|
log_debug("\t\t\tsearch: %s", nssl); |
1505 |
|
|
|
1506 |
|
|
free(nssl); |
1507 |
|
|
break; |
1508 |
|
|
default: |
1509 |
|
|
log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type); |
1510 |
|
|
break; |
1511 |
|
|
|
1512 |
|
|
} |
1513 |
|
|
len -= nd_opt_hdr->nd_opt_len * 8 - 2; |
1514 |
|
|
p += nd_opt_hdr->nd_opt_len * 8 - 2; |
1515 |
|
|
} |
1516 |
|
|
} |
1517 |
|
|
#endif /* SMALL */ |
1518 |
|
|
|
1519 |
|
|
char* |
1520 |
|
|
parse_dnssl(char* data, int datalen) |
1521 |
|
|
{ |
1522 |
|
|
int len, pos; |
1523 |
|
|
char *nssl, *nsslp; |
1524 |
|
|
|
1525 |
|
|
if((nssl = calloc(1, datalen + 1)) == NULL) { |
1526 |
|
|
log_warn("malloc"); |
1527 |
|
|
return NULL; |
1528 |
|
|
} |
1529 |
|
|
nsslp = nssl; |
1530 |
|
|
|
1531 |
|
|
pos = 0; |
1532 |
|
|
|
1533 |
|
|
do { |
1534 |
|
|
len = data[pos]; |
1535 |
|
|
if (len > 63 || len + pos + 1 > datalen) { |
1536 |
|
|
free(nssl); |
1537 |
|
|
log_warnx("invalid label in DNSSL"); |
1538 |
|
|
return NULL; |
1539 |
|
|
} |
1540 |
|
|
if (len == 0) { |
1541 |
|
|
if (pos < datalen && data[pos + 1] != 0) |
1542 |
|
|
*nsslp++ = ' '; /* seperator for next domain */ |
1543 |
|
|
else |
1544 |
|
|
break; |
1545 |
|
|
} else { |
1546 |
|
|
if (pos != 0 && data[pos - 1] != 0) /* no . at front */ |
1547 |
|
|
*nsslp++ = '.'; |
1548 |
|
|
memcpy(nsslp, data + pos + 1, len); |
1549 |
|
|
nsslp += len; |
1550 |
|
|
} |
1551 |
|
|
pos += len + 1; |
1552 |
|
|
} while(pos < datalen); |
1553 |
|
|
if (len != 0) { |
1554 |
|
|
free(nssl); |
1555 |
|
|
log_warnx("invalid label in DNSSL"); |
1556 |
|
|
return NULL; |
1557 |
|
|
} |
1558 |
|
|
return nssl; |
1559 |
|
|
} |
1560 |
|
|
|
1561 |
|
|
void update_iface_ra(struct slaacd_iface *iface, struct radv *ra) |
1562 |
|
|
{ |
1563 |
|
|
struct radv *old_ra; |
1564 |
|
|
struct radv_prefix *prefix; |
1565 |
|
|
struct address_proposal *addr_proposal; |
1566 |
|
|
struct dfr_proposal *dfr_proposal, *tmp; |
1567 |
|
|
uint32_t remaining_lifetime; |
1568 |
|
|
int found, found_privacy; |
1569 |
|
|
const char *hbuf; |
1570 |
|
|
|
1571 |
|
|
if ((old_ra = find_ra(iface, &ra->from)) == NULL) |
1572 |
|
|
LIST_INSERT_HEAD(&iface->radvs, ra, entries); |
1573 |
|
|
else { |
1574 |
|
|
LIST_REPLACE(old_ra, ra, entries); |
1575 |
|
|
free_ra(old_ra); |
1576 |
|
|
} |
1577 |
|
|
if (ra->router_lifetime == 0) { |
1578 |
|
|
LIST_FOREACH_SAFE(dfr_proposal, &iface->dfr_proposals, entries, |
1579 |
|
|
tmp) { |
1580 |
|
|
if (memcmp(&dfr_proposal->addr, |
1581 |
|
|
&ra->from, sizeof(struct sockaddr_in6)) == |
1582 |
|
|
0) { |
1583 |
|
|
free_dfr_proposal(dfr_proposal); |
1584 |
|
|
} |
1585 |
|
|
} |
1586 |
|
|
} else { |
1587 |
|
|
found = 0; |
1588 |
|
|
LIST_FOREACH(dfr_proposal, &iface->dfr_proposals, entries) { |
1589 |
|
|
if (memcmp(&dfr_proposal->addr, |
1590 |
|
|
&ra->from, sizeof(struct sockaddr_in6)) == |
1591 |
|
|
0) { |
1592 |
|
|
found = 1; |
1593 |
|
|
if (real_lifetime(&dfr_proposal->uptime, |
1594 |
|
|
dfr_proposal->router_lifetime) > |
1595 |
|
|
ra->router_lifetime) |
1596 |
|
|
log_warnx("ignoring router " |
1597 |
|
|
"advertisement that lowers router " |
1598 |
|
|
"lifetime"); |
1599 |
|
|
else { |
1600 |
|
|
dfr_proposal->when = ra->when; |
1601 |
|
|
dfr_proposal->uptime = ra->uptime; |
1602 |
|
|
dfr_proposal->router_lifetime = |
1603 |
|
|
ra->router_lifetime; |
1604 |
|
|
|
1605 |
|
|
log_debug("%s, dfr state: %s, rl: %d", |
1606 |
|
|
__func__, proposal_state_name[ |
1607 |
|
|
dfr_proposal->state], |
1608 |
|
|
real_lifetime(&dfr_proposal->uptime, |
1609 |
|
|
dfr_proposal->router_lifetime)); |
1610 |
|
|
|
1611 |
|
|
switch (dfr_proposal->state) { |
1612 |
|
|
case PROPOSAL_CONFIGURED: |
1613 |
|
|
case PROPOSAL_NEARLY_EXPIRED: |
1614 |
|
|
log_debug("updating dfr"); |
1615 |
|
|
configure_dfr(dfr_proposal); |
1616 |
|
|
break; |
1617 |
|
|
default: |
1618 |
|
|
hbuf = sin6_to_str( |
1619 |
|
|
&dfr_proposal->addr); |
1620 |
|
|
log_debug("%s: iface %d: %s", |
1621 |
|
|
__func__, iface->if_index, |
1622 |
|
|
hbuf); |
1623 |
|
|
break; |
1624 |
|
|
} |
1625 |
|
|
} |
1626 |
|
|
|
1627 |
|
|
break; |
1628 |
|
|
} |
1629 |
|
|
} |
1630 |
|
|
if (!found) |
1631 |
|
|
/* new proposal */ |
1632 |
|
|
gen_dfr_proposal(iface, ra); |
1633 |
|
|
|
1634 |
|
|
LIST_FOREACH(prefix, &ra->prefixes, entries) { |
1635 |
|
|
if (!prefix->autonomous || prefix->vltime == 0 || |
1636 |
|
|
prefix->pltime > prefix->vltime || |
1637 |
|
|
prefix->prefix_len != 64 || |
1638 |
|
|
IN6_IS_ADDR_LINKLOCAL(&prefix->prefix)) |
1639 |
|
|
continue; |
1640 |
|
|
found = 0; |
1641 |
|
|
found_privacy = 0; |
1642 |
|
|
LIST_FOREACH(addr_proposal, &iface->addr_proposals, |
1643 |
|
|
entries) { |
1644 |
|
|
if (prefix->prefix_len == |
1645 |
|
|
addr_proposal-> prefix_len && |
1646 |
|
|
memcmp(&prefix->prefix, |
1647 |
|
|
&addr_proposal->prefix, |
1648 |
|
|
sizeof(struct in6_addr)) != 0) |
1649 |
|
|
continue; |
1650 |
|
|
|
1651 |
|
|
if (memcmp(&addr_proposal->hw_address, |
1652 |
|
|
&iface->hw_address, |
1653 |
|
|
sizeof(addr_proposal->hw_address)) != 0) |
1654 |
|
|
continue; |
1655 |
|
|
|
1656 |
|
|
if (addr_proposal->privacy) { |
1657 |
|
|
/* |
1658 |
|
|
* create new privacy address if old |
1659 |
|
|
* expires |
1660 |
|
|
*/ |
1661 |
|
|
if (addr_proposal->state != |
1662 |
|
|
PROPOSAL_NEARLY_EXPIRED) |
1663 |
|
|
found_privacy = 1; |
1664 |
|
|
|
1665 |
|
|
if (!iface->autoconfprivacy) |
1666 |
|
|
log_debug("%s XXX need to " |
1667 |
|
|
"remove privacy address", |
1668 |
|
|
__func__); |
1669 |
|
|
|
1670 |
|
|
log_debug("%s, privacy addr state: %s", |
1671 |
|
|
__func__, proposal_state_name[ |
1672 |
|
|
addr_proposal->state]); |
1673 |
|
|
|
1674 |
|
|
/* privacy addresses just expire */ |
1675 |
|
|
continue; |
1676 |
|
|
} |
1677 |
|
|
|
1678 |
|
|
found = 1; |
1679 |
|
|
|
1680 |
|
|
remaining_lifetime = |
1681 |
|
|
real_lifetime(&addr_proposal->uptime, |
1682 |
|
|
addr_proposal->vltime); |
1683 |
|
|
|
1684 |
|
|
addr_proposal->when = ra->when; |
1685 |
|
|
addr_proposal->uptime = ra->uptime; |
1686 |
|
|
|
1687 |
|
|
/* RFC 4862 5.5.3 two hours rule */ |
1688 |
|
|
#define TWO_HOURS 2 * 3600 |
1689 |
|
|
if (prefix->vltime > TWO_HOURS || |
1690 |
|
|
prefix->vltime > remaining_lifetime) |
1691 |
|
|
addr_proposal->vltime = prefix->vltime; |
1692 |
|
|
else |
1693 |
|
|
addr_proposal->vltime = TWO_HOURS; |
1694 |
|
|
addr_proposal->pltime = prefix->pltime; |
1695 |
|
|
|
1696 |
|
|
log_debug("%s, addr state: %s", __func__, |
1697 |
|
|
proposal_state_name[addr_proposal->state]); |
1698 |
|
|
|
1699 |
|
|
switch (addr_proposal->state) { |
1700 |
|
|
case PROPOSAL_CONFIGURED: |
1701 |
|
|
case PROPOSAL_NEARLY_EXPIRED: |
1702 |
|
|
log_debug("updating address"); |
1703 |
|
|
configure_address(addr_proposal); |
1704 |
|
|
break; |
1705 |
|
|
default: |
1706 |
|
|
hbuf = sin6_to_str(&addr_proposal-> |
1707 |
|
|
addr); |
1708 |
|
|
log_debug("%s: iface %d: %s", __func__, |
1709 |
|
|
iface->if_index, hbuf); |
1710 |
|
|
break; |
1711 |
|
|
} |
1712 |
|
|
} |
1713 |
|
|
|
1714 |
|
|
if (!found) |
1715 |
|
|
/* new proposal */ |
1716 |
|
|
gen_address_proposal(iface, ra, prefix, 0); |
1717 |
|
|
|
1718 |
|
|
if (!found_privacy && iface->autoconfprivacy) { |
1719 |
|
|
if (prefix->pltime < |
1720 |
|
|
ND6_PRIV_MAX_DESYNC_FACTOR) { |
1721 |
|
|
hbuf = sin6_to_str(&ra->from); |
1722 |
|
|
log_warnx("%s: pltime from %s is too " |
1723 |
|
|
"small: %d < %d; not generating " |
1724 |
|
|
"privacy address", __func__, hbuf, |
1725 |
|
|
prefix->pltime, |
1726 |
|
|
ND6_PRIV_MAX_DESYNC_FACTOR); |
1727 |
|
|
} else |
1728 |
|
|
/* new privacy proposal */ |
1729 |
|
|
gen_address_proposal(iface, ra, prefix, |
1730 |
|
|
1); |
1731 |
|
|
} |
1732 |
|
|
} |
1733 |
|
|
} |
1734 |
|
|
} |
1735 |
|
|
|
1736 |
|
|
void |
1737 |
|
|
timeout_from_lifetime(struct address_proposal *addr_proposal) |
1738 |
|
|
{ |
1739 |
|
|
struct timeval tv; |
1740 |
|
|
time_t lifetime; |
1741 |
|
|
|
1742 |
|
|
addr_proposal->next_timeout = 0; |
1743 |
|
|
|
1744 |
|
|
if (addr_proposal->pltime > MAX_RTR_SOLICITATIONS * |
1745 |
|
|
(RTR_SOLICITATION_INTERVAL + 1)) |
1746 |
|
|
lifetime = addr_proposal->pltime; |
1747 |
|
|
else |
1748 |
|
|
lifetime = addr_proposal->vltime; |
1749 |
|
|
|
1750 |
|
|
if (lifetime > MAX_RTR_SOLICITATIONS * |
1751 |
|
|
(RTR_SOLICITATION_INTERVAL + 1)) { |
1752 |
|
|
addr_proposal->next_timeout = lifetime - MAX_RTR_SOLICITATIONS * |
1753 |
|
|
(RTR_SOLICITATION_INTERVAL + 1); |
1754 |
|
|
tv.tv_sec = addr_proposal->next_timeout; |
1755 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
1756 |
|
|
evtimer_add(&addr_proposal->timer, &tv); |
1757 |
|
|
log_debug("%s: %d, scheduling new timeout in %llds.%06ld", |
1758 |
|
|
__func__, addr_proposal->if_index, tv.tv_sec, tv.tv_usec); |
1759 |
|
|
} |
1760 |
|
|
} |
1761 |
|
|
|
1762 |
|
|
void |
1763 |
|
|
configure_address(struct address_proposal *addr_proposal) |
1764 |
|
|
{ |
1765 |
|
|
struct imsg_configure_address address; |
1766 |
|
|
|
1767 |
|
|
timeout_from_lifetime(addr_proposal); |
1768 |
|
|
addr_proposal->state = PROPOSAL_CONFIGURED; |
1769 |
|
|
|
1770 |
|
|
log_debug("%s: %d", __func__, addr_proposal->if_index); |
1771 |
|
|
|
1772 |
|
|
address.if_index = addr_proposal->if_index; |
1773 |
|
|
memcpy(&address.addr, &addr_proposal->addr, sizeof(address.addr)); |
1774 |
|
|
memcpy(&address.mask, &addr_proposal->mask, sizeof(address.mask)); |
1775 |
|
|
address.vltime = addr_proposal->vltime; |
1776 |
|
|
address.pltime = addr_proposal->pltime; |
1777 |
|
|
address.privacy = addr_proposal->privacy; |
1778 |
|
|
|
1779 |
|
|
engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, |
1780 |
|
|
sizeof(address)); |
1781 |
|
|
} |
1782 |
|
|
|
1783 |
|
|
void |
1784 |
|
|
gen_address_proposal(struct slaacd_iface *iface, struct radv *ra, struct |
1785 |
|
|
radv_prefix *prefix, int privacy) |
1786 |
|
|
{ |
1787 |
|
|
struct address_proposal *addr_proposal; |
1788 |
|
|
struct timeval tv; |
1789 |
|
|
const char *hbuf; |
1790 |
|
|
|
1791 |
|
|
if ((addr_proposal = calloc(1, sizeof(*addr_proposal))) == NULL) |
1792 |
|
|
fatal("calloc"); |
1793 |
|
|
evtimer_set(&addr_proposal->timer, address_proposal_timeout, |
1794 |
|
|
addr_proposal); |
1795 |
|
|
addr_proposal->next_timeout = 1; |
1796 |
|
|
addr_proposal->timeout_count = 0; |
1797 |
|
|
addr_proposal->state = PROPOSAL_NOT_CONFIGURED; |
1798 |
|
|
addr_proposal->when = ra->when; |
1799 |
|
|
addr_proposal->uptime = ra->uptime; |
1800 |
|
|
addr_proposal->if_index = iface->if_index; |
1801 |
|
|
memcpy(&addr_proposal->hw_address, &iface->hw_address, |
1802 |
|
|
sizeof(addr_proposal->hw_address)); |
1803 |
|
|
addr_proposal->privacy = privacy; |
1804 |
|
|
memcpy(&addr_proposal->prefix, &prefix->prefix, |
1805 |
|
|
sizeof(addr_proposal->prefix)); |
1806 |
|
|
addr_proposal->prefix_len = prefix->prefix_len; |
1807 |
|
|
|
1808 |
|
|
if (privacy) { |
1809 |
|
|
if (prefix->vltime > ND6_PRIV_VALID_LIFETIME) |
1810 |
|
|
addr_proposal->vltime = ND6_PRIV_VALID_LIFETIME; |
1811 |
|
|
else |
1812 |
|
|
addr_proposal->vltime = prefix->vltime; |
1813 |
|
|
|
1814 |
|
|
if (prefix->pltime > ND6_PRIV_PREFERRED_LIFETIME) |
1815 |
|
|
addr_proposal->pltime = ND6_PRIV_PREFERRED_LIFETIME |
1816 |
|
|
- arc4random_uniform(ND6_PRIV_MAX_DESYNC_FACTOR); |
1817 |
|
|
else |
1818 |
|
|
addr_proposal->pltime = prefix->pltime; |
1819 |
|
|
} else { |
1820 |
|
|
addr_proposal->vltime = prefix->vltime; |
1821 |
|
|
addr_proposal->pltime = prefix->pltime; |
1822 |
|
|
} |
1823 |
|
|
|
1824 |
|
|
gen_addr(iface, prefix, addr_proposal, privacy); |
1825 |
|
|
|
1826 |
|
|
tv.tv_sec = 0; |
1827 |
|
|
tv.tv_usec = 0; |
1828 |
|
|
evtimer_add(&addr_proposal->timer, &tv); |
1829 |
|
|
|
1830 |
|
|
LIST_INSERT_HEAD(&iface->addr_proposals, addr_proposal, entries); |
1831 |
|
|
|
1832 |
|
|
hbuf = sin6_to_str(&addr_proposal->addr); |
1833 |
|
|
log_debug("%s: iface %d: %s: %lld s", __func__, |
1834 |
|
|
iface->if_index, hbuf, tv.tv_sec); |
1835 |
|
|
} |
1836 |
|
|
|
1837 |
|
|
void |
1838 |
|
|
free_address_proposal(struct address_proposal *addr_proposal) |
1839 |
|
|
{ |
1840 |
|
|
if (addr_proposal == NULL) |
1841 |
|
|
return; |
1842 |
|
|
|
1843 |
|
|
evtimer_del(&addr_proposal->timer); |
1844 |
|
|
free(addr_proposal); |
1845 |
|
|
} |
1846 |
|
|
|
1847 |
|
|
void |
1848 |
|
|
gen_dfr_proposal(struct slaacd_iface *iface, struct radv *ra) |
1849 |
|
|
{ |
1850 |
|
|
struct dfr_proposal *dfr_proposal; |
1851 |
|
|
struct timeval tv; |
1852 |
|
|
const char *hbuf; |
1853 |
|
|
|
1854 |
|
|
if ((dfr_proposal = calloc(1, sizeof(*dfr_proposal))) == NULL) |
1855 |
|
|
fatal("calloc"); |
1856 |
|
|
evtimer_set(&dfr_proposal->timer, dfr_proposal_timeout, |
1857 |
|
|
dfr_proposal); |
1858 |
|
|
dfr_proposal->next_timeout = 1; |
1859 |
|
|
dfr_proposal->timeout_count = 0; |
1860 |
|
|
dfr_proposal->state = PROPOSAL_NOT_CONFIGURED; |
1861 |
|
|
dfr_proposal->when = ra->when; |
1862 |
|
|
dfr_proposal->uptime = ra->uptime; |
1863 |
|
|
dfr_proposal->if_index = iface->if_index; |
1864 |
|
|
memcpy(&dfr_proposal->addr, &ra->from, |
1865 |
|
|
sizeof(dfr_proposal->addr)); |
1866 |
|
|
dfr_proposal->router_lifetime = ra->router_lifetime; |
1867 |
|
|
dfr_proposal->rpref = ra->rpref; |
1868 |
|
|
|
1869 |
|
|
tv.tv_sec = 0; |
1870 |
|
|
tv.tv_usec = 0; |
1871 |
|
|
evtimer_add(&dfr_proposal->timer, &tv); |
1872 |
|
|
|
1873 |
|
|
LIST_INSERT_HEAD(&iface->dfr_proposals, dfr_proposal, entries); |
1874 |
|
|
|
1875 |
|
|
hbuf = sin6_to_str(&dfr_proposal->addr); |
1876 |
|
|
log_debug("%s: iface %d: %s: %lld s", __func__, |
1877 |
|
|
iface->if_index, hbuf, tv.tv_sec); |
1878 |
|
|
} |
1879 |
|
|
|
1880 |
|
|
void |
1881 |
|
|
configure_dfr(struct dfr_proposal *dfr_proposal) |
1882 |
|
|
{ |
1883 |
|
|
struct imsg_configure_dfr dfr; |
1884 |
|
|
struct timeval tv; |
1885 |
|
|
enum proposal_state prev_state; |
1886 |
|
|
|
1887 |
|
|
if (dfr_proposal->router_lifetime > MAX_RTR_SOLICITATIONS * |
1888 |
|
|
(RTR_SOLICITATION_INTERVAL + 1)) { |
1889 |
|
|
dfr_proposal->next_timeout = dfr_proposal->router_lifetime - |
1890 |
|
|
MAX_RTR_SOLICITATIONS * (RTR_SOLICITATION_INTERVAL + 1); |
1891 |
|
|
tv.tv_sec = dfr_proposal->next_timeout; |
1892 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
1893 |
|
|
evtimer_add(&dfr_proposal->timer, &tv); |
1894 |
|
|
log_debug("%s: %d, scheduling new timeout in %llds.%06ld", |
1895 |
|
|
__func__, dfr_proposal->if_index, tv.tv_sec, tv.tv_usec); |
1896 |
|
|
} else |
1897 |
|
|
dfr_proposal->next_timeout = 0; |
1898 |
|
|
|
1899 |
|
|
prev_state = dfr_proposal->state; |
1900 |
|
|
|
1901 |
|
|
dfr_proposal->state = PROPOSAL_CONFIGURED; |
1902 |
|
|
|
1903 |
|
|
log_debug("%s: %d", __func__, dfr_proposal->if_index); |
1904 |
|
|
|
1905 |
|
|
if (prev_state == PROPOSAL_CONFIGURED || prev_state == |
1906 |
|
|
PROPOSAL_NEARLY_EXPIRED) { |
1907 |
|
|
/* |
1908 |
|
|
* nothing to do here, routes do not expire in the kernel |
1909 |
|
|
* XXX check if the route got deleted and re-add it? |
1910 |
|
|
*/ |
1911 |
|
|
return; |
1912 |
|
|
} |
1913 |
|
|
|
1914 |
|
|
dfr.if_index = dfr_proposal->if_index; |
1915 |
|
|
memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); |
1916 |
|
|
dfr.router_lifetime = dfr_proposal->router_lifetime; |
1917 |
|
|
|
1918 |
|
|
engine_imsg_compose_main(IMSG_CONFIGURE_DFR, 0, &dfr, sizeof(dfr)); |
1919 |
|
|
} |
1920 |
|
|
|
1921 |
|
|
void |
1922 |
|
|
withdraw_dfr(struct dfr_proposal *dfr_proposal) |
1923 |
|
|
{ |
1924 |
|
|
struct imsg_configure_dfr dfr; |
1925 |
|
|
|
1926 |
|
|
log_debug("%s: %d", __func__, dfr_proposal->if_index); |
1927 |
|
|
|
1928 |
|
|
dfr.if_index = dfr_proposal->if_index; |
1929 |
|
|
memcpy(&dfr.addr, &dfr_proposal->addr, sizeof(dfr.addr)); |
1930 |
|
|
dfr.router_lifetime = dfr_proposal->router_lifetime; |
1931 |
|
|
|
1932 |
|
|
engine_imsg_compose_main(IMSG_WITHDRAW_DFR, 0, &dfr, sizeof(dfr)); |
1933 |
|
|
} |
1934 |
|
|
|
1935 |
|
|
void |
1936 |
|
|
free_dfr_proposal(struct dfr_proposal *dfr_proposal) |
1937 |
|
|
{ |
1938 |
|
|
|
1939 |
|
|
LIST_REMOVE(dfr_proposal, entries); |
1940 |
|
|
evtimer_del(&dfr_proposal->timer); |
1941 |
|
|
switch (dfr_proposal->state) { |
1942 |
|
|
case PROPOSAL_CONFIGURED: |
1943 |
|
|
case PROPOSAL_NEARLY_EXPIRED: |
1944 |
|
|
withdraw_dfr(dfr_proposal); |
1945 |
|
|
break; |
1946 |
|
|
default: |
1947 |
|
|
break; |
1948 |
|
|
} |
1949 |
|
|
free(dfr_proposal); |
1950 |
|
|
} |
1951 |
|
|
|
1952 |
|
|
void |
1953 |
|
|
send_proposal(struct imsg_proposal *proposal) |
1954 |
|
|
{ |
1955 |
|
|
#ifndef SKIP_PROPOSAL |
1956 |
|
|
engine_imsg_compose_main(IMSG_PROPOSAL, 0, proposal, sizeof(*proposal)); |
1957 |
|
|
#else |
1958 |
|
|
struct imsg_proposal_ack ack; |
1959 |
|
|
ack.id = proposal->id; |
1960 |
|
|
ack.pid = proposal->pid; |
1961 |
|
|
ack.if_index = proposal->if_index; |
1962 |
|
|
engine_imsg_compose_frontend(IMSG_FAKE_ACK, 0, &ack, sizeof(ack)); |
1963 |
|
|
#endif |
1964 |
|
|
} |
1965 |
|
|
|
1966 |
|
|
void |
1967 |
|
|
start_probe(struct slaacd_iface *iface) |
1968 |
|
|
{ |
1969 |
|
|
struct timeval tv; |
1970 |
|
|
|
1971 |
|
|
iface->state = IF_DELAY; |
1972 |
|
|
iface->probes = 0; |
1973 |
|
|
|
1974 |
|
|
tv.tv_sec = 0; |
1975 |
|
|
tv.tv_usec = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY_USEC); |
1976 |
|
|
|
1977 |
|
|
log_debug("%s: iface %d: sleeping for %ldusec", __func__, |
1978 |
|
|
iface->if_index, tv.tv_usec); |
1979 |
|
|
|
1980 |
|
|
evtimer_add(&iface->timer, &tv); |
1981 |
|
|
} |
1982 |
|
|
|
1983 |
|
|
void |
1984 |
|
|
address_proposal_timeout(int fd, short events, void *arg) |
1985 |
|
|
{ |
1986 |
|
|
struct address_proposal *addr_proposal; |
1987 |
|
|
struct imsg_proposal proposal; |
1988 |
|
|
struct timeval tv; |
1989 |
|
|
const char *hbuf; |
1990 |
|
|
|
1991 |
|
|
addr_proposal = (struct address_proposal *)arg; |
1992 |
|
|
|
1993 |
|
|
hbuf = sin6_to_str(&addr_proposal->addr); |
1994 |
|
|
log_debug("%s: iface %d: %s [%s], priv: %s", __func__, |
1995 |
|
|
addr_proposal->if_index, hbuf, |
1996 |
|
|
proposal_state_name[addr_proposal->state], |
1997 |
|
|
addr_proposal->privacy ? "y" : "n"); |
1998 |
|
|
|
1999 |
|
|
switch (addr_proposal->state) { |
2000 |
|
|
case PROPOSAL_NOT_CONFIGURED: |
2001 |
|
|
case PROPOSAL_SENT: |
2002 |
|
|
if (addr_proposal->timeout_count++ < 6) { |
2003 |
|
|
addr_proposal->id = ++proposal_id; |
2004 |
|
|
|
2005 |
|
|
memset(&proposal, 0, sizeof(proposal)); |
2006 |
|
|
proposal.if_index = addr_proposal->if_index; |
2007 |
|
|
proposal.pid = getpid(); |
2008 |
|
|
proposal.id = addr_proposal->id; |
2009 |
|
|
memcpy(&proposal.addr, &addr_proposal->addr, |
2010 |
|
|
sizeof(proposal.addr)); |
2011 |
|
|
memcpy(&proposal.mask, &addr_proposal->mask, |
2012 |
|
|
sizeof(proposal.mask)); |
2013 |
|
|
|
2014 |
|
|
proposal.rtm_addrs = RTA_NETMASK | RTA_IFA; |
2015 |
|
|
|
2016 |
|
|
addr_proposal->state = PROPOSAL_SENT; |
2017 |
|
|
|
2018 |
|
|
send_proposal(&proposal); |
2019 |
|
|
|
2020 |
|
|
tv.tv_sec = addr_proposal->next_timeout; |
2021 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
2022 |
|
|
addr_proposal->next_timeout *= 2; |
2023 |
|
|
evtimer_add(&addr_proposal->timer, &tv); |
2024 |
|
|
log_debug("%s: scheduling new timeout in %llds.%06ld", |
2025 |
|
|
__func__, tv.tv_sec, tv.tv_usec); |
2026 |
|
|
} else { |
2027 |
|
|
log_debug("%s: giving up, no response to proposal", |
2028 |
|
|
__func__); |
2029 |
|
|
LIST_REMOVE(addr_proposal, entries); |
2030 |
|
|
free_address_proposal(addr_proposal); |
2031 |
|
|
} |
2032 |
|
|
break; |
2033 |
|
|
case PROPOSAL_CONFIGURED: |
2034 |
|
|
log_debug("PROPOSAL_CONFIGURED timeout: id: %lld, privacy: %s", |
2035 |
|
|
addr_proposal->id, addr_proposal->privacy ? "y" : "n"); |
2036 |
|
|
|
2037 |
|
|
addr_proposal->next_timeout = 1; |
2038 |
|
|
addr_proposal->timeout_count = 0; |
2039 |
|
|
addr_proposal->state = PROPOSAL_NEARLY_EXPIRED; |
2040 |
|
|
|
2041 |
|
|
tv.tv_sec = 0; |
2042 |
|
|
tv.tv_usec = 0; |
2043 |
|
|
evtimer_add(&addr_proposal->timer, &tv); |
2044 |
|
|
|
2045 |
|
|
break; |
2046 |
|
|
case PROPOSAL_NEARLY_EXPIRED: |
2047 |
|
|
log_debug("%s: rl: %d", __func__, |
2048 |
|
|
real_lifetime(&addr_proposal->uptime, |
2049 |
|
|
addr_proposal->vltime)); |
2050 |
|
|
/* |
2051 |
|
|
* we should have gotten a RTM_DELADDR from the kernel, |
2052 |
|
|
* in case we missed it, delete to not waste memory |
2053 |
|
|
*/ |
2054 |
|
|
if (real_lifetime(&addr_proposal->uptime, |
2055 |
|
|
addr_proposal->vltime) == 0) { |
2056 |
|
|
evtimer_del(&addr_proposal->timer); |
2057 |
|
|
LIST_REMOVE(addr_proposal, entries); |
2058 |
|
|
free_address_proposal(addr_proposal); |
2059 |
|
|
log_debug("%s: removing address proposal", __func__); |
2060 |
|
|
break; |
2061 |
|
|
} |
2062 |
|
|
if (addr_proposal->privacy) |
2063 |
|
|
break; /* just let it expire */ |
2064 |
|
|
|
2065 |
|
|
engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, |
2066 |
|
|
0, &addr_proposal->if_index, |
2067 |
|
|
sizeof(addr_proposal->if_index)); |
2068 |
|
|
tv.tv_sec = addr_proposal->next_timeout; |
2069 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
2070 |
|
|
addr_proposal->next_timeout *= 2; |
2071 |
|
|
evtimer_add(&addr_proposal->timer, &tv); |
2072 |
|
|
log_debug("%s: scheduling new timeout in %llds.%06ld", |
2073 |
|
|
__func__, tv.tv_sec, tv.tv_usec); |
2074 |
|
|
break; |
2075 |
|
|
default: |
2076 |
|
|
log_debug("%s: unhandled state: %s", __func__, |
2077 |
|
|
proposal_state_name[addr_proposal->state]); |
2078 |
|
|
} |
2079 |
|
|
} |
2080 |
|
|
|
2081 |
|
|
void |
2082 |
|
|
dfr_proposal_timeout(int fd, short events, void *arg) |
2083 |
|
|
{ |
2084 |
|
|
struct dfr_proposal *dfr_proposal; |
2085 |
|
|
struct imsg_proposal proposal; |
2086 |
|
|
struct timeval tv; |
2087 |
|
|
const char *hbuf; |
2088 |
|
|
|
2089 |
|
|
dfr_proposal = (struct dfr_proposal *)arg; |
2090 |
|
|
|
2091 |
|
|
hbuf = sin6_to_str(&dfr_proposal->addr); |
2092 |
|
|
log_debug("%s: iface %d: %s [%s]", __func__, dfr_proposal->if_index, |
2093 |
|
|
hbuf, proposal_state_name[dfr_proposal->state]); |
2094 |
|
|
|
2095 |
|
|
switch (dfr_proposal->state) { |
2096 |
|
|
case PROPOSAL_NOT_CONFIGURED: |
2097 |
|
|
case PROPOSAL_SENT: |
2098 |
|
|
if (dfr_proposal->timeout_count++ < 6) { |
2099 |
|
|
dfr_proposal->id = ++proposal_id; |
2100 |
|
|
|
2101 |
|
|
memset(&proposal, 0, sizeof(proposal)); |
2102 |
|
|
proposal.if_index = dfr_proposal->if_index; |
2103 |
|
|
proposal.pid = getpid(); |
2104 |
|
|
proposal.id = dfr_proposal->id; |
2105 |
|
|
memcpy(&proposal.addr, &dfr_proposal->addr, |
2106 |
|
|
sizeof(proposal.addr)); |
2107 |
|
|
|
2108 |
|
|
proposal.rtm_addrs = RTA_GATEWAY; |
2109 |
|
|
|
2110 |
|
|
dfr_proposal->state = PROPOSAL_SENT; |
2111 |
|
|
|
2112 |
|
|
send_proposal(&proposal); |
2113 |
|
|
|
2114 |
|
|
tv.tv_sec = dfr_proposal->next_timeout; |
2115 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
2116 |
|
|
dfr_proposal->next_timeout *= 2; |
2117 |
|
|
evtimer_add(&dfr_proposal->timer, &tv); |
2118 |
|
|
log_debug("%s: scheduling new timeout in %llds.%06ld", |
2119 |
|
|
__func__, tv.tv_sec, tv.tv_usec); |
2120 |
|
|
} else { |
2121 |
|
|
log_debug("%s: giving up, no response to proposal", |
2122 |
|
|
__func__); |
2123 |
|
|
free_dfr_proposal(dfr_proposal); |
2124 |
|
|
} |
2125 |
|
|
break; |
2126 |
|
|
case PROPOSAL_CONFIGURED: |
2127 |
|
|
log_debug("PROPOSAL_CONFIGURED timeout: id: %lld", |
2128 |
|
|
dfr_proposal->id); |
2129 |
|
|
|
2130 |
|
|
dfr_proposal->next_timeout = 1; |
2131 |
|
|
dfr_proposal->timeout_count = 0; |
2132 |
|
|
dfr_proposal->state = PROPOSAL_NEARLY_EXPIRED; |
2133 |
|
|
|
2134 |
|
|
tv.tv_sec = 0; |
2135 |
|
|
tv.tv_usec = 0; |
2136 |
|
|
evtimer_add(&dfr_proposal->timer, &tv); |
2137 |
|
|
|
2138 |
|
|
break; |
2139 |
|
|
case PROPOSAL_NEARLY_EXPIRED: |
2140 |
|
|
if (real_lifetime(&dfr_proposal->uptime, |
2141 |
|
|
dfr_proposal->router_lifetime) == 0) { |
2142 |
|
|
free_dfr_proposal(dfr_proposal); |
2143 |
|
|
log_debug("%s: removing dfr proposal", __func__); |
2144 |
|
|
break; |
2145 |
|
|
} |
2146 |
|
|
engine_imsg_compose_frontend(IMSG_CTL_SEND_SOLICITATION, |
2147 |
|
|
0, &dfr_proposal->if_index, |
2148 |
|
|
sizeof(dfr_proposal->if_index)); |
2149 |
|
|
tv.tv_sec = dfr_proposal->next_timeout; |
2150 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
2151 |
|
|
dfr_proposal->next_timeout *= 2; |
2152 |
|
|
evtimer_add(&dfr_proposal->timer, &tv); |
2153 |
|
|
log_debug("%s: scheduling new timeout in %llds.%06ld", |
2154 |
|
|
__func__, tv.tv_sec, tv.tv_usec); |
2155 |
|
|
break; |
2156 |
|
|
default: |
2157 |
|
|
log_debug("%s: unhandled state: %s", __func__, |
2158 |
|
|
proposal_state_name[dfr_proposal->state]); |
2159 |
|
|
} |
2160 |
|
|
} |
2161 |
|
|
|
2162 |
|
|
void |
2163 |
|
|
iface_timeout(int fd, short events, void *arg) |
2164 |
|
|
{ |
2165 |
|
|
struct slaacd_iface *iface = (struct slaacd_iface *)arg; |
2166 |
|
|
struct timeval tv; |
2167 |
|
|
|
2168 |
|
|
log_debug("%s[%d]: %s", __func__, iface->if_index, |
2169 |
|
|
if_state_name[iface->state]); |
2170 |
|
|
|
2171 |
|
|
switch (iface->state) { |
2172 |
|
|
case IF_DELAY: |
2173 |
|
|
case IF_PROBE: |
2174 |
|
|
iface->state = IF_PROBE; |
2175 |
|
|
engine_imsg_compose_frontend( |
2176 |
|
|
IMSG_CTL_SEND_SOLICITATION, 0, &iface->if_index, |
2177 |
|
|
sizeof(iface->if_index)); |
2178 |
|
|
if (++iface->probes >= MAX_RTR_SOLICITATIONS) |
2179 |
|
|
iface->state = IF_IDLE; |
2180 |
|
|
else { |
2181 |
|
|
tv.tv_sec = RTR_SOLICITATION_INTERVAL; |
2182 |
|
|
tv.tv_usec = arc4random_uniform(1000000); |
2183 |
|
|
evtimer_add(&iface->timer, &tv); |
2184 |
|
|
} |
2185 |
|
|
break; |
2186 |
|
|
case IF_DOWN: |
2187 |
|
|
case IF_IDLE: |
2188 |
|
|
default: |
2189 |
|
|
break; |
2190 |
|
|
} |
2191 |
|
|
} |
2192 |
|
|
|
2193 |
|
|
struct radv* |
2194 |
|
|
find_ra(struct slaacd_iface *iface, struct sockaddr_in6 *from) |
2195 |
|
|
{ |
2196 |
|
|
struct radv *ra; |
2197 |
|
|
|
2198 |
|
|
LIST_FOREACH (ra, &iface->radvs, entries) { |
2199 |
|
|
if (memcmp(&ra->from.sin6_addr, &from->sin6_addr, |
2200 |
|
|
sizeof(from->sin6_addr)) == 0) |
2201 |
|
|
return (ra); |
2202 |
|
|
} |
2203 |
|
|
|
2204 |
|
|
return (NULL); |
2205 |
|
|
} |
2206 |
|
|
|
2207 |
|
|
struct address_proposal* |
2208 |
|
|
find_address_proposal_by_id(struct slaacd_iface *iface, int64_t id) |
2209 |
|
|
{ |
2210 |
|
|
struct address_proposal *addr_proposal; |
2211 |
|
|
|
2212 |
|
|
LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) { |
2213 |
|
|
if (addr_proposal->id == id) |
2214 |
|
|
return (addr_proposal); |
2215 |
|
|
} |
2216 |
|
|
|
2217 |
|
|
return (NULL); |
2218 |
|
|
} |
2219 |
|
|
|
2220 |
|
|
struct address_proposal* |
2221 |
|
|
find_address_proposal_by_addr(struct slaacd_iface *iface, struct sockaddr_in6 |
2222 |
|
|
*addr) |
2223 |
|
|
{ |
2224 |
|
|
struct address_proposal *addr_proposal; |
2225 |
|
|
|
2226 |
|
|
LIST_FOREACH (addr_proposal, &iface->addr_proposals, entries) { |
2227 |
|
|
if (memcmp(&addr_proposal->addr, addr, sizeof(*addr)) == 0) |
2228 |
|
|
return (addr_proposal); |
2229 |
|
|
} |
2230 |
|
|
|
2231 |
|
|
return (NULL); |
2232 |
|
|
} |
2233 |
|
|
|
2234 |
|
|
struct dfr_proposal* |
2235 |
|
|
find_dfr_proposal_by_id(struct slaacd_iface *iface, int64_t id) |
2236 |
|
|
{ |
2237 |
|
|
struct dfr_proposal *dfr_proposal; |
2238 |
|
|
|
2239 |
|
|
LIST_FOREACH (dfr_proposal, &iface->dfr_proposals, entries) { |
2240 |
|
|
if (dfr_proposal->id == id) |
2241 |
|
|
return (dfr_proposal); |
2242 |
|
|
} |
2243 |
|
|
|
2244 |
|
|
return (NULL); |
2245 |
|
|
} |
2246 |
|
|
|
2247 |
|
|
|
2248 |
|
|
/* XXX currently unused */ |
2249 |
|
|
void |
2250 |
|
|
find_prefix(struct slaacd_iface *iface, struct address_proposal *addr_proposal, |
2251 |
|
|
struct radv **result_ra, struct radv_prefix **result_prefix) |
2252 |
|
|
{ |
2253 |
|
|
struct radv *ra; |
2254 |
|
|
struct radv_prefix *prefix; |
2255 |
|
|
uint32_t lifetime, max_lifetime = 0; |
2256 |
|
|
|
2257 |
|
|
*result_ra = NULL; |
2258 |
|
|
*result_prefix = NULL; |
2259 |
|
|
|
2260 |
|
|
LIST_FOREACH(ra, &iface->radvs, entries) { |
2261 |
|
|
LIST_FOREACH(prefix, &ra->prefixes, entries) { |
2262 |
|
|
if (memcmp(&prefix->prefix, &addr_proposal->prefix, |
2263 |
|
|
sizeof(addr_proposal->prefix)) != 0) |
2264 |
|
|
continue; |
2265 |
|
|
lifetime = real_lifetime(&ra->uptime, |
2266 |
|
|
prefix->vltime); |
2267 |
|
|
if (lifetime > max_lifetime) { |
2268 |
|
|
max_lifetime = lifetime; |
2269 |
|
|
*result_ra = ra; |
2270 |
|
|
*result_prefix = prefix; |
2271 |
|
|
} |
2272 |
|
|
} |
2273 |
|
|
} |
2274 |
|
|
} |
2275 |
|
|
|
2276 |
|
|
uint32_t |
2277 |
|
|
real_lifetime(struct timespec *received_uptime, uint32_t ltime) |
2278 |
|
|
{ |
2279 |
|
|
struct timespec now, diff; |
2280 |
|
|
int64_t remaining; |
2281 |
|
|
|
2282 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &now)) |
2283 |
|
|
fatal("clock_gettime"); |
2284 |
|
|
|
2285 |
|
|
timespecsub(&now, received_uptime, &diff); |
2286 |
|
|
|
2287 |
|
|
remaining = ((int64_t)ltime) - diff.tv_sec; |
2288 |
|
|
|
2289 |
|
|
if (remaining < 0) |
2290 |
|
|
remaining = 0; |
2291 |
|
|
|
2292 |
|
|
return (remaining); |
2293 |
|
|
} |