]>
Commit | Line | Data |
---|---|---|
7ba0088d A |
1 | /* $KAME: rtsold.c,v 1.31 2001/05/22 06:03:06 jinmei Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. Neither the name of the project nor the names of its contributors | |
16 | * may be used to endorse or promote products derived from this software | |
17 | * without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
29 | * SUCH DAMAGE. | |
30 | * | |
31 | * $FreeBSD: src/usr.sbin/rtsold/rtsold.c,v 1.1.2.3 2001/07/03 11:02:16 ume Exp $ | |
32 | */ | |
33 | ||
34 | #include <sys/types.h> | |
35 | #include <sys/time.h> | |
36 | #include <sys/socket.h> | |
37 | ||
38 | #include <net/if.h> | |
39 | #include <net/if_dl.h> | |
40 | ||
41 | #include <netinet/in.h> | |
42 | #include <netinet/icmp6.h> | |
43 | ||
44 | #include <signal.h> | |
45 | #include <unistd.h> | |
46 | #include <syslog.h> | |
47 | #include <string.h> | |
48 | #include <stdlib.h> | |
49 | #include <stdio.h> | |
50 | #include <errno.h> | |
51 | #include <err.h> | |
52 | #include <stdarg.h> | |
53 | #include <ifaddrs.h> | |
54 | #include "rtsold.h" | |
55 | ||
56 | struct ifinfo *iflist; | |
57 | struct timeval tm_max = {0x7fffffff, 0x7fffffff}; | |
58 | int aflag = 0; | |
59 | int dflag = 0; | |
60 | static int log_upto = 999; | |
61 | static int fflag = 0; | |
62 | ||
63 | /* protocol constatns */ | |
64 | #define MAX_RTR_SOLICITATION_DELAY 1 /* second */ | |
65 | #define RTR_SOLICITATION_INTERVAL 4 /* seconds */ | |
66 | #define MAX_RTR_SOLICITATIONS 3 /* times */ | |
67 | ||
68 | /* implementation dependent constants */ | |
69 | #define PROBE_INTERVAL 60 /* secondes XXX: should be configurable */ | |
70 | ||
71 | /* utility macros */ | |
72 | /* a < b */ | |
73 | #define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\ | |
74 | (((a).tv_sec == (b).tv_sec) && \ | |
75 | ((a).tv_usec < (b).tv_usec))) | |
76 | ||
77 | /* a <= b */ | |
78 | #define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\ | |
79 | (((a).tv_sec == (b).tv_sec) &&\ | |
80 | ((a).tv_usec <= (b).tv_usec))) | |
81 | ||
82 | /* a == b */ | |
83 | #define TIMEVAL_EQ(a, b) (((a).tv_sec==(b).tv_sec) && ((a).tv_usec==(b).tv_usec)) | |
84 | ||
85 | int main __P((int argc, char *argv[])); | |
86 | ||
87 | /* static variables and functions */ | |
88 | static int mobile_node = 0; | |
89 | static int do_dump; | |
90 | static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */ | |
91 | static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */ | |
92 | ||
93 | static int ifconfig __P((char *ifname)); | |
94 | #if 0 | |
95 | static int ifreconfig __P((char *ifname)); | |
96 | #endif | |
97 | static int make_packet __P((struct ifinfo *ifinfo)); | |
98 | static struct timeval *rtsol_check_timer __P((void)); | |
99 | static void TIMEVAL_ADD __P((struct timeval *a, struct timeval *b, | |
100 | struct timeval *result)); | |
101 | static void TIMEVAL_SUB __P((struct timeval *a, struct timeval *b, | |
102 | struct timeval *result)); | |
103 | ||
104 | static void rtsold_set_dump_file __P((void)); | |
105 | static void usage __P((char *progname)); | |
106 | static char **autoifprobe __P((void)); | |
107 | ||
108 | int | |
109 | main(argc, argv) | |
110 | int argc; | |
111 | char *argv[]; | |
112 | { | |
113 | int s, rtsock, maxfd, ch; | |
114 | int once = 0; | |
115 | struct timeval *timeout; | |
116 | struct fd_set fdset; | |
117 | char *argv0; | |
118 | char *opts; | |
119 | ||
120 | /* | |
121 | * Initialization | |
122 | */ | |
123 | argv0 = argv[0]; | |
124 | ||
125 | /* get option */ | |
126 | if (argv0 && argv0[strlen(argv0) - 1] != 'd') { | |
127 | fflag = 1; | |
128 | once = 1; | |
129 | opts = "adD"; | |
130 | } else | |
131 | opts = "adDfm1"; | |
132 | ||
133 | while ((ch = getopt(argc, argv, opts)) != -1) { | |
134 | switch (ch) { | |
135 | case 'a': | |
136 | aflag = 1; | |
137 | break; | |
138 | case 'd': | |
139 | dflag = 1; | |
140 | break; | |
141 | case 'D': | |
142 | dflag = 2; | |
143 | break; | |
144 | case 'f': | |
145 | fflag = 1; | |
146 | break; | |
147 | case 'm': | |
148 | mobile_node = 1; | |
149 | break; | |
150 | case '1': | |
151 | once = 1; | |
152 | break; | |
153 | default: | |
154 | usage(argv0); | |
155 | /*NOTREACHED*/ | |
156 | } | |
157 | } | |
158 | argc -= optind; | |
159 | argv += optind; | |
160 | ||
161 | if (aflag) { | |
162 | int i; | |
163 | ||
164 | if (argc != 0) { | |
165 | usage(argv0); | |
166 | /*NOTREACHED*/ | |
167 | } | |
168 | ||
169 | argv = autoifprobe(); | |
170 | if (!argv) { | |
171 | errx(1, "could not autoprobe interface"); | |
172 | /*NOTREACHED*/ | |
173 | } | |
174 | ||
175 | for (i = 0; argv[i]; i++) | |
176 | ; | |
177 | argc = i; | |
178 | } | |
179 | if (argc == 0) { | |
180 | usage(argv0); | |
181 | /*NOTREACHED*/ | |
182 | } | |
183 | ||
184 | /* set log level */ | |
185 | if (dflag == 0) | |
186 | log_upto = LOG_NOTICE; | |
187 | if (!fflag) { | |
188 | char *ident; | |
189 | ident = strrchr(argv0, '/'); | |
190 | if (!ident) | |
191 | ident = argv0; | |
192 | else | |
193 | ident++; | |
194 | openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON); | |
195 | if (log_upto >= 0) | |
196 | setlogmask(LOG_UPTO(log_upto)); | |
197 | } | |
198 | ||
199 | #ifndef HAVE_ARC4RANDOM | |
200 | /* random value initilization */ | |
201 | srandom((u_long)time(NULL)); | |
202 | #endif | |
203 | ||
204 | /* warn if accept_rtadv is down */ | |
205 | if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV)) | |
206 | warnx("kernel is configured not to accept RAs"); | |
207 | /* warn if forwarding is up */ | |
208 | if (getinet6sysctl(IPV6CTL_FORWARDING)) | |
209 | warnx("kernel is configured as a router, not a host"); | |
210 | ||
211 | /* initialization to dump internal status to a file */ | |
212 | if (signal(SIGUSR1, (void *)rtsold_set_dump_file) < 0) { | |
213 | errx(1, "failed to set signal for dump status"); | |
214 | /*NOTREACHED*/ | |
215 | } | |
216 | ||
217 | /* | |
218 | * Open a socket for sending RS and receiving RA. | |
219 | * This should be done before calling ifinit(), since the function | |
220 | * uses the socket. | |
221 | */ | |
222 | if ((s = sockopen()) < 0) { | |
223 | errx(1, "failed to open a socket"); | |
224 | /*NOTREACHED*/ | |
225 | } | |
226 | maxfd = s; | |
227 | if ((rtsock = rtsock_open()) < 0) { | |
228 | errx(1, "failed to open a socket"); | |
229 | /*NOTREACHED*/ | |
230 | } | |
231 | if (rtsock > maxfd) | |
232 | maxfd = rtsock; | |
233 | ||
234 | /* configuration per interface */ | |
235 | if (ifinit()) { | |
236 | errx(1, "failed to initilizatoin interfaces"); | |
237 | /*NOTREACHED*/ | |
238 | } | |
239 | while (argc--) { | |
240 | if (ifconfig(*argv)) { | |
241 | errx(1, "failed to initialize %s", *argv); | |
242 | /*NOTREACHED*/ | |
243 | } | |
244 | argv++; | |
245 | } | |
246 | ||
247 | /* setup for probing default routers */ | |
248 | if (probe_init()) { | |
249 | errx(1, "failed to setup for probing routers"); | |
250 | /*NOTREACHED*/ | |
251 | } | |
252 | ||
253 | if (!fflag) | |
254 | daemon(0, 0); /* act as a daemon */ | |
255 | ||
256 | /* dump the current pid */ | |
257 | if (!once) { | |
258 | pid_t pid = getpid(); | |
259 | FILE *fp; | |
260 | ||
261 | if ((fp = fopen(pidfilename, "w")) == NULL) | |
262 | warnmsg(LOG_ERR, __FUNCTION__, | |
263 | "failed to open a log file(%s): %s", | |
264 | pidfilename, strerror(errno)); | |
265 | else { | |
266 | fprintf(fp, "%d\n", pid); | |
267 | fclose(fp); | |
268 | } | |
269 | } | |
270 | ||
271 | FD_ZERO(&fdset); | |
272 | FD_SET(s, &fdset); | |
273 | FD_SET(rtsock, &fdset); | |
274 | while (1) { /* main loop */ | |
275 | int e; | |
276 | struct fd_set select_fd = fdset; | |
277 | ||
278 | if (do_dump) { /* SIGUSR1 */ | |
279 | do_dump = 0; | |
280 | rtsold_dump_file(dumpfilename); | |
281 | } | |
282 | ||
283 | timeout = rtsol_check_timer(); | |
284 | ||
285 | if (once) { | |
286 | struct ifinfo *ifi; | |
287 | ||
288 | /* if we have no timeout, we are done (or failed) */ | |
289 | if (timeout == NULL) | |
290 | break; | |
291 | ||
292 | /* if all interfaces have got RA packet, we are done */ | |
293 | for (ifi = iflist; ifi; ifi = ifi->next) { | |
294 | if (ifi->state != IFS_DOWN && ifi->racnt == 0) | |
295 | break; | |
296 | } | |
297 | if (ifi == NULL) | |
298 | break; | |
299 | } | |
300 | e = select(maxfd + 1, &select_fd, NULL, NULL, timeout); | |
301 | if (e < 1) { | |
302 | if (e < 0 && errno != EINTR) { | |
303 | warnmsg(LOG_ERR, __FUNCTION__, "select: %s", | |
304 | strerror(errno)); | |
305 | } | |
306 | continue; | |
307 | } | |
308 | ||
309 | /* packet reception */ | |
310 | if (FD_ISSET(rtsock, &select_fd)) | |
311 | rtsock_input(rtsock); | |
312 | if (FD_ISSET(s, &select_fd)) | |
313 | rtsol_input(s); | |
314 | } | |
315 | /* NOTREACHED */ | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | static int | |
321 | ifconfig(char *ifname) | |
322 | { | |
323 | struct ifinfo *ifinfo; | |
324 | struct sockaddr_dl *sdl; | |
325 | int flags; | |
326 | ||
327 | if ((sdl = if_nametosdl(ifname)) == NULL) { | |
328 | warnmsg(LOG_ERR, __FUNCTION__, | |
329 | "failed to get link layer information for %s", ifname); | |
330 | return(-1); | |
331 | } | |
332 | if (find_ifinfo(sdl->sdl_index)) { | |
333 | warnmsg(LOG_ERR, __FUNCTION__, | |
334 | "interface %s was already cofigured", ifname); | |
335 | free(sdl); | |
336 | return(-1); | |
337 | } | |
338 | ||
339 | if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) { | |
340 | warnmsg(LOG_ERR, __FUNCTION__, "memory allocation failed"); | |
341 | free(sdl); | |
342 | return(-1); | |
343 | } | |
344 | memset(ifinfo, 0, sizeof(*ifinfo)); | |
345 | ifinfo->sdl = sdl; | |
346 | ||
347 | strncpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname)); | |
348 | ||
349 | /* construct a router solicitation message */ | |
350 | if (make_packet(ifinfo)) | |
351 | goto bad; | |
352 | ||
353 | /* | |
354 | * check if the interface is available. | |
355 | * also check if SIOCGIFMEDIA ioctl is OK on the interface. | |
356 | */ | |
357 | ifinfo->mediareqok = 1; | |
358 | ifinfo->active = interface_status(ifinfo); | |
359 | if (!ifinfo->mediareqok) { | |
360 | /* | |
361 | * probe routers periodically even if the link status | |
362 | * does not change. | |
363 | */ | |
364 | ifinfo->probeinterval = PROBE_INTERVAL; | |
365 | } | |
366 | ||
367 | /* activate interface: interface_up returns 0 on success */ | |
368 | flags = interface_up(ifinfo->ifname); | |
369 | if (flags == 0) | |
370 | ifinfo->state = IFS_DELAY; | |
371 | else if (flags == IFS_TENTATIVE) | |
372 | ifinfo->state = IFS_TENTATIVE; | |
373 | else | |
374 | ifinfo->state = IFS_DOWN; | |
375 | ||
376 | rtsol_timer_update(ifinfo); | |
377 | ||
378 | /* link into chain */ | |
379 | if (iflist) | |
380 | ifinfo->next = iflist; | |
381 | iflist = ifinfo; | |
382 | ||
383 | return(0); | |
384 | ||
385 | bad: | |
386 | free(ifinfo->sdl); | |
387 | free(ifinfo); | |
388 | return(-1); | |
389 | } | |
390 | ||
391 | #if 0 | |
392 | static int | |
393 | ifreconfig(char *ifname) | |
394 | { | |
395 | struct ifinfo *ifi, *prev; | |
396 | int rv; | |
397 | ||
398 | prev = NULL; | |
399 | for (ifi = iflist; ifi; ifi = ifi->next) { | |
400 | if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0) | |
401 | break; | |
402 | prev = ifi; | |
403 | } | |
404 | prev->next = ifi->next; | |
405 | ||
406 | rv = ifconfig(ifname); | |
407 | ||
408 | /* reclaim it after ifconfig() in case ifname is pointer inside ifi */ | |
409 | if (ifi->rs_data) | |
410 | free(ifi->rs_data); | |
411 | free(ifi->sdl); | |
412 | free(ifi); | |
413 | ||
414 | return rv; | |
415 | } | |
416 | #endif | |
417 | ||
418 | struct ifinfo * | |
419 | find_ifinfo(int ifindex) | |
420 | { | |
421 | struct ifinfo *ifi; | |
422 | ||
423 | for (ifi = iflist; ifi; ifi = ifi->next) | |
424 | if (ifi->sdl->sdl_index == ifindex) | |
425 | return(ifi); | |
426 | ||
427 | return(NULL); | |
428 | } | |
429 | ||
430 | static int | |
431 | make_packet(struct ifinfo *ifinfo) | |
432 | { | |
433 | char *buf; | |
434 | struct nd_router_solicit *rs; | |
435 | size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0; | |
436 | ||
437 | if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) { | |
438 | warnmsg(LOG_INFO, __FUNCTION__, | |
439 | "link-layer address option has null length" | |
440 | " on %s. Treat as not included.", ifinfo->ifname); | |
441 | } | |
442 | packlen += lladdroptlen; | |
443 | ifinfo->rs_datalen = packlen; | |
444 | ||
445 | /* allocate buffer */ | |
446 | if ((buf = malloc(packlen)) == NULL) { | |
447 | warnmsg(LOG_ERR, __FUNCTION__, | |
448 | "memory allocation failed for %s", ifinfo->ifname); | |
449 | return(-1); | |
450 | } | |
b8dff150 | 451 | ifinfo->rs_data = (u_char *)buf; |
7ba0088d A |
452 | |
453 | /* fill in the message */ | |
454 | rs = (struct nd_router_solicit *)buf; | |
455 | rs->nd_rs_type = ND_ROUTER_SOLICIT; | |
456 | rs->nd_rs_code = 0; | |
457 | rs->nd_rs_cksum = 0; | |
458 | rs->nd_rs_reserved = 0; | |
459 | buf += sizeof(*rs); | |
460 | ||
461 | /* fill in source link-layer address option */ | |
462 | if (lladdroptlen) | |
463 | lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf); | |
464 | ||
465 | return(0); | |
466 | } | |
467 | ||
468 | static struct timeval * | |
469 | rtsol_check_timer() | |
470 | { | |
471 | static struct timeval returnval; | |
472 | struct timeval now, rtsol_timer; | |
473 | struct ifinfo *ifinfo; | |
474 | int flags; | |
475 | ||
476 | gettimeofday(&now, NULL); | |
477 | ||
478 | rtsol_timer = tm_max; | |
479 | ||
480 | for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) { | |
481 | if (TIMEVAL_LEQ(ifinfo->expire, now)) { | |
482 | if (dflag > 1) | |
483 | warnmsg(LOG_DEBUG, __FUNCTION__, | |
484 | "timer expiration on %s, " | |
485 | "state = %d", ifinfo->ifname, | |
486 | ifinfo->state); | |
487 | ||
488 | switch (ifinfo->state) { | |
489 | case IFS_DOWN: | |
490 | case IFS_TENTATIVE: | |
491 | /* interface_up returns 0 on success */ | |
492 | flags = interface_up(ifinfo->ifname); | |
493 | if (flags == 0) | |
494 | ifinfo->state = IFS_DELAY; | |
495 | else if (flags == IFS_TENTATIVE) | |
496 | ifinfo->state = IFS_TENTATIVE; | |
497 | else | |
498 | ifinfo->state = IFS_DOWN; | |
499 | break; | |
500 | case IFS_IDLE: | |
501 | { | |
502 | int oldstatus = ifinfo->active; | |
503 | int probe = 0; | |
504 | ||
505 | ifinfo->active = | |
506 | interface_status(ifinfo); | |
507 | ||
508 | if (oldstatus != ifinfo->active) { | |
509 | warnmsg(LOG_DEBUG, __FUNCTION__, | |
510 | "%s status is changed" | |
511 | " from %d to %d", | |
512 | ifinfo->ifname, | |
513 | oldstatus, ifinfo->active); | |
514 | probe = 1; | |
515 | ifinfo->state = IFS_DELAY; | |
516 | } | |
517 | else if (ifinfo->probeinterval && | |
518 | (ifinfo->probetimer -= | |
519 | ifinfo->timer.tv_sec) <= 0) { | |
520 | /* probe timer expired */ | |
521 | ifinfo->probetimer = | |
522 | ifinfo->probeinterval; | |
523 | probe = 1; | |
524 | ifinfo->state = IFS_PROBE; | |
525 | } | |
526 | ||
527 | if (probe && mobile_node) | |
528 | defrouter_probe(ifinfo->sdl->sdl_index); | |
529 | break; | |
530 | } | |
531 | case IFS_DELAY: | |
532 | ifinfo->state = IFS_PROBE; | |
533 | sendpacket(ifinfo); | |
534 | break; | |
535 | case IFS_PROBE: | |
536 | if (ifinfo->probes < MAX_RTR_SOLICITATIONS) | |
537 | sendpacket(ifinfo); | |
538 | else { | |
539 | warnmsg(LOG_INFO, __FUNCTION__, | |
540 | "No answer " | |
541 | "after sending %d RSs", | |
542 | ifinfo->probes); | |
543 | ifinfo->probes = 0; | |
544 | ifinfo->state = IFS_IDLE; | |
545 | } | |
546 | break; | |
547 | } | |
548 | rtsol_timer_update(ifinfo); | |
549 | } | |
550 | ||
551 | if (TIMEVAL_LT(ifinfo->expire, rtsol_timer)) | |
552 | rtsol_timer = ifinfo->expire; | |
553 | } | |
554 | ||
555 | if (TIMEVAL_EQ(rtsol_timer, tm_max)) { | |
556 | warnmsg(LOG_DEBUG, __FUNCTION__, "there is no timer"); | |
557 | return(NULL); | |
558 | } | |
559 | else if (TIMEVAL_LT(rtsol_timer, now)) | |
560 | /* this may occur when the interval is too small */ | |
561 | returnval.tv_sec = returnval.tv_usec = 0; | |
562 | else | |
563 | TIMEVAL_SUB(&rtsol_timer, &now, &returnval); | |
564 | ||
565 | if (dflag > 1) | |
566 | warnmsg(LOG_DEBUG, __FUNCTION__, "New timer is %ld:%08ld", | |
567 | (long)returnval.tv_sec, (long)returnval.tv_usec); | |
568 | ||
569 | return(&returnval); | |
570 | } | |
571 | ||
572 | void | |
573 | rtsol_timer_update(struct ifinfo *ifinfo) | |
574 | { | |
575 | #define MILLION 1000000 | |
576 | #define DADRETRY 10 /* XXX: adhoc */ | |
577 | long interval; | |
578 | struct timeval now; | |
579 | ||
580 | bzero(&ifinfo->timer, sizeof(ifinfo->timer)); | |
581 | ||
582 | switch (ifinfo->state) { | |
583 | case IFS_DOWN: | |
584 | case IFS_TENTATIVE: | |
585 | if (++ifinfo->dadcount > DADRETRY) { | |
586 | ifinfo->dadcount = 0; | |
587 | ifinfo->timer.tv_sec = PROBE_INTERVAL; | |
588 | } | |
589 | else | |
590 | ifinfo->timer.tv_sec = 1; | |
591 | break; | |
592 | case IFS_IDLE: | |
593 | if (mobile_node) { | |
594 | /* XXX should be configurable */ | |
595 | ifinfo->timer.tv_sec = 3; | |
596 | } | |
597 | else | |
598 | ifinfo->timer = tm_max; /* stop timer(valid?) */ | |
599 | break; | |
600 | case IFS_DELAY: | |
601 | #ifndef HAVE_ARC4RANDOM | |
602 | interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); | |
603 | #else | |
604 | interval = arc4random() % (MAX_RTR_SOLICITATION_DELAY * MILLION); | |
605 | #endif | |
606 | ifinfo->timer.tv_sec = interval / MILLION; | |
607 | ifinfo->timer.tv_usec = interval % MILLION; | |
608 | break; | |
609 | case IFS_PROBE: | |
610 | if (ifinfo->probes < MAX_RTR_SOLICITATIONS) | |
611 | ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL; | |
612 | else { | |
613 | /* | |
614 | * After sending MAX_RTR_SOLICITATIONS solicitations, | |
615 | * we're just waiting for possible replies; there | |
616 | * will be no more solicatation. Thus, we change | |
617 | * the timer value to MAX_RTR_SOLICITATION_DELAY based | |
618 | * on RFC 2461, Section 6.3.7. | |
619 | */ | |
620 | ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY; | |
621 | } | |
622 | break; | |
623 | default: | |
624 | warnmsg(LOG_ERR, __FUNCTION__, | |
625 | "illegal interface state(%d) on %s", | |
626 | ifinfo->state, ifinfo->ifname); | |
627 | return; | |
628 | } | |
629 | ||
630 | /* reset the timer */ | |
631 | if (TIMEVAL_EQ(ifinfo->timer, tm_max)) { | |
632 | ifinfo->expire = tm_max; | |
633 | warnmsg(LOG_DEBUG, __FUNCTION__, | |
634 | "stop timer for %s", ifinfo->ifname); | |
635 | } | |
636 | else { | |
637 | gettimeofday(&now, NULL); | |
638 | TIMEVAL_ADD(&now, &ifinfo->timer, &ifinfo->expire); | |
639 | ||
640 | if (dflag > 1) | |
641 | warnmsg(LOG_DEBUG, __FUNCTION__, | |
642 | "set timer for %s to %d:%d", ifinfo->ifname, | |
643 | (int)ifinfo->timer.tv_sec, | |
644 | (int)ifinfo->timer.tv_usec); | |
645 | } | |
646 | ||
647 | #undef MILLION | |
648 | } | |
649 | ||
650 | /* timer related utility functions */ | |
651 | #define MILLION 1000000 | |
652 | ||
653 | /* result = a + b */ | |
654 | static void | |
655 | TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result) | |
656 | { | |
657 | long l; | |
658 | ||
659 | if ((l = a->tv_usec + b->tv_usec) < MILLION) { | |
660 | result->tv_usec = l; | |
661 | result->tv_sec = a->tv_sec + b->tv_sec; | |
662 | } | |
663 | else { | |
664 | result->tv_usec = l - MILLION; | |
665 | result->tv_sec = a->tv_sec + b->tv_sec + 1; | |
666 | } | |
667 | } | |
668 | ||
669 | /* | |
670 | * result = a - b | |
671 | * XXX: this function assumes that a >= b. | |
672 | */ | |
673 | void | |
674 | TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result) | |
675 | { | |
676 | long l; | |
677 | ||
678 | if ((l = a->tv_usec - b->tv_usec) >= 0) { | |
679 | result->tv_usec = l; | |
680 | result->tv_sec = a->tv_sec - b->tv_sec; | |
681 | } | |
682 | else { | |
683 | result->tv_usec = MILLION + l; | |
684 | result->tv_sec = a->tv_sec - b->tv_sec - 1; | |
685 | } | |
686 | } | |
687 | ||
688 | static void | |
689 | rtsold_set_dump_file() | |
690 | { | |
691 | do_dump = 1; | |
692 | } | |
693 | ||
694 | static void | |
695 | usage(char *progname) | |
696 | { | |
697 | if (progname && progname[strlen(progname) - 1] != 'd') { | |
698 | fprintf(stderr, "usage: rtsol [-dD] interfaces...\n"); | |
699 | fprintf(stderr, "usage: rtsol [-dD] -a\n"); | |
700 | } else { | |
701 | fprintf(stderr, "usage: rtsold [-adDfm1] interfaces...\n"); | |
702 | fprintf(stderr, "usage: rtsold [-dDfm1] -a\n"); | |
703 | } | |
704 | exit(1); | |
705 | } | |
706 | ||
707 | void | |
708 | #if __STDC__ | |
709 | warnmsg(int priority, const char *func, const char *msg, ...) | |
710 | #else | |
711 | warnmsg(priority, func, msg, va_alist) | |
712 | int priority; | |
713 | const char *func; | |
714 | const char *msg; | |
715 | va_dcl | |
716 | #endif | |
717 | { | |
718 | va_list ap; | |
719 | char buf[BUFSIZ]; | |
720 | ||
721 | va_start(ap, msg); | |
722 | if (fflag) { | |
723 | if (priority <= log_upto) { | |
724 | (void)vfprintf(stderr, msg, ap); | |
725 | (void)fprintf(stderr, "\n"); | |
726 | } | |
727 | } else { | |
728 | snprintf(buf, sizeof(buf), "<%s> %s", func, msg); | |
729 | msg = buf; | |
730 | vsyslog(priority, msg, ap); | |
731 | } | |
732 | va_end(ap); | |
733 | } | |
734 | ||
735 | static char ** | |
736 | autoifprobe() | |
737 | { | |
738 | #ifndef HAVE_GETIFADDRS | |
739 | errx(1, "-a is not available with the configuration"); | |
740 | #else | |
741 | static char ifname[IFNAMSIZ + 1]; | |
742 | static char *argv[2]; | |
743 | struct ifaddrs *ifap, *ifa, *target; | |
744 | ||
745 | if (getifaddrs(&ifap) != 0) | |
746 | return NULL; | |
747 | ||
748 | target = NULL; | |
749 | /* find an ethernet */ | |
750 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { | |
751 | if ((ifa->ifa_flags & IFF_UP) == 0) | |
752 | continue; | |
753 | if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) | |
754 | continue; | |
755 | if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) | |
756 | continue; | |
757 | if ((ifa->ifa_flags & IFF_MULTICAST) == 0) | |
758 | continue; | |
759 | ||
760 | if (ifa->ifa_addr->sa_family != AF_INET6) | |
761 | continue; | |
762 | ||
763 | if (target && strcmp(target->ifa_name, ifa->ifa_name) == 0) | |
764 | continue; | |
765 | ||
766 | if (!target) | |
767 | target = ifa; | |
768 | else { | |
769 | /* if we find multiple candidates, failure. */ | |
770 | if (dflag > 1) | |
771 | warnx("multiple interfaces found"); | |
772 | target = NULL; | |
773 | break; | |
774 | } | |
775 | } | |
776 | ||
777 | if (target) { | |
778 | strncpy(ifname, target->ifa_name, sizeof(ifname) - 1); | |
779 | ifname[sizeof(ifname) - 1] = '\0'; | |
780 | argv[0] = ifname; | |
781 | argv[1] = NULL; | |
782 | ||
783 | if (dflag > 0) | |
784 | warnx("probing %s", argv[0]); | |
785 | } | |
786 | freeifaddrs(ifap); | |
787 | if (target) | |
788 | return argv; | |
789 | else | |
790 | return (char **)NULL; | |
791 | #endif | |
792 | } |