1 /* 2 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the project nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/socket.h> 34 #include <sys/uio.h> 35 #include <sys/time.h> 36 37 #include <string.h> 38 39 #include <net/route.h> 40 41 #include <netinet/in_systm.h> 42 #include <netinet/in.h> 43 #include <netinet/ip.h> 44 #include <netinet/ip6.h> 45 #include <netinet/icmp6.h> 46 47 #ifdef IPSEC 48 #include <netinet6/ipsec.h> 49 #endif 50 51 #include <stdio.h> 52 #include <errno.h> 53 #include <stdlib.h> 54 #include <unistd.h> 55 #include <syslog.h> 56 57 #include "rrenumd.h" 58 59 #define LL_ALLROUTERS "ff02::2" 60 #define SL_ALLROUTERS "ff05::2" 61 62 #ifndef IN6_IS_SCOPE_LINKLOCAL 63 #define IN6_IS_SCOPE_LINKLOCAL(a) \ 64 ((IN6_IS_ADDR_LINKLOCAL(a)) || \ 65 (IN6_IS_ADDR_MC_LINKLOCAL(a))) 66 #endif /* IN6_IS_SCOPE_LINKLOCAL */ 67 68 struct flags { 69 u_long debug : 1; 70 u_long fg : 1; 71 #ifdef IPSEC 72 #ifdef IPSEC_POLICY_IPSEC 73 u_long policy : 1; 74 #endif /* IPSEC_POLICY_IPSEC */ 75 #endif /*IPSEC*/ 76 }; 77 78 struct msghdr sndmhdr; 79 struct msghdr rcvmhdr; 80 struct sockaddr_in6 from; 81 struct sockaddr_in6 sin6_ll_allrouters; 82 83 int s6; 84 int with_v6dest; 85 struct in6_addr prefix; /* ADHOC */ 86 int prefixlen = 64; /* ADHOC */ 87 88 extern int parse(FILE **fp); 89 90 /* Print usage. Don't call this after daemonized. */ 91 static void 92 show_usage() 93 { 94 fprintf(stderr, "usage: rrenumd [-c conf_file|-s] [-df" 95 #ifdef IPSEC 96 #ifdef IPSEC_POLICY_IPSEC 97 "] [-P policy" 98 #endif /* IPSEC_POLICY_IPSEC */ 99 #endif /* IPSEC */ 100 "]\n"); 101 exit(1); 102 } 103 104 void 105 init_sin6(struct sockaddr_in6 *sin6, const char *addr_ascii) 106 { 107 memset(sin6, 0, sizeof(*sin6)); 108 sin6->sin6_len = sizeof(*sin6); 109 sin6->sin6_family = AF_INET6; 110 if (inet_pton(AF_INET6, addr_ascii, &sin6->sin6_addr) != 1) 111 ; /* XXX do something */ 112 } 113 114 void 115 init_globals() 116 { 117 static struct iovec rcviov; 118 static u_char rprdata[4500]; /* maximal MTU of connected links */ 119 static u_char rcvcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + 120 CMSG_SPACE(sizeof(int))]; 121 static u_char sndcmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + 122 CMSG_SPACE(sizeof(int))]; 123 124 /* init ll_allrouters */ 125 init_sin6(&sin6_ll_allrouters, LL_ALLROUTERS); 126 127 /* initialize msghdr for receiving packets */ 128 rcviov.iov_base = (caddr_t)rprdata; 129 rcviov.iov_len = sizeof(rprdata); 130 rcvmhdr.msg_namelen = sizeof(struct sockaddr_in6); 131 rcvmhdr.msg_iov = &rcviov; 132 rcvmhdr.msg_iovlen = 1; 133 rcvmhdr.msg_control = (caddr_t)rcvcmsgbuf; 134 rcvmhdr.msg_controllen = sizeof(rcvcmsgbuf); 135 136 /* initialize msghdr for sending packets */ 137 sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 138 sndmhdr.msg_iovlen = 1; 139 sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 140 sndmhdr.msg_controllen = sizeof(sndcmsgbuf); 141 } 142 143 void 144 config(FILE **fpp) 145 { 146 struct payload_list *pl; 147 struct iovec *iov; 148 struct icmp6_router_renum *irr; 149 struct rr_pco_match *rpm; 150 151 if (parse(fpp) < 0) { 152 syslog(LOG_ERR, "<%s> parse failed", __FUNCTION__); 153 exit(1); 154 } 155 156 /* initialize fields not configured by parser */ 157 for (pl = pl_head; pl; pl = pl->pl_next) { 158 iov = (struct iovec *)&pl->pl_sndiov; 159 irr = (struct icmp6_router_renum *)&pl->pl_irr; 160 rpm = (struct rr_pco_match *)&pl->pl_rpm; 161 162 irr->rr_type = ICMP6_ROUTER_RENUMBERING; 163 irr->rr_code = 0; 164 /* 165 * now we don't support multiple PCOs in a rr message. 166 * so segment number is not supported. 167 */ 168 /* TODO: rr flags config in parser */ 169 irr->rr_flags |= ICMP6_RR_FLAGS_SPECSITE; 170 /* TODO: max delay config in parser */ 171 172 /* 173 * means only 1 use_prefix is contained as router-renum-05.txt. 174 * now we don't support multiple PCOs in a rr message, 175 * nor multiple use_prefix in one PCO. 176 */ 177 rpm->rpm_len = 4*1 +3; 178 rpm->rpm_ordinal = 0; 179 iov->iov_base = (caddr_t)irr; 180 iov->iov_len = sizeof(struct icmp6_router_renum) 181 + sizeof(struct rr_pco_match) 182 + sizeof(struct rr_pco_use); 183 } 184 } 185 186 void 187 sock6_open(struct flags *flags 188 #ifdef IPSEC_POLICY_IPSEC 189 , char *policy 190 #endif /* IPSEC_POLICY_IPSEC */ 191 ) 192 { 193 struct icmp6_filter filt; 194 int on, optval; 195 196 if (with_v6dest == 0) 197 return; 198 if (with_v6dest && 199 (s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 200 syslog(LOG_ERR, "<%s> socket(v6): %s", __FUNCTION__, 201 strerror(errno)); 202 exit(1); 203 } 204 205 /* join all routers multicast addresses, not necessary? */ 206 207 /* set icmpv6 filter */ 208 ICMP6_FILTER_SETBLOCKALL(&filt); 209 ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); 210 if (setsockopt(s6, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 211 sizeof(filt)) < 0) { 212 syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", 213 __FUNCTION__, strerror(errno)); 214 exit(1); 215 } 216 217 /* specify to tell receiving interface */ 218 on = 1; 219 if (setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &on, 220 sizeof(on)) < 0) { 221 syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s", 222 __FUNCTION__, strerror(errno)); 223 exit(1); 224 } 225 226 #ifdef IPSEC 227 #ifdef IPSEC_POLICY_IPSEC 228 if (flags->policy) { 229 char *buf; 230 buf = ipsec_set_policy(policy, strlen(policy)); 231 if (buf == NULL) 232 errx(1, ipsec_strerror()); 233 /* XXX should handle in/out bound policy. */ 234 if (setsockopt(s6, IPPROTO_IPV6, IPV6_IPSEC_POLICY, 235 buf, ipsec_get_policylen(buf)) < 0) 236 err(1, NULL); 237 free(buf); 238 } 239 #endif /* IPSEC_POLICY_IPSEC */ 240 #endif /* IPSEC */ 241 242 return; 243 } 244 245 void 246 rrenum_output(struct payload_list *pl, struct dst_list *dl) 247 { 248 int i, msglen = 0; 249 struct cmsghdr *cm; 250 struct in6_pktinfo *pi; 251 struct icmp6_router_renum *rr; 252 struct sockaddr_in6 *sin6 = NULL; 253 254 sndmhdr.msg_name = (caddr_t)dl->dl_dst; 255 if (dl->dl_dst->sa_family == AF_INET6) 256 sin6 = (struct sockaddr_in6 *)dl->dl_dst; 257 258 if (sin6 != NULL && 259 IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr)) { 260 int hoplimit = 255; 261 262 cm = CMSG_FIRSTHDR(&sndmhdr); 263 /* specify the outgoing interface */ 264 cm->cmsg_level = IPPROTO_IPV6; 265 cm->cmsg_type = IPV6_PKTINFO; 266 cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 267 pi = (struct in6_pktinfo *)CMSG_DATA(cm); 268 memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 269 pi->ipi6_ifindex = sin6->sin6_scope_id; 270 msglen += CMSG_SPACE(sizeof(struct in6_pktinfo)); 271 272 /* specify the hop limit of the packet if dest is link local */ 273 /* not defined by router-renum-05.txt, but maybe its OK */ 274 cm = CMSG_NXTHDR(&sndmhdr, cm); 275 cm->cmsg_level = IPPROTO_IPV6; 276 cm->cmsg_type = IPV6_HOPLIMIT; 277 cm->cmsg_len = CMSG_LEN(sizeof(int)); 278 memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 279 msglen += CMSG_SPACE(sizeof(int)); 280 } 281 sndmhdr.msg_controllen = msglen; 282 if (sndmhdr.msg_controllen == 0) 283 sndmhdr.msg_control = 0; 284 285 sndmhdr.msg_iov = &pl->pl_sndiov; 286 i = sendmsg(s6, &sndmhdr, 0); 287 288 if (i < 0 || i != sndmhdr.msg_iov->iov_len) 289 syslog(LOG_ERR, "<%s> sendmsg: %s", __FUNCTION__, 290 strerror(errno)); 291 } 292 293 void 294 rrenum_snd_eachdst(struct payload_list *pl) 295 { 296 struct dst_list *dl; 297 298 for (dl = dl_head; dl; dl = dl->dl_next) { 299 rrenum_output(pl, dl); 300 } 301 } 302 303 void 304 rrenum_snd_fullsequence() 305 { 306 struct payload_list *pl; 307 308 for (pl = pl_head; pl; pl = pl->pl_next) { 309 rrenum_snd_eachdst(pl); 310 } 311 } 312 313 void 314 rrenum_input(int s) 315 { 316 int i; 317 struct icmp6_router_renum *rr; 318 319 /* get message */ 320 if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 321 syslog(LOG_ERR, "<%s> recvmsg: %s", __FUNCTION__, 322 strerror(errno)); 323 return; 324 } 325 if (i < sizeof(struct icmp6_router_renum)) { 326 syslog(LOG_ERR, "<%s> packet size(%d) is too short", 327 __FUNCTION__, i); 328 return; 329 } 330 rr = (struct icmp6_router_renum *)rcvmhdr.msg_iov->iov_base; 331 332 switch(rr->rr_code) { 333 case ICMP6_ROUTER_RENUMBERING_COMMAND: 334 /* COMMAND will be processed by rtadvd */ 335 break; 336 case ICMP6_ROUTER_RENUMBERING_RESULT: 337 /* TODO: receiving result message */ 338 break; 339 default: 340 syslog(LOG_ERR, "<%s> received unknown code %d" 341 __FUNCTION__, rr->rr_code); 342 break; 343 } 344 } 345 346 int 347 main(int argc, char *argv[]) 348 { 349 char *cfile = NULL; 350 FILE *fp = stdin; 351 fd_set fdset; 352 struct timeval timeout; 353 int ch, i, maxfd = 0, send_counter = 0; 354 struct flags flags; 355 struct payload_list *pl; 356 #ifdef IPSEC_POLICY_IPSEC 357 char *policy = NULL; 358 #endif 359 360 memset(&flags, 0, sizeof(flags)); 361 openlog(*argv, LOG_PID, LOG_DAEMON); 362 363 /* get options */ 364 while ((ch = getopt(argc, argv, "c:sdf" 365 #ifdef IPSEC 366 #ifdef IPSEC_POLICY_IPSEC 367 "P:" 368 #endif /* IPSEC_POLICY_IPSEC */ 369 #endif /* IPSEC */ 370 )) != -1){ 371 switch (ch) { 372 case 'c': 373 if((fp = fopen(optarg, "r")) == NULL) { 374 syslog(LOG_ERR, 375 "<%s> config file %s open failed", 376 __FUNCTION__, optarg); 377 exit(1); 378 } 379 break; 380 case 's': 381 fp = stdin; 382 break; 383 case 'd': 384 flags.debug = 1; 385 break; 386 case 'f': 387 flags.fg = 1; 388 break; 389 #ifdef IPSEC 390 #ifdef IPSEC_POLICY_IPSEC 391 case 'P': 392 flags.policy = 1; 393 policy = strdup(optarg); 394 break; 395 #endif /* IPSEC_POLICY_IPSEC */ 396 #endif /*IPSEC*/ 397 default: 398 show_usage(); 399 } 400 } 401 argc -= optind; 402 argv += optind; 403 404 /* set log level */ 405 if (flags.debug == 0) 406 (void)setlogmask(LOG_UPTO(LOG_ERR)); 407 if (flags.debug == 1) 408 (void)setlogmask(LOG_UPTO(LOG_INFO)); 409 410 /* init global variables */ 411 init_globals(); 412 413 config(&fp); 414 415 sock6_open(&flags 416 #ifdef IPSEC_POLICY_IPSEC 417 , policy 418 #endif /* IPSEC_POLICY_IPSEC */ 419 ); 420 421 if (!flags.fg) 422 daemon(0, 0); 423 424 FD_ZERO(&fdset); 425 if (with_v6dest) { 426 FD_SET(s6, &fdset); 427 if (s6 > maxfd) 428 maxfd = s6; 429 } 430 431 /* ADHOC: timeout each 30seconds */ 432 memset(&timeout, 0, sizeof(timeout)); 433 timeout.tv_sec = 30; 434 435 /* init temporal payload_list and send_counter*/ 436 pl = pl_head; 437 send_counter = retry + 1; 438 while (1) { 439 struct fd_set select_fd = fdset; /* reinitialize */ 440 441 if ((i = select(maxfd + 1, &select_fd, NULL, NULL, 442 &timeout)) < 0){ 443 syslog(LOG_ERR, "<%s> select: %s", 444 __FUNCTION__, strerror(errno)); 445 continue; 446 } 447 if (i == 0) { /* timeout */ 448 if (pl == NULL) 449 exit(0); 450 rrenum_snd_eachdst(pl); 451 send_counter--; 452 if (send_counter == 0) { 453 pl = pl->pl_next; 454 send_counter = retry + 1; 455 } 456 } 457 if (FD_ISSET(s6, &select_fd)) 458 rrenum_input(s6); 459 } 460 } 461