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