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: src/usr.sbin/rrenumd/rrenumd.c,v 1.1.2.4 2001/07/09 09:49:49 ume Exp $ 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 #include <stdio.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <stdlib.h> 55 #include <unistd.h> 56 #include <syslog.h> 57 58 #include "rrenumd.h" 59 60 #define LL_ALLROUTERS "ff02::2" 61 #define SL_ALLROUTERS "ff05::2" 62 63 #define RR_MCHLIM_DEFAULT 64 64 65 #ifndef IN6_IS_SCOPE_LINKLOCAL 66 #define IN6_IS_SCOPE_LINKLOCAL(a) \ 67 ((IN6_IS_ADDR_LINKLOCAL(a)) || \ 68 (IN6_IS_ADDR_MC_LINKLOCAL(a))) 69 #endif /* IN6_IS_SCOPE_LINKLOCAL */ 70 71 struct flags { 72 u_long debug : 1; 73 u_long fg : 1; 74 }; 75 76 struct msghdr sndmhdr; 77 struct msghdr rcvmhdr; 78 struct sockaddr_in6 from; 79 struct sockaddr_in6 sin6_ll_allrouters; 80 81 int s4, s6; 82 int with_v4dest, with_v6dest; 83 struct in6_addr prefix; /* ADHOC */ 84 int prefixlen = 64; /* ADHOC */ 85 86 extern int parse(FILE **); 87 88 static void show_usage(void); 89 static void init_sin6(struct sockaddr_in6 *, const char *); 90 #if 0 91 static void join_multi(const char *); 92 #endif 93 static void init_globals(void); 94 static void config(FILE **); 95 static void sock6_open(struct flags *); 96 static void sock4_open(struct flags *); 97 static void rrenum_output(struct payload_list *, struct dst_list *); 98 static void rrenum_snd_eachdst(struct payload_list *); 99 #if 0 100 static void rrenum_snd_fullsequence(void); 101 #endif 102 static void rrenum_input(int); 103 104 105 /* Print usage. Don't call this after daemonized. */ 106 static void 107 show_usage(void) 108 { 109 fprintf(stderr, "usage: rrenumd [-c conf_file|-s] [-df]\n"); 110 exit(1); 111 } 112 113 static void 114 init_sin6(struct sockaddr_in6 *sin6, const char *addr_ascii) 115 { 116 memset(sin6, 0, sizeof(*sin6)); 117 sin6->sin6_len = sizeof(*sin6); 118 sin6->sin6_family = AF_INET6; 119 if (inet_pton(AF_INET6, addr_ascii, &sin6->sin6_addr) != 1) 120 ; /* XXX do something */ 121 } 122 123 #if 0 /* XXX: not necessary ?? */ 124 static void 125 join_multi(const char *addrname) 126 { 127 struct ipv6_mreq mreq; 128 129 if (inet_pton(AF_INET6, addrname, &mreq.ipv6mr_multiaddr.s6_addr) 130 != 1) { 131 syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)", 132 __func__); 133 exit(1); 134 } 135 /* ADHOC: currently join only one */ 136 { 137 if ((mreq.ipv6mr_interface = if_nametoindex(ifname)) == 0) { 138 syslog(LOG_ERR, "<%s> ifname %s should be invalid: %s", 139 __func__, ifname, strerror(errno)); 140 exit(1); 141 } 142 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, 143 &mreq, 144 sizeof(mreq)) < 0) { 145 syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s", 146 __func__, ifname, strerror(errno)); 147 exit(1); 148 } 149 } 150 } 151 #endif 152 153 static void 154 init_globals(void) 155 { 156 static struct iovec rcviov; 157 static u_char rprdata[4500]; /* maximal MTU of connected links */ 158 static u_char *rcvcmsgbuf = NULL; 159 static u_char *sndcmsgbuf = NULL; 160 int sndcmsglen, rcvcmsglen; 161 162 /* init ll_allrouters */ 163 init_sin6(&sin6_ll_allrouters, LL_ALLROUTERS); 164 165 /* initialize msghdr for receiving packets */ 166 rcviov.iov_base = (caddr_t)rprdata; 167 rcviov.iov_len = sizeof(rprdata); 168 rcvmhdr.msg_namelen = sizeof(struct sockaddr_in6); 169 rcvmhdr.msg_iov = &rcviov; 170 rcvmhdr.msg_iovlen = 1; 171 rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 172 CMSG_SPACE(sizeof(int)); 173 if (rcvcmsgbuf == NULL && 174 (rcvcmsgbuf = (u_char *)malloc(rcvcmsglen)) == NULL) { 175 syslog(LOG_ERR, "<%s>: malloc failed", __func__); 176 exit(1); 177 } 178 rcvmhdr.msg_control = (caddr_t)rcvcmsgbuf; 179 rcvmhdr.msg_controllen = rcvcmsglen; 180 181 /* initialize msghdr for sending packets */ 182 sndmhdr.msg_namelen = sizeof(struct sockaddr_in6); 183 sndmhdr.msg_iovlen = 1; 184 sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + 185 CMSG_SPACE(sizeof(int)); 186 if (sndcmsgbuf == NULL && 187 (sndcmsgbuf = (u_char *)malloc(sndcmsglen)) == NULL) { 188 syslog(LOG_ERR, "<%s>: malloc failed", __func__); 189 exit(1); 190 } 191 sndmhdr.msg_control = (caddr_t)sndcmsgbuf; 192 sndmhdr.msg_controllen = sndcmsglen; 193 } 194 195 static void 196 config(FILE **fpp) 197 { 198 struct payload_list *pl; 199 struct iovec *iov; 200 struct icmp6_router_renum *irr; 201 struct rr_pco_match *rpm; 202 203 if (parse(fpp) < 0) { 204 syslog(LOG_ERR, "<%s> parse failed", __func__); 205 exit(1); 206 } 207 208 /* initialize fields not configured by parser */ 209 for (pl = pl_head; pl; pl = pl->pl_next) { 210 iov = (struct iovec *)&pl->pl_sndiov; 211 irr = (struct icmp6_router_renum *)&pl->pl_irr; 212 rpm = (struct rr_pco_match *)&pl->pl_rpm; 213 214 irr->rr_type = ICMP6_ROUTER_RENUMBERING; 215 irr->rr_code = 0; 216 /* 217 * now we don't support multiple PCOs in a rr message. 218 * so segment number is not supported. 219 */ 220 /* TODO: rr flags config in parser */ 221 irr->rr_flags |= ICMP6_RR_FLAGS_SPECSITE; 222 /* TODO: max delay config in parser */ 223 224 /* 225 * means only 1 use_prefix is contained as router-renum-05.txt. 226 * now we don't support multiple PCOs in a rr message, 227 * nor multiple use_prefix in one PCO. 228 */ 229 rpm->rpm_len = 4*1 +3; 230 rpm->rpm_ordinal = 0; 231 iov->iov_base = (caddr_t)irr; 232 iov->iov_len = sizeof(struct icmp6_router_renum) 233 + sizeof(struct rr_pco_match) 234 + sizeof(struct rr_pco_use); 235 } 236 } 237 238 static void 239 sock6_open(struct flags *flags) 240 { 241 struct icmp6_filter filt; 242 int on; 243 244 if (with_v6dest == 0) 245 return; 246 if (with_v6dest && 247 (s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 248 syslog(LOG_ERR, "<%s> socket(v6): %s", __func__, 249 strerror(errno)); 250 exit(1); 251 } 252 253 /* 254 * join all routers multicast addresses. 255 */ 256 #if 0 /* XXX: not necessary ?? */ 257 join_multi(LL_ALLROUTERS); 258 join_multi(SL_ALLROUTERS); 259 #endif 260 261 /* set icmpv6 filter */ 262 ICMP6_FILTER_SETBLOCKALL(&filt); 263 ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt); 264 if (setsockopt(s6, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, 265 sizeof(filt)) < 0) { 266 syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s", 267 __func__, strerror(errno)); 268 exit(1); 269 } 270 271 /* specify to tell receiving interface */ 272 on = 1; 273 if (setsockopt(s6, IPPROTO_IPV6, IPV6_PKTINFO, &on, 274 sizeof(on)) < 0) { 275 syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s", 276 __func__, strerror(errno)); 277 exit(1); 278 } 279 280 return; 281 } 282 283 static void 284 sock4_open(struct flags *flags) 285 { 286 if (with_v4dest == 0) 287 return; 288 if ((s4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 289 syslog(LOG_ERR, "<%s> socket(v4): %s", __func__, 290 strerror(errno)); 291 exit(1); 292 } 293 294 #if 0 /* XXX: not necessary ?? */ 295 /* 296 * join all routers multicast addresses. 297 */ 298 some_join_function(); 299 #endif 300 301 return; 302 } 303 304 static void 305 rrenum_output(struct payload_list *pl, struct dst_list *dl) 306 { 307 int i, msglen = 0; 308 struct cmsghdr *cm; 309 struct in6_pktinfo *pi; 310 struct sockaddr_in6 *sin6 = NULL; 311 312 sndmhdr.msg_name = (caddr_t)dl->dl_dst; 313 if (dl->dl_dst->sa_family == AF_INET6) 314 sin6 = (struct sockaddr_in6 *)dl->dl_dst; 315 316 if (sin6 != NULL && 317 IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 318 int hoplimit = RR_MCHLIM_DEFAULT; 319 320 cm = CMSG_FIRSTHDR(&sndmhdr); 321 /* specify the outgoing interface */ 322 cm->cmsg_level = IPPROTO_IPV6; 323 cm->cmsg_type = IPV6_PKTINFO; 324 cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 325 pi = (struct in6_pktinfo *)CMSG_DATA(cm); 326 memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/ 327 pi->ipi6_ifindex = sin6->sin6_scope_id; 328 msglen += CMSG_LEN(sizeof(struct in6_pktinfo)); 329 330 /* specify the hop limit of the packet if dest is link local */ 331 /* not defined by router-renum-05.txt, but maybe its OK */ 332 cm = CMSG_NXTHDR(&sndmhdr, cm); 333 cm->cmsg_level = IPPROTO_IPV6; 334 cm->cmsg_type = IPV6_HOPLIMIT; 335 cm->cmsg_len = CMSG_LEN(sizeof(int)); 336 memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int)); 337 msglen += CMSG_LEN(sizeof(int)); 338 } 339 sndmhdr.msg_controllen = msglen; 340 if (sndmhdr.msg_controllen == 0) 341 sndmhdr.msg_control = 0; 342 343 sndmhdr.msg_iov = &pl->pl_sndiov; 344 i = sendmsg(dl->dl_dst->sa_family == AF_INET ? s4 : s6, &sndmhdr, 0); 345 346 if (i < 0 || i != sndmhdr.msg_iov->iov_len) 347 syslog(LOG_ERR, "<%s> sendmsg: %s", __func__, 348 strerror(errno)); 349 } 350 351 static void 352 rrenum_snd_eachdst(struct payload_list *pl) 353 { 354 struct dst_list *dl; 355 356 for (dl = dl_head; dl; dl = dl->dl_next) { 357 rrenum_output(pl, dl); 358 } 359 } 360 361 #if 0 362 static void 363 rrenum_snd_fullsequence(void) 364 { 365 struct payload_list *pl; 366 367 for (pl = pl_head; pl; pl = pl->pl_next) { 368 rrenum_snd_eachdst(pl); 369 } 370 } 371 #endif 372 373 static void 374 rrenum_input(int s) 375 { 376 int i; 377 struct icmp6_router_renum *rr; 378 379 /* get message */ 380 if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) { 381 syslog(LOG_ERR, "<%s> recvmsg: %s", __func__, 382 strerror(errno)); 383 return; 384 } 385 if (s == s4) 386 i -= sizeof(struct ip); 387 if (i < sizeof(struct icmp6_router_renum)) { 388 syslog(LOG_ERR, "<%s> packet size(%d) is too short", 389 __func__, i); 390 return; 391 } 392 if (s == s4) { 393 struct ip *ip = (struct ip *)rcvmhdr.msg_iov->iov_base; 394 395 rr = (struct icmp6_router_renum *)(ip + 1); 396 } else /* s == s6 */ 397 rr = (struct icmp6_router_renum *)rcvmhdr.msg_iov->iov_base; 398 399 switch(rr->rr_code) { 400 case ICMP6_ROUTER_RENUMBERING_COMMAND: 401 /* COMMAND will be processed by rtadvd */ 402 break; 403 case ICMP6_ROUTER_RENUMBERING_RESULT: 404 /* TODO: receiving result message */ 405 break; 406 default: 407 syslog(LOG_ERR, "<%s> received unknown code %d", 408 __func__, rr->rr_code); 409 break; 410 } 411 } 412 413 int 414 main(int argc, char *argv[]) 415 { 416 FILE *fp = stdin; 417 fd_set fdset; 418 struct timeval timeout; 419 int ch, i, maxfd = 0, send_counter = 0; 420 struct flags flags; 421 struct payload_list *pl; 422 423 memset(&flags, 0, sizeof(flags)); 424 openlog("rrenumd", LOG_PID, LOG_DAEMON); 425 426 /* get options */ 427 while ((ch = getopt(argc, argv, "c:sdf")) != -1) { 428 switch (ch) { 429 case 'c': 430 if((fp = fopen(optarg, "r")) == NULL) { 431 syslog(LOG_ERR, 432 "<%s> config file %s open failed", 433 __func__, optarg); 434 exit(1); 435 } 436 break; 437 case 's': 438 fp = stdin; 439 break; 440 case 'd': 441 flags.debug = 1; 442 break; 443 case 'f': 444 flags.fg = 1; 445 break; 446 default: 447 show_usage(); 448 } 449 } 450 argc -= optind; 451 argv += optind; 452 453 /* set log level */ 454 if (flags.debug == 0) 455 setlogmask(LOG_UPTO(LOG_ERR)); 456 if (flags.debug == 1) 457 setlogmask(LOG_UPTO(LOG_INFO)); 458 459 /* init global variables */ 460 init_globals(); 461 462 config(&fp); 463 464 sock6_open(&flags); 465 sock4_open(&flags); 466 467 if (!flags.fg) 468 daemon(0, 0); 469 470 FD_ZERO(&fdset); 471 if (with_v6dest) { 472 FD_SET(s6, &fdset); 473 if (s6 > maxfd) 474 maxfd = s6; 475 } 476 if (with_v4dest) { 477 FD_SET(s4, &fdset); 478 if (s4 > maxfd) 479 maxfd = s4; 480 } 481 482 /* ADHOC: timeout each 30seconds */ 483 memset(&timeout, 0, sizeof(timeout)); 484 485 /* init temporary payload_list and send_counter*/ 486 pl = pl_head; 487 send_counter = retry + 1; 488 while (1) { 489 struct fd_set select_fd = fdset; /* reinitialize */ 490 491 if ((i = select(maxfd + 1, &select_fd, NULL, NULL, 492 &timeout)) < 0){ 493 syslog(LOG_ERR, "<%s> select: %s", 494 __func__, strerror(errno)); 495 continue; 496 } 497 if (i == 0) { /* timeout */ 498 if (pl == NULL) 499 exit(0); 500 rrenum_snd_eachdst(pl); 501 send_counter--; 502 timeout.tv_sec = 30; 503 if (send_counter == 0) { 504 timeout.tv_sec = 0; 505 pl = pl->pl_next; 506 send_counter = retry + 1; 507 } 508 } 509 if (FD_ISSET(s4, &select_fd)) 510 rrenum_input(s4); 511 if (FD_ISSET(s6, &select_fd)) 512 rrenum_input(s6); 513 } 514 } 515