]>
Commit | Line | Data |
---|---|---|
7ba0088d A |
1 | /* $KAME: config.c,v 1.37 2001/05/25 07:34:00 itojun Exp $ */ |
2 | ||
3 | /* | |
4 | * Copyright (C) 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/rtadvd/config.c,v 1.3.2.3 2001/07/03 11:02:14 ume Exp $ | |
32 | */ | |
33 | ||
34 | #include <sys/param.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/time.h> | |
38 | #include <sys/sysctl.h> | |
39 | ||
40 | #include <net/if.h> | |
41 | #if defined(__FreeBSD__) && __FreeBSD__ >= 3 | |
42 | #include <net/if_var.h> | |
43 | #endif /* __FreeBSD__ >= 3 */ | |
44 | #include <net/route.h> | |
45 | #include <net/if_dl.h> | |
46 | ||
47 | #include <netinet/in.h> | |
48 | #include <netinet/in_var.h> | |
49 | #include <netinet/ip6.h> | |
50 | #include <netinet6/ip6_var.h> | |
51 | #include <netinet/icmp6.h> | |
52 | #ifdef MIP6 | |
53 | #include <netinet6/mip6.h> | |
54 | #endif | |
55 | ||
56 | #include <arpa/inet.h> | |
57 | ||
58 | #include <stdio.h> | |
59 | #include <syslog.h> | |
60 | #include <errno.h> | |
61 | #include <string.h> | |
62 | #include <stdlib.h> | |
63 | #if defined(__NetBSD__) || defined(__OpenBSD__) | |
64 | #include <search.h> | |
65 | #endif | |
66 | #include <unistd.h> | |
67 | #include <ifaddrs.h> | |
68 | ||
69 | #include "rtadvd.h" | |
70 | #include "advcap.h" | |
71 | #include "timer.h" | |
72 | #include "if.h" | |
73 | #include "config.h" | |
74 | ||
75 | static void makeentry __P((char *, int, char *, int)); | |
76 | static void get_prefix __P((struct rainfo *)); | |
77 | static int getinet6sysctl __P((int)); | |
78 | ||
79 | extern struct rainfo *ralist; | |
80 | ||
81 | void | |
82 | getconfig(intface) | |
83 | char *intface; | |
84 | { | |
85 | int stat, pfxs, i; | |
86 | char tbuf[BUFSIZ]; | |
87 | struct rainfo *tmp; | |
88 | long val; | |
89 | long long val64; | |
90 | char buf[BUFSIZ]; | |
91 | char *bp = buf; | |
92 | char *addr; | |
93 | static int forwarding = -1; | |
94 | ||
95 | #define MUSTHAVE(var, cap) \ | |
96 | do { \ | |
97 | int t; \ | |
98 | if ((t = agetnum(cap)) < 0) { \ | |
99 | fprintf(stderr, "rtadvd: need %s for interface %s\n", \ | |
100 | cap, intface); \ | |
101 | exit(1); \ | |
102 | } \ | |
103 | var = t; \ | |
104 | } while (0) | |
105 | #define MAYHAVE(var, cap, def) \ | |
106 | do { \ | |
107 | if ((var = agetnum(cap)) < 0) \ | |
108 | var = def; \ | |
109 | } while (0) | |
110 | ||
111 | if ((stat = agetent(tbuf, intface)) <= 0) { | |
112 | memset(tbuf, 0, sizeof(tbuf)); | |
113 | syslog(LOG_INFO, | |
114 | "<%s> %s isn't defined in the configuration file" | |
115 | " or the configuration file doesn't exist." | |
116 | " Treat it as default", | |
117 | __FUNCTION__, intface); | |
118 | } | |
119 | ||
120 | tmp = (struct rainfo *)malloc(sizeof(*ralist)); | |
121 | memset(tmp, 0, sizeof(*tmp)); | |
122 | tmp->prefix.next = tmp->prefix.prev = &tmp->prefix; | |
123 | tmp->route.next = tmp->route.prev = &tmp->route; | |
124 | ||
125 | /* check if we are allowed to forward packets (if not determined) */ | |
126 | if (forwarding < 0) { | |
127 | if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0) | |
128 | exit(1); | |
129 | } | |
130 | ||
131 | /* get interface information */ | |
132 | if (agetflag("nolladdr")) | |
133 | tmp->advlinkopt = 0; | |
134 | else | |
135 | tmp->advlinkopt = 1; | |
136 | if (tmp->advlinkopt) { | |
137 | if ((tmp->sdl = if_nametosdl(intface)) == NULL) { | |
138 | syslog(LOG_ERR, | |
139 | "<%s> can't get information of %s", | |
140 | __FUNCTION__, intface); | |
141 | exit(1); | |
142 | } | |
143 | tmp->ifindex = tmp->sdl->sdl_index; | |
144 | } else | |
145 | tmp->ifindex = if_nametoindex(intface); | |
146 | strncpy(tmp->ifname, intface, sizeof(tmp->ifname)); | |
147 | if ((tmp->phymtu = if_getmtu(intface)) == 0) { | |
148 | tmp->phymtu = IPV6_MMTU; | |
149 | syslog(LOG_WARNING, | |
150 | "<%s> can't get interface mtu of %s. Treat as %d", | |
151 | __FUNCTION__, intface, IPV6_MMTU); | |
152 | } | |
153 | ||
154 | /* | |
155 | * set router configuration variables. | |
156 | */ | |
157 | MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL); | |
158 | if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) { | |
159 | syslog(LOG_ERR, | |
160 | "<%s> maxinterval must be between %e and %u", | |
161 | __FUNCTION__, MIN_MAXINTERVAL, MAX_MAXINTERVAL); | |
162 | exit(1); | |
163 | } | |
164 | tmp->maxinterval = (u_int)val; | |
165 | MAYHAVE(val, "mininterval", tmp->maxinterval/3); | |
166 | if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) { | |
167 | syslog(LOG_ERR, | |
168 | "<%s> mininterval must be between %e and %d", | |
169 | __FUNCTION__, | |
170 | MIN_MININTERVAL, | |
171 | (tmp->maxinterval * 3) / 4); | |
172 | exit(1); | |
173 | } | |
174 | tmp->mininterval = (u_int)val; | |
175 | ||
176 | MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT); | |
177 | tmp->hoplimit = val & 0xff; | |
178 | ||
179 | MAYHAVE(val, "raflags", 0); | |
180 | tmp->managedflg = val & ND_RA_FLAG_MANAGED; | |
181 | tmp->otherflg = val & ND_RA_FLAG_OTHER; | |
182 | #ifdef MIP6 | |
183 | if (mobileip6) | |
184 | tmp->haflg = val & ND_RA_FLAG_HA; | |
185 | #endif | |
186 | #ifndef ND_RA_FLAG_RTPREF_MASK | |
187 | #define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */ | |
188 | #define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */ | |
189 | #endif | |
190 | tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK; | |
191 | if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) { | |
192 | syslog(LOG_ERR, "<%s> invalid router preference on %s", | |
193 | __FUNCTION__, intface); | |
194 | exit(1); | |
195 | } | |
196 | ||
197 | MAYHAVE(val, "rltime", tmp->maxinterval * 3); | |
198 | if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) { | |
199 | syslog(LOG_ERR, | |
200 | "<%s> router lifetime on %s must be 0 or" | |
201 | " between %d and %d", | |
202 | __FUNCTION__, intface, | |
203 | tmp->maxinterval, MAXROUTERLIFETIME); | |
204 | exit(1); | |
205 | } | |
206 | /* | |
207 | * Basically, hosts MUST NOT send Router Advertisement messages at any | |
208 | * time (RFC 2461, Section 6.2.3). However, it would sometimes be | |
209 | * useful to allow hosts to advertise some parameters such as prefix | |
210 | * information and link MTU. Thus, we allow hosts to invoke rtadvd | |
211 | * only when router lifetime (on every advertising interface) is | |
212 | * explicitly set zero. (see also the above section) | |
213 | */ | |
214 | if (val && forwarding == 0) { | |
215 | syslog(LOG_WARNING, | |
216 | "<%s> non zero router lifetime is specified for %s, " | |
217 | "which must not be allowed for hosts.", | |
218 | __FUNCTION__, intface); | |
219 | exit(1); | |
220 | } | |
221 | tmp->lifetime = val & 0xffff; | |
222 | ||
223 | MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME); | |
224 | if (val > MAXREACHABLETIME) { | |
225 | syslog(LOG_ERR, | |
226 | "<%s> reachable time must be no greater than %d", | |
227 | __FUNCTION__, MAXREACHABLETIME); | |
228 | exit(1); | |
229 | } | |
230 | tmp->reachabletime = (u_int32_t)val; | |
231 | ||
232 | MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER); | |
233 | if (val64 < 0 || val64 > 0xffffffff) { | |
234 | syslog(LOG_ERR, | |
235 | "<%s> retrans time out of range", __FUNCTION__); | |
236 | exit(1); | |
237 | } | |
238 | tmp->retranstimer = (u_int32_t)val64; | |
239 | ||
240 | #ifndef MIP6 | |
241 | if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { | |
242 | syslog(LOG_ERR, | |
243 | "<%s> mobile-ip6 configuration not supported", | |
244 | __FUNCTION__); | |
245 | exit(1); | |
246 | } | |
247 | #else | |
248 | if (!mobileip6) { | |
249 | if (agetstr("hapref", &bp) || agetstr("hatime", &bp)) { | |
250 | syslog(LOG_ERR, | |
251 | "<%s> mobile-ip6 configuration without " | |
252 | "proper command line option", | |
253 | __FUNCTION__); | |
254 | exit(1); | |
255 | } | |
256 | } else { | |
257 | tmp->hapref = 0; | |
258 | if ((val = agetnum("hapref")) >= 0) | |
259 | tmp->hapref = (int16_t)val; | |
260 | if (tmp->hapref != 0) { | |
261 | tmp->hatime = 0; | |
262 | MUSTHAVE(val, "hatime"); | |
263 | tmp->hatime = (u_int16_t)val; | |
264 | if (tmp->hatime <= 0) { | |
265 | syslog(LOG_ERR, | |
266 | "<%s> home agent lifetime must be greater than 0", | |
267 | __FUNCTION__); | |
268 | exit(1); | |
269 | } | |
270 | } | |
271 | } | |
272 | #endif | |
273 | ||
274 | /* prefix information */ | |
275 | ||
276 | /* | |
277 | * This is an implementation specific parameter to consinder | |
278 | * link propagation delays and poorly synchronized clocks when | |
279 | * checking consistency of advertised lifetimes. | |
280 | */ | |
281 | MAYHAVE(val, "clockskew", 0); | |
282 | tmp->clockskew = val; | |
283 | ||
284 | if ((pfxs = agetnum("addrs")) < 0) { | |
285 | /* auto configure prefix information */ | |
286 | if (agetstr("addr", &bp) || agetstr("addr1", &bp)) { | |
287 | syslog(LOG_ERR, | |
288 | "<%s> conflicting prefix configuration for %s: " | |
289 | "automatic and manual config at the same time", | |
290 | __FUNCTION__, intface); | |
291 | exit(1); | |
292 | } | |
293 | get_prefix(tmp); | |
294 | } | |
295 | else { | |
296 | tmp->pfxs = pfxs; | |
297 | for (i = 0; i < pfxs; i++) { | |
298 | struct prefix *pfx; | |
299 | char entbuf[256]; | |
300 | int added = (pfxs > 1) ? 1 : 0; | |
301 | ||
302 | /* allocate memory to store prefix information */ | |
303 | if ((pfx = malloc(sizeof(struct prefix))) == NULL) { | |
304 | syslog(LOG_ERR, | |
305 | "<%s> can't allocate enough memory", | |
306 | __FUNCTION__); | |
307 | exit(1); | |
308 | } | |
309 | memset(pfx, 0, sizeof(*pfx)); | |
310 | ||
311 | /* link into chain */ | |
312 | insque(pfx, &tmp->prefix); | |
313 | ||
314 | pfx->origin = PREFIX_FROM_CONFIG; | |
315 | ||
316 | makeentry(entbuf, i, "prefixlen", added); | |
317 | MAYHAVE(val, entbuf, 64); | |
318 | if (val < 0 || val > 128) { | |
319 | syslog(LOG_ERR, | |
320 | "<%s> prefixlen out of range", | |
321 | __FUNCTION__); | |
322 | exit(1); | |
323 | } | |
324 | pfx->prefixlen = (int)val; | |
325 | ||
326 | makeentry(entbuf, i, "pinfoflags", added); | |
327 | #ifdef MIP6 | |
328 | if (mobileip6) | |
329 | { | |
330 | MAYHAVE(val, entbuf, | |
331 | (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO| | |
332 | ND_OPT_PI_FLAG_ROUTER)); | |
333 | } else | |
334 | #endif | |
335 | { | |
336 | MAYHAVE(val, entbuf, | |
337 | (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO)); | |
338 | } | |
339 | pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK; | |
340 | pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO; | |
341 | #ifdef MIP6 | |
342 | pfx->routeraddr = val & ND_OPT_PI_FLAG_ROUTER; | |
343 | #endif | |
344 | ||
345 | makeentry(entbuf, i, "vltime", added); | |
346 | MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); | |
347 | if (val64 < 0 || val64 > 0xffffffff) { | |
348 | syslog(LOG_ERR, | |
349 | "<%s> vltime out of range", | |
350 | __FUNCTION__); | |
351 | exit(1); | |
352 | } | |
353 | pfx->validlifetime = (u_int32_t)val64; | |
354 | ||
355 | makeentry(entbuf, i, "vltimedecr", added); | |
356 | if (agetflag(entbuf)) { | |
357 | struct timeval now; | |
358 | gettimeofday(&now, 0); | |
359 | pfx->vltimeexpire = | |
360 | now.tv_sec + pfx->validlifetime; | |
361 | } | |
362 | ||
363 | makeentry(entbuf, i, "pltime", added); | |
364 | MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME); | |
365 | if (val64 < 0 || val64 > 0xffffffff) { | |
366 | syslog(LOG_ERR, | |
367 | "<%s> pltime out of range", | |
368 | __FUNCTION__); | |
369 | exit(1); | |
370 | } | |
371 | pfx->preflifetime = (u_int32_t)val64; | |
372 | ||
373 | makeentry(entbuf, i, "pltimedecr", added); | |
374 | if (agetflag(entbuf)) { | |
375 | struct timeval now; | |
376 | gettimeofday(&now, 0); | |
377 | pfx->pltimeexpire = | |
378 | now.tv_sec + pfx->preflifetime; | |
379 | } | |
380 | ||
381 | makeentry(entbuf, i, "addr", added); | |
382 | addr = (char *)agetstr(entbuf, &bp); | |
383 | if (addr == NULL) { | |
384 | syslog(LOG_ERR, | |
385 | "<%s> need %s as an prefix for " | |
386 | "interface %s", | |
387 | __FUNCTION__, entbuf, intface); | |
388 | exit(1); | |
389 | } | |
390 | if (inet_pton(AF_INET6, addr, | |
391 | &pfx->prefix) != 1) { | |
392 | syslog(LOG_ERR, | |
393 | "<%s> inet_pton failed for %s", | |
394 | __FUNCTION__, addr); | |
395 | exit(1); | |
396 | } | |
397 | if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) { | |
398 | syslog(LOG_ERR, | |
399 | "<%s> multicast prefix(%s) must " | |
400 | "not be advertised (IF=%s)", | |
401 | __FUNCTION__, addr, intface); | |
402 | exit(1); | |
403 | } | |
404 | if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix)) | |
405 | syslog(LOG_NOTICE, | |
406 | "<%s> link-local prefix(%s) will be" | |
407 | " advertised on %s", | |
408 | __FUNCTION__, addr, intface); | |
409 | } | |
410 | } | |
411 | ||
412 | MAYHAVE(val, "mtu", 0); | |
413 | if (val < 0 || val > 0xffffffff) { | |
414 | syslog(LOG_ERR, | |
415 | "<%s> mtu out of range", __FUNCTION__); | |
416 | exit(1); | |
417 | } | |
418 | tmp->linkmtu = (u_int32_t)val; | |
419 | if (tmp->linkmtu == 0) { | |
420 | char *mtustr; | |
421 | ||
422 | if ((mtustr = (char *)agetstr("mtu", &bp)) && | |
423 | strcmp(mtustr, "auto") == 0) | |
424 | tmp->linkmtu = tmp->phymtu; | |
425 | } | |
426 | else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) { | |
427 | syslog(LOG_ERR, | |
428 | "<%s> advertised link mtu must be between" | |
429 | " least MTU and physical link MTU", | |
430 | __FUNCTION__); | |
431 | exit(1); | |
432 | } | |
433 | ||
434 | /* route information */ | |
435 | ||
436 | MAYHAVE(val, "routes", 0); | |
437 | if (val < 0 || val > 0xffffffff) { | |
438 | syslog(LOG_ERR, | |
439 | "<%s> number of route information improper", __FUNCTION__); | |
440 | exit(1); | |
441 | } | |
442 | tmp->routes = val; | |
443 | for (i = 0; i < tmp->routes; i++) { | |
444 | struct rtinfo *rti; | |
445 | char entbuf[256]; | |
446 | int added = (tmp->routes > 1) ? 1 : 0; | |
447 | ||
448 | /* allocate memory to store prefix information */ | |
449 | if ((rti = malloc(sizeof(struct rtinfo))) == NULL) { | |
450 | syslog(LOG_ERR, | |
451 | "<%s> can't allocate enough memory", | |
452 | __FUNCTION__); | |
453 | exit(1); | |
454 | } | |
455 | memset(rti, 0, sizeof(*rti)); | |
456 | ||
457 | /* link into chain */ | |
458 | insque(rti, &tmp->route); | |
459 | ||
460 | makeentry(entbuf, i, "rtrplen", added); | |
461 | MAYHAVE(val, entbuf, 64); | |
462 | if (val < 0 || val > 128) { | |
463 | syslog(LOG_ERR, | |
464 | "<%s> prefixlen out of range", | |
465 | __FUNCTION__); | |
466 | exit(1); | |
467 | } | |
468 | rti->prefixlen = (int)val; | |
469 | ||
470 | makeentry(entbuf, i, "rtrflags", added); | |
471 | MAYHAVE(val, entbuf, 0); | |
472 | rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK; | |
473 | if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) { | |
474 | syslog(LOG_ERR, "<%s> invalid router preference", | |
475 | __FUNCTION__); | |
476 | exit(1); | |
477 | } | |
478 | ||
479 | makeentry(entbuf, i, "rtrltime", added); | |
480 | /* | |
481 | * XXX: since default value of route lifetime is not defined in | |
482 | * draft-draves-route-selection-01.txt, I took the default | |
483 | * value of valid lifetime of prefix as its default. | |
484 | * It need be much considered. | |
485 | */ | |
486 | MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); | |
487 | if (val64 < 0 || val64 > 0xffffffff) { | |
488 | syslog(LOG_ERR, | |
489 | "<%s> rtrltime out of range", | |
490 | __FUNCTION__); | |
491 | exit(1); | |
492 | } | |
493 | rti->ltime = (u_int32_t)val64; | |
494 | ||
495 | makeentry(entbuf, i, "rtrprefix", added); | |
496 | addr = (char *)agetstr(entbuf, &bp); | |
497 | if (addr == NULL) { | |
498 | syslog(LOG_ERR, | |
499 | "<%s> need %s as an route for " | |
500 | "interface %s", | |
501 | __FUNCTION__, entbuf, intface); | |
502 | exit(1); | |
503 | } | |
504 | if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) { | |
505 | syslog(LOG_ERR, | |
506 | "<%s> inet_pton failed for %s", | |
507 | __FUNCTION__, addr); | |
508 | exit(1); | |
509 | } | |
510 | #if 0 | |
511 | /* | |
512 | * XXX: currently there's no restriction in route information | |
513 | * prefix according to draft-draves-route-selection-01.txt, | |
514 | * however I think the similar restriction be necessary. | |
515 | */ | |
516 | MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME); | |
517 | if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) { | |
518 | syslog(LOG_ERR, | |
519 | "<%s> multicast route (%s) must " | |
520 | "not be advertised (IF=%s)", | |
521 | __FUNCTION__, addr, intface); | |
522 | exit(1); | |
523 | } | |
524 | if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) { | |
525 | syslog(LOG_NOTICE, | |
526 | "<%s> link-local route (%s) must " | |
527 | "not be advertised on %s", | |
528 | __FUNCTION__, addr, intface); | |
529 | exit(1); | |
530 | } | |
531 | #endif | |
532 | } | |
533 | ||
534 | /* okey */ | |
535 | tmp->next = ralist; | |
536 | ralist = tmp; | |
537 | ||
538 | /* construct the sending packet */ | |
539 | make_packet(tmp); | |
540 | ||
541 | /* set timer */ | |
542 | tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update, | |
543 | tmp, tmp); | |
544 | ra_timer_update((void *)tmp, &tmp->timer->tm); | |
545 | rtadvd_set_timer(&tmp->timer->tm, tmp->timer); | |
546 | } | |
547 | ||
548 | static void | |
549 | get_prefix(struct rainfo *rai) | |
550 | { | |
551 | struct ifaddrs *ifap, *ifa; | |
552 | struct prefix *pp; | |
553 | struct in6_addr *a; | |
554 | u_char *p, *ep, *m, *lim; | |
555 | u_char ntopbuf[INET6_ADDRSTRLEN]; | |
556 | ||
557 | if (getifaddrs(&ifap) < 0) { | |
558 | syslog(LOG_ERR, | |
559 | "<%s> can't get interface addresses", | |
560 | __FUNCTION__); | |
561 | exit(1); | |
562 | } | |
563 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { | |
564 | if (strcmp(ifa->ifa_name, rai->ifname) != 0) | |
565 | continue; | |
566 | if (ifa->ifa_addr->sa_family != AF_INET6) | |
567 | continue; | |
568 | a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; | |
569 | if (IN6_IS_ADDR_LINKLOCAL(a)) | |
570 | continue; | |
571 | ||
572 | /* allocate memory to store prefix info. */ | |
573 | if ((pp = malloc(sizeof(*pp))) == NULL) { | |
574 | syslog(LOG_ERR, | |
575 | "<%s> can't get allocate buffer for prefix", | |
576 | __FUNCTION__); | |
577 | exit(1); | |
578 | } | |
579 | memset(pp, 0, sizeof(*pp)); | |
580 | ||
581 | /* set prefix length */ | |
582 | m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr; | |
583 | lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len; | |
584 | pp->prefixlen = prefixlen(m, lim); | |
585 | if (pp->prefixlen < 0 || pp->prefixlen > 128) { | |
586 | syslog(LOG_ERR, | |
587 | "<%s> failed to get prefixlen " | |
588 | "or prefix is invalid", | |
589 | __FUNCTION__); | |
590 | exit(1); | |
591 | } | |
592 | ||
593 | /* set prefix, sweep bits outside of prefixlen */ | |
594 | memcpy(&pp->prefix, a, sizeof(*a)); | |
595 | p = (u_char *)&pp->prefix; | |
596 | ep = (u_char *)(&pp->prefix + 1); | |
597 | while (m < lim) | |
598 | *p++ &= *m++; | |
599 | while (p < ep) | |
600 | *p++ = 0x00; | |
601 | ||
602 | if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf, | |
603 | sizeof(ntopbuf))) { | |
604 | syslog(LOG_ERR, "<%s> inet_ntop failed", __FUNCTION__); | |
605 | exit(1); | |
606 | } | |
607 | syslog(LOG_DEBUG, | |
608 | "<%s> add %s/%d to prefix list on %s", | |
609 | __FUNCTION__, ntopbuf, pp->prefixlen, rai->ifname); | |
610 | ||
611 | /* set other fields with protocol defaults */ | |
612 | pp->validlifetime = DEF_ADVVALIDLIFETIME; | |
613 | pp->preflifetime = DEF_ADVPREFERREDLIFETIME; | |
614 | pp->onlinkflg = 1; | |
615 | pp->autoconfflg = 1; | |
616 | pp->origin = PREFIX_FROM_KERNEL; | |
617 | ||
618 | /* link into chain */ | |
619 | insque(pp, &rai->prefix); | |
620 | ||
621 | /* counter increment */ | |
622 | rai->pfxs++; | |
623 | } | |
624 | ||
625 | freeifaddrs(ifap); | |
626 | } | |
627 | ||
628 | static void | |
629 | makeentry(buf, id, string, add) | |
630 | char *buf, *string; | |
631 | int id, add; | |
632 | { | |
633 | strcpy(buf, string); | |
634 | if (add) { | |
635 | char *cp; | |
636 | ||
637 | cp = (char *)index(buf, '\0'); | |
638 | cp += sprintf(cp, "%d", id); | |
639 | *cp = '\0'; | |
640 | } | |
641 | } | |
642 | ||
643 | /* | |
644 | * Add a prefix to the list of specified interface and reconstruct | |
645 | * the outgoing packet. | |
646 | * The prefix must not be in the list. | |
647 | * XXX: other parameter of the prefix(e.g. lifetime) shoule be | |
648 | * able to be specified. | |
649 | */ | |
650 | static void | |
651 | add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr) | |
652 | { | |
653 | struct prefix *prefix; | |
654 | u_char ntopbuf[INET6_ADDRSTRLEN]; | |
655 | ||
656 | if ((prefix = malloc(sizeof(*prefix))) == NULL) { | |
657 | syslog(LOG_ERR, "<%s> memory allocation failed", | |
658 | __FUNCTION__); | |
659 | return; /* XXX: error or exit? */ | |
660 | } | |
661 | memset(prefix, 0, sizeof(*prefix)); | |
662 | prefix->prefix = ipr->ipr_prefix.sin6_addr; | |
663 | prefix->prefixlen = ipr->ipr_plen; | |
664 | prefix->validlifetime = ipr->ipr_vltime; | |
665 | prefix->preflifetime = ipr->ipr_pltime; | |
666 | prefix->onlinkflg = ipr->ipr_raf_onlink; | |
667 | prefix->autoconfflg = ipr->ipr_raf_auto; | |
668 | prefix->origin = PREFIX_FROM_DYNAMIC; | |
669 | ||
670 | insque(prefix, &rai->prefix); | |
671 | ||
672 | syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s", | |
673 | __FUNCTION__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, | |
674 | ntopbuf, INET6_ADDRSTRLEN), | |
675 | ipr->ipr_plen, rai->ifname); | |
676 | ||
677 | /* free the previous packet */ | |
678 | free(rai->ra_data); | |
679 | rai->ra_data = NULL; | |
680 | ||
681 | /* reconstruct the packet */ | |
682 | rai->pfxs++; | |
683 | make_packet(rai); | |
684 | ||
685 | /* | |
686 | * reset the timer so that the new prefix will be advertised quickly. | |
687 | */ | |
688 | rai->initcounter = 0; | |
689 | ra_timer_update((void *)rai, &rai->timer->tm); | |
690 | rtadvd_set_timer(&rai->timer->tm, rai->timer); | |
691 | } | |
692 | ||
693 | /* | |
694 | * Delete a prefix to the list of specified interface and reconstruct | |
695 | * the outgoing packet. | |
696 | * The prefix must be in the list. | |
697 | */ | |
698 | void | |
699 | delete_prefix(struct rainfo *rai, struct prefix *prefix) | |
700 | { | |
701 | u_char ntopbuf[INET6_ADDRSTRLEN]; | |
702 | ||
703 | remque(prefix); | |
704 | syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s", | |
705 | __FUNCTION__, inet_ntop(AF_INET6, &prefix->prefix, | |
706 | ntopbuf, INET6_ADDRSTRLEN), | |
707 | prefix->prefixlen, rai->ifname); | |
708 | free(prefix); | |
709 | rai->pfxs--; | |
710 | make_packet(rai); | |
711 | } | |
712 | ||
713 | /* | |
714 | * Try to get an in6_prefixreq contents for a prefix which matches | |
715 | * ipr->ipr_prefix and ipr->ipr_plen and belongs to | |
716 | * the interface whose name is ipr->ipr_name[]. | |
717 | */ | |
718 | static int | |
719 | init_prefix(struct in6_prefixreq *ipr) | |
720 | { | |
721 | int s; | |
722 | ||
723 | if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { | |
724 | syslog(LOG_ERR, "<%s> socket: %s", __FUNCTION__, | |
725 | strerror(errno)); | |
726 | exit(1); | |
727 | } | |
728 | ||
729 | if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) { | |
730 | syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __FUNCTION__, | |
731 | strerror(errno)); | |
732 | ||
733 | ipr->ipr_vltime = DEF_ADVVALIDLIFETIME; | |
734 | ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME; | |
735 | ipr->ipr_raf_onlink = 1; | |
736 | ipr->ipr_raf_auto = 1; | |
737 | /* omit other field initialization */ | |
738 | } | |
739 | else if (ipr->ipr_origin < PR_ORIG_RR) { | |
740 | u_char ntopbuf[INET6_ADDRSTRLEN]; | |
741 | ||
742 | syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is" | |
743 | "lower than PR_ORIG_RR(router renumbering)." | |
744 | "This should not happen if I am router", __FUNCTION__, | |
745 | inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf, | |
746 | sizeof(ntopbuf)), ipr->ipr_origin); | |
747 | close(s); | |
748 | return 1; | |
749 | } | |
750 | ||
751 | close(s); | |
752 | return 0; | |
753 | } | |
754 | ||
755 | void | |
756 | make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen) | |
757 | { | |
758 | struct in6_prefixreq ipr; | |
759 | ||
760 | memset(&ipr, 0, sizeof(ipr)); | |
761 | if (if_indextoname(ifindex, ipr.ipr_name) == NULL) { | |
762 | syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't" | |
763 | "exist. This should not happen! %s", __FUNCTION__, | |
764 | ifindex, strerror(errno)); | |
765 | exit(1); | |
766 | } | |
767 | ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix); | |
768 | ipr.ipr_prefix.sin6_family = AF_INET6; | |
769 | ipr.ipr_prefix.sin6_addr = *addr; | |
770 | ipr.ipr_plen = plen; | |
771 | ||
772 | if (init_prefix(&ipr)) | |
773 | return; /* init failed by some error */ | |
774 | add_prefix(rai, &ipr); | |
775 | } | |
776 | ||
777 | void | |
778 | make_packet(struct rainfo *rainfo) | |
779 | { | |
780 | size_t packlen, lladdroptlen = 0; | |
781 | char *buf; | |
782 | struct nd_router_advert *ra; | |
783 | struct nd_opt_prefix_info *ndopt_pi; | |
784 | struct nd_opt_mtu *ndopt_mtu; | |
785 | #ifdef MIP6 | |
786 | struct nd_opt_advinterval *ndopt_advint; | |
787 | struct nd_opt_homeagent_info *ndopt_hai; | |
788 | #endif | |
789 | struct nd_opt_route_info *ndopt_rti; | |
790 | struct prefix *pfx; | |
791 | struct rtinfo *rti; | |
792 | ||
793 | /* calculate total length */ | |
794 | packlen = sizeof(struct nd_router_advert); | |
795 | if (rainfo->advlinkopt) { | |
796 | if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) { | |
797 | syslog(LOG_INFO, | |
798 | "<%s> link-layer address option has" | |
799 | " null length on %s." | |
800 | " Treat as not included.", | |
801 | __FUNCTION__, rainfo->ifname); | |
802 | rainfo->advlinkopt = 0; | |
803 | } | |
804 | packlen += lladdroptlen; | |
805 | } | |
806 | if (rainfo->pfxs) | |
807 | packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs; | |
808 | if (rainfo->linkmtu) | |
809 | packlen += sizeof(struct nd_opt_mtu); | |
810 | #ifdef MIP6 | |
811 | if (mobileip6 && rainfo->maxinterval) | |
812 | packlen += sizeof(struct nd_opt_advinterval); | |
813 | if (mobileip6 && rainfo->hatime) | |
814 | packlen += sizeof(struct nd_opt_homeagent_info); | |
815 | #endif | |
816 | #ifdef ND_OPT_ROUTE_INFO | |
817 | for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) | |
818 | packlen += sizeof(struct nd_opt_route_info) + | |
819 | ((rti->prefixlen + 0x3f) >> 6) * 8; | |
820 | #endif | |
821 | ||
822 | /* allocate memory for the packet */ | |
823 | if ((buf = malloc(packlen)) == NULL) { | |
824 | syslog(LOG_ERR, | |
825 | "<%s> can't get enough memory for an RA packet", | |
826 | __FUNCTION__); | |
827 | exit(1); | |
828 | } | |
829 | if (rainfo->ra_data) { | |
830 | /* free the previous packet */ | |
831 | free(rainfo->ra_data); | |
832 | rainfo->ra_data = NULL; | |
833 | } | |
834 | rainfo->ra_data = buf; | |
835 | /* XXX: what if packlen > 576? */ | |
836 | rainfo->ra_datalen = packlen; | |
837 | ||
838 | /* | |
839 | * construct the packet | |
840 | */ | |
841 | ra = (struct nd_router_advert *)buf; | |
842 | ra->nd_ra_type = ND_ROUTER_ADVERT; | |
843 | ra->nd_ra_code = 0; | |
844 | ra->nd_ra_cksum = 0; | |
845 | ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit); | |
846 | ra->nd_ra_flags_reserved = 0; /* just in case */ | |
847 | /* | |
848 | * XXX: the router preference field, which is a 2-bit field, should be | |
849 | * initialized before other fields. | |
850 | */ | |
851 | ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref; | |
852 | ra->nd_ra_flags_reserved |= | |
853 | rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0; | |
854 | ra->nd_ra_flags_reserved |= | |
855 | rainfo->otherflg ? ND_RA_FLAG_OTHER : 0; | |
856 | #ifdef MIP6 | |
857 | ra->nd_ra_flags_reserved |= | |
858 | rainfo->haflg ? ND_RA_FLAG_HA : 0; | |
859 | #endif | |
860 | ra->nd_ra_router_lifetime = htons(rainfo->lifetime); | |
861 | ra->nd_ra_reachable = htonl(rainfo->reachabletime); | |
862 | ra->nd_ra_retransmit = htonl(rainfo->retranstimer); | |
863 | buf += sizeof(*ra); | |
864 | ||
865 | if (rainfo->advlinkopt) { | |
866 | lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf); | |
867 | buf += lladdroptlen; | |
868 | } | |
869 | ||
870 | if (rainfo->linkmtu) { | |
871 | ndopt_mtu = (struct nd_opt_mtu *)buf; | |
872 | ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU; | |
873 | ndopt_mtu->nd_opt_mtu_len = 1; | |
874 | ndopt_mtu->nd_opt_mtu_reserved = 0; | |
875 | ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu); | |
876 | buf += sizeof(struct nd_opt_mtu); | |
877 | } | |
878 | ||
879 | #ifdef MIP6 | |
880 | if (mobileip6 && rainfo->maxinterval) { | |
881 | ndopt_advint = (struct nd_opt_advinterval *)buf; | |
882 | ndopt_advint->nd_opt_adv_type = ND_OPT_ADVINTERVAL; | |
883 | ndopt_advint->nd_opt_adv_len = 1; | |
884 | ndopt_advint->nd_opt_adv_reserved = 0; | |
885 | ndopt_advint->nd_opt_adv_interval = htonl(rainfo->maxinterval * | |
886 | 1000); | |
887 | buf += sizeof(struct nd_opt_advinterval); | |
888 | } | |
889 | #endif | |
890 | ||
891 | #ifdef MIP6 | |
892 | if (rainfo->hatime) { | |
893 | ndopt_hai = (struct nd_opt_homeagent_info *)buf; | |
894 | ndopt_hai->nd_opt_hai_type = ND_OPT_HOMEAGENT_INFO; | |
895 | ndopt_hai->nd_opt_hai_len = 1; | |
896 | ndopt_hai->nd_opt_hai_reserved = 0; | |
897 | ndopt_hai->nd_opt_hai_preference = htons(rainfo->hapref); | |
898 | ndopt_hai->nd_opt_hai_lifetime = htons(rainfo->hatime); | |
899 | buf += sizeof(struct nd_opt_homeagent_info); | |
900 | } | |
901 | #endif | |
902 | ||
903 | for (pfx = rainfo->prefix.next; | |
904 | pfx != &rainfo->prefix; pfx = pfx->next) { | |
905 | u_int32_t vltime, pltime; | |
906 | struct timeval now; | |
907 | ||
908 | ndopt_pi = (struct nd_opt_prefix_info *)buf; | |
909 | ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; | |
910 | ndopt_pi->nd_opt_pi_len = 4; | |
911 | ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen; | |
912 | ndopt_pi->nd_opt_pi_flags_reserved = 0; | |
913 | if (pfx->onlinkflg) | |
914 | ndopt_pi->nd_opt_pi_flags_reserved |= | |
915 | ND_OPT_PI_FLAG_ONLINK; | |
916 | if (pfx->autoconfflg) | |
917 | ndopt_pi->nd_opt_pi_flags_reserved |= | |
918 | ND_OPT_PI_FLAG_AUTO; | |
919 | #ifdef MIP6 | |
920 | if (pfx->routeraddr) | |
921 | ndopt_pi->nd_opt_pi_flags_reserved |= | |
922 | ND_OPT_PI_FLAG_ROUTER; | |
923 | #endif | |
924 | if (pfx->vltimeexpire || pfx->pltimeexpire) | |
925 | gettimeofday(&now, NULL); | |
926 | if (pfx->vltimeexpire == 0) | |
927 | vltime = pfx->validlifetime; | |
928 | else | |
929 | vltime = (pfx->vltimeexpire > now.tv_sec) ? | |
930 | pfx->vltimeexpire - now.tv_sec : 0; | |
931 | if (pfx->pltimeexpire == 0) | |
932 | pltime = pfx->preflifetime; | |
933 | else | |
934 | pltime = (pfx->pltimeexpire > now.tv_sec) ? | |
935 | pfx->pltimeexpire - now.tv_sec : 0; | |
936 | if (vltime < pltime) { | |
937 | /* | |
938 | * this can happen if vltime is decrement but pltime | |
939 | * is not. | |
940 | */ | |
941 | pltime = vltime; | |
942 | } | |
943 | ndopt_pi->nd_opt_pi_valid_time = htonl(vltime); | |
944 | ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime); | |
945 | ndopt_pi->nd_opt_pi_reserved2 = 0; | |
946 | ndopt_pi->nd_opt_pi_prefix = pfx->prefix; | |
947 | ||
948 | buf += sizeof(struct nd_opt_prefix_info); | |
949 | } | |
950 | ||
951 | #ifdef ND_OPT_ROUTE_INFO | |
952 | for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) { | |
953 | u_int8_t psize = (rti->prefixlen + 0x3f) >> 6; | |
954 | ||
955 | ndopt_rti = (struct nd_opt_route_info *)buf; | |
956 | ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO; | |
957 | ndopt_rti->nd_opt_rti_len = 1 + psize; | |
958 | ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen; | |
959 | ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref; | |
960 | ndopt_rti->nd_opt_rti_lifetime = rti->ltime; | |
961 | memcpy(ndopt_rti + 1, &rti->prefix, psize * 8); | |
962 | buf += sizeof(struct nd_opt_route_info) + psize * 8; | |
963 | } | |
964 | #endif | |
965 | ||
966 | return; | |
967 | } | |
968 | ||
969 | static int | |
970 | getinet6sysctl(int code) | |
971 | { | |
972 | int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 }; | |
973 | int value; | |
974 | size_t size; | |
975 | ||
976 | mib[3] = code; | |
977 | size = sizeof(value); | |
978 | if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) | |
979 | < 0) { | |
980 | syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s", | |
981 | __FUNCTION__, code, | |
982 | strerror(errno)); | |
983 | return(-1); | |
984 | } | |
985 | else | |
986 | return(value); | |
987 | } |