1 |
|
|
/* $OpenBSD: rtadvd.c,v 1.91 2017/08/22 01:44:09 jca Exp $ */ |
2 |
|
|
/* $KAME: rtadvd.c,v 1.66 2002/05/29 14:18:36 itojun Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
6 |
|
|
* All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of the project nor the names of its contributors |
17 |
|
|
* may be used to endorse or promote products derived from this software |
18 |
|
|
* without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
21 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
24 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 |
|
|
* SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
#include <sys/types.h> |
34 |
|
|
#include <sys/socket.h> |
35 |
|
|
#include <sys/uio.h> |
36 |
|
|
#include <sys/time.h> |
37 |
|
|
#include <sys/queue.h> |
38 |
|
|
|
39 |
|
|
#include <net/if.h> |
40 |
|
|
#include <net/route.h> |
41 |
|
|
#include <netinet/in.h> |
42 |
|
|
#include <netinet/ip6.h> |
43 |
|
|
#include <netinet6/ip6_var.h> |
44 |
|
|
#include <netinet/icmp6.h> |
45 |
|
|
|
46 |
|
|
#include <arpa/inet.h> |
47 |
|
|
|
48 |
|
|
#include <time.h> |
49 |
|
|
#include <unistd.h> |
50 |
|
|
#include <stdio.h> |
51 |
|
|
#include <stdlib.h> |
52 |
|
|
#include <err.h> |
53 |
|
|
#include <errno.h> |
54 |
|
|
#include <event.h> |
55 |
|
|
#include <string.h> |
56 |
|
|
#include <pwd.h> |
57 |
|
|
#include <signal.h> |
58 |
|
|
#include <fcntl.h> |
59 |
|
|
#include <paths.h> |
60 |
|
|
|
61 |
|
|
#include "rtadvd.h" |
62 |
|
|
#include "advcap.h" |
63 |
|
|
#include "if.h" |
64 |
|
|
#include "config.h" |
65 |
|
|
#include "dump.h" |
66 |
|
|
#include "log.h" |
67 |
|
|
|
68 |
|
|
struct msghdr rcvmhdr; |
69 |
|
|
static u_char *rcvcmsgbuf; |
70 |
|
|
static size_t rcvcmsgbuflen; |
71 |
|
|
static u_char *sndcmsgbuf = NULL; |
72 |
|
|
static size_t sndcmsgbuflen; |
73 |
|
|
struct msghdr sndmhdr; |
74 |
|
|
struct iovec rcviov[2]; |
75 |
|
|
struct iovec sndiov[2]; |
76 |
|
|
static char *rtsockbuf; |
77 |
|
|
static size_t rtsockbuflen; |
78 |
|
|
struct sockaddr_in6 from; |
79 |
|
|
struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6}; |
80 |
|
|
int sock; |
81 |
|
|
int rtsock = -1; |
82 |
|
|
int ioctl_sock; |
83 |
|
|
int dflag = 0, sflag = 0; |
84 |
|
|
|
85 |
|
|
u_char *conffile = NULL; |
86 |
|
|
|
87 |
|
|
struct ralist ralist; |
88 |
|
|
|
89 |
|
|
struct nd_opt { |
90 |
|
|
SLIST_ENTRY(nd_opt) entry; |
91 |
|
|
struct nd_opt_hdr *opt; |
92 |
|
|
}; |
93 |
|
|
|
94 |
|
|
union nd_opts { |
95 |
|
|
struct nd_opt_hdr *nd_opt_array[9]; |
96 |
|
|
struct { |
97 |
|
|
struct nd_opt_hdr *zero; |
98 |
|
|
struct nd_opt_hdr *src_lladdr; |
99 |
|
|
struct nd_opt_hdr *tgt_lladdr; |
100 |
|
|
struct nd_opt_prefix_info *pi; |
101 |
|
|
struct nd_opt_rd_hdr *rh; |
102 |
|
|
struct nd_opt_mtu *mtu; |
103 |
|
|
SLIST_HEAD(nd_optlist, nd_opt) list; |
104 |
|
|
} nd_opt_each; |
105 |
|
|
}; |
106 |
|
|
#define nd_opts_src_lladdr nd_opt_each.src_lladdr |
107 |
|
|
#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr |
108 |
|
|
#define nd_opts_pi nd_opt_each.pi |
109 |
|
|
#define nd_opts_rh nd_opt_each.rh |
110 |
|
|
#define nd_opts_mtu nd_opt_each.mtu |
111 |
|
|
#define nd_opts_list nd_opt_each.list |
112 |
|
|
|
113 |
|
|
#define NDOPT_FLAG_SRCLINKADDR (1 << 0) |
114 |
|
|
#define NDOPT_FLAG_TGTLINKADDR (1 << 1) |
115 |
|
|
#define NDOPT_FLAG_PREFIXINFO (1 << 2) |
116 |
|
|
#define NDOPT_FLAG_RDHDR (1 << 3) |
117 |
|
|
#define NDOPT_FLAG_MTU (1 << 4) |
118 |
|
|
#define NDOPT_FLAG_RDNSS (1 << 5) |
119 |
|
|
#define NDOPT_FLAG_DNSSL (1 << 6) |
120 |
|
|
#define NDOPT_FLAG_ROUTE_INFO (1 << 7) |
121 |
|
|
|
122 |
|
|
u_int32_t ndopt_flags[] = { |
123 |
|
|
[ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR, |
124 |
|
|
[ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR, |
125 |
|
|
[ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO, |
126 |
|
|
[ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR, |
127 |
|
|
[ND_OPT_MTU] = NDOPT_FLAG_MTU, |
128 |
|
|
[ND_OPT_ROUTE_INFO] = NDOPT_FLAG_ROUTE_INFO, |
129 |
|
|
[ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS, |
130 |
|
|
[ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL, |
131 |
|
|
}; |
132 |
|
|
|
133 |
|
|
static __dead void usage(void); |
134 |
|
|
static void sock_open(void); |
135 |
|
|
static void rtsock_open(void); |
136 |
|
|
static void rs_input(int, struct nd_router_solicit *, |
137 |
|
|
struct in6_pktinfo *, struct sockaddr_in6 *); |
138 |
|
|
static void ra_input(int, struct nd_router_advert *, |
139 |
|
|
struct in6_pktinfo *, struct sockaddr_in6 *); |
140 |
|
|
static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *, |
141 |
|
|
struct sockaddr_in6 *); |
142 |
|
|
static int nd6_options(struct nd_opt_hdr *, int, |
143 |
|
|
union nd_opts *, u_int32_t); |
144 |
|
|
static void free_ndopts(union nd_opts *); |
145 |
|
|
static void ra_output(struct rainfo *, struct sockaddr_in6 *); |
146 |
|
|
static struct rainfo *if_indextorainfo(int); |
147 |
|
|
static int rdaemon(int); |
148 |
|
|
|
149 |
|
|
static void dump_cb(int, short, void *); |
150 |
|
|
static void die_cb(int, short, void *); |
151 |
|
|
static void rtsock_cb(int, short, void *); |
152 |
|
|
static void sock_cb(int, short, void *); |
153 |
|
|
static void timer_cb(int, short, void *); |
154 |
|
|
|
155 |
|
|
int |
156 |
|
|
main(int argc, char *argv[]) |
157 |
|
|
{ |
158 |
|
|
struct passwd *pw; |
159 |
|
|
int ch; |
160 |
|
|
int devnull = -1; |
161 |
|
|
struct event ev_sock; |
162 |
|
|
struct event ev_rtsock; |
163 |
|
|
struct event ev_sigterm; |
164 |
|
|
struct event ev_sigusr1; |
165 |
|
|
struct rainfo *rai; |
166 |
|
|
|
167 |
|
|
log_procname = getprogname(); |
168 |
|
|
log_init(1); /* log to stderr until daemonized */ |
169 |
|
|
|
170 |
|
|
closefrom(3); |
171 |
|
|
|
172 |
|
|
/* get command line options and arguments */ |
173 |
|
|
while ((ch = getopt(argc, argv, "c:ds")) != -1) { |
174 |
|
|
switch (ch) { |
175 |
|
|
case 'c': |
176 |
|
|
conffile = optarg; |
177 |
|
|
break; |
178 |
|
|
case 'd': |
179 |
|
|
dflag = 1; |
180 |
|
|
break; |
181 |
|
|
case 's': |
182 |
|
|
sflag = 1; |
183 |
|
|
break; |
184 |
|
|
default: |
185 |
|
|
usage(); |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
|
argc -= optind; |
189 |
|
|
argv += optind; |
190 |
|
|
if (argc == 0) |
191 |
|
|
usage(); |
192 |
|
|
|
193 |
|
|
if (!dflag) { |
194 |
|
|
devnull = open(_PATH_DEVNULL, O_RDWR, 0); |
195 |
|
|
if (devnull == -1) |
196 |
|
|
fatal("open(\"" _PATH_DEVNULL "\")"); |
197 |
|
|
} else |
198 |
|
|
log_verbose(1); |
199 |
|
|
|
200 |
|
|
SLIST_INIT(&ralist); |
201 |
|
|
|
202 |
|
|
/* get iflist block from kernel */ |
203 |
|
|
init_iflist(); |
204 |
|
|
|
205 |
|
|
if (conffile == NULL) |
206 |
|
|
log_init(dflag); |
207 |
|
|
|
208 |
|
|
if ((ioctl_sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) |
209 |
|
|
fatal("socket"); |
210 |
|
|
|
211 |
|
|
while (argc--) |
212 |
|
|
getconfig(*argv++); |
213 |
|
|
|
214 |
|
|
if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) |
215 |
|
|
fatal("inet_pton failed"); |
216 |
|
|
|
217 |
|
|
sock_open(); |
218 |
|
|
|
219 |
|
|
if (sflag == 0) |
220 |
|
|
rtsock_open(); |
221 |
|
|
|
222 |
|
|
if ((pw = getpwnam(RTADVD_USER)) == NULL) |
223 |
|
|
fatal("getpwnam(" RTADVD_USER ")"); |
224 |
|
|
if (chroot(pw->pw_dir) == -1) |
225 |
|
|
fatal("chroot"); |
226 |
|
|
if (chdir("/") == -1) |
227 |
|
|
fatal("chdir(\"/\")"); |
228 |
|
|
if (setgroups(1, &pw->pw_gid) == -1 || |
229 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
230 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
231 |
|
|
fatal("cannot drop privileges"); |
232 |
|
|
|
233 |
|
|
if (!dflag) { |
234 |
|
|
if (rdaemon(devnull) == -1) |
235 |
|
|
fatal("rdaemon"); |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
if (conffile != NULL) |
239 |
|
|
log_init(dflag); |
240 |
|
|
|
241 |
|
|
if (pledge("stdio inet route flock rpath cpath wpath", NULL) == -1) |
242 |
|
|
err(1, "pledge"); |
243 |
|
|
|
244 |
|
|
event_init(); |
245 |
|
|
|
246 |
|
|
signal_set(&ev_sigterm, SIGTERM, die_cb, NULL); |
247 |
|
|
signal_add(&ev_sigterm, NULL); |
248 |
|
|
signal_set(&ev_sigusr1, SIGUSR1, dump_cb, NULL); |
249 |
|
|
signal_add(&ev_sigusr1, NULL); |
250 |
|
|
|
251 |
|
|
event_set(&ev_sock, sock, EV_READ|EV_PERSIST, sock_cb, NULL); |
252 |
|
|
event_add(&ev_sock, NULL); |
253 |
|
|
if (rtsock != -1) { |
254 |
|
|
event_set(&ev_rtsock, rtsock, EV_READ|EV_PERSIST, rtsock_cb, |
255 |
|
|
NULL); |
256 |
|
|
event_add(&ev_rtsock, NULL); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
SLIST_FOREACH(rai, &ralist, entry) { |
260 |
|
|
evtimer_set(&rai->timer.ev, timer_cb, rai); |
261 |
|
|
evtimer_add(&rai->timer.ev, &rai->timer.tm); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
event_dispatch(); |
265 |
|
|
|
266 |
|
|
log_warn("event_dispatch returned"); |
267 |
|
|
|
268 |
|
|
return 1; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
static void |
272 |
|
|
usage(void) |
273 |
|
|
{ |
274 |
|
|
fprintf(stderr, "usage: %s [-ds] [-c configfile] interface ...\n", |
275 |
|
|
getprogname()); |
276 |
|
|
exit(1); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
static void |
280 |
|
|
dump_cb(int sig, short event, void *arg) |
281 |
|
|
{ |
282 |
|
|
rtadvd_dump(); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
static void |
286 |
|
|
die_cb(int sig, short event, void *arg) |
287 |
|
|
{ |
288 |
|
|
struct rainfo *ra; |
289 |
|
|
int i; |
290 |
|
|
const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS; |
291 |
|
|
|
292 |
|
|
log_debug("cease to be an advertising router"); |
293 |
|
|
|
294 |
|
|
SLIST_FOREACH(ra, &ralist, entry) { |
295 |
|
|
ra->lifetime = 0; |
296 |
|
|
make_packet(ra); |
297 |
|
|
} |
298 |
|
|
for (i = 0; i < retrans; i++) { |
299 |
|
|
SLIST_FOREACH(ra, &ralist, entry) |
300 |
|
|
ra_output(ra, &sin6_allnodes); |
301 |
|
|
sleep(MIN_DELAY_BETWEEN_RAS); |
302 |
|
|
} |
303 |
|
|
exit(0); |
304 |
|
|
/*NOTREACHED*/ |
305 |
|
|
} |
306 |
|
|
|
307 |
|
|
static void |
308 |
|
|
rtsock_cb(int fd, short event, void *arg) |
309 |
|
|
{ |
310 |
|
|
int n, type, ifindex = 0, oldifflags, plen; |
311 |
|
|
char *rtm; |
312 |
|
|
u_char ifname[IF_NAMESIZE]; |
313 |
|
|
struct prefix *prefix; |
314 |
|
|
struct rainfo *rai; |
315 |
|
|
struct in6_addr *addr; |
316 |
|
|
char addrbuf[INET6_ADDRSTRLEN]; |
317 |
|
|
|
318 |
|
|
n = read(rtsock, rtsockbuf, rtsockbuflen); |
319 |
|
|
log_debug("received a routing message " |
320 |
|
|
"(type = %d, len = %d)", rtmsg_type(rtsockbuf), n); |
321 |
|
|
|
322 |
|
|
rtm = rtsockbuf; |
323 |
|
|
if (validate_msg(rtm) == -1) |
324 |
|
|
return; |
325 |
|
|
|
326 |
|
|
type = rtmsg_type(rtm); |
327 |
|
|
switch (type) { |
328 |
|
|
case RTM_ADD: |
329 |
|
|
case RTM_DELETE: |
330 |
|
|
ifindex = get_rtm_ifindex(rtm); |
331 |
|
|
break; |
332 |
|
|
case RTM_NEWADDR: |
333 |
|
|
case RTM_DELADDR: |
334 |
|
|
ifindex = get_ifam_ifindex(rtm); |
335 |
|
|
break; |
336 |
|
|
case RTM_IFINFO: |
337 |
|
|
ifindex = get_ifm_ifindex(rtm); |
338 |
|
|
break; |
339 |
|
|
default: |
340 |
|
|
/* should not reach here */ |
341 |
|
|
log_debug("unknown rtmsg %d on %s", |
342 |
|
|
type, if_indextoname(ifindex, ifname)); |
343 |
|
|
return; |
344 |
|
|
} |
345 |
|
|
|
346 |
|
|
if ((rai = if_indextorainfo(ifindex)) == NULL) { |
347 |
|
|
log_debug("route changed on " |
348 |
|
|
"non advertising interface(%s)", |
349 |
|
|
if_indextoname(ifindex, ifname)); |
350 |
|
|
return; |
351 |
|
|
} |
352 |
|
|
oldifflags = iflist[ifindex]->ifm_flags; |
353 |
|
|
|
354 |
|
|
switch (type) { |
355 |
|
|
case RTM_ADD: |
356 |
|
|
/* init ifflags because it may have changed */ |
357 |
|
|
iflist[ifindex]->ifm_flags = |
358 |
|
|
if_getflags(ifindex, iflist[ifindex]->ifm_flags); |
359 |
|
|
|
360 |
|
|
if (sflag) |
361 |
|
|
break; /* we aren't interested in prefixes */ |
362 |
|
|
|
363 |
|
|
addr = get_addr(rtm); |
364 |
|
|
plen = get_prefixlen(rtm); |
365 |
|
|
/* sanity check for plen */ |
366 |
|
|
/* as RFC2373, prefixlen is at least 4 */ |
367 |
|
|
if (plen < 4 || plen > 127) { |
368 |
|
|
log_info("new interface route's" |
369 |
|
|
" plen %d is invalid for a prefix", plen); |
370 |
|
|
break; |
371 |
|
|
} |
372 |
|
|
prefix = find_prefix(rai, addr, plen); |
373 |
|
|
if (prefix) { |
374 |
|
|
log_debug("new prefix(%s/%d) " |
375 |
|
|
"added on %s, " |
376 |
|
|
"but it was already in list", |
377 |
|
|
inet_ntop(AF_INET6, addr, |
378 |
|
|
addrbuf, INET6_ADDRSTRLEN), |
379 |
|
|
plen, rai->ifname); |
380 |
|
|
break; |
381 |
|
|
} |
382 |
|
|
make_prefix(rai, ifindex, addr, plen); |
383 |
|
|
break; |
384 |
|
|
case RTM_DELETE: |
385 |
|
|
/* init ifflags because it may have changed */ |
386 |
|
|
iflist[ifindex]->ifm_flags = |
387 |
|
|
if_getflags(ifindex, iflist[ifindex]->ifm_flags); |
388 |
|
|
|
389 |
|
|
if (sflag) |
390 |
|
|
break; |
391 |
|
|
|
392 |
|
|
addr = get_addr(rtm); |
393 |
|
|
plen = get_prefixlen(rtm); |
394 |
|
|
/* sanity check for plen */ |
395 |
|
|
/* as RFC2373, prefixlen is at least 4 */ |
396 |
|
|
if (plen < 4 || plen > 127) { |
397 |
|
|
log_info("deleted interface route's " |
398 |
|
|
"plen %d is invalid for a prefix", plen); |
399 |
|
|
break; |
400 |
|
|
} |
401 |
|
|
prefix = find_prefix(rai, addr, plen); |
402 |
|
|
if (prefix == NULL) { |
403 |
|
|
log_debug("prefix(%s/%d) was " |
404 |
|
|
"deleted on %s, " |
405 |
|
|
"but it was not in list", |
406 |
|
|
inet_ntop(AF_INET6, addr, |
407 |
|
|
addrbuf, INET6_ADDRSTRLEN), |
408 |
|
|
plen, rai->ifname); |
409 |
|
|
break; |
410 |
|
|
} |
411 |
|
|
delete_prefix(rai, prefix); |
412 |
|
|
break; |
413 |
|
|
case RTM_NEWADDR: |
414 |
|
|
case RTM_DELADDR: |
415 |
|
|
/* init ifflags because it may have changed */ |
416 |
|
|
iflist[ifindex]->ifm_flags = |
417 |
|
|
if_getflags(ifindex, iflist[ifindex]->ifm_flags); |
418 |
|
|
break; |
419 |
|
|
case RTM_IFINFO: |
420 |
|
|
iflist[ifindex]->ifm_flags = get_ifm_flags(rtm); |
421 |
|
|
break; |
422 |
|
|
default: |
423 |
|
|
/* should not reach here */ |
424 |
|
|
log_debug("unknown rtmsg %d on %s", |
425 |
|
|
type, if_indextoname(ifindex, ifname)); |
426 |
|
|
return; |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
/* check if an interface flag is changed */ |
430 |
|
|
if ((oldifflags & IFF_UP) != 0 && /* UP to DOWN */ |
431 |
|
|
(iflist[ifindex]->ifm_flags & IFF_UP) == 0) { |
432 |
|
|
log_info("interface %s becomes down. stop timer.", |
433 |
|
|
rai->ifname); |
434 |
|
|
evtimer_del(&rai->timer.ev); |
435 |
|
|
} else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */ |
436 |
|
|
(iflist[ifindex]->ifm_flags & IFF_UP) != 0) { |
437 |
|
|
log_info("interface %s becomes up. restart timer.", |
438 |
|
|
rai->ifname); |
439 |
|
|
|
440 |
|
|
rai->initcounter = 0; /* reset the counter */ |
441 |
|
|
rai->waiting = 0; /* XXX */ |
442 |
|
|
ra_timer_update(rai); |
443 |
|
|
evtimer_add(&rai->timer.ev, &rai->timer.tm); |
444 |
|
|
} |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
void |
448 |
|
|
sock_cb(int fd, short event, void *arg) |
449 |
|
|
{ |
450 |
|
|
ssize_t len; |
451 |
|
|
int *hlimp = NULL; |
452 |
|
|
struct icmp6_hdr *icp; |
453 |
|
|
int ifindex = 0; |
454 |
|
|
struct cmsghdr *cm; |
455 |
|
|
struct in6_pktinfo *pi = NULL; |
456 |
|
|
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; |
457 |
|
|
struct in6_addr dst = in6addr_any; |
458 |
|
|
|
459 |
|
|
/* |
460 |
|
|
* Get message. We reset msg_controllen since the field could |
461 |
|
|
* be modified if we had received a message before setting |
462 |
|
|
* receive options. |
463 |
|
|
*/ |
464 |
|
|
rcvmhdr.msg_controllen = rcvcmsgbuflen; |
465 |
|
|
if ((len = recvmsg(sock, &rcvmhdr, 0)) < 0) |
466 |
|
|
return; |
467 |
|
|
|
468 |
|
|
/* extract optional information via Advanced API */ |
469 |
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); |
470 |
|
|
cm; |
471 |
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { |
472 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
473 |
|
|
cm->cmsg_type == IPV6_PKTINFO && |
474 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { |
475 |
|
|
pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); |
476 |
|
|
ifindex = pi->ipi6_ifindex; |
477 |
|
|
dst = pi->ipi6_addr; |
478 |
|
|
} |
479 |
|
|
if (cm->cmsg_level == IPPROTO_IPV6 && |
480 |
|
|
cm->cmsg_type == IPV6_HOPLIMIT && |
481 |
|
|
cm->cmsg_len == CMSG_LEN(sizeof(int))) |
482 |
|
|
hlimp = (int *)CMSG_DATA(cm); |
483 |
|
|
} |
484 |
|
|
if (ifindex == 0) { |
485 |
|
|
log_warnx("failed to get receiving interface"); |
486 |
|
|
return; |
487 |
|
|
} |
488 |
|
|
if (hlimp == NULL) { |
489 |
|
|
log_warnx("failed to get receiving hop limit"); |
490 |
|
|
return; |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
/* |
494 |
|
|
* If we happen to receive data on an interface which is now down, |
495 |
|
|
* just discard the data. |
496 |
|
|
*/ |
497 |
|
|
if ((iflist[pi->ipi6_ifindex]->ifm_flags & IFF_UP) == 0) { |
498 |
|
|
log_info("received data on a disabled interface (%s)", |
499 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
500 |
|
|
return; |
501 |
|
|
} |
502 |
|
|
|
503 |
|
|
if (len < sizeof(struct icmp6_hdr)) { |
504 |
|
|
log_warnx("packet size(%zd) is too short", len); |
505 |
|
|
return; |
506 |
|
|
} |
507 |
|
|
|
508 |
|
|
icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; |
509 |
|
|
|
510 |
|
|
switch (icp->icmp6_type) { |
511 |
|
|
case ND_ROUTER_SOLICIT: |
512 |
|
|
/* |
513 |
|
|
* Message verification - RFC-2461 6.1.1 |
514 |
|
|
* XXX: these checks must be done in the kernel as well, |
515 |
|
|
* but we can't completely rely on them. |
516 |
|
|
*/ |
517 |
|
|
if (*hlimp != 255) { |
518 |
|
|
log_info("RS with invalid hop limit(%d) " |
519 |
|
|
"received from %s on %s", |
520 |
|
|
*hlimp, |
521 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
522 |
|
|
INET6_ADDRSTRLEN), |
523 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
524 |
|
|
return; |
525 |
|
|
} |
526 |
|
|
if (icp->icmp6_code) { |
527 |
|
|
log_info("RS with invalid ICMP6 code(%d) " |
528 |
|
|
"received from %s on %s", |
529 |
|
|
icp->icmp6_code, |
530 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
531 |
|
|
INET6_ADDRSTRLEN), |
532 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
533 |
|
|
return; |
534 |
|
|
} |
535 |
|
|
if (len < sizeof(struct nd_router_solicit)) { |
536 |
|
|
log_info("RS from %s on %s too short (len = %zd)", |
537 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
538 |
|
|
INET6_ADDRSTRLEN), |
539 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf), len); |
540 |
|
|
return; |
541 |
|
|
} |
542 |
|
|
rs_input(len, (struct nd_router_solicit *)icp, pi, &from); |
543 |
|
|
break; |
544 |
|
|
case ND_ROUTER_ADVERT: |
545 |
|
|
/* |
546 |
|
|
* Message verification - RFC-2461 6.1.2 |
547 |
|
|
* XXX: there's a same dilemma as above... |
548 |
|
|
*/ |
549 |
|
|
if (*hlimp != 255) { |
550 |
|
|
log_info("RA with invalid hop limit(%d) " |
551 |
|
|
"received from %s on %s", |
552 |
|
|
*hlimp, |
553 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
554 |
|
|
INET6_ADDRSTRLEN), |
555 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
556 |
|
|
return; |
557 |
|
|
} |
558 |
|
|
if (icp->icmp6_code) { |
559 |
|
|
log_info("RA with invalid ICMP6 code(%d) " |
560 |
|
|
"received from %s on %s", |
561 |
|
|
icp->icmp6_code, |
562 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
563 |
|
|
INET6_ADDRSTRLEN), |
564 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
565 |
|
|
return; |
566 |
|
|
} |
567 |
|
|
if (len < sizeof(struct nd_router_advert)) { |
568 |
|
|
log_info("RA from %s on %s too short (len = %zd)", |
569 |
|
|
inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, |
570 |
|
|
INET6_ADDRSTRLEN), |
571 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf), len); |
572 |
|
|
return; |
573 |
|
|
} |
574 |
|
|
ra_input(len, (struct nd_router_advert *)icp, pi, &from); |
575 |
|
|
break; |
576 |
|
|
default: |
577 |
|
|
/* |
578 |
|
|
* Note that this case is POSSIBLE, especially just |
579 |
|
|
* after invocation of the daemon. This is because we |
580 |
|
|
* could receive message after opening the socket and |
581 |
|
|
* before setting ICMP6 type filter(see sock_open()). |
582 |
|
|
*/ |
583 |
|
|
log_warnx("invalid icmp type(%d)", icp->icmp6_type); |
584 |
|
|
} |
585 |
|
|
} |
586 |
|
|
|
587 |
|
|
static void |
588 |
|
|
rs_input(int len, struct nd_router_solicit *rs, |
589 |
|
|
struct in6_pktinfo *pi, struct sockaddr_in6 *from) |
590 |
|
|
{ |
591 |
|
|
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; |
592 |
|
|
union nd_opts ndopts; |
593 |
|
|
struct rainfo *ra; |
594 |
|
|
|
595 |
|
|
log_debug("RS received from %s on %s", |
596 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
597 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
598 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
599 |
|
|
|
600 |
|
|
/* ND option check */ |
601 |
|
|
memset(&ndopts, 0, sizeof(ndopts)); |
602 |
|
|
SLIST_INIT(&ndopts.nd_opts_list); |
603 |
|
|
if (nd6_options((struct nd_opt_hdr *)(rs + 1), |
604 |
|
|
len - sizeof(struct nd_router_solicit), |
605 |
|
|
&ndopts, NDOPT_FLAG_SRCLINKADDR)) { |
606 |
|
|
log_debug("ND option check failed for an RS from %s on %s", |
607 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
608 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
609 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
610 |
|
|
return; |
611 |
|
|
} |
612 |
|
|
|
613 |
|
|
/* |
614 |
|
|
* If the IP source address is the unspecified address, there |
615 |
|
|
* must be no source link-layer address option in the message. |
616 |
|
|
* (RFC-2461 6.1.1) |
617 |
|
|
*/ |
618 |
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) && |
619 |
|
|
ndopts.nd_opts_src_lladdr) { |
620 |
|
|
log_warnx("RS from unspecified src on %s has a link-layer" |
621 |
|
|
" address option", |
622 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
623 |
|
|
goto done; |
624 |
|
|
} |
625 |
|
|
|
626 |
|
|
SLIST_FOREACH(ra, &ralist, entry) { |
627 |
|
|
if (pi->ipi6_ifindex == ra->ifindex) |
628 |
|
|
break; |
629 |
|
|
} |
630 |
|
|
if (ra == NULL) { |
631 |
|
|
log_info("RS received on non advertising interface(%s)", |
632 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
633 |
|
|
goto done; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
ra->rsinput++; /* increment statistics */ |
637 |
|
|
|
638 |
|
|
if (ndopts.nd_opts_src_lladdr) |
639 |
|
|
ra_output(ra, from); |
640 |
|
|
else { |
641 |
|
|
/* |
642 |
|
|
* Decide whether to send RA according to the rate-limit |
643 |
|
|
* consideration. |
644 |
|
|
*/ |
645 |
|
|
long delay; /* must not be greater than 1000000 */ |
646 |
|
|
struct timeval interval, now, min_delay, tm_tmp, next, |
647 |
|
|
computed; |
648 |
|
|
|
649 |
|
|
/* |
650 |
|
|
* If there is already a waiting RS packet, don't |
651 |
|
|
* update the timer. |
652 |
|
|
*/ |
653 |
|
|
if (ra->waiting++) |
654 |
|
|
goto done; |
655 |
|
|
|
656 |
|
|
gettimeofday(&now, NULL); |
657 |
|
|
|
658 |
|
|
/* |
659 |
|
|
* Compute a random delay. If the computed value |
660 |
|
|
* corresponds to a time later than the time the next |
661 |
|
|
* multicast RA is scheduled to be sent, ignore the random |
662 |
|
|
* delay and send the advertisement at the |
663 |
|
|
* already-scheduled time. RFC-2461 6.2.6 |
664 |
|
|
*/ |
665 |
|
|
delay = arc4random_uniform(MAX_RA_DELAY_TIME); |
666 |
|
|
interval.tv_sec = 0; |
667 |
|
|
interval.tv_usec = delay; |
668 |
|
|
/* |
669 |
|
|
* Could happen if an interface has transitioned from DOWN to |
670 |
|
|
* UP and we haven't re-enabled the timer yet. |
671 |
|
|
*/ |
672 |
|
|
if (!evtimer_pending(&ra->timer.ev, &next)) |
673 |
|
|
goto done; |
674 |
|
|
timeradd(&now, &interval, &computed); |
675 |
|
|
if (timercmp(&computed, &next, >)) { |
676 |
|
|
log_debug("random delay is larger than " |
677 |
|
|
"the rest of normal timer"); |
678 |
|
|
goto done; |
679 |
|
|
} |
680 |
|
|
|
681 |
|
|
/* |
682 |
|
|
* If we sent a multicast Router Advertisement within |
683 |
|
|
* the last MIN_DELAY_BETWEEN_RAS seconds, schedule |
684 |
|
|
* the advertisement to be sent at a time corresponding to |
685 |
|
|
* MIN_DELAY_BETWEEN_RAS plus the random value after the |
686 |
|
|
* previous advertisement was sent. |
687 |
|
|
*/ |
688 |
|
|
min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS; |
689 |
|
|
min_delay.tv_usec = 0; |
690 |
|
|
timeradd(&ra->lastsent, &min_delay, &tm_tmp); |
691 |
|
|
if (timercmp(&computed, &tm_tmp, <)) |
692 |
|
|
computed = tm_tmp; |
693 |
|
|
timersub(&computed, &now, &computed); |
694 |
|
|
evtimer_add(&ra->timer.ev, &computed); |
695 |
|
|
} |
696 |
|
|
|
697 |
|
|
done: |
698 |
|
|
free_ndopts(&ndopts); |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
static void |
702 |
|
|
ra_input(int len, struct nd_router_advert *ra, |
703 |
|
|
struct in6_pktinfo *pi, struct sockaddr_in6 *from) |
704 |
|
|
{ |
705 |
|
|
struct rainfo *rai; |
706 |
|
|
u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; |
707 |
|
|
union nd_opts ndopts; |
708 |
|
|
char *on_off[] = {"OFF", "ON"}; |
709 |
|
|
u_int32_t reachabletime, retranstimer, mtu; |
710 |
|
|
int inconsistent = 0; |
711 |
|
|
|
712 |
|
|
log_debug("RA received from %s on %s", |
713 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
714 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
715 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
716 |
|
|
|
717 |
|
|
/* ND option check */ |
718 |
|
|
memset(&ndopts, 0, sizeof(ndopts)); |
719 |
|
|
SLIST_INIT(&ndopts.nd_opts_list); |
720 |
|
|
if (nd6_options((struct nd_opt_hdr *)(ra + 1), |
721 |
|
|
len - sizeof(struct nd_router_advert), |
722 |
|
|
&ndopts, NDOPT_FLAG_SRCLINKADDR | NDOPT_FLAG_PREFIXINFO |
723 |
|
|
| NDOPT_FLAG_MTU | NDOPT_FLAG_ROUTE_INFO |
724 |
|
|
| NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL)) { |
725 |
|
|
log_warnx("ND option check failed for an RA from %s on %s", |
726 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
727 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
728 |
|
|
if_indextoname(pi->ipi6_ifindex, ifnamebuf)); |
729 |
|
|
return; |
730 |
|
|
} |
731 |
|
|
|
732 |
|
|
/* |
733 |
|
|
* RA consistency check according to RFC-2461 6.2.7 |
734 |
|
|
*/ |
735 |
|
|
if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) |
736 |
|
|
goto done; /* not our interface */ |
737 |
|
|
|
738 |
|
|
rai->rainput++; /* increment statistics */ |
739 |
|
|
|
740 |
|
|
/* Cur Hop Limit value */ |
741 |
|
|
if (ra->nd_ra_curhoplimit && rai->hoplimit && |
742 |
|
|
ra->nd_ra_curhoplimit != rai->hoplimit) { |
743 |
|
|
log_info("CurHopLimit inconsistent on %s: %d from %s," |
744 |
|
|
" %d from us", |
745 |
|
|
rai->ifname, |
746 |
|
|
ra->nd_ra_curhoplimit, |
747 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
748 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
749 |
|
|
rai->hoplimit); |
750 |
|
|
inconsistent++; |
751 |
|
|
} |
752 |
|
|
/* M flag */ |
753 |
|
|
if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != |
754 |
|
|
rai->managedflg) { |
755 |
|
|
log_info("M flag inconsistent on %s: %s from %s, %s from us", |
756 |
|
|
rai->ifname, on_off[rai->managedflg ? 0 : 1], |
757 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
758 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
759 |
|
|
on_off[rai->managedflg ? 1 : 0]); |
760 |
|
|
inconsistent++; |
761 |
|
|
} |
762 |
|
|
/* O flag */ |
763 |
|
|
if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != |
764 |
|
|
rai->otherflg) { |
765 |
|
|
log_info("O flag inconsistent on %s: %s from %s, %s from us", |
766 |
|
|
rai->ifname, on_off[rai->otherflg ? 0 : 1], |
767 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
768 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
769 |
|
|
on_off[rai->otherflg ? 1 : 0]); |
770 |
|
|
inconsistent++; |
771 |
|
|
} |
772 |
|
|
/* Reachable Time */ |
773 |
|
|
reachabletime = ntohl(ra->nd_ra_reachable); |
774 |
|
|
if (reachabletime && rai->reachabletime && |
775 |
|
|
reachabletime != rai->reachabletime) { |
776 |
|
|
log_info("ReachableTime inconsistent on %s:" |
777 |
|
|
" %d from %s, %d from us", |
778 |
|
|
rai->ifname, reachabletime, |
779 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
780 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
781 |
|
|
rai->reachabletime); |
782 |
|
|
inconsistent++; |
783 |
|
|
} |
784 |
|
|
/* Retrans Timer */ |
785 |
|
|
retranstimer = ntohl(ra->nd_ra_retransmit); |
786 |
|
|
if (retranstimer && rai->retranstimer && |
787 |
|
|
retranstimer != rai->retranstimer) { |
788 |
|
|
log_info("RetransTimer inconsistent on %s:" |
789 |
|
|
" %d from %s, %d from us", |
790 |
|
|
rai->ifname, retranstimer, |
791 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
792 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
793 |
|
|
rai->retranstimer); |
794 |
|
|
inconsistent++; |
795 |
|
|
} |
796 |
|
|
/* Values in the MTU options */ |
797 |
|
|
if (ndopts.nd_opts_mtu) { |
798 |
|
|
mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu); |
799 |
|
|
if (mtu && rai->linkmtu && mtu != rai->linkmtu) { |
800 |
|
|
log_info("MTU option value inconsistent on %s:" |
801 |
|
|
" %d from %s, %d from us", |
802 |
|
|
rai->ifname, mtu, |
803 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
804 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
805 |
|
|
rai->linkmtu); |
806 |
|
|
inconsistent++; |
807 |
|
|
} |
808 |
|
|
} |
809 |
|
|
/* Preferred and Valid Lifetimes for prefixes */ |
810 |
|
|
{ |
811 |
|
|
struct nd_opt *optp; |
812 |
|
|
|
813 |
|
|
if (ndopts.nd_opts_pi) |
814 |
|
|
if (prefix_check(ndopts.nd_opts_pi, rai, from)) |
815 |
|
|
inconsistent++; |
816 |
|
|
SLIST_FOREACH(optp, &ndopts.nd_opts_list, entry) { |
817 |
|
|
if (prefix_check((struct nd_opt_prefix_info *)optp->opt, |
818 |
|
|
rai, from)) |
819 |
|
|
inconsistent++; |
820 |
|
|
} |
821 |
|
|
} |
822 |
|
|
|
823 |
|
|
if (inconsistent) |
824 |
|
|
rai->rainconsistent++; |
825 |
|
|
|
826 |
|
|
done: |
827 |
|
|
free_ndopts(&ndopts); |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
/* return a non-zero value if the received prefix is inconsistent with ours */ |
831 |
|
|
static int |
832 |
|
|
prefix_check(struct nd_opt_prefix_info *pinfo, |
833 |
|
|
struct rainfo *rai, struct sockaddr_in6 *from) |
834 |
|
|
{ |
835 |
|
|
time_t preferred_time, valid_time; |
836 |
|
|
struct prefix *pp; |
837 |
|
|
int inconsistent = 0; |
838 |
|
|
u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN]; |
839 |
|
|
struct timeval now; |
840 |
|
|
|
841 |
|
|
/* |
842 |
|
|
* log if the advertised prefix has link-local scope(sanity check?) |
843 |
|
|
*/ |
844 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) |
845 |
|
|
log_info("link-local prefix %s/%d is advertised " |
846 |
|
|
"from %s on %s", |
847 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
848 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
849 |
|
|
pinfo->nd_opt_pi_prefix_len, |
850 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
851 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
852 |
|
|
rai->ifname); |
853 |
|
|
|
854 |
|
|
if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix, |
855 |
|
|
pinfo->nd_opt_pi_prefix_len)) == NULL) { |
856 |
|
|
log_info("prefix %s/%d from %s on %s is not in our list", |
857 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
858 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
859 |
|
|
pinfo->nd_opt_pi_prefix_len, |
860 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
861 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
862 |
|
|
rai->ifname); |
863 |
|
|
return(0); |
864 |
|
|
} |
865 |
|
|
|
866 |
|
|
preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time); |
867 |
|
|
if (pp->pltimeexpire) { |
868 |
|
|
/* |
869 |
|
|
* The lifetime is decremented in real time, so we should |
870 |
|
|
* compare the expiration time. |
871 |
|
|
* (RFC 2461 Section 6.2.7.) |
872 |
|
|
* XXX: can we really expect that all routers on the link |
873 |
|
|
* have synchronized clocks? |
874 |
|
|
*/ |
875 |
|
|
gettimeofday(&now, NULL); |
876 |
|
|
preferred_time += now.tv_sec; |
877 |
|
|
|
878 |
|
|
if (rai->clockskew && |
879 |
|
|
llabs(preferred_time - pp->pltimeexpire) > rai->clockskew) { |
880 |
|
|
log_info("preferred lifetime for %s/%d" |
881 |
|
|
" (decr. in real time) inconsistent on %s:" |
882 |
|
|
" %lld from %s, %lld from us", |
883 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
884 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
885 |
|
|
pinfo->nd_opt_pi_prefix_len, |
886 |
|
|
rai->ifname, (long long)preferred_time, |
887 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
888 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
889 |
|
|
(long long)pp->pltimeexpire); |
890 |
|
|
inconsistent++; |
891 |
|
|
} |
892 |
|
|
} else if (preferred_time != pp->preflifetime) |
893 |
|
|
log_info("preferred lifetime for %s/%d" |
894 |
|
|
" inconsistent on %s:" |
895 |
|
|
" %lld from %s, %d from us", |
896 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
897 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
898 |
|
|
pinfo->nd_opt_pi_prefix_len, |
899 |
|
|
rai->ifname, (long long)preferred_time, |
900 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
901 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
902 |
|
|
pp->preflifetime); |
903 |
|
|
|
904 |
|
|
valid_time = ntohl(pinfo->nd_opt_pi_valid_time); |
905 |
|
|
if (pp->vltimeexpire) { |
906 |
|
|
gettimeofday(&now, NULL); |
907 |
|
|
valid_time += now.tv_sec; |
908 |
|
|
|
909 |
|
|
if (rai->clockskew && |
910 |
|
|
llabs(valid_time - pp->vltimeexpire) > rai->clockskew) { |
911 |
|
|
log_info("valid lifetime for %s/%d" |
912 |
|
|
" (decr. in real time) inconsistent on %s:" |
913 |
|
|
" %lld from %s, %lld from us", |
914 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
915 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
916 |
|
|
pinfo->nd_opt_pi_prefix_len, |
917 |
|
|
rai->ifname, (long long)preferred_time, |
918 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
919 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
920 |
|
|
(long long)pp->vltimeexpire); |
921 |
|
|
inconsistent++; |
922 |
|
|
} |
923 |
|
|
} else if (valid_time != pp->validlifetime) { |
924 |
|
|
log_info("valid lifetime for %s/%d" |
925 |
|
|
" inconsistent on %s:" |
926 |
|
|
" %lld from %s, %d from us", |
927 |
|
|
inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix, |
928 |
|
|
prefixbuf, INET6_ADDRSTRLEN), |
929 |
|
|
pinfo->nd_opt_pi_prefix_len, |
930 |
|
|
rai->ifname, (long long)valid_time, |
931 |
|
|
inet_ntop(AF_INET6, &from->sin6_addr, |
932 |
|
|
ntopbuf, INET6_ADDRSTRLEN), |
933 |
|
|
pp->validlifetime); |
934 |
|
|
inconsistent++; |
935 |
|
|
} |
936 |
|
|
|
937 |
|
|
return(inconsistent); |
938 |
|
|
} |
939 |
|
|
|
940 |
|
|
struct prefix * |
941 |
|
|
find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen) |
942 |
|
|
{ |
943 |
|
|
struct prefix *pp; |
944 |
|
|
int bytelen, bitlen; |
945 |
|
|
u_char bitmask; |
946 |
|
|
|
947 |
|
|
TAILQ_FOREACH(pp, &rai->prefixes, entry) { |
948 |
|
|
if (plen != pp->prefixlen) |
949 |
|
|
continue; |
950 |
|
|
bytelen = plen / 8; |
951 |
|
|
bitlen = plen % 8; |
952 |
|
|
bitmask = 0xff << (8 - bitlen); |
953 |
|
|
if (memcmp(prefix, &pp->prefix, bytelen)) |
954 |
|
|
continue; |
955 |
|
|
if (bitlen == 0 || |
956 |
|
|
((prefix->s6_addr[bytelen] & bitmask) == |
957 |
|
|
(pp->prefix.s6_addr[bytelen] & bitmask))) { |
958 |
|
|
return(pp); |
959 |
|
|
} |
960 |
|
|
} |
961 |
|
|
|
962 |
|
|
return(NULL); |
963 |
|
|
} |
964 |
|
|
|
965 |
|
|
static int |
966 |
|
|
nd6_options(struct nd_opt_hdr *hdr, int limit, |
967 |
|
|
union nd_opts *ndopts, u_int32_t optflags) |
968 |
|
|
{ |
969 |
|
|
int optlen = 0; |
970 |
|
|
|
971 |
|
|
for (; limit > 0; limit -= optlen) { |
972 |
|
|
if (limit < sizeof(struct nd_opt_hdr)) { |
973 |
|
|
log_info("short option header"); |
974 |
|
|
goto bad; |
975 |
|
|
} |
976 |
|
|
|
977 |
|
|
hdr = (struct nd_opt_hdr *)((char *)hdr + optlen); |
978 |
|
|
if (hdr->nd_opt_len == 0) { |
979 |
|
|
log_warnx("bad ND option length(0) (type = %d)", |
980 |
|
|
hdr->nd_opt_type); |
981 |
|
|
goto bad; |
982 |
|
|
} |
983 |
|
|
optlen = hdr->nd_opt_len << 3; |
984 |
|
|
if (optlen > limit) { |
985 |
|
|
log_info("short option"); |
986 |
|
|
goto bad; |
987 |
|
|
} |
988 |
|
|
|
989 |
|
|
if (hdr->nd_opt_type > ND_OPT_MTU && |
990 |
|
|
hdr->nd_opt_type != ND_OPT_ROUTE_INFO && |
991 |
|
|
hdr->nd_opt_type != ND_OPT_RDNSS && |
992 |
|
|
hdr->nd_opt_type != ND_OPT_DNSSL) |
993 |
|
|
{ |
994 |
|
|
log_info("unknown ND option(type %d)", |
995 |
|
|
hdr->nd_opt_type); |
996 |
|
|
continue; |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) { |
1000 |
|
|
log_info("unexpected ND option(type %d)", |
1001 |
|
|
hdr->nd_opt_type); |
1002 |
|
|
continue; |
1003 |
|
|
} |
1004 |
|
|
|
1005 |
|
|
/* |
1006 |
|
|
* Option length check. Do it here for all fixed-length |
1007 |
|
|
* options. |
1008 |
|
|
*/ |
1009 |
|
|
if ((hdr->nd_opt_type == ND_OPT_RDNSS && (optlen < 24 || |
1010 |
|
|
((optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0))) || |
1011 |
|
|
(hdr->nd_opt_type == ND_OPT_DNSSL && optlen < 16) || |
1012 |
|
|
(hdr->nd_opt_type == ND_OPT_MTU && |
1013 |
|
|
(optlen != sizeof(struct nd_opt_mtu))) || |
1014 |
|
|
((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION && |
1015 |
|
|
optlen != sizeof(struct nd_opt_prefix_info)))) { |
1016 |
|
|
log_info("invalid option length"); |
1017 |
|
|
continue; |
1018 |
|
|
} |
1019 |
|
|
|
1020 |
|
|
switch (hdr->nd_opt_type) { |
1021 |
|
|
case ND_OPT_SOURCE_LINKADDR: |
1022 |
|
|
ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; |
1023 |
|
|
break; |
1024 |
|
|
case ND_OPT_TARGET_LINKADDR: |
1025 |
|
|
case ND_OPT_REDIRECTED_HEADER: |
1026 |
|
|
case ND_OPT_ROUTE_INFO: |
1027 |
|
|
case ND_OPT_RDNSS: |
1028 |
|
|
case ND_OPT_DNSSL: |
1029 |
|
|
break; /* we don't care about these options */ |
1030 |
|
|
case ND_OPT_MTU: |
1031 |
|
|
if (ndopts->nd_opt_array[hdr->nd_opt_type]) { |
1032 |
|
|
log_info("duplicated ND option (type = %d)", |
1033 |
|
|
hdr->nd_opt_type); |
1034 |
|
|
} |
1035 |
|
|
ndopts->nd_opt_array[hdr->nd_opt_type] = hdr; |
1036 |
|
|
break; |
1037 |
|
|
case ND_OPT_PREFIX_INFORMATION: |
1038 |
|
|
{ |
1039 |
|
|
struct nd_opt *pfx; |
1040 |
|
|
|
1041 |
|
|
if (ndopts->nd_opts_pi == 0) { |
1042 |
|
|
ndopts->nd_opts_pi = |
1043 |
|
|
(struct nd_opt_prefix_info *)hdr; |
1044 |
|
|
continue; |
1045 |
|
|
} |
1046 |
|
|
if ((pfx = malloc(sizeof(*pfx))) == NULL) { |
1047 |
|
|
log_warn(NULL); |
1048 |
|
|
goto bad; |
1049 |
|
|
} |
1050 |
|
|
|
1051 |
|
|
pfx->opt = hdr; |
1052 |
|
|
SLIST_INSERT_HEAD(&ndopts->nd_opts_list, pfx, entry); |
1053 |
|
|
|
1054 |
|
|
break; |
1055 |
|
|
} |
1056 |
|
|
default: /* impossible */ |
1057 |
|
|
break; |
1058 |
|
|
} |
1059 |
|
|
} |
1060 |
|
|
|
1061 |
|
|
return(0); |
1062 |
|
|
|
1063 |
|
|
bad: |
1064 |
|
|
free_ndopts(ndopts); |
1065 |
|
|
|
1066 |
|
|
return(-1); |
1067 |
|
|
} |
1068 |
|
|
|
1069 |
|
|
static void |
1070 |
|
|
free_ndopts(union nd_opts *ndopts) |
1071 |
|
|
{ |
1072 |
|
|
struct nd_opt *opt; |
1073 |
|
|
|
1074 |
|
|
while (!SLIST_EMPTY(&ndopts->nd_opts_list)) { |
1075 |
|
|
opt = SLIST_FIRST(&ndopts->nd_opts_list); |
1076 |
|
|
SLIST_REMOVE_HEAD(&ndopts->nd_opts_list, entry); |
1077 |
|
|
free(opt); |
1078 |
|
|
} |
1079 |
|
|
} |
1080 |
|
|
|
1081 |
|
|
static void |
1082 |
|
|
sock_open(void) |
1083 |
|
|
{ |
1084 |
|
|
struct rainfo *ra; |
1085 |
|
|
struct icmp6_filter filt; |
1086 |
|
|
struct ipv6_mreq mreq; |
1087 |
|
|
int on; |
1088 |
|
|
/* XXX: should be max MTU attached to the node */ |
1089 |
|
|
static u_char answer[1500]; |
1090 |
|
|
|
1091 |
|
|
rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + |
1092 |
|
|
CMSG_SPACE(sizeof(int)); |
1093 |
|
|
rcvcmsgbuf = malloc(rcvcmsgbuflen); |
1094 |
|
|
if (rcvcmsgbuf == NULL) |
1095 |
|
|
fatal(NULL); |
1096 |
|
|
|
1097 |
|
|
sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + |
1098 |
|
|
CMSG_SPACE(sizeof(int)); |
1099 |
|
|
sndcmsgbuf = malloc(sndcmsgbuflen); |
1100 |
|
|
if (sndcmsgbuf == NULL) |
1101 |
|
|
fatal(NULL); |
1102 |
|
|
|
1103 |
|
|
if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) |
1104 |
|
|
fatal("socket"); |
1105 |
|
|
|
1106 |
|
|
/* specify to tell receiving interface */ |
1107 |
|
|
on = 1; |
1108 |
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) |
1109 |
|
|
< 0) |
1110 |
|
|
fatal("IPV6_RECVPKTINFO"); |
1111 |
|
|
|
1112 |
|
|
on = 1; |
1113 |
|
|
/* specify to tell value of hoplimit field of received IP6 hdr */ |
1114 |
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) |
1115 |
|
|
< 0) |
1116 |
|
|
fatal("IPV6_RECVHOPLIMIT"); |
1117 |
|
|
|
1118 |
|
|
ICMP6_FILTER_SETBLOCKALL(&filt); |
1119 |
|
|
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); |
1120 |
|
|
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); |
1121 |
|
|
if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) |
1122 |
|
|
< 0) |
1123 |
|
|
fatal("ICMP6_FILTER"); |
1124 |
|
|
|
1125 |
|
|
/* |
1126 |
|
|
* join all routers multicast address on each advertising interface. |
1127 |
|
|
*/ |
1128 |
|
|
if (inet_pton(AF_INET6, ALLROUTERS_LINK, &mreq.ipv6mr_multiaddr.s6_addr) |
1129 |
|
|
!= 1) |
1130 |
|
|
fatal("inet_pton"); |
1131 |
|
|
SLIST_FOREACH(ra, &ralist, entry) { |
1132 |
|
|
mreq.ipv6mr_interface = ra->ifindex; |
1133 |
|
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, |
1134 |
|
|
sizeof(mreq)) < 0) |
1135 |
|
|
fatal("IPV6_JOIN_GROUP(link) on %s", ra->ifname); |
1136 |
|
|
} |
1137 |
|
|
|
1138 |
|
|
/* initialize msghdr for receiving packets */ |
1139 |
|
|
rcviov[0].iov_base = answer; |
1140 |
|
|
rcviov[0].iov_len = sizeof(answer); |
1141 |
|
|
rcvmhdr.msg_name = &from; |
1142 |
|
|
rcvmhdr.msg_namelen = sizeof(from); |
1143 |
|
|
rcvmhdr.msg_iov = rcviov; |
1144 |
|
|
rcvmhdr.msg_iovlen = 1; |
1145 |
|
|
rcvmhdr.msg_control = rcvcmsgbuf; |
1146 |
|
|
rcvmhdr.msg_controllen = rcvcmsgbuflen; |
1147 |
|
|
|
1148 |
|
|
/* initialize msghdr for sending packets */ |
1149 |
|
|
sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); |
1150 |
|
|
sndmhdr.msg_iov = sndiov; |
1151 |
|
|
sndmhdr.msg_iovlen = 1; |
1152 |
|
|
sndmhdr.msg_control = sndcmsgbuf; |
1153 |
|
|
sndmhdr.msg_controllen = sndcmsgbuflen; |
1154 |
|
|
} |
1155 |
|
|
|
1156 |
|
|
/* open a routing socket to watch the routing table */ |
1157 |
|
|
static void |
1158 |
|
|
rtsock_open(void) |
1159 |
|
|
{ |
1160 |
|
|
unsigned int rtfilter; |
1161 |
|
|
|
1162 |
|
|
if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) |
1163 |
|
|
fatal("socket"); |
1164 |
|
|
|
1165 |
|
|
rtfilter = |
1166 |
|
|
ROUTE_FILTER(RTM_ADD) | |
1167 |
|
|
ROUTE_FILTER(RTM_DELETE) | |
1168 |
|
|
ROUTE_FILTER(RTM_NEWADDR) | |
1169 |
|
|
ROUTE_FILTER(RTM_DELADDR) | |
1170 |
|
|
ROUTE_FILTER(RTM_IFINFO); |
1171 |
|
|
|
1172 |
|
|
if (setsockopt(rtsock, PF_ROUTE, ROUTE_MSGFILTER, |
1173 |
|
|
&rtfilter, sizeof(rtfilter)) == -1) |
1174 |
|
|
fatal("setsockopt(ROUTE_MSGFILTER)"); |
1175 |
|
|
|
1176 |
|
|
rtsockbuflen = 2048; |
1177 |
|
|
rtsockbuf = malloc(rtsockbuflen); |
1178 |
|
|
if (rtsockbuf == NULL) |
1179 |
|
|
fatal(NULL); |
1180 |
|
|
} |
1181 |
|
|
|
1182 |
|
|
static struct rainfo * |
1183 |
|
|
if_indextorainfo(int index) |
1184 |
|
|
{ |
1185 |
|
|
struct rainfo *rai; |
1186 |
|
|
|
1187 |
|
|
SLIST_FOREACH(rai, &ralist, entry) { |
1188 |
|
|
if (rai->ifindex == index) |
1189 |
|
|
return(rai); |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
return(NULL); /* search failed */ |
1193 |
|
|
} |
1194 |
|
|
|
1195 |
|
|
static void |
1196 |
|
|
ra_output(struct rainfo *rainfo, struct sockaddr_in6 *to) |
1197 |
|
|
{ |
1198 |
|
|
struct cmsghdr *cm; |
1199 |
|
|
struct in6_pktinfo *pi; |
1200 |
|
|
ssize_t len; |
1201 |
|
|
|
1202 |
|
|
if ((iflist[rainfo->ifindex]->ifm_flags & IFF_UP) == 0) { |
1203 |
|
|
log_debug("%s is not up, skip sending RA", rainfo->ifname); |
1204 |
|
|
return; |
1205 |
|
|
} |
1206 |
|
|
|
1207 |
|
|
make_packet(rainfo); /* XXX: inefficient */ |
1208 |
|
|
|
1209 |
|
|
sndmhdr.msg_name = to; |
1210 |
|
|
sndmhdr.msg_iov[0].iov_base = rainfo->ra_data; |
1211 |
|
|
sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen; |
1212 |
|
|
|
1213 |
|
|
cm = CMSG_FIRSTHDR(&sndmhdr); |
1214 |
|
|
/* specify the outgoing interface */ |
1215 |
|
|
cm->cmsg_level = IPPROTO_IPV6; |
1216 |
|
|
cm->cmsg_type = IPV6_PKTINFO; |
1217 |
|
|
cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); |
1218 |
|
|
pi = (struct in6_pktinfo *)CMSG_DATA(cm); |
1219 |
|
|
memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ |
1220 |
|
|
pi->ipi6_ifindex = rainfo->ifindex; |
1221 |
|
|
|
1222 |
|
|
/* specify the hop limit of the packet */ |
1223 |
|
|
{ |
1224 |
|
|
int hoplimit = 255; |
1225 |
|
|
|
1226 |
|
|
cm = CMSG_NXTHDR(&sndmhdr, cm); |
1227 |
|
|
cm->cmsg_level = IPPROTO_IPV6; |
1228 |
|
|
cm->cmsg_type = IPV6_HOPLIMIT; |
1229 |
|
|
cm->cmsg_len = CMSG_LEN(sizeof(int)); |
1230 |
|
|
memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); |
1231 |
|
|
} |
1232 |
|
|
|
1233 |
|
|
log_debug("send RA on %s, # of waitings = %u", |
1234 |
|
|
rainfo->ifname, rainfo->waiting); |
1235 |
|
|
|
1236 |
|
|
len = sendmsg(sock, &sndmhdr, 0); |
1237 |
|
|
if (len < 0) { |
1238 |
|
|
log_warn("sendmsg on %s", rainfo->ifname); |
1239 |
|
|
return; |
1240 |
|
|
} |
1241 |
|
|
|
1242 |
|
|
rainfo->raoutput++; |
1243 |
|
|
|
1244 |
|
|
if (memcmp(to, &sin6_allnodes, sizeof(sin6_allnodes)) == 0) { |
1245 |
|
|
/* update counter */ |
1246 |
|
|
if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS) |
1247 |
|
|
rainfo->initcounter++; |
1248 |
|
|
/* update timestamp */ |
1249 |
|
|
gettimeofday(&rainfo->lastsent, NULL); |
1250 |
|
|
|
1251 |
|
|
/* reset waiting counter */ |
1252 |
|
|
rainfo->waiting = 0; |
1253 |
|
|
} |
1254 |
|
|
} |
1255 |
|
|
|
1256 |
|
|
/* process RA timer */ |
1257 |
|
|
void |
1258 |
|
|
timer_cb(int fd, short event, void *data) |
1259 |
|
|
{ |
1260 |
|
|
struct rainfo *rai = (struct rainfo *)data; |
1261 |
|
|
|
1262 |
|
|
log_debug("RA timer on %s is expired", rai->ifname); |
1263 |
|
|
|
1264 |
|
|
ra_output(rai, &sin6_allnodes); |
1265 |
|
|
|
1266 |
|
|
ra_timer_update(rai); |
1267 |
|
|
evtimer_add(&rai->timer.ev, &rai->timer.tm); |
1268 |
|
|
} |
1269 |
|
|
|
1270 |
|
|
/* update RA timer */ |
1271 |
|
|
void |
1272 |
|
|
ra_timer_update(struct rainfo *rai) |
1273 |
|
|
{ |
1274 |
|
|
struct timeval *tm = &rai->timer.tm; |
1275 |
|
|
long interval; |
1276 |
|
|
|
1277 |
|
|
/* |
1278 |
|
|
* Whenever a multicast advertisement is sent from an interface, |
1279 |
|
|
* the timer is reset to a uniformly-distributed random value |
1280 |
|
|
* between the interface's configured MinRtrAdvInterval and |
1281 |
|
|
* MaxRtrAdvInterval (RFC2461 6.2.4). |
1282 |
|
|
*/ |
1283 |
|
|
interval = rai->mininterval; |
1284 |
|
|
interval += arc4random_uniform(rai->maxinterval - rai->mininterval); |
1285 |
|
|
|
1286 |
|
|
/* |
1287 |
|
|
* For the first few advertisements (up to |
1288 |
|
|
* MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval |
1289 |
|
|
* is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer |
1290 |
|
|
* SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead. |
1291 |
|
|
* (RFC-2461 6.2.4) |
1292 |
|
|
*/ |
1293 |
|
|
if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS && |
1294 |
|
|
interval > MAX_INITIAL_RTR_ADVERT_INTERVAL) |
1295 |
|
|
interval = MAX_INITIAL_RTR_ADVERT_INTERVAL; |
1296 |
|
|
|
1297 |
|
|
tm->tv_sec = interval; |
1298 |
|
|
tm->tv_usec = 0; |
1299 |
|
|
|
1300 |
|
|
log_debug("RA timer on %s set to %lld.%lds", rai->ifname, |
1301 |
|
|
(long long)tm->tv_sec, tm->tv_usec); |
1302 |
|
|
} |
1303 |
|
|
|
1304 |
|
|
int |
1305 |
|
|
rdaemon(int devnull) |
1306 |
|
|
{ |
1307 |
|
|
if (devnull == -1) { |
1308 |
|
|
errno = EBADF; |
1309 |
|
|
return (-1); |
1310 |
|
|
} |
1311 |
|
|
if (fcntl(devnull, F_GETFL) == -1) |
1312 |
|
|
return (-1); |
1313 |
|
|
|
1314 |
|
|
switch (fork()) { |
1315 |
|
|
case -1: |
1316 |
|
|
return (-1); |
1317 |
|
|
case 0: |
1318 |
|
|
break; |
1319 |
|
|
default: |
1320 |
|
|
_exit(0); |
1321 |
|
|
} |
1322 |
|
|
|
1323 |
|
|
if (setsid() == -1) |
1324 |
|
|
return (-1); |
1325 |
|
|
|
1326 |
|
|
(void)dup2(devnull, STDIN_FILENO); |
1327 |
|
|
(void)dup2(devnull, STDOUT_FILENO); |
1328 |
|
|
(void)dup2(devnull, STDERR_FILENO); |
1329 |
|
|
if (devnull > 2) |
1330 |
|
|
(void)close(devnull); |
1331 |
|
|
|
1332 |
|
|
return (0); |
1333 |
|
|
} |