1*8f957290SDavid van Moolenbroek /* $NetBSD: rtadvd.c,v 1.50 2015/06/15 04:15:33 ozaki-r Exp $ */
2*8f957290SDavid van Moolenbroek /* $KAME: rtadvd.c,v 1.92 2005/10/17 14:40:02 suz Exp $ */
3*8f957290SDavid van Moolenbroek
4*8f957290SDavid van Moolenbroek /*
5*8f957290SDavid van Moolenbroek * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6*8f957290SDavid van Moolenbroek * All rights reserved.
7*8f957290SDavid van Moolenbroek *
8*8f957290SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
9*8f957290SDavid van Moolenbroek * modification, are permitted provided that the following conditions
10*8f957290SDavid van Moolenbroek * are met:
11*8f957290SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
12*8f957290SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer.
13*8f957290SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
14*8f957290SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
15*8f957290SDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
16*8f957290SDavid van Moolenbroek * 3. Neither the name of the project nor the names of its contributors
17*8f957290SDavid van Moolenbroek * may be used to endorse or promote products derived from this software
18*8f957290SDavid van Moolenbroek * without specific prior written permission.
19*8f957290SDavid van Moolenbroek *
20*8f957290SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21*8f957290SDavid van Moolenbroek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*8f957290SDavid van Moolenbroek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*8f957290SDavid van Moolenbroek * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24*8f957290SDavid van Moolenbroek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*8f957290SDavid van Moolenbroek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*8f957290SDavid van Moolenbroek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*8f957290SDavid van Moolenbroek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*8f957290SDavid van Moolenbroek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*8f957290SDavid van Moolenbroek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*8f957290SDavid van Moolenbroek * SUCH DAMAGE.
31*8f957290SDavid van Moolenbroek */
32*8f957290SDavid van Moolenbroek
33*8f957290SDavid van Moolenbroek #include <sys/param.h>
34*8f957290SDavid van Moolenbroek #include <sys/socket.h>
35*8f957290SDavid van Moolenbroek #include <sys/uio.h>
36*8f957290SDavid van Moolenbroek #include <sys/time.h>
37*8f957290SDavid van Moolenbroek #include <sys/queue.h>
38*8f957290SDavid van Moolenbroek
39*8f957290SDavid van Moolenbroek #include <net/if.h>
40*8f957290SDavid van Moolenbroek #include <net/route.h>
41*8f957290SDavid van Moolenbroek #include <net/if_dl.h>
42*8f957290SDavid van Moolenbroek #include <netinet/in.h>
43*8f957290SDavid van Moolenbroek #include <netinet/ip6.h>
44*8f957290SDavid van Moolenbroek #include <netinet6/ip6_var.h>
45*8f957290SDavid van Moolenbroek #include <netinet/icmp6.h>
46*8f957290SDavid van Moolenbroek
47*8f957290SDavid van Moolenbroek #include <arpa/inet.h>
48*8f957290SDavid van Moolenbroek
49*8f957290SDavid van Moolenbroek #include <time.h>
50*8f957290SDavid van Moolenbroek #include <unistd.h>
51*8f957290SDavid van Moolenbroek #include <stdio.h>
52*8f957290SDavid van Moolenbroek #include <err.h>
53*8f957290SDavid van Moolenbroek #include <errno.h>
54*8f957290SDavid van Moolenbroek #include <string.h>
55*8f957290SDavid van Moolenbroek #include <stdlib.h>
56*8f957290SDavid van Moolenbroek #include <syslog.h>
57*8f957290SDavid van Moolenbroek #if defined(__NetBSD__) || defined(__minix)
58*8f957290SDavid van Moolenbroek #include <util.h>
59*8f957290SDavid van Moolenbroek #endif
60*8f957290SDavid van Moolenbroek #include <poll.h>
61*8f957290SDavid van Moolenbroek #include <pwd.h>
62*8f957290SDavid van Moolenbroek
63*8f957290SDavid van Moolenbroek #include "rtadvd.h"
64*8f957290SDavid van Moolenbroek #include "rrenum.h"
65*8f957290SDavid van Moolenbroek #include "advcap.h"
66*8f957290SDavid van Moolenbroek #include "timer.h"
67*8f957290SDavid van Moolenbroek #include "if.h"
68*8f957290SDavid van Moolenbroek #include "config.h"
69*8f957290SDavid van Moolenbroek #include "dump.h"
70*8f957290SDavid van Moolenbroek
71*8f957290SDavid van Moolenbroek struct msghdr rcvmhdr;
72*8f957290SDavid van Moolenbroek static unsigned char *rcvcmsgbuf;
73*8f957290SDavid van Moolenbroek static size_t rcvcmsgbuflen;
74*8f957290SDavid van Moolenbroek static unsigned char *sndcmsgbuf;
75*8f957290SDavid van Moolenbroek static size_t sndcmsgbuflen;
76*8f957290SDavid van Moolenbroek volatile sig_atomic_t do_dump;
77*8f957290SDavid van Moolenbroek volatile sig_atomic_t do_reconf;
78*8f957290SDavid van Moolenbroek volatile sig_atomic_t do_die;
79*8f957290SDavid van Moolenbroek struct msghdr sndmhdr;
80*8f957290SDavid van Moolenbroek struct iovec rcviov[2];
81*8f957290SDavid van Moolenbroek struct iovec sndiov[2];
82*8f957290SDavid van Moolenbroek struct sockaddr_in6 rcvfrom;
83*8f957290SDavid van Moolenbroek static const char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX configurable */
84*8f957290SDavid van Moolenbroek static char *mcastif;
85*8f957290SDavid van Moolenbroek int sock;
86*8f957290SDavid van Moolenbroek int rtsock = -1;
87*8f957290SDavid van Moolenbroek int accept_rr = 0;
88*8f957290SDavid van Moolenbroek int dflag = 0, sflag = 0;
89*8f957290SDavid van Moolenbroek
90*8f957290SDavid van Moolenbroek static char **if_argv;
91*8f957290SDavid van Moolenbroek static int if_argc;
92*8f957290SDavid van Moolenbroek
93*8f957290SDavid van Moolenbroek char *conffile = NULL;
94*8f957290SDavid van Moolenbroek
95*8f957290SDavid van Moolenbroek struct ralist_head_t ralist = TAILQ_HEAD_INITIALIZER(ralist);
96*8f957290SDavid van Moolenbroek
97*8f957290SDavid van Moolenbroek struct nd_optlist {
98*8f957290SDavid van Moolenbroek TAILQ_ENTRY(nd_optlist) next;
99*8f957290SDavid van Moolenbroek struct nd_opt_hdr *opt;
100*8f957290SDavid van Moolenbroek };
101*8f957290SDavid van Moolenbroek union nd_opts {
102*8f957290SDavid van Moolenbroek struct nd_opt_hdr *nd_opt_array[9];
103*8f957290SDavid van Moolenbroek struct {
104*8f957290SDavid van Moolenbroek struct nd_opt_hdr *zero;
105*8f957290SDavid van Moolenbroek struct nd_opt_hdr *src_lladdr;
106*8f957290SDavid van Moolenbroek struct nd_opt_hdr *tgt_lladdr;
107*8f957290SDavid van Moolenbroek struct nd_opt_prefix_info *pi;
108*8f957290SDavid van Moolenbroek struct nd_opt_rd_hdr *rh;
109*8f957290SDavid van Moolenbroek struct nd_opt_mtu *mtu;
110*8f957290SDavid van Moolenbroek TAILQ_HEAD(, nd_optlist) list;
111*8f957290SDavid van Moolenbroek } nd_opt_each;
112*8f957290SDavid van Moolenbroek };
113*8f957290SDavid van Moolenbroek #define nd_opts_src_lladdr nd_opt_each.src_lladdr
114*8f957290SDavid van Moolenbroek #define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
115*8f957290SDavid van Moolenbroek #define nd_opts_pi nd_opt_each.pi
116*8f957290SDavid van Moolenbroek #define nd_opts_rh nd_opt_each.rh
117*8f957290SDavid van Moolenbroek #define nd_opts_mtu nd_opt_each.mtu
118*8f957290SDavid van Moolenbroek #define nd_opts_list nd_opt_each.list
119*8f957290SDavid van Moolenbroek
120*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_SRCLINKADDR (1 << 0)
121*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_TGTLINKADDR (1 << 1)
122*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_PREFIXINFO (1 << 2)
123*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_RDHDR (1 << 3)
124*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_MTU (1 << 4)
125*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_RDNSS (1 << 5)
126*8f957290SDavid van Moolenbroek #define NDOPT_FLAG_DNSSL (1 << 6)
127*8f957290SDavid van Moolenbroek
128*8f957290SDavid van Moolenbroek uint32_t ndopt_flags[] = {
129*8f957290SDavid van Moolenbroek [ND_OPT_SOURCE_LINKADDR] = NDOPT_FLAG_SRCLINKADDR,
130*8f957290SDavid van Moolenbroek [ND_OPT_TARGET_LINKADDR] = NDOPT_FLAG_TGTLINKADDR,
131*8f957290SDavid van Moolenbroek [ND_OPT_PREFIX_INFORMATION] = NDOPT_FLAG_PREFIXINFO,
132*8f957290SDavid van Moolenbroek [ND_OPT_REDIRECTED_HEADER] = NDOPT_FLAG_RDHDR,
133*8f957290SDavid van Moolenbroek [ND_OPT_MTU] = NDOPT_FLAG_MTU,
134*8f957290SDavid van Moolenbroek [ND_OPT_RDNSS] = NDOPT_FLAG_RDNSS,
135*8f957290SDavid van Moolenbroek [ND_OPT_DNSSL] = NDOPT_FLAG_DNSSL,
136*8f957290SDavid van Moolenbroek };
137*8f957290SDavid van Moolenbroek
138*8f957290SDavid van Moolenbroek struct sockaddr_in6 sin6_linklocal_allnodes = {
139*8f957290SDavid van Moolenbroek .sin6_len = sizeof(sin6_linklocal_allnodes),
140*8f957290SDavid van Moolenbroek .sin6_family = AF_INET6,
141*8f957290SDavid van Moolenbroek .sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
142*8f957290SDavid van Moolenbroek };
143*8f957290SDavid van Moolenbroek #ifdef notdef
144*8f957290SDavid van Moolenbroek struct sockaddr_in6 sin6_linklocal_allrouters = {
145*8f957290SDavid van Moolenbroek .sin6_len = sizeof(sin6_linklocal_allrouters),
146*8f957290SDavid van Moolenbroek .sin6_family = AF_INET6,
147*8f957290SDavid van Moolenbroek .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
148*8f957290SDavid van Moolenbroek };
149*8f957290SDavid van Moolenbroek #endif
150*8f957290SDavid van Moolenbroek struct sockaddr_in6 sin6_sitelocal_allrouters = {
151*8f957290SDavid van Moolenbroek .sin6_len = sizeof(sin6_sitelocal_allrouters),
152*8f957290SDavid van Moolenbroek .sin6_family = AF_INET6,
153*8f957290SDavid van Moolenbroek .sin6_addr = IN6ADDR_SITELOCAL_ALLROUTERS_INIT,
154*8f957290SDavid van Moolenbroek };
155*8f957290SDavid van Moolenbroek
156*8f957290SDavid van Moolenbroek static void set_die(int);
157*8f957290SDavid van Moolenbroek static void die(void);
158*8f957290SDavid van Moolenbroek static void set_reconf(int);
159*8f957290SDavid van Moolenbroek static void sock_open(void);
160*8f957290SDavid van Moolenbroek static void rtsock_open(void);
161*8f957290SDavid van Moolenbroek static void rtadvd_input(void);
162*8f957290SDavid van Moolenbroek static void rs_input(int, struct nd_router_solicit *,
163*8f957290SDavid van Moolenbroek struct in6_pktinfo *, struct sockaddr_in6 *);
164*8f957290SDavid van Moolenbroek static void ra_input(int, struct nd_router_advert *,
165*8f957290SDavid van Moolenbroek struct in6_pktinfo *, struct sockaddr_in6 *);
166*8f957290SDavid van Moolenbroek static struct rainfo *ra_output(struct rainfo *);
167*8f957290SDavid van Moolenbroek static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
168*8f957290SDavid van Moolenbroek struct sockaddr_in6 *);
169*8f957290SDavid van Moolenbroek static int nd6_options(struct nd_opt_hdr *, int, union nd_opts *, uint32_t);
170*8f957290SDavid van Moolenbroek static void free_ndopts(union nd_opts *);
171*8f957290SDavid van Moolenbroek static void rtmsg_input(void);
172*8f957290SDavid van Moolenbroek static void rtadvd_set_dump_file(int);
173*8f957290SDavid van Moolenbroek
174*8f957290SDavid van Moolenbroek int
main(int argc,char * argv[])175*8f957290SDavid van Moolenbroek main(int argc, char *argv[])
176*8f957290SDavid van Moolenbroek {
177*8f957290SDavid van Moolenbroek struct pollfd set[2];
178*8f957290SDavid van Moolenbroek struct timespec *timeout;
179*8f957290SDavid van Moolenbroek int i, ch;
180*8f957290SDavid van Moolenbroek int fflag = 0, logopt;
181*8f957290SDavid van Moolenbroek struct passwd *pw;
182*8f957290SDavid van Moolenbroek
183*8f957290SDavid van Moolenbroek /* get command line options and arguments */
184*8f957290SDavid van Moolenbroek #define OPTIONS "c:dDfM:Rs"
185*8f957290SDavid van Moolenbroek while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
186*8f957290SDavid van Moolenbroek #undef OPTIONS
187*8f957290SDavid van Moolenbroek switch (ch) {
188*8f957290SDavid van Moolenbroek case 'c':
189*8f957290SDavid van Moolenbroek conffile = optarg;
190*8f957290SDavid van Moolenbroek break;
191*8f957290SDavid van Moolenbroek case 'd':
192*8f957290SDavid van Moolenbroek dflag = 1;
193*8f957290SDavid van Moolenbroek break;
194*8f957290SDavid van Moolenbroek case 'D':
195*8f957290SDavid van Moolenbroek dflag = 2;
196*8f957290SDavid van Moolenbroek break;
197*8f957290SDavid van Moolenbroek case 'f':
198*8f957290SDavid van Moolenbroek fflag = 1;
199*8f957290SDavid van Moolenbroek break;
200*8f957290SDavid van Moolenbroek case 'M':
201*8f957290SDavid van Moolenbroek mcastif = optarg;
202*8f957290SDavid van Moolenbroek break;
203*8f957290SDavid van Moolenbroek case 'R':
204*8f957290SDavid van Moolenbroek fprintf(stderr, "rtadvd: "
205*8f957290SDavid van Moolenbroek "the -R option is currently ignored.\n");
206*8f957290SDavid van Moolenbroek /* accept_rr = 1; */
207*8f957290SDavid van Moolenbroek /* run anyway... */
208*8f957290SDavid van Moolenbroek break;
209*8f957290SDavid van Moolenbroek case 's':
210*8f957290SDavid van Moolenbroek sflag = 1;
211*8f957290SDavid van Moolenbroek break;
212*8f957290SDavid van Moolenbroek }
213*8f957290SDavid van Moolenbroek }
214*8f957290SDavid van Moolenbroek argc -= optind;
215*8f957290SDavid van Moolenbroek argv += optind;
216*8f957290SDavid van Moolenbroek if (argc == 0) {
217*8f957290SDavid van Moolenbroek fprintf(stderr,
218*8f957290SDavid van Moolenbroek "usage: rtadvd [-DdfRs] [-c conffile]"
219*8f957290SDavid van Moolenbroek " [-M ifname] interface ...\n");
220*8f957290SDavid van Moolenbroek exit(1);
221*8f957290SDavid van Moolenbroek }
222*8f957290SDavid van Moolenbroek
223*8f957290SDavid van Moolenbroek logopt = LOG_NDELAY | LOG_PID;
224*8f957290SDavid van Moolenbroek if (fflag)
225*8f957290SDavid van Moolenbroek logopt |= LOG_PERROR;
226*8f957290SDavid van Moolenbroek openlog("rtadvd", logopt, LOG_DAEMON);
227*8f957290SDavid van Moolenbroek
228*8f957290SDavid van Moolenbroek /* set log level */
229*8f957290SDavid van Moolenbroek if (dflag == 0)
230*8f957290SDavid van Moolenbroek (void)setlogmask(LOG_UPTO(LOG_ERR));
231*8f957290SDavid van Moolenbroek if (dflag == 1)
232*8f957290SDavid van Moolenbroek (void)setlogmask(LOG_UPTO(LOG_INFO));
233*8f957290SDavid van Moolenbroek
234*8f957290SDavid van Moolenbroek errno = 0; /* Ensure errno is 0 so we know if getpwnam errors or not */
235*8f957290SDavid van Moolenbroek if ((pw = getpwnam(RTADVD_USER)) == NULL) {
236*8f957290SDavid van Moolenbroek if (errno == 0)
237*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
238*8f957290SDavid van Moolenbroek "user %s does not exist, aborting",
239*8f957290SDavid van Moolenbroek RTADVD_USER);
240*8f957290SDavid van Moolenbroek else
241*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "getpwnam: %s: %m", RTADVD_USER);
242*8f957290SDavid van Moolenbroek exit(1);
243*8f957290SDavid van Moolenbroek }
244*8f957290SDavid van Moolenbroek
245*8f957290SDavid van Moolenbroek /* timer initialization */
246*8f957290SDavid van Moolenbroek rtadvd_timer_init();
247*8f957290SDavid van Moolenbroek
248*8f957290SDavid van Moolenbroek if_argc = argc;
249*8f957290SDavid van Moolenbroek if_argv = argv;
250*8f957290SDavid van Moolenbroek while (argc--)
251*8f957290SDavid van Moolenbroek getconfig(*argv++, 1);
252*8f957290SDavid van Moolenbroek
253*8f957290SDavid van Moolenbroek if (!fflag)
254*8f957290SDavid van Moolenbroek daemon(1, 0);
255*8f957290SDavid van Moolenbroek
256*8f957290SDavid van Moolenbroek sock_open();
257*8f957290SDavid van Moolenbroek
258*8f957290SDavid van Moolenbroek #if defined(__NetBSD__) || defined(__minix)
259*8f957290SDavid van Moolenbroek /* record the current PID */
260*8f957290SDavid van Moolenbroek if (pidfile(NULL) < 0) {
261*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
262*8f957290SDavid van Moolenbroek "<%s> failed to open the pid log file, run anyway.",
263*8f957290SDavid van Moolenbroek __func__);
264*8f957290SDavid van Moolenbroek }
265*8f957290SDavid van Moolenbroek #endif
266*8f957290SDavid van Moolenbroek
267*8f957290SDavid van Moolenbroek set[0].fd = sock;
268*8f957290SDavid van Moolenbroek set[0].events = POLLIN;
269*8f957290SDavid van Moolenbroek if (sflag == 0) {
270*8f957290SDavid van Moolenbroek rtsock_open();
271*8f957290SDavid van Moolenbroek set[1].fd = rtsock;
272*8f957290SDavid van Moolenbroek set[1].events = POLLIN;
273*8f957290SDavid van Moolenbroek } else
274*8f957290SDavid van Moolenbroek set[1].fd = -1;
275*8f957290SDavid van Moolenbroek
276*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "dropping privileges to %s", RTADVD_USER);
277*8f957290SDavid van Moolenbroek if (chroot(pw->pw_dir) == -1) {
278*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "chroot: %s: %m", pw->pw_dir);
279*8f957290SDavid van Moolenbroek exit(1);
280*8f957290SDavid van Moolenbroek }
281*8f957290SDavid van Moolenbroek if (chdir("/") == -1) {
282*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "chdir: /: %m");
283*8f957290SDavid van Moolenbroek exit(1);
284*8f957290SDavid van Moolenbroek }
285*8f957290SDavid van Moolenbroek if (setgroups(1, &pw->pw_gid) == -1 ||
286*8f957290SDavid van Moolenbroek setgid(pw->pw_gid) == -1 ||
287*8f957290SDavid van Moolenbroek setuid(pw->pw_uid) == -1)
288*8f957290SDavid van Moolenbroek {
289*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "failed to drop privileges: %m");
290*8f957290SDavid van Moolenbroek exit(1);
291*8f957290SDavid van Moolenbroek }
292*8f957290SDavid van Moolenbroek
293*8f957290SDavid van Moolenbroek signal(SIGINT, set_die);
294*8f957290SDavid van Moolenbroek signal(SIGTERM, set_die);
295*8f957290SDavid van Moolenbroek signal(SIGHUP, set_reconf);
296*8f957290SDavid van Moolenbroek signal(SIGUSR1, rtadvd_set_dump_file);
297*8f957290SDavid van Moolenbroek
298*8f957290SDavid van Moolenbroek for (;;) {
299*8f957290SDavid van Moolenbroek if (do_dump) { /* SIGUSR1 */
300*8f957290SDavid van Moolenbroek do_dump = 0;
301*8f957290SDavid van Moolenbroek rtadvd_dump_file(dumpfilename);
302*8f957290SDavid van Moolenbroek }
303*8f957290SDavid van Moolenbroek
304*8f957290SDavid van Moolenbroek if (do_reconf) { /* SIGHUP */
305*8f957290SDavid van Moolenbroek do_reconf = 0;
306*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> reloading config on SIGHUP",
307*8f957290SDavid van Moolenbroek __func__);
308*8f957290SDavid van Moolenbroek argc = if_argc;
309*8f957290SDavid van Moolenbroek argv = if_argv;
310*8f957290SDavid van Moolenbroek while (argc--)
311*8f957290SDavid van Moolenbroek getconfig(*argv++, 0);
312*8f957290SDavid van Moolenbroek }
313*8f957290SDavid van Moolenbroek
314*8f957290SDavid van Moolenbroek /* timer expiration check and reset the timer */
315*8f957290SDavid van Moolenbroek timeout = rtadvd_check_timer();
316*8f957290SDavid van Moolenbroek
317*8f957290SDavid van Moolenbroek if (do_die) {
318*8f957290SDavid van Moolenbroek die();
319*8f957290SDavid van Moolenbroek /*NOTREACHED*/
320*8f957290SDavid van Moolenbroek }
321*8f957290SDavid van Moolenbroek
322*8f957290SDavid van Moolenbroek if (timeout != NULL) {
323*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
324*8f957290SDavid van Moolenbroek "<%s> set timer to %ld:%ld. waiting for "
325*8f957290SDavid van Moolenbroek "inputs or timeout", __func__,
326*8f957290SDavid van Moolenbroek (long int)timeout->tv_sec,
327*8f957290SDavid van Moolenbroek (long int)timeout->tv_nsec);
328*8f957290SDavid van Moolenbroek } else {
329*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
330*8f957290SDavid van Moolenbroek "<%s> there's no timer. waiting for inputs",
331*8f957290SDavid van Moolenbroek __func__);
332*8f957290SDavid van Moolenbroek }
333*8f957290SDavid van Moolenbroek
334*8f957290SDavid van Moolenbroek if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 +
335*8f957290SDavid van Moolenbroek (timeout->tv_nsec + 999999) / 1000000) : INFTIM)) < 0)
336*8f957290SDavid van Moolenbroek {
337*8f957290SDavid van Moolenbroek /* EINTR would occur upon SIGUSR1 for status dump */
338*8f957290SDavid van Moolenbroek if (errno != EINTR)
339*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> poll: %m", __func__);
340*8f957290SDavid van Moolenbroek continue;
341*8f957290SDavid van Moolenbroek }
342*8f957290SDavid van Moolenbroek if (i == 0) /* timeout */
343*8f957290SDavid van Moolenbroek continue;
344*8f957290SDavid van Moolenbroek if (rtsock != -1 && set[1].revents & POLLIN)
345*8f957290SDavid van Moolenbroek rtmsg_input();
346*8f957290SDavid van Moolenbroek if (set[0].revents & POLLIN)
347*8f957290SDavid van Moolenbroek rtadvd_input();
348*8f957290SDavid van Moolenbroek }
349*8f957290SDavid van Moolenbroek exit(0); /* NOTREACHED */
350*8f957290SDavid van Moolenbroek }
351*8f957290SDavid van Moolenbroek
352*8f957290SDavid van Moolenbroek static void
rtadvd_set_dump_file(__unused int sig)353*8f957290SDavid van Moolenbroek rtadvd_set_dump_file(__unused int sig)
354*8f957290SDavid van Moolenbroek {
355*8f957290SDavid van Moolenbroek
356*8f957290SDavid van Moolenbroek do_dump = 1;
357*8f957290SDavid van Moolenbroek }
358*8f957290SDavid van Moolenbroek
359*8f957290SDavid van Moolenbroek static void
set_reconf(__unused int sig)360*8f957290SDavid van Moolenbroek set_reconf(__unused int sig)
361*8f957290SDavid van Moolenbroek {
362*8f957290SDavid van Moolenbroek
363*8f957290SDavid van Moolenbroek do_reconf = 1;
364*8f957290SDavid van Moolenbroek }
365*8f957290SDavid van Moolenbroek
366*8f957290SDavid van Moolenbroek static void
set_die(__unused int sig)367*8f957290SDavid van Moolenbroek set_die(__unused int sig)
368*8f957290SDavid van Moolenbroek {
369*8f957290SDavid van Moolenbroek
370*8f957290SDavid van Moolenbroek do_die = 1;
371*8f957290SDavid van Moolenbroek }
372*8f957290SDavid van Moolenbroek
373*8f957290SDavid van Moolenbroek static void
die(void)374*8f957290SDavid van Moolenbroek die(void)
375*8f957290SDavid van Moolenbroek {
376*8f957290SDavid van Moolenbroek static int waiting;
377*8f957290SDavid van Moolenbroek struct rainfo *rai, *ran;
378*8f957290SDavid van Moolenbroek struct rdnss *rdnss;
379*8f957290SDavid van Moolenbroek struct dnssl *dnssl;
380*8f957290SDavid van Moolenbroek
381*8f957290SDavid van Moolenbroek if (waiting) {
382*8f957290SDavid van Moolenbroek if (TAILQ_FIRST(&ralist)) {
383*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
384*8f957290SDavid van Moolenbroek "<%s> waiting for expiration of all RA timers",
385*8f957290SDavid van Moolenbroek __func__);
386*8f957290SDavid van Moolenbroek return;
387*8f957290SDavid van Moolenbroek }
388*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__);
389*8f957290SDavid van Moolenbroek free(rcvcmsgbuf);
390*8f957290SDavid van Moolenbroek free(sndcmsgbuf);
391*8f957290SDavid van Moolenbroek exit(0);
392*8f957290SDavid van Moolenbroek /* NOT REACHED */
393*8f957290SDavid van Moolenbroek }
394*8f957290SDavid van Moolenbroek
395*8f957290SDavid van Moolenbroek if (TAILQ_FIRST(&ralist) == NULL) {
396*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE, "<%s> gracefully terminated", __func__);
397*8f957290SDavid van Moolenbroek exit(0);
398*8f957290SDavid van Moolenbroek /* NOT REACHED */
399*8f957290SDavid van Moolenbroek }
400*8f957290SDavid van Moolenbroek
401*8f957290SDavid van Moolenbroek waiting = 1;
402*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE, "<%s> final RA transmission started", __func__);
403*8f957290SDavid van Moolenbroek
404*8f957290SDavid van Moolenbroek TAILQ_FOREACH_SAFE(rai, &ralist, next, ran) {
405*8f957290SDavid van Moolenbroek if (rai->leaving) {
406*8f957290SDavid van Moolenbroek TAILQ_REMOVE(&ralist, rai, next);
407*8f957290SDavid van Moolenbroek TAILQ_INSERT_HEAD(&ralist, rai->leaving, next);
408*8f957290SDavid van Moolenbroek rai->leaving->leaving = rai->leaving;
409*8f957290SDavid van Moolenbroek rai->leaving->leaving_for = rai->leaving;
410*8f957290SDavid van Moolenbroek free_rainfo(rai);
411*8f957290SDavid van Moolenbroek continue;
412*8f957290SDavid van Moolenbroek }
413*8f957290SDavid van Moolenbroek rai->lifetime = 0;
414*8f957290SDavid van Moolenbroek TAILQ_FOREACH(rdnss, &rai->rdnss, next)
415*8f957290SDavid van Moolenbroek rdnss->lifetime = 0;
416*8f957290SDavid van Moolenbroek TAILQ_FOREACH(dnssl, &rai->dnssl, next)
417*8f957290SDavid van Moolenbroek dnssl->lifetime = 0;
418*8f957290SDavid van Moolenbroek make_packet(rai);
419*8f957290SDavid van Moolenbroek rai->leaving = rai;
420*8f957290SDavid van Moolenbroek rai->leaving_for = rai;
421*8f957290SDavid van Moolenbroek rai->initcounter = MAX_INITIAL_RTR_ADVERTISEMENTS;
422*8f957290SDavid van Moolenbroek rai->mininterval = MIN_DELAY_BETWEEN_RAS;
423*8f957290SDavid van Moolenbroek rai->maxinterval = MIN_DELAY_BETWEEN_RAS;
424*8f957290SDavid van Moolenbroek rai->leaving_adv = MAX_FINAL_RTR_ADVERTISEMENTS;
425*8f957290SDavid van Moolenbroek ra_output(rai);
426*8f957290SDavid van Moolenbroek ra_timer_update((void *)rai, &rai->timer->tm);
427*8f957290SDavid van Moolenbroek rtadvd_set_timer(&rai->timer->tm, rai->timer);
428*8f957290SDavid van Moolenbroek }
429*8f957290SDavid van Moolenbroek }
430*8f957290SDavid van Moolenbroek
431*8f957290SDavid van Moolenbroek static void
rtmsg_input(void)432*8f957290SDavid van Moolenbroek rtmsg_input(void)
433*8f957290SDavid van Moolenbroek {
434*8f957290SDavid van Moolenbroek int n, type, ifindex = 0, plen;
435*8f957290SDavid van Moolenbroek size_t len;
436*8f957290SDavid van Moolenbroek union rt_msghdr_buf {
437*8f957290SDavid van Moolenbroek struct rt_msghdr rt_msghdr;
438*8f957290SDavid van Moolenbroek char data[2048];
439*8f957290SDavid van Moolenbroek } buffer;
440*8f957290SDavid van Moolenbroek char *msg, *next, *lim, **argv;
441*8f957290SDavid van Moolenbroek char ifname[IF_NAMESIZE];
442*8f957290SDavid van Moolenbroek struct prefix *prefix;
443*8f957290SDavid van Moolenbroek struct rainfo *rai;
444*8f957290SDavid van Moolenbroek struct in6_addr *addr;
445*8f957290SDavid van Moolenbroek char addrbuf[INET6_ADDRSTRLEN];
446*8f957290SDavid van Moolenbroek int prefixchange = 0, argc;
447*8f957290SDavid van Moolenbroek
448*8f957290SDavid van Moolenbroek memset(&buffer, 0, sizeof(buffer));
449*8f957290SDavid van Moolenbroek n = read(rtsock, &buffer, sizeof(buffer));
450*8f957290SDavid van Moolenbroek
451*8f957290SDavid van Moolenbroek /* We read the buffer first to clear the FD */
452*8f957290SDavid van Moolenbroek if (do_die)
453*8f957290SDavid van Moolenbroek return;
454*8f957290SDavid van Moolenbroek
455*8f957290SDavid van Moolenbroek msg = buffer.data;
456*8f957290SDavid van Moolenbroek if (dflag > 1) {
457*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG, "<%s> received a routing message "
458*8f957290SDavid van Moolenbroek "(type = %d, len = %d)", __func__, rtmsg_type(msg),
459*8f957290SDavid van Moolenbroek rtmsg_len(msg));
460*8f957290SDavid van Moolenbroek }
461*8f957290SDavid van Moolenbroek if (n > rtmsg_len(msg)) {
462*8f957290SDavid van Moolenbroek /*
463*8f957290SDavid van Moolenbroek * This usually won't happen for messages received on
464*8f957290SDavid van Moolenbroek * a routing socket.
465*8f957290SDavid van Moolenbroek */
466*8f957290SDavid van Moolenbroek if (dflag > 1)
467*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
468*8f957290SDavid van Moolenbroek "<%s> received data length is larger than "
469*8f957290SDavid van Moolenbroek "1st routing message len. multiple messages? "
470*8f957290SDavid van Moolenbroek "read %d bytes, but 1st msg len = %d",
471*8f957290SDavid van Moolenbroek __func__, n, rtmsg_len(msg));
472*8f957290SDavid van Moolenbroek #if 0
473*8f957290SDavid van Moolenbroek /* adjust length */
474*8f957290SDavid van Moolenbroek n = rtmsg_len(msg);
475*8f957290SDavid van Moolenbroek #endif
476*8f957290SDavid van Moolenbroek }
477*8f957290SDavid van Moolenbroek
478*8f957290SDavid van Moolenbroek lim = msg + n;
479*8f957290SDavid van Moolenbroek for (next = msg; next < lim; next += len) {
480*8f957290SDavid van Moolenbroek int oldifflags;
481*8f957290SDavid van Moolenbroek
482*8f957290SDavid van Moolenbroek next = get_next_msg(next, lim, 0, &len,
483*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_ADD) |
484*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_DELETE) |
485*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_NEWADDR) |
486*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_DELADDR) |
487*8f957290SDavid van Moolenbroek #ifdef RTM_IFANNOUNCE
488*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_IFANNOUNCE) |
489*8f957290SDavid van Moolenbroek #endif
490*8f957290SDavid van Moolenbroek RTADV_TYPE2BITMASK(RTM_IFINFO));
491*8f957290SDavid van Moolenbroek if (len == 0)
492*8f957290SDavid van Moolenbroek break;
493*8f957290SDavid van Moolenbroek type = rtmsg_type(next);
494*8f957290SDavid van Moolenbroek switch (type) {
495*8f957290SDavid van Moolenbroek case RTM_ADD:
496*8f957290SDavid van Moolenbroek case RTM_DELETE:
497*8f957290SDavid van Moolenbroek ifindex = get_rtm_ifindex(next);
498*8f957290SDavid van Moolenbroek break;
499*8f957290SDavid van Moolenbroek case RTM_NEWADDR:
500*8f957290SDavid van Moolenbroek case RTM_DELADDR:
501*8f957290SDavid van Moolenbroek ifindex = get_ifam_ifindex(next);
502*8f957290SDavid van Moolenbroek break;
503*8f957290SDavid van Moolenbroek #ifdef RTM_IFANNOUNCE
504*8f957290SDavid van Moolenbroek case RTM_IFANNOUNCE:
505*8f957290SDavid van Moolenbroek ifindex = get_ifan_ifindex(next);
506*8f957290SDavid van Moolenbroek if (get_ifan_what(next) == IFAN_ARRIVAL) {
507*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
508*8f957290SDavid van Moolenbroek "<%s> interface %s arrived",
509*8f957290SDavid van Moolenbroek __func__,
510*8f957290SDavid van Moolenbroek if_indextoname(ifindex, ifname));
511*8f957290SDavid van Moolenbroek if (if_argc == 0) {
512*8f957290SDavid van Moolenbroek getconfig(ifname, 0);
513*8f957290SDavid van Moolenbroek continue;
514*8f957290SDavid van Moolenbroek }
515*8f957290SDavid van Moolenbroek argc = if_argc;
516*8f957290SDavid van Moolenbroek argv = if_argv;
517*8f957290SDavid van Moolenbroek while (argc--) {
518*8f957290SDavid van Moolenbroek if (strcmp(ifname, *argv++) == 0) {
519*8f957290SDavid van Moolenbroek getconfig(ifname, 0);
520*8f957290SDavid van Moolenbroek break;
521*8f957290SDavid van Moolenbroek }
522*8f957290SDavid van Moolenbroek }
523*8f957290SDavid van Moolenbroek continue;
524*8f957290SDavid van Moolenbroek }
525*8f957290SDavid van Moolenbroek break;
526*8f957290SDavid van Moolenbroek #endif
527*8f957290SDavid van Moolenbroek case RTM_IFINFO:
528*8f957290SDavid van Moolenbroek ifindex = get_ifm_ifindex(next);
529*8f957290SDavid van Moolenbroek break;
530*8f957290SDavid van Moolenbroek default:
531*8f957290SDavid van Moolenbroek /* should not reach here */
532*8f957290SDavid van Moolenbroek if (dflag > 1) {
533*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
534*8f957290SDavid van Moolenbroek "<%s:%d> unknown rtmsg %d on %s",
535*8f957290SDavid van Moolenbroek __func__, __LINE__, type,
536*8f957290SDavid van Moolenbroek if_indextoname(ifindex, ifname));
537*8f957290SDavid van Moolenbroek }
538*8f957290SDavid van Moolenbroek continue;
539*8f957290SDavid van Moolenbroek }
540*8f957290SDavid van Moolenbroek
541*8f957290SDavid van Moolenbroek if ((rai = if_indextorainfo(ifindex)) == NULL) {
542*8f957290SDavid van Moolenbroek if (dflag > 1) {
543*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
544*8f957290SDavid van Moolenbroek "<%s> route changed on "
545*8f957290SDavid van Moolenbroek "non advertising interface %s (%d)",
546*8f957290SDavid van Moolenbroek __func__,
547*8f957290SDavid van Moolenbroek if_indextoname(ifindex, ifname),
548*8f957290SDavid van Moolenbroek ifindex);
549*8f957290SDavid van Moolenbroek }
550*8f957290SDavid van Moolenbroek continue;
551*8f957290SDavid van Moolenbroek }
552*8f957290SDavid van Moolenbroek oldifflags = rai->ifflags;
553*8f957290SDavid van Moolenbroek
554*8f957290SDavid van Moolenbroek switch (type) {
555*8f957290SDavid van Moolenbroek case RTM_ADD:
556*8f957290SDavid van Moolenbroek /* init ifflags because it may have changed */
557*8f957290SDavid van Moolenbroek rai->ifflags = if_getflags(ifindex, rai->ifflags);
558*8f957290SDavid van Moolenbroek
559*8f957290SDavid van Moolenbroek if (sflag)
560*8f957290SDavid van Moolenbroek break; /* we aren't interested in prefixes */
561*8f957290SDavid van Moolenbroek
562*8f957290SDavid van Moolenbroek addr = get_addr(msg);
563*8f957290SDavid van Moolenbroek plen = get_prefixlen(msg);
564*8f957290SDavid van Moolenbroek /* sanity check for plen */
565*8f957290SDavid van Moolenbroek /* as RFC2373, prefixlen is at least 4 */
566*8f957290SDavid van Moolenbroek if (plen < 4 || plen > 127) {
567*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> new interface route's"
568*8f957290SDavid van Moolenbroek "plen %d is invalid for a prefix",
569*8f957290SDavid van Moolenbroek __func__, plen);
570*8f957290SDavid van Moolenbroek break;
571*8f957290SDavid van Moolenbroek }
572*8f957290SDavid van Moolenbroek prefix = find_prefix(rai, addr, plen);
573*8f957290SDavid van Moolenbroek if (prefix) {
574*8f957290SDavid van Moolenbroek if (prefix->timer) {
575*8f957290SDavid van Moolenbroek /*
576*8f957290SDavid van Moolenbroek * If the prefix has been invalidated,
577*8f957290SDavid van Moolenbroek * make it available again.
578*8f957290SDavid van Moolenbroek */
579*8f957290SDavid van Moolenbroek update_prefix(prefix);
580*8f957290SDavid van Moolenbroek prefixchange = 1;
581*8f957290SDavid van Moolenbroek } else if (dflag > 1) {
582*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
583*8f957290SDavid van Moolenbroek "<%s> new prefix(%s/%d) "
584*8f957290SDavid van Moolenbroek "added on %s, "
585*8f957290SDavid van Moolenbroek "but it was already in list",
586*8f957290SDavid van Moolenbroek __func__,
587*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, addr,
588*8f957290SDavid van Moolenbroek (char *)addrbuf, INET6_ADDRSTRLEN),
589*8f957290SDavid van Moolenbroek plen, rai->ifname);
590*8f957290SDavid van Moolenbroek }
591*8f957290SDavid van Moolenbroek break;
592*8f957290SDavid van Moolenbroek }
593*8f957290SDavid van Moolenbroek make_prefix(rai, ifindex, addr, plen);
594*8f957290SDavid van Moolenbroek prefixchange = 1;
595*8f957290SDavid van Moolenbroek break;
596*8f957290SDavid van Moolenbroek case RTM_DELETE:
597*8f957290SDavid van Moolenbroek /* init ifflags because it may have changed */
598*8f957290SDavid van Moolenbroek rai->ifflags = if_getflags(ifindex, rai->ifflags);
599*8f957290SDavid van Moolenbroek
600*8f957290SDavid van Moolenbroek if (sflag)
601*8f957290SDavid van Moolenbroek break;
602*8f957290SDavid van Moolenbroek
603*8f957290SDavid van Moolenbroek addr = get_addr(msg);
604*8f957290SDavid van Moolenbroek plen = get_prefixlen(msg);
605*8f957290SDavid van Moolenbroek /* sanity check for plen */
606*8f957290SDavid van Moolenbroek /* as RFC2373, prefixlen is at least 4 */
607*8f957290SDavid van Moolenbroek if (plen < 4 || plen > 127) {
608*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
609*8f957290SDavid van Moolenbroek "<%s> deleted interface route's "
610*8f957290SDavid van Moolenbroek "plen %d is invalid for a prefix",
611*8f957290SDavid van Moolenbroek __func__, plen);
612*8f957290SDavid van Moolenbroek break;
613*8f957290SDavid van Moolenbroek }
614*8f957290SDavid van Moolenbroek prefix = find_prefix(rai, addr, plen);
615*8f957290SDavid van Moolenbroek if (prefix == NULL) {
616*8f957290SDavid van Moolenbroek if (dflag > 1) {
617*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
618*8f957290SDavid van Moolenbroek "<%s> prefix(%s/%d) was "
619*8f957290SDavid van Moolenbroek "deleted on %s, "
620*8f957290SDavid van Moolenbroek "but it was not in list",
621*8f957290SDavid van Moolenbroek __func__,
622*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, addr,
623*8f957290SDavid van Moolenbroek (char *)addrbuf, INET6_ADDRSTRLEN),
624*8f957290SDavid van Moolenbroek plen, rai->ifname);
625*8f957290SDavid van Moolenbroek }
626*8f957290SDavid van Moolenbroek break;
627*8f957290SDavid van Moolenbroek }
628*8f957290SDavid van Moolenbroek invalidate_prefix(prefix);
629*8f957290SDavid van Moolenbroek prefixchange = 1;
630*8f957290SDavid van Moolenbroek break;
631*8f957290SDavid van Moolenbroek case RTM_NEWADDR:
632*8f957290SDavid van Moolenbroek case RTM_DELADDR:
633*8f957290SDavid van Moolenbroek /* init ifflags because it may have changed */
634*8f957290SDavid van Moolenbroek rai->ifflags = if_getflags(ifindex, rai->ifflags);
635*8f957290SDavid van Moolenbroek break;
636*8f957290SDavid van Moolenbroek case RTM_IFINFO:
637*8f957290SDavid van Moolenbroek rai->ifflags = get_ifm_flags(next);
638*8f957290SDavid van Moolenbroek break;
639*8f957290SDavid van Moolenbroek #ifdef RTM_IFANNOUNCE
640*8f957290SDavid van Moolenbroek case RTM_IFANNOUNCE:
641*8f957290SDavid van Moolenbroek if (get_ifan_what(next) == IFAN_DEPARTURE) {
642*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
643*8f957290SDavid van Moolenbroek "<%s> interface %s departed",
644*8f957290SDavid van Moolenbroek __func__, rai->ifname);
645*8f957290SDavid van Moolenbroek TAILQ_REMOVE(&ralist, rai, next);
646*8f957290SDavid van Moolenbroek if (rai->leaving)
647*8f957290SDavid van Moolenbroek free_rainfo(rai->leaving);
648*8f957290SDavid van Moolenbroek free_rainfo(rai);
649*8f957290SDavid van Moolenbroek continue;
650*8f957290SDavid van Moolenbroek }
651*8f957290SDavid van Moolenbroek break;
652*8f957290SDavid van Moolenbroek #endif
653*8f957290SDavid van Moolenbroek default:
654*8f957290SDavid van Moolenbroek /* should not reach here */
655*8f957290SDavid van Moolenbroek if (dflag > 1) {
656*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
657*8f957290SDavid van Moolenbroek "<%s:%d> unknown rtmsg %d on %s",
658*8f957290SDavid van Moolenbroek __func__, __LINE__, type,
659*8f957290SDavid van Moolenbroek if_indextoname(ifindex, ifname));
660*8f957290SDavid van Moolenbroek }
661*8f957290SDavid van Moolenbroek return;
662*8f957290SDavid van Moolenbroek }
663*8f957290SDavid van Moolenbroek
664*8f957290SDavid van Moolenbroek /* check if an interface flag is changed */
665*8f957290SDavid van Moolenbroek if ((oldifflags & IFF_UP) != 0 && /* UP to DOWN */
666*8f957290SDavid van Moolenbroek (rai->ifflags & IFF_UP) == 0) {
667*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
668*8f957290SDavid van Moolenbroek "<%s> interface %s becomes down. stop timer.",
669*8f957290SDavid van Moolenbroek __func__, rai->ifname);
670*8f957290SDavid van Moolenbroek rtadvd_remove_timer(&rai->timer);
671*8f957290SDavid van Moolenbroek } else if ((oldifflags & IFF_UP) == 0 && /* DOWN to UP */
672*8f957290SDavid van Moolenbroek (rai->ifflags & IFF_UP) != 0) {
673*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
674*8f957290SDavid van Moolenbroek "<%s> interface %s becomes up. restart timer.",
675*8f957290SDavid van Moolenbroek __func__, rai->ifname);
676*8f957290SDavid van Moolenbroek
677*8f957290SDavid van Moolenbroek rai->initcounter = 0; /* reset the counter */
678*8f957290SDavid van Moolenbroek rai->waiting = 0; /* XXX */
679*8f957290SDavid van Moolenbroek rtadvd_remove_timer(&rai->timer);
680*8f957290SDavid van Moolenbroek rai->timer = rtadvd_add_timer(ra_timeout,
681*8f957290SDavid van Moolenbroek ra_timer_update, rai, rai);
682*8f957290SDavid van Moolenbroek ra_timer_update((void *)rai, &rai->timer->tm);
683*8f957290SDavid van Moolenbroek rtadvd_set_timer(&rai->timer->tm, rai->timer);
684*8f957290SDavid van Moolenbroek } else if (prefixchange && rai->ifflags & IFF_UP) {
685*8f957290SDavid van Moolenbroek /*
686*8f957290SDavid van Moolenbroek * An advertised prefix has been added or invalidated.
687*8f957290SDavid van Moolenbroek * Will notice the change in a short delay.
688*8f957290SDavid van Moolenbroek */
689*8f957290SDavid van Moolenbroek rai->initcounter = 0;
690*8f957290SDavid van Moolenbroek ra_timer_set_short_delay(rai);
691*8f957290SDavid van Moolenbroek }
692*8f957290SDavid van Moolenbroek }
693*8f957290SDavid van Moolenbroek
694*8f957290SDavid van Moolenbroek return;
695*8f957290SDavid van Moolenbroek }
696*8f957290SDavid van Moolenbroek
697*8f957290SDavid van Moolenbroek void
rtadvd_input(void)698*8f957290SDavid van Moolenbroek rtadvd_input(void)
699*8f957290SDavid van Moolenbroek {
700*8f957290SDavid van Moolenbroek ssize_t i;
701*8f957290SDavid van Moolenbroek int *hlimp = NULL;
702*8f957290SDavid van Moolenbroek #ifdef OLDRAWSOCKET
703*8f957290SDavid van Moolenbroek struct ip6_hdr *ip;
704*8f957290SDavid van Moolenbroek #endif
705*8f957290SDavid van Moolenbroek struct icmp6_hdr *icp;
706*8f957290SDavid van Moolenbroek int ifindex = 0;
707*8f957290SDavid van Moolenbroek struct cmsghdr *cm;
708*8f957290SDavid van Moolenbroek struct in6_pktinfo *pi = NULL;
709*8f957290SDavid van Moolenbroek char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
710*8f957290SDavid van Moolenbroek struct in6_addr dst = in6addr_any;
711*8f957290SDavid van Moolenbroek struct rainfo *rai;
712*8f957290SDavid van Moolenbroek
713*8f957290SDavid van Moolenbroek /*
714*8f957290SDavid van Moolenbroek * Get message. We reset msg_controllen since the field could
715*8f957290SDavid van Moolenbroek * be modified if we had received a message before setting
716*8f957290SDavid van Moolenbroek * receive options.
717*8f957290SDavid van Moolenbroek */
718*8f957290SDavid van Moolenbroek rcvmhdr.msg_controllen = rcvcmsgbuflen;
719*8f957290SDavid van Moolenbroek if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0)
720*8f957290SDavid van Moolenbroek return;
721*8f957290SDavid van Moolenbroek
722*8f957290SDavid van Moolenbroek /* We read the buffer first to clear the FD */
723*8f957290SDavid van Moolenbroek if (do_die)
724*8f957290SDavid van Moolenbroek return;
725*8f957290SDavid van Moolenbroek
726*8f957290SDavid van Moolenbroek /* extract optional information via Advanced API */
727*8f957290SDavid van Moolenbroek for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
728*8f957290SDavid van Moolenbroek cm;
729*8f957290SDavid van Moolenbroek cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
730*8f957290SDavid van Moolenbroek if (cm->cmsg_level == IPPROTO_IPV6 &&
731*8f957290SDavid van Moolenbroek cm->cmsg_type == IPV6_PKTINFO &&
732*8f957290SDavid van Moolenbroek cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
733*8f957290SDavid van Moolenbroek pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
734*8f957290SDavid van Moolenbroek ifindex = pi->ipi6_ifindex;
735*8f957290SDavid van Moolenbroek dst = pi->ipi6_addr;
736*8f957290SDavid van Moolenbroek }
737*8f957290SDavid van Moolenbroek if (cm->cmsg_level == IPPROTO_IPV6 &&
738*8f957290SDavid van Moolenbroek cm->cmsg_type == IPV6_HOPLIMIT &&
739*8f957290SDavid van Moolenbroek cm->cmsg_len == CMSG_LEN(sizeof(int)))
740*8f957290SDavid van Moolenbroek hlimp = (int *)CMSG_DATA(cm);
741*8f957290SDavid van Moolenbroek }
742*8f957290SDavid van Moolenbroek if (ifindex == 0) {
743*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
744*8f957290SDavid van Moolenbroek "<%s> failed to get receiving interface",
745*8f957290SDavid van Moolenbroek __func__);
746*8f957290SDavid van Moolenbroek return;
747*8f957290SDavid van Moolenbroek }
748*8f957290SDavid van Moolenbroek if (hlimp == NULL) {
749*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
750*8f957290SDavid van Moolenbroek "<%s> failed to get receiving hop limit",
751*8f957290SDavid van Moolenbroek __func__);
752*8f957290SDavid van Moolenbroek return;
753*8f957290SDavid van Moolenbroek }
754*8f957290SDavid van Moolenbroek
755*8f957290SDavid van Moolenbroek if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
756*8f957290SDavid van Moolenbroek if (dflag > 1) {
757*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
758*8f957290SDavid van Moolenbroek "<%s> received data for non advertising "
759*8f957290SDavid van Moolenbroek "interface (%s)",
760*8f957290SDavid van Moolenbroek __func__,
761*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
762*8f957290SDavid van Moolenbroek }
763*8f957290SDavid van Moolenbroek return;
764*8f957290SDavid van Moolenbroek }
765*8f957290SDavid van Moolenbroek /*
766*8f957290SDavid van Moolenbroek * If we happen to receive data on an interface which is now down,
767*8f957290SDavid van Moolenbroek * just discard the data.
768*8f957290SDavid van Moolenbroek */
769*8f957290SDavid van Moolenbroek if ((rai->ifflags & IFF_UP) == 0) {
770*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
771*8f957290SDavid van Moolenbroek "<%s> received data on a disabled interface (%s)",
772*8f957290SDavid van Moolenbroek __func__,
773*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
774*8f957290SDavid van Moolenbroek return;
775*8f957290SDavid van Moolenbroek }
776*8f957290SDavid van Moolenbroek
777*8f957290SDavid van Moolenbroek #ifdef OLDRAWSOCKET
778*8f957290SDavid van Moolenbroek if ((size_t)i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
779*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
780*8f957290SDavid van Moolenbroek "<%s> packet size(%d) is too short",
781*8f957290SDavid van Moolenbroek __func__, i);
782*8f957290SDavid van Moolenbroek return;
783*8f957290SDavid van Moolenbroek }
784*8f957290SDavid van Moolenbroek
785*8f957290SDavid van Moolenbroek ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
786*8f957290SDavid van Moolenbroek icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
787*8f957290SDavid van Moolenbroek #else
788*8f957290SDavid van Moolenbroek if ((size_t)i < sizeof(struct icmp6_hdr)) {
789*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
790*8f957290SDavid van Moolenbroek "<%s> packet size(%zd) is too short",
791*8f957290SDavid van Moolenbroek __func__, i);
792*8f957290SDavid van Moolenbroek return;
793*8f957290SDavid van Moolenbroek }
794*8f957290SDavid van Moolenbroek
795*8f957290SDavid van Moolenbroek icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
796*8f957290SDavid van Moolenbroek #endif
797*8f957290SDavid van Moolenbroek
798*8f957290SDavid van Moolenbroek switch (icp->icmp6_type) {
799*8f957290SDavid van Moolenbroek case ND_ROUTER_SOLICIT:
800*8f957290SDavid van Moolenbroek /*
801*8f957290SDavid van Moolenbroek * Message verification - RFC-2461 6.1.1
802*8f957290SDavid van Moolenbroek * XXX: these checks must be done in the kernel as well,
803*8f957290SDavid van Moolenbroek * but we can't completely rely on them.
804*8f957290SDavid van Moolenbroek */
805*8f957290SDavid van Moolenbroek if (*hlimp != 255) {
806*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
807*8f957290SDavid van Moolenbroek "<%s> RS with invalid hop limit(%d) "
808*8f957290SDavid van Moolenbroek "received from %s on %s",
809*8f957290SDavid van Moolenbroek __func__, *hlimp,
810*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
811*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
812*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
813*8f957290SDavid van Moolenbroek return;
814*8f957290SDavid van Moolenbroek }
815*8f957290SDavid van Moolenbroek if (icp->icmp6_code) {
816*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
817*8f957290SDavid van Moolenbroek "<%s> RS with invalid ICMP6 code(%d) "
818*8f957290SDavid van Moolenbroek "received from %s on %s",
819*8f957290SDavid van Moolenbroek __func__, icp->icmp6_code,
820*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
821*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
822*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
823*8f957290SDavid van Moolenbroek return;
824*8f957290SDavid van Moolenbroek }
825*8f957290SDavid van Moolenbroek if ((size_t)i < sizeof(struct nd_router_solicit)) {
826*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
827*8f957290SDavid van Moolenbroek "<%s> RS from %s on %s does not have enough "
828*8f957290SDavid van Moolenbroek "length (len = %zd)",
829*8f957290SDavid van Moolenbroek __func__,
830*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
831*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
832*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
833*8f957290SDavid van Moolenbroek return;
834*8f957290SDavid van Moolenbroek }
835*8f957290SDavid van Moolenbroek rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom);
836*8f957290SDavid van Moolenbroek break;
837*8f957290SDavid van Moolenbroek case ND_ROUTER_ADVERT:
838*8f957290SDavid van Moolenbroek /*
839*8f957290SDavid van Moolenbroek * Message verification - RFC-2461 6.1.2
840*8f957290SDavid van Moolenbroek * XXX: there's a same dilemma as above...
841*8f957290SDavid van Moolenbroek */
842*8f957290SDavid van Moolenbroek if (*hlimp != 255) {
843*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
844*8f957290SDavid van Moolenbroek "<%s> RA with invalid hop limit(%d) "
845*8f957290SDavid van Moolenbroek "received from %s on %s",
846*8f957290SDavid van Moolenbroek __func__, *hlimp,
847*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
848*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
849*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
850*8f957290SDavid van Moolenbroek return;
851*8f957290SDavid van Moolenbroek }
852*8f957290SDavid van Moolenbroek if (icp->icmp6_code) {
853*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
854*8f957290SDavid van Moolenbroek "<%s> RA with invalid ICMP6 code(%d) "
855*8f957290SDavid van Moolenbroek "received from %s on %s",
856*8f957290SDavid van Moolenbroek __func__, icp->icmp6_code,
857*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
858*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
859*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
860*8f957290SDavid van Moolenbroek return;
861*8f957290SDavid van Moolenbroek }
862*8f957290SDavid van Moolenbroek if ((size_t)i < sizeof(struct nd_router_advert)) {
863*8f957290SDavid van Moolenbroek syslog(LOG_NOTICE,
864*8f957290SDavid van Moolenbroek "<%s> RA from %s on %s does not have enough "
865*8f957290SDavid van Moolenbroek "length (len = %zd)",
866*8f957290SDavid van Moolenbroek __func__,
867*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
868*8f957290SDavid van Moolenbroek INET6_ADDRSTRLEN),
869*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
870*8f957290SDavid van Moolenbroek return;
871*8f957290SDavid van Moolenbroek }
872*8f957290SDavid van Moolenbroek ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
873*8f957290SDavid van Moolenbroek break;
874*8f957290SDavid van Moolenbroek case ICMP6_ROUTER_RENUMBERING:
875*8f957290SDavid van Moolenbroek if (accept_rr == 0) {
876*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> received a router renumbering "
877*8f957290SDavid van Moolenbroek "message, but not allowed to be accepted",
878*8f957290SDavid van Moolenbroek __func__);
879*8f957290SDavid van Moolenbroek break;
880*8f957290SDavid van Moolenbroek }
881*8f957290SDavid van Moolenbroek rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom,
882*8f957290SDavid van Moolenbroek &dst);
883*8f957290SDavid van Moolenbroek break;
884*8f957290SDavid van Moolenbroek default:
885*8f957290SDavid van Moolenbroek /*
886*8f957290SDavid van Moolenbroek * Note that this case is POSSIBLE, especially just
887*8f957290SDavid van Moolenbroek * after invocation of the daemon. This is because we
888*8f957290SDavid van Moolenbroek * could receive message after opening the socket and
889*8f957290SDavid van Moolenbroek * before setting ICMP6 type filter(see sock_open()).
890*8f957290SDavid van Moolenbroek */
891*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> invalid icmp type(%d)",
892*8f957290SDavid van Moolenbroek __func__, icp->icmp6_type);
893*8f957290SDavid van Moolenbroek return;
894*8f957290SDavid van Moolenbroek }
895*8f957290SDavid van Moolenbroek
896*8f957290SDavid van Moolenbroek return;
897*8f957290SDavid van Moolenbroek }
898*8f957290SDavid van Moolenbroek
899*8f957290SDavid van Moolenbroek static void
rs_input(int len,struct nd_router_solicit * rs,struct in6_pktinfo * pi,struct sockaddr_in6 * from)900*8f957290SDavid van Moolenbroek rs_input(int len, struct nd_router_solicit *rs,
901*8f957290SDavid van Moolenbroek struct in6_pktinfo *pi, struct sockaddr_in6 *from)
902*8f957290SDavid van Moolenbroek {
903*8f957290SDavid van Moolenbroek char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
904*8f957290SDavid van Moolenbroek union nd_opts ndopts;
905*8f957290SDavid van Moolenbroek struct rainfo *rai;
906*8f957290SDavid van Moolenbroek struct soliciter *sol;
907*8f957290SDavid van Moolenbroek
908*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
909*8f957290SDavid van Moolenbroek "<%s> RS received from %s on %s",
910*8f957290SDavid van Moolenbroek __func__,
911*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
912*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
913*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
914*8f957290SDavid van Moolenbroek
915*8f957290SDavid van Moolenbroek /* ND option check */
916*8f957290SDavid van Moolenbroek memset(&ndopts, 0, sizeof(ndopts));
917*8f957290SDavid van Moolenbroek TAILQ_INIT(&ndopts.nd_opts_list);
918*8f957290SDavid van Moolenbroek if (nd6_options((struct nd_opt_hdr *)(rs + 1),
919*8f957290SDavid van Moolenbroek len - sizeof(struct nd_router_solicit),
920*8f957290SDavid van Moolenbroek &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
921*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
922*8f957290SDavid van Moolenbroek "<%s> ND option check failed for an RS from %s on %s",
923*8f957290SDavid van Moolenbroek __func__,
924*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
925*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
926*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
927*8f957290SDavid van Moolenbroek return;
928*8f957290SDavid van Moolenbroek }
929*8f957290SDavid van Moolenbroek
930*8f957290SDavid van Moolenbroek /*
931*8f957290SDavid van Moolenbroek * If the IP source address is the unspecified address, there
932*8f957290SDavid van Moolenbroek * must be no source link-layer address option in the message.
933*8f957290SDavid van Moolenbroek * (RFC-2461 6.1.1)
934*8f957290SDavid van Moolenbroek */
935*8f957290SDavid van Moolenbroek if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
936*8f957290SDavid van Moolenbroek ndopts.nd_opts_src_lladdr) {
937*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
938*8f957290SDavid van Moolenbroek "<%s> RS from unspecified src on %s has a link-layer"
939*8f957290SDavid van Moolenbroek " address option",
940*8f957290SDavid van Moolenbroek __func__,
941*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
942*8f957290SDavid van Moolenbroek goto done;
943*8f957290SDavid van Moolenbroek }
944*8f957290SDavid van Moolenbroek
945*8f957290SDavid van Moolenbroek if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == NULL) {
946*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
947*8f957290SDavid van Moolenbroek "<%s> RS received on non advertising interface(%s)",
948*8f957290SDavid van Moolenbroek __func__,
949*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
950*8f957290SDavid van Moolenbroek goto done;
951*8f957290SDavid van Moolenbroek }
952*8f957290SDavid van Moolenbroek
953*8f957290SDavid van Moolenbroek if (rai->leaving) {
954*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
955*8f957290SDavid van Moolenbroek "<%s> RS received on reconfiguring advertising interface(%s)",
956*8f957290SDavid van Moolenbroek __func__, rai->ifname);
957*8f957290SDavid van Moolenbroek goto done;
958*8f957290SDavid van Moolenbroek }
959*8f957290SDavid van Moolenbroek
960*8f957290SDavid van Moolenbroek rai->rsinput++; /* increment statistics */
961*8f957290SDavid van Moolenbroek
962*8f957290SDavid van Moolenbroek /*
963*8f957290SDavid van Moolenbroek * Decide whether to send RA according to the rate-limit
964*8f957290SDavid van Moolenbroek * consideration.
965*8f957290SDavid van Moolenbroek */
966*8f957290SDavid van Moolenbroek
967*8f957290SDavid van Moolenbroek /* record sockaddr waiting for RA, if possible */
968*8f957290SDavid van Moolenbroek sol = malloc(sizeof(*sol));
969*8f957290SDavid van Moolenbroek if (sol) {
970*8f957290SDavid van Moolenbroek sol->addr = *from;
971*8f957290SDavid van Moolenbroek /* XXX RFC2553 need clarification on flowinfo */
972*8f957290SDavid van Moolenbroek sol->addr.sin6_flowinfo = 0;
973*8f957290SDavid van Moolenbroek TAILQ_INSERT_HEAD(&rai->soliciter, sol, next);
974*8f957290SDavid van Moolenbroek }
975*8f957290SDavid van Moolenbroek
976*8f957290SDavid van Moolenbroek /*
977*8f957290SDavid van Moolenbroek * If there is already a waiting RS packet, don't
978*8f957290SDavid van Moolenbroek * update the timer.
979*8f957290SDavid van Moolenbroek */
980*8f957290SDavid van Moolenbroek if (rai->waiting++)
981*8f957290SDavid van Moolenbroek goto done;
982*8f957290SDavid van Moolenbroek
983*8f957290SDavid van Moolenbroek ra_timer_set_short_delay(rai);
984*8f957290SDavid van Moolenbroek
985*8f957290SDavid van Moolenbroek done:
986*8f957290SDavid van Moolenbroek free_ndopts(&ndopts);
987*8f957290SDavid van Moolenbroek return;
988*8f957290SDavid van Moolenbroek }
989*8f957290SDavid van Moolenbroek
990*8f957290SDavid van Moolenbroek void
ra_timer_set_short_delay(struct rainfo * rai)991*8f957290SDavid van Moolenbroek ra_timer_set_short_delay(struct rainfo *rai)
992*8f957290SDavid van Moolenbroek {
993*8f957290SDavid van Moolenbroek long delay; /* must not be greater than 1000000 */
994*8f957290SDavid van Moolenbroek struct timespec interval, now, min_delay, tm_tmp, *rest;
995*8f957290SDavid van Moolenbroek
996*8f957290SDavid van Moolenbroek /*
997*8f957290SDavid van Moolenbroek * Compute a random delay. If the computed value
998*8f957290SDavid van Moolenbroek * corresponds to a time later than the time the next
999*8f957290SDavid van Moolenbroek * multicast RA is scheduled to be sent, ignore the random
1000*8f957290SDavid van Moolenbroek * delay and send the advertisement at the
1001*8f957290SDavid van Moolenbroek * already-scheduled time. RFC2461 6.2.6
1002*8f957290SDavid van Moolenbroek */
1003*8f957290SDavid van Moolenbroek delay = arc4random() % MAX_RA_DELAY_TIME;
1004*8f957290SDavid van Moolenbroek interval.tv_sec = 0;
1005*8f957290SDavid van Moolenbroek interval.tv_nsec = delay;
1006*8f957290SDavid van Moolenbroek rest = rtadvd_timer_rest(rai->timer);
1007*8f957290SDavid van Moolenbroek if (timespeccmp(rest, &interval, <)) {
1008*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG, "<%s> random delay is larger than "
1009*8f957290SDavid van Moolenbroek "the rest of current timer", __func__);
1010*8f957290SDavid van Moolenbroek interval = *rest;
1011*8f957290SDavid van Moolenbroek }
1012*8f957290SDavid van Moolenbroek
1013*8f957290SDavid van Moolenbroek /*
1014*8f957290SDavid van Moolenbroek * If we sent a multicast Router Advertisement within
1015*8f957290SDavid van Moolenbroek * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
1016*8f957290SDavid van Moolenbroek * the advertisement to be sent at a time corresponding to
1017*8f957290SDavid van Moolenbroek * MIN_DELAY_BETWEEN_RAS plus the random value after the
1018*8f957290SDavid van Moolenbroek * previous advertisement was sent.
1019*8f957290SDavid van Moolenbroek */
1020*8f957290SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &now);
1021*8f957290SDavid van Moolenbroek timespecsub(&now, &rai->lastsent, &tm_tmp);
1022*8f957290SDavid van Moolenbroek min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
1023*8f957290SDavid van Moolenbroek min_delay.tv_nsec = 0;
1024*8f957290SDavid van Moolenbroek if (timespeccmp(&tm_tmp, &min_delay, <)) {
1025*8f957290SDavid van Moolenbroek timespecsub(&min_delay, &tm_tmp, &min_delay);
1026*8f957290SDavid van Moolenbroek timespecadd(&min_delay, &interval, &interval);
1027*8f957290SDavid van Moolenbroek }
1028*8f957290SDavid van Moolenbroek rtadvd_set_timer(&interval, rai->timer);
1029*8f957290SDavid van Moolenbroek }
1030*8f957290SDavid van Moolenbroek
1031*8f957290SDavid van Moolenbroek static void
ra_input(int len,struct nd_router_advert * ra,struct in6_pktinfo * pi,struct sockaddr_in6 * from)1032*8f957290SDavid van Moolenbroek ra_input(int len, struct nd_router_advert *ra,
1033*8f957290SDavid van Moolenbroek struct in6_pktinfo *pi, struct sockaddr_in6 *from)
1034*8f957290SDavid van Moolenbroek {
1035*8f957290SDavid van Moolenbroek struct rainfo *rai;
1036*8f957290SDavid van Moolenbroek char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
1037*8f957290SDavid van Moolenbroek union nd_opts ndopts;
1038*8f957290SDavid van Moolenbroek const char *on_off[] = {"OFF", "ON"};
1039*8f957290SDavid van Moolenbroek uint32_t reachabletime, retranstimer, mtu;
1040*8f957290SDavid van Moolenbroek struct nd_optlist *optp;
1041*8f957290SDavid van Moolenbroek int inconsistent = 0;
1042*8f957290SDavid van Moolenbroek
1043*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1044*8f957290SDavid van Moolenbroek "<%s> RA received from %s on %s",
1045*8f957290SDavid van Moolenbroek __func__,
1046*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1047*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1048*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
1049*8f957290SDavid van Moolenbroek
1050*8f957290SDavid van Moolenbroek /* ND option check */
1051*8f957290SDavid van Moolenbroek memset(&ndopts, 0, sizeof(ndopts));
1052*8f957290SDavid van Moolenbroek TAILQ_INIT(&ndopts.nd_opts_list);
1053*8f957290SDavid van Moolenbroek if (nd6_options((struct nd_opt_hdr *)(ra + 1),
1054*8f957290SDavid van Moolenbroek len - sizeof(struct nd_router_advert),
1055*8f957290SDavid van Moolenbroek &ndopts, NDOPT_FLAG_SRCLINKADDR |
1056*8f957290SDavid van Moolenbroek NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU |
1057*8f957290SDavid van Moolenbroek NDOPT_FLAG_RDNSS | NDOPT_FLAG_DNSSL))
1058*8f957290SDavid van Moolenbroek {
1059*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1060*8f957290SDavid van Moolenbroek "<%s> ND option check failed for an RA from %s on %s",
1061*8f957290SDavid van Moolenbroek __func__,
1062*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1063*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1064*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
1065*8f957290SDavid van Moolenbroek return;
1066*8f957290SDavid van Moolenbroek }
1067*8f957290SDavid van Moolenbroek
1068*8f957290SDavid van Moolenbroek /*
1069*8f957290SDavid van Moolenbroek * RA consistency check according to RFC-2461 6.2.7
1070*8f957290SDavid van Moolenbroek */
1071*8f957290SDavid van Moolenbroek if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
1072*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1073*8f957290SDavid van Moolenbroek "<%s> received RA from %s on non-advertising"
1074*8f957290SDavid van Moolenbroek " interface(%s)",
1075*8f957290SDavid van Moolenbroek __func__,
1076*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1077*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1078*8f957290SDavid van Moolenbroek if_indextoname(pi->ipi6_ifindex, ifnamebuf));
1079*8f957290SDavid van Moolenbroek goto done;
1080*8f957290SDavid van Moolenbroek }
1081*8f957290SDavid van Moolenbroek if (rai->leaving) {
1082*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1083*8f957290SDavid van Moolenbroek "<%s> received RA on re-configuring interface (%s)",
1084*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1085*8f957290SDavid van Moolenbroek goto done;
1086*8f957290SDavid van Moolenbroek }
1087*8f957290SDavid van Moolenbroek rai->rainput++; /* increment statistics */
1088*8f957290SDavid van Moolenbroek
1089*8f957290SDavid van Moolenbroek /* Cur Hop Limit value */
1090*8f957290SDavid van Moolenbroek if (ra->nd_ra_curhoplimit && rai->hoplimit &&
1091*8f957290SDavid van Moolenbroek ra->nd_ra_curhoplimit != rai->hoplimit) {
1092*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1093*8f957290SDavid van Moolenbroek "<%s> CurHopLimit inconsistent on %s:"
1094*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1095*8f957290SDavid van Moolenbroek __func__,
1096*8f957290SDavid van Moolenbroek rai->ifname,
1097*8f957290SDavid van Moolenbroek ra->nd_ra_curhoplimit,
1098*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1099*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1100*8f957290SDavid van Moolenbroek rai->hoplimit);
1101*8f957290SDavid van Moolenbroek inconsistent++;
1102*8f957290SDavid van Moolenbroek }
1103*8f957290SDavid van Moolenbroek /* M flag */
1104*8f957290SDavid van Moolenbroek if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
1105*8f957290SDavid van Moolenbroek rai->managedflg) {
1106*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1107*8f957290SDavid van Moolenbroek "<%s> M flag inconsistent on %s:"
1108*8f957290SDavid van Moolenbroek " %s from %s, %s from us",
1109*8f957290SDavid van Moolenbroek __func__,
1110*8f957290SDavid van Moolenbroek rai->ifname,
1111*8f957290SDavid van Moolenbroek on_off[!rai->managedflg],
1112*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1113*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1114*8f957290SDavid van Moolenbroek on_off[rai->managedflg]);
1115*8f957290SDavid van Moolenbroek inconsistent++;
1116*8f957290SDavid van Moolenbroek }
1117*8f957290SDavid van Moolenbroek /* O flag */
1118*8f957290SDavid van Moolenbroek if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
1119*8f957290SDavid van Moolenbroek rai->otherflg) {
1120*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1121*8f957290SDavid van Moolenbroek "<%s> O flag inconsistent on %s:"
1122*8f957290SDavid van Moolenbroek " %s from %s, %s from us",
1123*8f957290SDavid van Moolenbroek __func__,
1124*8f957290SDavid van Moolenbroek rai->ifname,
1125*8f957290SDavid van Moolenbroek on_off[!rai->otherflg],
1126*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1127*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1128*8f957290SDavid van Moolenbroek on_off[rai->otherflg]);
1129*8f957290SDavid van Moolenbroek inconsistent++;
1130*8f957290SDavid van Moolenbroek }
1131*8f957290SDavid van Moolenbroek /* Reachable Time */
1132*8f957290SDavid van Moolenbroek reachabletime = ntohl(ra->nd_ra_reachable);
1133*8f957290SDavid van Moolenbroek if (reachabletime && rai->reachabletime &&
1134*8f957290SDavid van Moolenbroek reachabletime != rai->reachabletime) {
1135*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1136*8f957290SDavid van Moolenbroek "<%s> ReachableTime inconsistent on %s:"
1137*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1138*8f957290SDavid van Moolenbroek __func__,
1139*8f957290SDavid van Moolenbroek rai->ifname,
1140*8f957290SDavid van Moolenbroek reachabletime,
1141*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1142*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1143*8f957290SDavid van Moolenbroek rai->reachabletime);
1144*8f957290SDavid van Moolenbroek inconsistent++;
1145*8f957290SDavid van Moolenbroek }
1146*8f957290SDavid van Moolenbroek /* Retrans Timer */
1147*8f957290SDavid van Moolenbroek retranstimer = ntohl(ra->nd_ra_retransmit);
1148*8f957290SDavid van Moolenbroek if (retranstimer && rai->retranstimer &&
1149*8f957290SDavid van Moolenbroek retranstimer != rai->retranstimer) {
1150*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1151*8f957290SDavid van Moolenbroek "<%s> RetranceTimer inconsistent on %s:"
1152*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1153*8f957290SDavid van Moolenbroek __func__,
1154*8f957290SDavid van Moolenbroek rai->ifname,
1155*8f957290SDavid van Moolenbroek retranstimer,
1156*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1157*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1158*8f957290SDavid van Moolenbroek rai->retranstimer);
1159*8f957290SDavid van Moolenbroek inconsistent++;
1160*8f957290SDavid van Moolenbroek }
1161*8f957290SDavid van Moolenbroek /* Values in the MTU options */
1162*8f957290SDavid van Moolenbroek if (ndopts.nd_opts_mtu) {
1163*8f957290SDavid van Moolenbroek mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
1164*8f957290SDavid van Moolenbroek if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
1165*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1166*8f957290SDavid van Moolenbroek "<%s> MTU option value inconsistent on %s:"
1167*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1168*8f957290SDavid van Moolenbroek __func__,
1169*8f957290SDavid van Moolenbroek rai->ifname, mtu,
1170*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1171*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1172*8f957290SDavid van Moolenbroek rai->linkmtu);
1173*8f957290SDavid van Moolenbroek inconsistent++;
1174*8f957290SDavid van Moolenbroek }
1175*8f957290SDavid van Moolenbroek }
1176*8f957290SDavid van Moolenbroek /* Preferred and Valid Lifetimes for prefixes */
1177*8f957290SDavid van Moolenbroek if (ndopts.nd_opts_pi)
1178*8f957290SDavid van Moolenbroek if (prefix_check(ndopts.nd_opts_pi, rai, from))
1179*8f957290SDavid van Moolenbroek inconsistent++;
1180*8f957290SDavid van Moolenbroek TAILQ_FOREACH(optp, &ndopts.nd_opts_list, next)
1181*8f957290SDavid van Moolenbroek if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
1182*8f957290SDavid van Moolenbroek rai, from))
1183*8f957290SDavid van Moolenbroek inconsistent++;
1184*8f957290SDavid van Moolenbroek
1185*8f957290SDavid van Moolenbroek if (inconsistent)
1186*8f957290SDavid van Moolenbroek rai->rainconsistent++;
1187*8f957290SDavid van Moolenbroek
1188*8f957290SDavid van Moolenbroek done:
1189*8f957290SDavid van Moolenbroek free_ndopts(&ndopts);
1190*8f957290SDavid van Moolenbroek return;
1191*8f957290SDavid van Moolenbroek }
1192*8f957290SDavid van Moolenbroek
1193*8f957290SDavid van Moolenbroek /* return a non-zero value if the received prefix is inconsitent with ours */
1194*8f957290SDavid van Moolenbroek static int
prefix_check(struct nd_opt_prefix_info * pinfo,struct rainfo * rai,struct sockaddr_in6 * from)1195*8f957290SDavid van Moolenbroek prefix_check(struct nd_opt_prefix_info *pinfo,
1196*8f957290SDavid van Moolenbroek struct rainfo *rai, struct sockaddr_in6 *from)
1197*8f957290SDavid van Moolenbroek {
1198*8f957290SDavid van Moolenbroek uint32_t preferred_time, valid_time;
1199*8f957290SDavid van Moolenbroek struct prefix *pp;
1200*8f957290SDavid van Moolenbroek int inconsistent = 0;
1201*8f957290SDavid van Moolenbroek char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
1202*8f957290SDavid van Moolenbroek struct timespec now;
1203*8f957290SDavid van Moolenbroek
1204*8f957290SDavid van Moolenbroek #if 0 /* impossible */
1205*8f957290SDavid van Moolenbroek if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
1206*8f957290SDavid van Moolenbroek return(0);
1207*8f957290SDavid van Moolenbroek #endif
1208*8f957290SDavid van Moolenbroek
1209*8f957290SDavid van Moolenbroek /*
1210*8f957290SDavid van Moolenbroek * log if the adveritsed prefix has link-local scope(sanity check?)
1211*8f957290SDavid van Moolenbroek */
1212*8f957290SDavid van Moolenbroek if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) {
1213*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1214*8f957290SDavid van Moolenbroek "<%s> link-local prefix %s/%d is advertised "
1215*8f957290SDavid van Moolenbroek "from %s on %s",
1216*8f957290SDavid van Moolenbroek __func__,
1217*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1218*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1219*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1220*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1221*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1222*8f957290SDavid van Moolenbroek rai->ifname);
1223*8f957290SDavid van Moolenbroek }
1224*8f957290SDavid van Moolenbroek
1225*8f957290SDavid van Moolenbroek if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix,
1226*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len)) == NULL) {
1227*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1228*8f957290SDavid van Moolenbroek "<%s> prefix %s/%d from %s on %s is not in our list",
1229*8f957290SDavid van Moolenbroek __func__,
1230*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1231*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1232*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1233*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1234*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1235*8f957290SDavid van Moolenbroek rai->ifname);
1236*8f957290SDavid van Moolenbroek return(0);
1237*8f957290SDavid van Moolenbroek }
1238*8f957290SDavid van Moolenbroek
1239*8f957290SDavid van Moolenbroek preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
1240*8f957290SDavid van Moolenbroek if (pp->pltimeexpire) {
1241*8f957290SDavid van Moolenbroek /*
1242*8f957290SDavid van Moolenbroek * The lifetime is decremented in real time, so we should
1243*8f957290SDavid van Moolenbroek * compare the expiration time.
1244*8f957290SDavid van Moolenbroek * (RFC 2461 Section 6.2.7.)
1245*8f957290SDavid van Moolenbroek * XXX: can we really expect that all routers on the link
1246*8f957290SDavid van Moolenbroek * have synchronized clocks?
1247*8f957290SDavid van Moolenbroek */
1248*8f957290SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &now);
1249*8f957290SDavid van Moolenbroek preferred_time += now.tv_sec;
1250*8f957290SDavid van Moolenbroek
1251*8f957290SDavid van Moolenbroek if (!pp->timer && rai->clockskew &&
1252*8f957290SDavid van Moolenbroek llabs((long long)preferred_time - pp->pltimeexpire) > rai->clockskew) {
1253*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1254*8f957290SDavid van Moolenbroek "<%s> preferred lifetime for %s/%d"
1255*8f957290SDavid van Moolenbroek " (decr. in real time) inconsistent on %s:"
1256*8f957290SDavid van Moolenbroek " %d from %s, %ld from us",
1257*8f957290SDavid van Moolenbroek __func__,
1258*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1259*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1260*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1261*8f957290SDavid van Moolenbroek rai->ifname, preferred_time,
1262*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1263*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1264*8f957290SDavid van Moolenbroek pp->pltimeexpire);
1265*8f957290SDavid van Moolenbroek inconsistent++;
1266*8f957290SDavid van Moolenbroek }
1267*8f957290SDavid van Moolenbroek } else if (!pp->timer && preferred_time != pp->preflifetime) {
1268*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1269*8f957290SDavid van Moolenbroek "<%s> preferred lifetime for %s/%d"
1270*8f957290SDavid van Moolenbroek " inconsistent on %s:"
1271*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1272*8f957290SDavid van Moolenbroek __func__,
1273*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1274*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1275*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1276*8f957290SDavid van Moolenbroek rai->ifname, preferred_time,
1277*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1278*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1279*8f957290SDavid van Moolenbroek pp->preflifetime);
1280*8f957290SDavid van Moolenbroek }
1281*8f957290SDavid van Moolenbroek
1282*8f957290SDavid van Moolenbroek valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
1283*8f957290SDavid van Moolenbroek if (pp->vltimeexpire) {
1284*8f957290SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &now);
1285*8f957290SDavid van Moolenbroek valid_time += now.tv_sec;
1286*8f957290SDavid van Moolenbroek
1287*8f957290SDavid van Moolenbroek if (!pp->timer && rai->clockskew &&
1288*8f957290SDavid van Moolenbroek llabs((long long)valid_time - pp->vltimeexpire) > rai->clockskew) {
1289*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1290*8f957290SDavid van Moolenbroek "<%s> valid lifetime for %s/%d"
1291*8f957290SDavid van Moolenbroek " (decr. in real time) inconsistent on %s:"
1292*8f957290SDavid van Moolenbroek " %d from %s, %ld from us",
1293*8f957290SDavid van Moolenbroek __func__,
1294*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1295*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1296*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1297*8f957290SDavid van Moolenbroek rai->ifname, preferred_time,
1298*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1299*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1300*8f957290SDavid van Moolenbroek pp->vltimeexpire);
1301*8f957290SDavid van Moolenbroek inconsistent++;
1302*8f957290SDavid van Moolenbroek }
1303*8f957290SDavid van Moolenbroek } else if (!pp->timer && valid_time != pp->validlifetime) {
1304*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1305*8f957290SDavid van Moolenbroek "<%s> valid lifetime for %s/%d"
1306*8f957290SDavid van Moolenbroek " inconsistent on %s:"
1307*8f957290SDavid van Moolenbroek " %d from %s, %d from us",
1308*8f957290SDavid van Moolenbroek __func__,
1309*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
1310*8f957290SDavid van Moolenbroek prefixbuf, INET6_ADDRSTRLEN),
1311*8f957290SDavid van Moolenbroek pinfo->nd_opt_pi_prefix_len,
1312*8f957290SDavid van Moolenbroek rai->ifname, valid_time,
1313*8f957290SDavid van Moolenbroek inet_ntop(AF_INET6, &from->sin6_addr,
1314*8f957290SDavid van Moolenbroek ntopbuf, INET6_ADDRSTRLEN),
1315*8f957290SDavid van Moolenbroek pp->validlifetime);
1316*8f957290SDavid van Moolenbroek inconsistent++;
1317*8f957290SDavid van Moolenbroek }
1318*8f957290SDavid van Moolenbroek
1319*8f957290SDavid van Moolenbroek return(inconsistent);
1320*8f957290SDavid van Moolenbroek }
1321*8f957290SDavid van Moolenbroek
1322*8f957290SDavid van Moolenbroek struct prefix *
find_prefix(struct rainfo * rai,struct in6_addr * prefix,int plen)1323*8f957290SDavid van Moolenbroek find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
1324*8f957290SDavid van Moolenbroek {
1325*8f957290SDavid van Moolenbroek struct prefix *pp;
1326*8f957290SDavid van Moolenbroek int bytelen, bitlen;
1327*8f957290SDavid van Moolenbroek unsigned char bitmask;
1328*8f957290SDavid van Moolenbroek
1329*8f957290SDavid van Moolenbroek TAILQ_FOREACH(pp, &rai->prefix, next) {
1330*8f957290SDavid van Moolenbroek if (plen != pp->prefixlen)
1331*8f957290SDavid van Moolenbroek continue;
1332*8f957290SDavid van Moolenbroek bytelen = plen / 8;
1333*8f957290SDavid van Moolenbroek bitlen = plen % 8;
1334*8f957290SDavid van Moolenbroek bitmask = 0xff << (8 - bitlen);
1335*8f957290SDavid van Moolenbroek if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
1336*8f957290SDavid van Moolenbroek continue;
1337*8f957290SDavid van Moolenbroek if (bitlen == 0 ||
1338*8f957290SDavid van Moolenbroek ((prefix->s6_addr[bytelen] & bitmask) ==
1339*8f957290SDavid van Moolenbroek (pp->prefix.s6_addr[bytelen] & bitmask))) {
1340*8f957290SDavid van Moolenbroek return(pp);
1341*8f957290SDavid van Moolenbroek }
1342*8f957290SDavid van Moolenbroek }
1343*8f957290SDavid van Moolenbroek
1344*8f957290SDavid van Moolenbroek return(NULL);
1345*8f957290SDavid van Moolenbroek }
1346*8f957290SDavid van Moolenbroek
1347*8f957290SDavid van Moolenbroek /* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
1348*8f957290SDavid van Moolenbroek int
prefix_match(struct in6_addr * p0,int plen0,struct in6_addr * p1,int plen1)1349*8f957290SDavid van Moolenbroek prefix_match(struct in6_addr *p0, int plen0,
1350*8f957290SDavid van Moolenbroek struct in6_addr *p1, int plen1)
1351*8f957290SDavid van Moolenbroek {
1352*8f957290SDavid van Moolenbroek int bytelen, bitlen;
1353*8f957290SDavid van Moolenbroek unsigned char bitmask;
1354*8f957290SDavid van Moolenbroek
1355*8f957290SDavid van Moolenbroek if (plen0 < plen1)
1356*8f957290SDavid van Moolenbroek return(0);
1357*8f957290SDavid van Moolenbroek bytelen = plen1 / 8;
1358*8f957290SDavid van Moolenbroek bitlen = plen1 % 8;
1359*8f957290SDavid van Moolenbroek bitmask = 0xff << (8 - bitlen);
1360*8f957290SDavid van Moolenbroek if (memcmp((void *)p0, (void *)p1, bytelen))
1361*8f957290SDavid van Moolenbroek return(0);
1362*8f957290SDavid van Moolenbroek if (bitlen == 0 ||
1363*8f957290SDavid van Moolenbroek ((p0->s6_addr[bytelen] & bitmask) ==
1364*8f957290SDavid van Moolenbroek (p1->s6_addr[bytelen] & bitmask))) {
1365*8f957290SDavid van Moolenbroek return(1);
1366*8f957290SDavid van Moolenbroek }
1367*8f957290SDavid van Moolenbroek
1368*8f957290SDavid van Moolenbroek return(0);
1369*8f957290SDavid van Moolenbroek }
1370*8f957290SDavid van Moolenbroek
1371*8f957290SDavid van Moolenbroek static int
nd6_options(struct nd_opt_hdr * hdr,int limit,union nd_opts * ndopts,uint32_t optflags)1372*8f957290SDavid van Moolenbroek nd6_options(struct nd_opt_hdr *hdr, int limit,
1373*8f957290SDavid van Moolenbroek union nd_opts *ndopts, uint32_t optflags)
1374*8f957290SDavid van Moolenbroek {
1375*8f957290SDavid van Moolenbroek int optlen = 0;
1376*8f957290SDavid van Moolenbroek
1377*8f957290SDavid van Moolenbroek for (; limit > 0; limit -= optlen) {
1378*8f957290SDavid van Moolenbroek if ((size_t)limit < sizeof(struct nd_opt_hdr)) {
1379*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> short option header", __func__);
1380*8f957290SDavid van Moolenbroek goto bad;
1381*8f957290SDavid van Moolenbroek }
1382*8f957290SDavid van Moolenbroek
1383*8f957290SDavid van Moolenbroek hdr = (struct nd_opt_hdr *)((char *)hdr + optlen);
1384*8f957290SDavid van Moolenbroek if (hdr->nd_opt_len == 0) {
1385*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1386*8f957290SDavid van Moolenbroek "<%s> bad ND option length(0) (type = %d)",
1387*8f957290SDavid van Moolenbroek __func__, hdr->nd_opt_type);
1388*8f957290SDavid van Moolenbroek goto bad;
1389*8f957290SDavid van Moolenbroek }
1390*8f957290SDavid van Moolenbroek optlen = hdr->nd_opt_len << 3;
1391*8f957290SDavid van Moolenbroek if (optlen > limit) {
1392*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> short option", __func__);
1393*8f957290SDavid van Moolenbroek goto bad;
1394*8f957290SDavid van Moolenbroek }
1395*8f957290SDavid van Moolenbroek
1396*8f957290SDavid van Moolenbroek if (hdr->nd_opt_type > ND_OPT_MTU &&
1397*8f957290SDavid van Moolenbroek hdr->nd_opt_type != ND_OPT_RDNSS &&
1398*8f957290SDavid van Moolenbroek hdr->nd_opt_type != ND_OPT_DNSSL)
1399*8f957290SDavid van Moolenbroek {
1400*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
1401*8f957290SDavid van Moolenbroek __func__, hdr->nd_opt_type);
1402*8f957290SDavid van Moolenbroek continue;
1403*8f957290SDavid van Moolenbroek }
1404*8f957290SDavid van Moolenbroek
1405*8f957290SDavid van Moolenbroek if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
1406*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> unexpected ND option(type %d)",
1407*8f957290SDavid van Moolenbroek __func__, hdr->nd_opt_type);
1408*8f957290SDavid van Moolenbroek continue;
1409*8f957290SDavid van Moolenbroek }
1410*8f957290SDavid van Moolenbroek
1411*8f957290SDavid van Moolenbroek /*
1412*8f957290SDavid van Moolenbroek * Option length check. Do it here for all fixed-length
1413*8f957290SDavid van Moolenbroek * options.
1414*8f957290SDavid van Moolenbroek */
1415*8f957290SDavid van Moolenbroek if ((hdr->nd_opt_type == ND_OPT_MTU &&
1416*8f957290SDavid van Moolenbroek (optlen != sizeof(struct nd_opt_mtu))) ||
1417*8f957290SDavid van Moolenbroek ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
1418*8f957290SDavid van Moolenbroek optlen != sizeof(struct nd_opt_prefix_info))) ||
1419*8f957290SDavid van Moolenbroek (hdr->nd_opt_type == ND_OPT_RDNSS &&
1420*8f957290SDavid van Moolenbroek ((optlen < (int)sizeof(struct nd_opt_rdnss) ||
1421*8f957290SDavid van Moolenbroek (optlen - sizeof(struct nd_opt_rdnss)) % 16 != 0))) ||
1422*8f957290SDavid van Moolenbroek (hdr->nd_opt_type == ND_OPT_DNSSL &&
1423*8f957290SDavid van Moolenbroek optlen < (int)sizeof(struct nd_opt_dnssl)))
1424*8f957290SDavid van Moolenbroek {
1425*8f957290SDavid van Moolenbroek syslog(LOG_INFO, "<%s> invalid option length",
1426*8f957290SDavid van Moolenbroek __func__);
1427*8f957290SDavid van Moolenbroek continue;
1428*8f957290SDavid van Moolenbroek }
1429*8f957290SDavid van Moolenbroek
1430*8f957290SDavid van Moolenbroek switch (hdr->nd_opt_type) {
1431*8f957290SDavid van Moolenbroek case ND_OPT_TARGET_LINKADDR:
1432*8f957290SDavid van Moolenbroek case ND_OPT_REDIRECTED_HEADER:
1433*8f957290SDavid van Moolenbroek case ND_OPT_RDNSS:
1434*8f957290SDavid van Moolenbroek case ND_OPT_DNSSL:
1435*8f957290SDavid van Moolenbroek break; /* we don't care about these options */
1436*8f957290SDavid van Moolenbroek case ND_OPT_SOURCE_LINKADDR:
1437*8f957290SDavid van Moolenbroek case ND_OPT_MTU:
1438*8f957290SDavid van Moolenbroek if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
1439*8f957290SDavid van Moolenbroek syslog(LOG_INFO,
1440*8f957290SDavid van Moolenbroek "<%s> duplicated ND option (type = %d)",
1441*8f957290SDavid van Moolenbroek __func__, hdr->nd_opt_type);
1442*8f957290SDavid van Moolenbroek }
1443*8f957290SDavid van Moolenbroek ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
1444*8f957290SDavid van Moolenbroek break;
1445*8f957290SDavid van Moolenbroek case ND_OPT_PREFIX_INFORMATION:
1446*8f957290SDavid van Moolenbroek {
1447*8f957290SDavid van Moolenbroek struct nd_optlist *pfxlist;
1448*8f957290SDavid van Moolenbroek
1449*8f957290SDavid van Moolenbroek if (ndopts->nd_opts_pi == 0) {
1450*8f957290SDavid van Moolenbroek ndopts->nd_opts_pi =
1451*8f957290SDavid van Moolenbroek (struct nd_opt_prefix_info *)hdr;
1452*8f957290SDavid van Moolenbroek continue;
1453*8f957290SDavid van Moolenbroek }
1454*8f957290SDavid van Moolenbroek if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
1455*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> can't allocate memory",
1456*8f957290SDavid van Moolenbroek __func__);
1457*8f957290SDavid van Moolenbroek goto bad;
1458*8f957290SDavid van Moolenbroek }
1459*8f957290SDavid van Moolenbroek pfxlist->opt = hdr;
1460*8f957290SDavid van Moolenbroek TAILQ_INSERT_TAIL(&ndopts->nd_opts_list, pfxlist, next);
1461*8f957290SDavid van Moolenbroek
1462*8f957290SDavid van Moolenbroek break;
1463*8f957290SDavid van Moolenbroek }
1464*8f957290SDavid van Moolenbroek default: /* impossible */
1465*8f957290SDavid van Moolenbroek break;
1466*8f957290SDavid van Moolenbroek }
1467*8f957290SDavid van Moolenbroek }
1468*8f957290SDavid van Moolenbroek
1469*8f957290SDavid van Moolenbroek return(0);
1470*8f957290SDavid van Moolenbroek
1471*8f957290SDavid van Moolenbroek bad:
1472*8f957290SDavid van Moolenbroek free_ndopts(ndopts);
1473*8f957290SDavid van Moolenbroek
1474*8f957290SDavid van Moolenbroek return(-1);
1475*8f957290SDavid van Moolenbroek }
1476*8f957290SDavid van Moolenbroek
1477*8f957290SDavid van Moolenbroek static void
free_ndopts(union nd_opts * ndopts)1478*8f957290SDavid van Moolenbroek free_ndopts(union nd_opts *ndopts)
1479*8f957290SDavid van Moolenbroek {
1480*8f957290SDavid van Moolenbroek struct nd_optlist *opt;
1481*8f957290SDavid van Moolenbroek
1482*8f957290SDavid van Moolenbroek while ((opt = TAILQ_FIRST(&ndopts->nd_opts_list)) != NULL) {
1483*8f957290SDavid van Moolenbroek TAILQ_REMOVE(&ndopts->nd_opts_list, opt, next);
1484*8f957290SDavid van Moolenbroek free(opt);
1485*8f957290SDavid van Moolenbroek }
1486*8f957290SDavid van Moolenbroek }
1487*8f957290SDavid van Moolenbroek
1488*8f957290SDavid van Moolenbroek void
sock_open(void)1489*8f957290SDavid van Moolenbroek sock_open(void)
1490*8f957290SDavid van Moolenbroek {
1491*8f957290SDavid van Moolenbroek struct icmp6_filter filt;
1492*8f957290SDavid van Moolenbroek struct ipv6_mreq mreq;
1493*8f957290SDavid van Moolenbroek struct rainfo *ra;
1494*8f957290SDavid van Moolenbroek int on;
1495*8f957290SDavid van Moolenbroek /* XXX: should be max MTU attached to the node */
1496*8f957290SDavid van Moolenbroek static unsigned char answer[1500];
1497*8f957290SDavid van Moolenbroek
1498*8f957290SDavid van Moolenbroek rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
1499*8f957290SDavid van Moolenbroek CMSG_SPACE(sizeof(int));
1500*8f957290SDavid van Moolenbroek rcvcmsgbuf = malloc(rcvcmsgbuflen);
1501*8f957290SDavid van Moolenbroek if (rcvcmsgbuf == NULL) {
1502*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> malloc: %m", __func__);
1503*8f957290SDavid van Moolenbroek exit(1);
1504*8f957290SDavid van Moolenbroek }
1505*8f957290SDavid van Moolenbroek
1506*8f957290SDavid van Moolenbroek sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo));
1507*8f957290SDavid van Moolenbroek sndcmsgbuf = malloc(sndcmsgbuflen);
1508*8f957290SDavid van Moolenbroek if (sndcmsgbuf == NULL) {
1509*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> malloc: %m", __func__);
1510*8f957290SDavid van Moolenbroek exit(1);
1511*8f957290SDavid van Moolenbroek }
1512*8f957290SDavid van Moolenbroek
1513*8f957290SDavid van Moolenbroek if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
1514*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> socket: %m", __func__);
1515*8f957290SDavid van Moolenbroek exit(1);
1516*8f957290SDavid van Moolenbroek }
1517*8f957290SDavid van Moolenbroek
1518*8f957290SDavid van Moolenbroek /* RFC 4861 Section 4.2 */
1519*8f957290SDavid van Moolenbroek on = 255;
1520*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on,
1521*8f957290SDavid van Moolenbroek sizeof(on)) == -1) {
1522*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_MULTICAST_HOPS: %m", __func__);
1523*8f957290SDavid van Moolenbroek exit(1);
1524*8f957290SDavid van Moolenbroek }
1525*8f957290SDavid van Moolenbroek
1526*8f957290SDavid van Moolenbroek /* specify to tell receiving interface */
1527*8f957290SDavid van Moolenbroek on = 1;
1528*8f957290SDavid van Moolenbroek #ifdef IPV6_RECVPKTINFO
1529*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
1530*8f957290SDavid van Moolenbroek sizeof(on)) < 0) {
1531*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %m", __func__);
1532*8f957290SDavid van Moolenbroek exit(1);
1533*8f957290SDavid van Moolenbroek }
1534*8f957290SDavid van Moolenbroek #else /* old adv. API */
1535*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
1536*8f957290SDavid van Moolenbroek sizeof(on)) < 0) {
1537*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %m", __func__);
1538*8f957290SDavid van Moolenbroek exit(1);
1539*8f957290SDavid van Moolenbroek }
1540*8f957290SDavid van Moolenbroek #endif
1541*8f957290SDavid van Moolenbroek
1542*8f957290SDavid van Moolenbroek on = 1;
1543*8f957290SDavid van Moolenbroek /* specify to tell value of hoplimit field of received IP6 hdr */
1544*8f957290SDavid van Moolenbroek #ifdef IPV6_RECVHOPLIMIT
1545*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
1546*8f957290SDavid van Moolenbroek sizeof(on)) < 0) {
1547*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %m", __func__);
1548*8f957290SDavid van Moolenbroek exit(1);
1549*8f957290SDavid van Moolenbroek }
1550*8f957290SDavid van Moolenbroek #else /* old adv. API */
1551*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
1552*8f957290SDavid van Moolenbroek sizeof(on)) < 0) {
1553*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %m", __func__);
1554*8f957290SDavid van Moolenbroek exit(1);
1555*8f957290SDavid van Moolenbroek }
1556*8f957290SDavid van Moolenbroek #endif
1557*8f957290SDavid van Moolenbroek
1558*8f957290SDavid van Moolenbroek ICMP6_FILTER_SETBLOCKALL(&filt);
1559*8f957290SDavid van Moolenbroek ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
1560*8f957290SDavid van Moolenbroek ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
1561*8f957290SDavid van Moolenbroek if (accept_rr)
1562*8f957290SDavid van Moolenbroek ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
1563*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
1564*8f957290SDavid van Moolenbroek sizeof(filt)) < 0) {
1565*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IICMP6_FILTER: %m", __func__);
1566*8f957290SDavid van Moolenbroek exit(1);
1567*8f957290SDavid van Moolenbroek }
1568*8f957290SDavid van Moolenbroek
1569*8f957290SDavid van Moolenbroek /*
1570*8f957290SDavid van Moolenbroek * join all routers multicast address on each advertising interface.
1571*8f957290SDavid van Moolenbroek */
1572*8f957290SDavid van Moolenbroek if (inet_pton(AF_INET6, ALLROUTERS_LINK,
1573*8f957290SDavid van Moolenbroek mreq.ipv6mr_multiaddr.s6_addr) != 1)
1574*8f957290SDavid van Moolenbroek {
1575*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
1576*8f957290SDavid van Moolenbroek __func__);
1577*8f957290SDavid van Moolenbroek exit(1);
1578*8f957290SDavid van Moolenbroek }
1579*8f957290SDavid van Moolenbroek TAILQ_FOREACH(ra, &ralist, next) {
1580*8f957290SDavid van Moolenbroek mreq.ipv6mr_interface = ra->ifindex;
1581*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
1582*8f957290SDavid van Moolenbroek sizeof(mreq)) < 0) {
1583*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %m",
1584*8f957290SDavid van Moolenbroek __func__, ra->ifname);
1585*8f957290SDavid van Moolenbroek exit(1);
1586*8f957290SDavid van Moolenbroek }
1587*8f957290SDavid van Moolenbroek }
1588*8f957290SDavid van Moolenbroek
1589*8f957290SDavid van Moolenbroek /*
1590*8f957290SDavid van Moolenbroek * When attending router renumbering, join all-routers site-local
1591*8f957290SDavid van Moolenbroek * multicast group.
1592*8f957290SDavid van Moolenbroek */
1593*8f957290SDavid van Moolenbroek if (accept_rr) {
1594*8f957290SDavid van Moolenbroek if (inet_pton(AF_INET6, ALLROUTERS_SITE,
1595*8f957290SDavid van Moolenbroek mreq.ipv6mr_multiaddr.s6_addr) != 1)
1596*8f957290SDavid van Moolenbroek {
1597*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
1598*8f957290SDavid van Moolenbroek __func__);
1599*8f957290SDavid van Moolenbroek exit(1);
1600*8f957290SDavid van Moolenbroek }
1601*8f957290SDavid van Moolenbroek ra = TAILQ_FIRST(&ralist);
1602*8f957290SDavid van Moolenbroek if (mcastif) {
1603*8f957290SDavid van Moolenbroek if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
1604*8f957290SDavid van Moolenbroek == 0) {
1605*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
1606*8f957290SDavid van Moolenbroek "<%s> invalid interface: %s",
1607*8f957290SDavid van Moolenbroek __func__, mcastif);
1608*8f957290SDavid van Moolenbroek exit(1);
1609*8f957290SDavid van Moolenbroek }
1610*8f957290SDavid van Moolenbroek } else
1611*8f957290SDavid van Moolenbroek mreq.ipv6mr_interface = ra->ifindex;
1612*8f957290SDavid van Moolenbroek if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
1613*8f957290SDavid van Moolenbroek &mreq, sizeof(mreq)) < 0) {
1614*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
1615*8f957290SDavid van Moolenbroek "<%s> IPV6_JOIN_GROUP(site) on %s: %m",
1616*8f957290SDavid van Moolenbroek __func__,
1617*8f957290SDavid van Moolenbroek mcastif ? mcastif : ra->ifname);
1618*8f957290SDavid van Moolenbroek exit(1);
1619*8f957290SDavid van Moolenbroek }
1620*8f957290SDavid van Moolenbroek }
1621*8f957290SDavid van Moolenbroek
1622*8f957290SDavid van Moolenbroek /* initialize msghdr for receiving packets */
1623*8f957290SDavid van Moolenbroek rcviov[0].iov_base = answer;
1624*8f957290SDavid van Moolenbroek rcviov[0].iov_len = sizeof(answer);
1625*8f957290SDavid van Moolenbroek rcvmhdr.msg_name = &rcvfrom;
1626*8f957290SDavid van Moolenbroek rcvmhdr.msg_namelen = sizeof(rcvfrom);
1627*8f957290SDavid van Moolenbroek rcvmhdr.msg_iov = rcviov;
1628*8f957290SDavid van Moolenbroek rcvmhdr.msg_iovlen = 1;
1629*8f957290SDavid van Moolenbroek rcvmhdr.msg_control = rcvcmsgbuf;
1630*8f957290SDavid van Moolenbroek rcvmhdr.msg_controllen = rcvcmsgbuflen;
1631*8f957290SDavid van Moolenbroek
1632*8f957290SDavid van Moolenbroek /* initialize msghdr for sending packets */
1633*8f957290SDavid van Moolenbroek sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
1634*8f957290SDavid van Moolenbroek sndmhdr.msg_iov = sndiov;
1635*8f957290SDavid van Moolenbroek sndmhdr.msg_iovlen = 1;
1636*8f957290SDavid van Moolenbroek sndmhdr.msg_control = (void *)sndcmsgbuf;
1637*8f957290SDavid van Moolenbroek sndmhdr.msg_controllen = sndcmsgbuflen;
1638*8f957290SDavid van Moolenbroek
1639*8f957290SDavid van Moolenbroek return;
1640*8f957290SDavid van Moolenbroek }
1641*8f957290SDavid van Moolenbroek
1642*8f957290SDavid van Moolenbroek /* open a routing socket to watch the routing table */
1643*8f957290SDavid van Moolenbroek static void
rtsock_open(void)1644*8f957290SDavid van Moolenbroek rtsock_open(void)
1645*8f957290SDavid van Moolenbroek {
1646*8f957290SDavid van Moolenbroek if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
1647*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> socket: %m", __func__);
1648*8f957290SDavid van Moolenbroek exit(1);
1649*8f957290SDavid van Moolenbroek }
1650*8f957290SDavid van Moolenbroek }
1651*8f957290SDavid van Moolenbroek
1652*8f957290SDavid van Moolenbroek struct rainfo *
if_indextorainfo(unsigned int idx)1653*8f957290SDavid van Moolenbroek if_indextorainfo(unsigned int idx)
1654*8f957290SDavid van Moolenbroek {
1655*8f957290SDavid van Moolenbroek struct rainfo *rai;
1656*8f957290SDavid van Moolenbroek
1657*8f957290SDavid van Moolenbroek TAILQ_FOREACH(rai, &ralist, next) {
1658*8f957290SDavid van Moolenbroek if (rai->ifindex == idx)
1659*8f957290SDavid van Moolenbroek return(rai);
1660*8f957290SDavid van Moolenbroek }
1661*8f957290SDavid van Moolenbroek
1662*8f957290SDavid van Moolenbroek return(NULL); /* search failed */
1663*8f957290SDavid van Moolenbroek }
1664*8f957290SDavid van Moolenbroek
1665*8f957290SDavid van Moolenbroek struct rainfo *
ra_output(struct rainfo * rai)1666*8f957290SDavid van Moolenbroek ra_output(struct rainfo *rai)
1667*8f957290SDavid van Moolenbroek {
1668*8f957290SDavid van Moolenbroek int i;
1669*8f957290SDavid van Moolenbroek struct cmsghdr *cm;
1670*8f957290SDavid van Moolenbroek struct in6_pktinfo *pi;
1671*8f957290SDavid van Moolenbroek struct soliciter *sol;
1672*8f957290SDavid van Moolenbroek
1673*8f957290SDavid van Moolenbroek if ((rai->ifflags & IFF_UP) == 0) {
1674*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA",
1675*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1676*8f957290SDavid van Moolenbroek return NULL;
1677*8f957290SDavid van Moolenbroek }
1678*8f957290SDavid van Moolenbroek
1679*8f957290SDavid van Moolenbroek make_packet(rai); /* XXX: inefficient */
1680*8f957290SDavid van Moolenbroek
1681*8f957290SDavid van Moolenbroek sndmhdr.msg_name = (void *)&sin6_linklocal_allnodes;
1682*8f957290SDavid van Moolenbroek sndmhdr.msg_iov[0].iov_base = (void *)rai->ra_data;
1683*8f957290SDavid van Moolenbroek sndmhdr.msg_iov[0].iov_len = rai->ra_datalen;
1684*8f957290SDavid van Moolenbroek
1685*8f957290SDavid van Moolenbroek cm = CMSG_FIRSTHDR(&sndmhdr);
1686*8f957290SDavid van Moolenbroek /* specify the outgoing interface */
1687*8f957290SDavid van Moolenbroek cm->cmsg_level = IPPROTO_IPV6;
1688*8f957290SDavid van Moolenbroek cm->cmsg_type = IPV6_PKTINFO;
1689*8f957290SDavid van Moolenbroek cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
1690*8f957290SDavid van Moolenbroek pi = (struct in6_pktinfo *)CMSG_DATA(cm);
1691*8f957290SDavid van Moolenbroek memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
1692*8f957290SDavid van Moolenbroek pi->ipi6_ifindex = rai->ifindex;
1693*8f957290SDavid van Moolenbroek
1694*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1695*8f957290SDavid van Moolenbroek "<%s> send RA on %s, # of waitings = %d",
1696*8f957290SDavid van Moolenbroek __func__, rai->ifname, rai->waiting);
1697*8f957290SDavid van Moolenbroek
1698*8f957290SDavid van Moolenbroek i = sendmsg(sock, &sndmhdr, 0);
1699*8f957290SDavid van Moolenbroek
1700*8f957290SDavid van Moolenbroek if (i < 0 || (size_t)i != rai->ra_datalen) {
1701*8f957290SDavid van Moolenbroek if (i < 0) {
1702*8f957290SDavid van Moolenbroek syslog(LOG_ERR, "<%s> sendmsg on %s: %m",
1703*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1704*8f957290SDavid van Moolenbroek }
1705*8f957290SDavid van Moolenbroek }
1706*8f957290SDavid van Moolenbroek
1707*8f957290SDavid van Moolenbroek /*
1708*8f957290SDavid van Moolenbroek * unicast advertisements
1709*8f957290SDavid van Moolenbroek * XXX commented out. reason: though spec does not forbit it, unicast
1710*8f957290SDavid van Moolenbroek * advert does not really help
1711*8f957290SDavid van Moolenbroek */
1712*8f957290SDavid van Moolenbroek while ((sol = TAILQ_FIRST(&rai->soliciter)) != NULL) {
1713*8f957290SDavid van Moolenbroek #if 0
1714*8f957290SDavid van Moolenbroek sndmhdr.msg_name = (void *)&sol->addr;
1715*8f957290SDavid van Moolenbroek i = sendmsg(sock, &sndmhdr, 0);
1716*8f957290SDavid van Moolenbroek if (i < 0 || i != rai->ra_datalen) {
1717*8f957290SDavid van Moolenbroek if (i < 0) {
1718*8f957290SDavid van Moolenbroek syslog(LOG_ERR,
1719*8f957290SDavid van Moolenbroek "<%s> unicast sendmsg on %s: %m",
1720*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1721*8f957290SDavid van Moolenbroek }
1722*8f957290SDavid van Moolenbroek }
1723*8f957290SDavid van Moolenbroek #endif
1724*8f957290SDavid van Moolenbroek TAILQ_REMOVE(&rai->soliciter, sol, next);
1725*8f957290SDavid van Moolenbroek free(sol);
1726*8f957290SDavid van Moolenbroek }
1727*8f957290SDavid van Moolenbroek
1728*8f957290SDavid van Moolenbroek if (rai->leaving_adv > 0) {
1729*8f957290SDavid van Moolenbroek if (--(rai->leaving_adv) == 0) {
1730*8f957290SDavid van Moolenbroek /* leaving for ourself means we're shutting down */
1731*8f957290SDavid van Moolenbroek if (rai->leaving_for == rai) {
1732*8f957290SDavid van Moolenbroek TAILQ_REMOVE(&ralist, rai, next);
1733*8f957290SDavid van Moolenbroek free_rainfo(rai);
1734*8f957290SDavid van Moolenbroek return NULL;
1735*8f957290SDavid van Moolenbroek }
1736*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1737*8f957290SDavid van Moolenbroek "<%s> expired RA,"
1738*8f957290SDavid van Moolenbroek " new config active for interface (%s)",
1739*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1740*8f957290SDavid van Moolenbroek rai->leaving_for->timer = rtadvd_add_timer(ra_timeout,
1741*8f957290SDavid van Moolenbroek ra_timer_update,
1742*8f957290SDavid van Moolenbroek rai->leaving_for, rai->leaving_for);
1743*8f957290SDavid van Moolenbroek ra_timer_set_short_delay(rai->leaving_for);
1744*8f957290SDavid van Moolenbroek rai->leaving_for->leaving = NULL;
1745*8f957290SDavid van Moolenbroek free_rainfo(rai);
1746*8f957290SDavid van Moolenbroek return NULL;
1747*8f957290SDavid van Moolenbroek }
1748*8f957290SDavid van Moolenbroek }
1749*8f957290SDavid van Moolenbroek
1750*8f957290SDavid van Moolenbroek /* update counter */
1751*8f957290SDavid van Moolenbroek if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
1752*8f957290SDavid van Moolenbroek rai->initcounter++;
1753*8f957290SDavid van Moolenbroek rai->raoutput++;
1754*8f957290SDavid van Moolenbroek
1755*8f957290SDavid van Moolenbroek /* update timestamp */
1756*8f957290SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &rai->lastsent);
1757*8f957290SDavid van Moolenbroek
1758*8f957290SDavid van Moolenbroek /* reset waiting conter */
1759*8f957290SDavid van Moolenbroek rai->waiting = 0;
1760*8f957290SDavid van Moolenbroek
1761*8f957290SDavid van Moolenbroek return rai;
1762*8f957290SDavid van Moolenbroek }
1763*8f957290SDavid van Moolenbroek
1764*8f957290SDavid van Moolenbroek /* process RA timer */
1765*8f957290SDavid van Moolenbroek struct rtadvd_timer *
ra_timeout(void * data)1766*8f957290SDavid van Moolenbroek ra_timeout(void *data)
1767*8f957290SDavid van Moolenbroek {
1768*8f957290SDavid van Moolenbroek struct rainfo *rai = (struct rainfo *)data;
1769*8f957290SDavid van Moolenbroek
1770*8f957290SDavid van Moolenbroek #ifdef notyet
1771*8f957290SDavid van Moolenbroek /* if necessary, reconstruct the packet. */
1772*8f957290SDavid van Moolenbroek #endif
1773*8f957290SDavid van Moolenbroek
1774*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1775*8f957290SDavid van Moolenbroek "<%s> RA timer on %s is expired",
1776*8f957290SDavid van Moolenbroek __func__, rai->ifname);
1777*8f957290SDavid van Moolenbroek
1778*8f957290SDavid van Moolenbroek if (ra_output(rai))
1779*8f957290SDavid van Moolenbroek return(rai->timer);
1780*8f957290SDavid van Moolenbroek return NULL;
1781*8f957290SDavid van Moolenbroek }
1782*8f957290SDavid van Moolenbroek
1783*8f957290SDavid van Moolenbroek /* update RA timer */
1784*8f957290SDavid van Moolenbroek void
ra_timer_update(void * data,struct timespec * tm)1785*8f957290SDavid van Moolenbroek ra_timer_update(void *data, struct timespec *tm)
1786*8f957290SDavid van Moolenbroek {
1787*8f957290SDavid van Moolenbroek struct rainfo *rai = (struct rainfo *)data;
1788*8f957290SDavid van Moolenbroek long interval;
1789*8f957290SDavid van Moolenbroek
1790*8f957290SDavid van Moolenbroek /*
1791*8f957290SDavid van Moolenbroek * Whenever a multicast advertisement is sent from an interface,
1792*8f957290SDavid van Moolenbroek * the timer is reset to a uniformly-distributed random value
1793*8f957290SDavid van Moolenbroek * between the interface's configured MinRtrAdvInterval and
1794*8f957290SDavid van Moolenbroek * MaxRtrAdvInterval (RFC2461 6.2.4).
1795*8f957290SDavid van Moolenbroek */
1796*8f957290SDavid van Moolenbroek interval = rai->mininterval;
1797*8f957290SDavid van Moolenbroek if (rai->mininterval != rai->maxinterval)
1798*8f957290SDavid van Moolenbroek interval += arc4random() % (rai->maxinterval-rai->mininterval);
1799*8f957290SDavid van Moolenbroek
1800*8f957290SDavid van Moolenbroek /*
1801*8f957290SDavid van Moolenbroek * For the first few advertisements (up to
1802*8f957290SDavid van Moolenbroek * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval
1803*8f957290SDavid van Moolenbroek * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer
1804*8f957290SDavid van Moolenbroek * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead.
1805*8f957290SDavid van Moolenbroek * (RFC-2461 6.2.4)
1806*8f957290SDavid van Moolenbroek */
1807*8f957290SDavid van Moolenbroek if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS &&
1808*8f957290SDavid van Moolenbroek interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
1809*8f957290SDavid van Moolenbroek interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
1810*8f957290SDavid van Moolenbroek
1811*8f957290SDavid van Moolenbroek tm->tv_sec = interval;
1812*8f957290SDavid van Moolenbroek tm->tv_nsec = 0;
1813*8f957290SDavid van Moolenbroek
1814*8f957290SDavid van Moolenbroek syslog(LOG_DEBUG,
1815*8f957290SDavid van Moolenbroek "<%s> RA timer on %s is set to %ld:%ld",
1816*8f957290SDavid van Moolenbroek __func__, rai->ifname,
1817*8f957290SDavid van Moolenbroek (long int)tm->tv_sec, (long int)tm->tv_nsec);
1818*8f957290SDavid van Moolenbroek
1819*8f957290SDavid van Moolenbroek return;
1820*8f957290SDavid van Moolenbroek }
1821