1/* $KAME: rrenumd.c,v 1.20 2000/11/08 02:40:53 itojun Exp $ */ 2 3/* 4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34#include <sys/param.h> 35#include <sys/socket.h> 36#include <sys/uio.h> 37#include <sys/time.h> 38 39#include <string.h> 40 41#include <net/route.h> 42 43#include <netinet/in_systm.h> 44#include <netinet/in.h> 45#include <netinet/ip.h> 46#include <netinet/ip6.h> 47#include <netinet/icmp6.h> 48 49#include <arpa/inet.h> 50 51#ifdef IPSEC 52#include <netipsec/ipsec.h> 53#endif 54 55#include <stdio.h> 56#include <err.h> 57#include <errno.h> 58#include <stdlib.h> 59#include <unistd.h> 60#include <syslog.h> 61 62#include "rrenumd.h" 63 64#define LL_ALLROUTERS "ff02::2" 65#define SL_ALLROUTERS "ff05::2" 66 67#define RR_MCHLIM_DEFAULT 64 68 69#ifndef IN6_IS_SCOPE_LINKLOCAL 70#define IN6_IS_SCOPE_LINKLOCAL(a) \ 71 ((IN6_IS_ADDR_LINKLOCAL(a)) || \ 72 (IN6_IS_ADDR_MC_LINKLOCAL(a))) 73#endif /* IN6_IS_SCOPE_LINKLOCAL */ 74 75struct flags { 76 u_long debug : 1; 77 u_long fg : 1; 78#ifdef IPSEC 79#ifdef IPSEC_POLICY_IPSEC 80 u_long policy : 1; 81#else /* IPSEC_POLICY_IPSEC */ 82 u_long auth : 1; 83 u_long encrypt : 1; 84#endif /* IPSEC_POLICY_IPSEC */ 85#endif /*IPSEC*/ 86}; 87 88struct msghdr sndmhdr; 89struct msghdr rcvmhdr; 90struct sockaddr_in6 from; 91struct sockaddr_in6 sin6_ll_allrouters; 92 93int s4, s6; 94int with_v4dest, with_v6dest; 95struct in6_addr prefix; /* ADHOC */ 96int prefixlen = 64; /* ADHOC */ 97 98extern int parse(FILE **); 99 100static void show_usage(void); 101static void init_sin6(struct sockaddr_in6 *, const char *); 102#if 0 103static void join_multi(const char *); 104#endif 105static void init_globals(void); 106static void config(FILE **); 107#ifdef IPSEC_POLICY_IPSEC 108static void sock6_open(struct flags *, char *); 109static void sock4_open(struct flags *, char *); 110#else 111static void sock6_open(struct flags *); 112static void sock4_open(struct flags *); 113#endif 114static void rrenum_output(struct payload_list *, struct dst_list *); 115static void rrenum_snd_eachdst(struct payload_list *); 116#if 0 117static void rrenum_snd_fullsequence(void); 118#endif 119static void rrenum_input(int); 120int main(int, char *[]); 121 122 123/* Print usage. Don't call this after daemonized. */ 124static void 125show_usage() 126{ 127 fprintf(stderr, "usage: rrenumd [-c conf_file|-s] [-df" 128#ifdef IPSEC 129#ifdef IPSEC_POLICY_IPSEC 130 "] [-P policy" 131#else /* IPSEC_POLICY_IPSEC */ 132 "AE" 133#endif /* IPSEC_POLICY_IPSEC */ 134#endif /* IPSEC */ 135 "]\n"); 136 exit(1); 137} 138 139static void 140init_sin6(struct sockaddr_in6 *sin6, const char *addr_ascii) 141{ 142 memset(sin6, 0, sizeof(*sin6)); 143 sin6->sin6_len = sizeof(*sin6); 144 sin6->sin6_family = AF_INET6; 145 if (inet_pton(AF_INET6, addr_ascii, &sin6->sin6_addr) != 1) 146 ; /* XXX do something */ 147} 148 149#if 0 /* XXX: not necessary ?? */ 150static void 151join_multi(const char *addrname) 152{ 153 struct ipv6_mreq mreq; 154 155 if (inet_pton(AF_INET6, addrname, &mreq.ipv6mr_multiaddr.s6_addr) 156 != 1) { 157 syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", 158 __func__); 159 exit(1); 160 } 161 /* ADHOC: currently join only one */ 162 { 163 if ((mreq.ipv6mr_interface = if_nametoindex(ifname)) == 0) { 164 syslog(LOG_ERR, "<%s> ifname %s should be invalid: %s", 165 __func__, ifname, strerror(errno)); 166 exit(1); 167 } 168 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, 169 &mreq, 170 sizeof(mreq)) < 0) { 171 syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s", 172 __func__, ifname, strerror(errno)); 173 exit(1); 174 } 175 } 176} 177#endif 178 179static void 180init_globals() 181{ 182 static struct iovec rcviov; 183 static u_char rprdata[4500]; /* maximal MTU of connected links */ 184 static u_char *rcvcmsgbuf = NULL; 185 static u_char *sndcmsgbuf = NULL; 186 int sndcmsglen, rcvcmsglen; 187 188 /* init ll_allrouters */ 189 init_sin6(&sin6_ll_allrouters, LL_ALLROUTERS); 190 191 /* initialize msghdr for receiving packets */ 192 rcviov.iov_base = (caddr_t)rprdata; 193 rcviov.iov_len = sizeof(rprdata); 194 rcvmhdr.msg_namelen = sizeof(struct sockaddr_in6); 195 rcvmhdr.msg_iov = &rcviov; 196 rcvmhdr.msg_iovlen = 1; 197 rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 198 CMSG_SPACE(sizeof(int)); 199 if (rcvcmsgbuf == NULL && 200 (rcvcmsgbuf = (u_char *)malloc(rcvcmsglen)) == NULL) { 201 syslog(LOG_ERR, "<%s>: malloc failed", __func__); 202 exit(1); 203 } 204 rcvmhdr.msg_control = (caddr_t)rcvcmsgbuf; 205 rcvmhdr.msg_controllen = rcvcmsglen; 206 207 /* initialize msghdr for sending packets */ 208 sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 209 sndmhdr.msg_iovlen = 1; 210 sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 211 CMSG_SPACE(sizeof(int)); 212 if (sndcmsgbuf == NULL && 213 (sndcmsgbuf = (u_char *)malloc(sndcmsglen)) == NULL) { 214 syslog(LOG_ERR, "<%s>: malloc failed", __func__); 215 exit(1); 216 } 217 sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 218 sndmhdr.msg_controllen = sndcmsglen; 219} 220 221static void 222config(FILE **fpp) 223{ 224 struct payload_list *pl; 225 struct iovec *iov; 226 struct icmp6_router_renum *irr; 227 struct rr_pco_match *rpm; 228 229 if (parse(fpp) < 0) { 230 syslog(LOG_ERR, "<%s> parse failed", __func__); 231 exit(1); 232 } 233 234 /* initialize fields not configured by parser */ 235 for (pl = pl_head; pl; pl = pl->pl_next) { 236 iov = (struct iovec *)&pl->pl_sndiov; 237 irr = (struct icmp6_router_renum *)&pl->pl_irr; 238 rpm = (struct rr_pco_match *)&pl->pl_rpm; 239 240 irr->rr_type = ICMP6_ROUTER_RENUMBERING; 241 irr->rr_code = 0; 242 /* 243 * now we don't support multiple PCOs in a rr message. 244 * so segment number is not supported. 245 */ 246 /* TODO: rr flags config in parser */ 247 irr->rr_flags |= ICMP6_RR_FLAGS_SPECSITE; 248 /* TODO: max delay config in parser */ 249 250 /* 251 * means only 1 use_prefix is contained as router-renum-05.txt. 252 * now we don't support multiple PCOs in a rr message, 253 * nor multiple use_prefix in one PCO. 254 */ 255 rpm->rpm_len = 4*1 +3; 256 rpm->rpm_ordinal = 0; 257 iov->iov_base = (caddr_t)irr; 258 iov->iov_len = sizeof(struct icmp6_router_renum) 259 + sizeof(struct rr_pco_match) 260 + sizeof(struct rr_pco_use); 261 } 262} 263 264static void 265sock6_open(struct flags *flags 266#ifdef IPSEC_POLICY_IPSEC 267 , char *policy 268#endif /* IPSEC_POLICY_IPSEC */ 269 ) 270{ 271 struct icmp6_filter filt; 272 int on; 273#ifdef IPSEC 274#ifndef IPSEC_POLICY_IPSEC 275 int optval; 276#endif 277#endif 278 279 if (with_v6dest == 0) 280 return; 281 if (with_v6dest && 282 (s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 283 syslog(LOG_ERR, "<%s> socket(v6): %s", __func__, 284 strerror(errno)); 285 exit(1); 286 } 287 288 /* 289 * join all routers multicast addresses. 290 */ 291#if 0 /* XXX: not necessary ?? */ 292 join_multi(LL_ALLROUTERS); 293 join_multi(SL_ALLROUTERS); 294#endif 295 296 /* set icmpv6 filter */ 297 ICMP6_FILTER_SETBLOCKALL(&filt); 298 ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); 299 if (setsockopt(s6, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 300 sizeof(filt)) < 0) { 301 syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", 302 __func__, strerror(errno)); 303 exit(1); 304 } 305 306 /* specify to tell receiving interface */ 307 on = 1; 308 if (setsockopt(s6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, 309 sizeof(on)) < 0) { 310 syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s", 311 __func__, strerror(errno)); 312 exit(1); 313 } 314 315#ifdef IPSEC 316#ifdef IPSEC_POLICY_IPSEC 317 if (flags->policy) { 318 char *buf; 319 buf = ipsec_set_policy(policy, strlen(policy)); 320 if (buf == NULL) 321 errx(1, "%s", ipsec_strerror()); 322 /* XXX should handle in/out bound policy. */ 323 if (setsockopt(s6, IPPROTO_IPV6, IPV6_IPSEC_POLICY, 324 buf, ipsec_get_policylen(buf)) < 0) 325 err(1, "setsockopt(IPV6_IPSEC_POLICY)"); 326 free(buf); 327 } 328#else /* IPSEC_POLICY_IPSEC */ 329 if (flags->auth) { 330 optval = IPSEC_LEVEL_REQUIRE; 331 if (setsockopt(s6, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, 332 &optval, sizeof(optval)) == -1) { 333 syslog(LOG_ERR, "<%s> IPV6_AUTH_TRANS_LEVEL: %s", 334 __func__, strerror(errno)); 335 exit(1); 336 } 337 } 338 if (flags->encrypt) { 339 optval = IPSEC_LEVEL_REQUIRE; 340 if (setsockopt(s6, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, 341 &optval, sizeof(optval)) == -1) { 342 syslog(LOG_ERR, "<%s> IPV6_ESP_TRANS_LEVEL: %s", 343 __func__, strerror(errno)); 344 exit(1); 345 } 346 } 347#endif /* IPSEC_POLICY_IPSEC */ 348#endif /* IPSEC */ 349 350 return; 351} 352 353static void 354sock4_open(struct flags *flags 355#ifdef IPSEC_POLICY_IPSEC 356 , char *policy 357#endif /* IPSEC_POLICY_IPSEC */ 358 ) 359{ 360#ifdef IPSEC 361#ifndef IPSEC_POLICY_IPSEC 362 int optval; 363#endif 364#endif 365 366 if (with_v4dest == 0) 367 return; 368 if ((s4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 369 syslog(LOG_ERR, "<%s> socket(v4): %s", __func__, 370 strerror(errno)); 371 exit(1); 372 } 373 374#if 0 /* XXX: not necessary ?? */ 375 /* 376 * join all routers multicast addresses. 377 */ 378 some_join_function(); 379#endif 380 381#ifdef IPSEC 382#ifdef IPSEC_POLICY_IPSEC 383 if (flags->policy) { 384 char *buf; 385 buf = ipsec_set_policy(policy, strlen(policy)); 386 if (buf == NULL) 387 errx(1, "%s", ipsec_strerror()); 388 /* XXX should handle in/out bound policy. */ 389 if (setsockopt(s4, IPPROTO_IP, IP_IPSEC_POLICY, 390 buf, ipsec_get_policylen(buf)) < 0) 391 err(1, "setsockopt(IP_IPSEC_POLICY)"); 392 free(buf); 393 } 394#else /* IPSEC_POLICY_IPSEC */ 395 if (flags->auth) { 396 optval = IPSEC_LEVEL_REQUIRE; 397 if (setsockopt(s4, IPPROTO_IP, IP_AUTH_TRANS_LEVEL, 398 &optval, sizeof(optval)) == -1) { 399 syslog(LOG_ERR, "<%s> IP_AUTH_TRANS_LEVEL: %s", 400 __func__, strerror(errno)); 401 exit(1); 402 } 403 } 404 if (flags->encrypt) { 405 optval = IPSEC_LEVEL_REQUIRE; 406 if (setsockopt(s4, IPPROTO_IP, IP_ESP_TRANS_LEVEL, 407 &optval, sizeof(optval)) == -1) { 408 syslog(LOG_ERR, "<%s> IP_ESP_TRANS_LEVEL: %s", 409 __func__, strerror(errno)); 410 exit(1); 411 } 412 } 413#endif /* IPSEC_POLICY_IPSEC */ 414#endif /* IPSEC */ 415 416 return; 417} 418 419static void 420rrenum_output(struct payload_list *pl, struct dst_list *dl) 421{ 422 int i, msglen = 0; 423 struct cmsghdr *cm; 424 struct in6_pktinfo *pi; 425 struct sockaddr_in6 *sin6 = NULL; 426 427 sndmhdr.msg_name = (caddr_t)dl->dl_dst; 428 if (dl->dl_dst->sa_family == AF_INET6) 429 sin6 = (struct sockaddr_in6 *)dl->dl_dst; 430 431 if (sin6 != NULL && 432 IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 433 int hoplimit = RR_MCHLIM_DEFAULT; 434 435 cm = CMSG_FIRSTHDR(&sndmhdr); 436 /* specify the outgoing interface */ 437 cm->cmsg_level = IPPROTO_IPV6; 438 cm->cmsg_type = IPV6_PKTINFO; 439 cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 440 pi = (struct in6_pktinfo *)CMSG_DATA(cm); 441 memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 442 pi->ipi6_ifindex = sin6->sin6_scope_id; 443 msglen += CMSG_LEN(sizeof(struct in6_pktinfo)); 444 445 /* specify the hop limit of the packet if dest is link local */ 446 /* not defined by router-renum-05.txt, but maybe its OK */ 447 cm = CMSG_NXTHDR(&sndmhdr, cm); 448 cm->cmsg_level = IPPROTO_IPV6; 449 cm->cmsg_type = IPV6_HOPLIMIT; 450 cm->cmsg_len = CMSG_LEN(sizeof(int)); 451 memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 452 msglen += CMSG_LEN(sizeof(int)); 453 } 454 sndmhdr.msg_controllen = msglen; 455 if (sndmhdr.msg_controllen == 0) 456 sndmhdr.msg_control = 0; 457 458 sndmhdr.msg_iov = &pl->pl_sndiov; 459 i = sendmsg(dl->dl_dst->sa_family == AF_INET ? s4 : s6, &sndmhdr, 0); 460 461 if (i < 0 || i != sndmhdr.msg_iov->iov_len) 462 syslog(LOG_ERR, "<%s> sendmsg: %s", __func__, 463 strerror(errno)); 464} 465 466static void 467rrenum_snd_eachdst(struct payload_list *pl) 468{ 469 struct dst_list *dl; 470 471 for (dl = dl_head; dl; dl = dl->dl_next) { 472 rrenum_output(pl, dl); 473 } 474} 475 476#if 0 477static void 478rrenum_snd_fullsequence() 479{ 480 struct payload_list *pl; 481 482 for (pl = pl_head; pl; pl = pl->pl_next) { 483 rrenum_snd_eachdst(pl); 484 } 485} 486#endif 487 488static void 489rrenum_input(int s) 490{ 491 int i; 492 struct icmp6_router_renum *rr; 493 494 /* get message */ 495 if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 496 syslog(LOG_ERR, "<%s> recvmsg: %s", __func__, 497 strerror(errno)); 498 return; 499 } 500 if (s == s4) 501 i -= sizeof(struct ip); 502 if (i < sizeof(struct icmp6_router_renum)) { 503 syslog(LOG_ERR, "<%s> packet size(%d) is too short", 504 __func__, i); 505 return; 506 } 507 if (s == s4) { 508 struct ip *ip = (struct ip *)rcvmhdr.msg_iov->iov_base; 509 510 rr = (struct icmp6_router_renum *)(ip + 1); 511 } else /* s == s6 */ 512 rr = (struct icmp6_router_renum *)rcvmhdr.msg_iov->iov_base; 513 514 switch(rr->rr_code) { 515 case ICMP6_ROUTER_RENUMBERING_COMMAND: 516 /* COMMAND will be processed by rtadvd */ 517 break; 518 case ICMP6_ROUTER_RENUMBERING_RESULT: 519 /* TODO: receiving result message */ 520 break; 521 default: 522 syslog(LOG_ERR, "<%s> received unknown code %d", 523 __func__, rr->rr_code); 524 break; 525 } 526} 527 528int 529main(int argc, char *argv[]) 530{ 531 FILE *fp = stdin; 532 fd_set fdset; 533 struct timeval timeout; 534 int ch, i, maxfd = 0, send_counter = 0; 535 struct flags flags; 536 struct payload_list *pl; 537#ifdef IPSEC_POLICY_IPSEC 538 char *policy = NULL; 539#endif 540 541 memset(&flags, 0, sizeof(flags)); 542 openlog("rrenumd", LOG_PID, LOG_DAEMON); 543 544 /* get options */ 545 while ((ch = getopt(argc, argv, "c:sdf" 546#ifdef IPSEC 547#ifdef IPSEC_POLICY_IPSEC 548 "P:" 549#else /* IPSEC_POLICY_IPSEC */ 550 "AE" 551#endif /* IPSEC_POLICY_IPSEC */ 552#endif /* IPSEC */ 553 )) != -1){ 554 switch (ch) { 555 case 'c': 556 if((fp = fopen(optarg, "r")) == NULL) { 557 syslog(LOG_ERR, 558 "<%s> config file %s open failed", 559 __func__, optarg); 560 exit(1); 561 } 562 break; 563 case 's': 564 fp = stdin; 565 break; 566 case 'd': 567 flags.debug = 1; 568 break; 569 case 'f': 570 flags.fg = 1; 571 break; 572#ifdef IPSEC 573#ifdef IPSEC_POLICY_IPSEC 574 case 'P': 575 flags.policy = 1; 576 policy = strdup(optarg); 577 break; 578#else /* IPSEC_POLICY_IPSEC */ 579 case 'A': 580 flags.auth = 1; 581 break; 582 case 'E': 583 flags.encrypt = 1; 584 break; 585#endif /* IPSEC_POLICY_IPSEC */ 586#endif /*IPSEC*/ 587 default: 588 show_usage(); 589 } 590 } 591 argc -= optind; 592 argv += optind; 593 594 /* set log level */ 595 if (flags.debug == 0) 596 (void)setlogmask(LOG_UPTO(LOG_ERR)); 597 if (flags.debug == 1) 598 (void)setlogmask(LOG_UPTO(LOG_INFO)); 599 600 /* init global variables */ 601 init_globals(); 602 603 config(&fp); 604 605 sock6_open(&flags 606#ifdef IPSEC_POLICY_IPSEC 607 , policy 608#endif /* IPSEC_POLICY_IPSEC */ 609 ); 610 sock4_open(&flags 611#ifdef IPSEC_POLICY_IPSEC 612 , policy 613#endif /* IPSEC_POLICY_IPSEC */ 614 ); 615 616 if (!flags.fg) 617 daemon(0, 0); 618 619 FD_ZERO(&fdset); 620 if (with_v6dest) { 621 FD_SET(s6, &fdset); 622 if (s6 > maxfd) 623 maxfd = s6; 624 } 625 if (with_v4dest) { 626 FD_SET(s4, &fdset); 627 if (s4 > maxfd) 628 maxfd = s4; 629 } 630 631 /* ADHOC: timeout each 30seconds */ 632 memset(&timeout, 0, sizeof(timeout)); 633 634 /* init temporary payload_list and send_counter*/ 635 pl = pl_head; 636 send_counter = retry + 1; 637 while (1) { 638 struct fd_set select_fd = fdset; /* reinitialize */ 639 640 if ((i = select(maxfd + 1, &select_fd, NULL, NULL, 641 &timeout)) < 0){ 642 syslog(LOG_ERR, "<%s> select: %s", 643 __func__, strerror(errno)); 644 continue; 645 } 646 if (i == 0) { /* timeout */ 647 if (pl == NULL) 648 exit(0); 649 rrenum_snd_eachdst(pl); 650 send_counter--; 651 timeout.tv_sec = 30; 652 if (send_counter == 0) { 653 timeout.tv_sec = 0; 654 pl = pl->pl_next; 655 send_counter = retry + 1; 656 } 657 } 658 if (FD_ISSET(s4, &select_fd)) 659 rrenum_input(s4); 660 if (FD_ISSET(s6, &select_fd)) 661 rrenum_input(s6); 662 } 663} 664