rrenum.c revision 222732
168673Sobrien/* $FreeBSD: head/usr.sbin/rtadvd/rrenum.c 222732 2011-06-06 03:06:43Z hrs $ */ 277298Sobrien/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59:47 itojun Exp $ */ 368673Sobrien 468673Sobrien/* 568673Sobrien * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 668673Sobrien * All rights reserved. 768673Sobrien * 868673Sobrien * Redistribution and use in source and binary forms, with or without 968673Sobrien * modification, are permitted provided that the following conditions 1068673Sobrien * are met: 1168673Sobrien * 1. Redistributions of source code must retain the above copyright 1268673Sobrien * notice, this list of conditions and the following disclaimer. 1368673Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1468673Sobrien * notice, this list of conditions and the following disclaimer in the 1568673Sobrien * documentation and/or other materials provided with the distribution. 1668673Sobrien * 3. Neither the name of the project nor the names of its contributors 1768673Sobrien * may be used to endorse or promote products derived from this software 1868673Sobrien * without specific prior written permission. 1968673Sobrien * 2068673Sobrien * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2168673Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2268673Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2368673Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2468673Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2568673Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2668673Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2768673Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2868673Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2968673Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3068673Sobrien * SUCH DAMAGE. 3168673Sobrien */ 3268673Sobrien#include <sys/types.h> 3368673Sobrien#include <sys/param.h> 3468673Sobrien#include <sys/ioctl.h> 3568673Sobrien#include <sys/socket.h> 3668673Sobrien#include <sys/sysctl.h> 3777298Sobrien 3868673Sobrien#include <net/if.h> 3968673Sobrien#include <net/if_var.h> 40#include <net/route.h> 41#include <netinet/in.h> 42#include <netinet/in_var.h> 43#include <netinet/icmp6.h> 44 45#include <arpa/inet.h> 46 47#include <errno.h> 48#include <netdb.h> 49#include <string.h> 50#include <stdlib.h> 51#include <syslog.h> 52#include "rtadvd.h" 53#include "rrenum.h" 54#include "if.h" 55 56#define RR_ISSET_SEGNUM(segnum_bits, segnum) \ 57 ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0) 58#define RR_SET_SEGNUM(segnum_bits, segnum) \ 59 (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31))) 60 61struct rr_operation { 62 u_long rro_seqnum; 63 u_long rro_segnum_bits[8]; 64}; 65 66static struct rr_operation rro; 67static int rr_rcvifindex; 68static int rrcmd2pco[RPM_PCO_MAX] = { 69 0, 70 SIOCAIFPREFIX_IN6, 71 SIOCCIFPREFIX_IN6, 72 SIOCSGIFPREFIX_IN6 73}; 74static int s = -1; 75 76/* 77 * Check validity of a Prefix Control Operation(PCO). 78 * return 0 on success, 1 on failure. 79 */ 80static int 81rr_pco_check(int len, struct rr_pco_match *rpm) 82{ 83 struct rr_pco_use *rpu, *rpulim; 84 int checklen; 85 86 /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */ 87 if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */ 88 (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */ 89 syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3", 90 __func__, rpm->rpm_len); 91 return (1); 92 } 93 /* rpm->rpm_code must be valid value */ 94 switch (rpm->rpm_code) { 95 case RPM_PCO_ADD: 96 case RPM_PCO_CHANGE: 97 case RPM_PCO_SETGLOBAL: 98 break; 99 default: 100 syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__, 101 rpm->rpm_code); 102 return (1); 103 } 104 /* rpm->rpm_matchlen must be 0 to 128 inclusive */ 105 if (rpm->rpm_matchlen > 128) { 106 syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128", 107 __func__, rpm->rpm_matchlen); 108 return (1); 109 } 110 111 /* 112 * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be 113 * between 0 and 128 inclusive 114 */ 115 for (rpu = (struct rr_pco_use *)(rpm + 1), 116 rpulim = (struct rr_pco_use *)((char *)rpm + len); 117 rpu < rpulim; 118 rpu += 1) { 119 checklen = rpu->rpu_uselen; 120 checklen += rpu->rpu_keeplen; 121 /* 122 * omit these check, because either of rpu_uselen 123 * and rpu_keeplen is unsigned char 124 * (128 > rpu_uselen > 0) 125 * (128 > rpu_keeplen > 0) 126 * (rpu_uselen + rpu_keeplen > 0) 127 */ 128 if (checklen > 128) { 129 syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and" 130 " rpu_keeplen %d is %d(over 128)", 131 __func__, rpu->rpu_uselen, rpu->rpu_keeplen, 132 rpu->rpu_uselen + rpu->rpu_keeplen); 133 return (1); 134 } 135 } 136 return (0); 137} 138 139static void 140do_use_prefix(int len, struct rr_pco_match *rpm, 141 struct in6_rrenumreq *irr, int ifindex) 142{ 143 struct rr_pco_use *rpu, *rpulim; 144 struct rainfo *rai; 145 struct prefix *pfx; 146 147 rpu = (struct rr_pco_use *)(rpm + 1); 148 rpulim = (struct rr_pco_use *)((char *)rpm + len); 149 150 if (rpu == rpulim) { /* no use prefix */ 151 if (rpm->rpm_code == RPM_PCO_ADD) 152 return; 153 154 irr->irr_u_uselen = 0; 155 irr->irr_u_keeplen = 0; 156 irr->irr_raf_mask_onlink = 0; 157 irr->irr_raf_mask_auto = 0; 158 irr->irr_vltime = 0; 159 irr->irr_pltime = 0; 160 memset(&irr->irr_flags, 0, sizeof(irr->irr_flags)); 161 irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */ 162 irr->irr_useprefix.sin6_family = 0; 163 irr->irr_useprefix.sin6_addr = in6addr_any; 164 if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && 165 errno != EADDRNOTAVAIL) 166 syslog(LOG_ERR, "<%s> ioctl: %s", __func__, 167 strerror(errno)); 168 return; 169 } 170 171 for (rpu = (struct rr_pco_use *)(rpm + 1), 172 rpulim = (struct rr_pco_use *)((char *)rpm + len); 173 rpu < rpulim; 174 rpu += 1) { 175 /* init in6_rrenumreq fields */ 176 irr->irr_u_uselen = rpu->rpu_uselen; 177 irr->irr_u_keeplen = rpu->rpu_keeplen; 178 irr->irr_raf_mask_onlink = 179 !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK); 180 irr->irr_raf_mask_auto = 181 !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO); 182 irr->irr_vltime = ntohl(rpu->rpu_vltime); 183 irr->irr_pltime = ntohl(rpu->rpu_pltime); 184 irr->irr_raf_onlink = 185 (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 186 0 : 1; 187 irr->irr_raf_auto = 188 (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 189 0 : 1; 190 irr->irr_rrf_decrvalid = 191 (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 192 0 : 1; 193 irr->irr_rrf_decrprefd = 194 (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 195 0 : 1; 196 irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix); 197 irr->irr_useprefix.sin6_family = AF_INET6; 198 irr->irr_useprefix.sin6_addr = rpu->rpu_prefix; 199 200 if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 && 201 errno != EADDRNOTAVAIL) 202 syslog(LOG_ERR, "<%s> ioctl: %s", __func__, 203 strerror(errno)); 204 205 /* very adhoc: should be rewritten */ 206 if (rpm->rpm_code == RPM_PCO_CHANGE && 207 IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) && 208 rpm->rpm_matchlen == rpu->rpu_uselen && 209 rpu->rpu_uselen == rpu->rpu_keeplen) { 210 if ((rai = if_indextorainfo(ifindex)) == NULL) 211 continue; /* non-advertising IF */ 212 213 TAILQ_FOREACH(pfx, &rai->rai_prefix, pfx_next) { 214 struct timeval now; 215 216 if (prefix_match(&pfx->pfx_prefix, 217 pfx->pfx_prefixlen, &rpm->rpm_prefix, 218 rpm->rpm_matchlen)) { 219 /* change parameters */ 220 pfx->pfx_validlifetime = 221 ntohl(rpu->rpu_vltime); 222 pfx->pfx_preflifetime = 223 ntohl(rpu->rpu_pltime); 224 if (irr->irr_rrf_decrvalid) { 225 gettimeofday(&now, 0); 226 pfx->pfx_vltimeexpire = 227 now.tv_sec + 228 pfx->pfx_validlifetime; 229 } else 230 pfx->pfx_vltimeexpire = 0; 231 if (irr->irr_rrf_decrprefd) { 232 gettimeofday(&now, 0); 233 pfx->pfx_pltimeexpire = 234 now.tv_sec + 235 pfx->pfx_preflifetime; 236 } else 237 pfx->pfx_pltimeexpire = 0; 238 } 239 } 240 } 241 } 242} 243 244/* 245 * process a Prefix Control Operation(PCO). 246 * return 0 on success, 1 on failure 247 */ 248static int 249do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm) 250{ 251 int ifindex = 0; 252 struct in6_rrenumreq irr; 253 254 if ((rr_pco_check(len, rpm) != 0)) 255 return (1); 256 257 if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { 258 syslog(LOG_ERR, "<%s> socket: %s", __func__, 259 strerror(errno)); 260 exit(1); 261 } 262 263 memset(&irr, 0, sizeof(irr)); 264 irr.irr_origin = PR_ORIG_RR; 265 irr.irr_m_len = rpm->rpm_matchlen; 266 irr.irr_m_minlen = rpm->rpm_minlen; 267 irr.irr_m_maxlen = rpm->rpm_maxlen; 268 irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix); 269 irr.irr_matchprefix.sin6_family = AF_INET6; 270 irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix; 271 272 while (if_indextoname(++ifindex, irr.irr_name)) { 273 /* 274 * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and 275 * IFF_UP is off, the interface is not applied 276 */ 277 if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 && 278 (iflist[ifindex]->ifm_flags & IFF_UP) == 0) 279 continue; 280 /* TODO: interface scope check */ 281 do_use_prefix(len, rpm, &irr, ifindex); 282 } 283 if (errno == ENXIO) 284 return (0); 285 else if (errno) { 286 syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__, 287 strerror(errno)); 288 return (1); 289 } 290 return (0); 291} 292 293/* 294 * call do_pco() for each Prefix Control Operations(PCOs) in a received 295 * Router Renumbering Command packet. 296 * return 0 on success, 1 on failure 297 */ 298static int 299do_rr(int len, struct icmp6_router_renum *rr) 300{ 301 struct rr_pco_match *rpm; 302 char *cp, *lim; 303 304 lim = (char *)rr + len; 305 cp = (char *)(rr + 1); 306 len -= sizeof(struct icmp6_router_renum); 307 308 /* get iflist block from kernel again, to get up-to-date information */ 309 init_iflist(); 310 311 while (cp < lim) { 312 int rpmlen; 313 314 rpm = (struct rr_pco_match *)cp; 315 if ((size_t)len < sizeof(struct rr_pco_match)) { 316 tooshort: 317 syslog(LOG_ERR, "<%s> pkt too short. left len = %d. " 318 "gabage at end of pkt?", __func__, len); 319 return (1); 320 } 321 rpmlen = rpm->rpm_len << 3; 322 if (len < rpmlen) 323 goto tooshort; 324 325 if (do_pco(rr, rpmlen, rpm)) { 326 syslog(LOG_WARNING, "<%s> invalid PCO", __func__); 327 goto next; 328 } 329 330 next: 331 cp += rpmlen; 332 len -= rpmlen; 333 } 334 335 return (0); 336} 337 338/* 339 * check validity of a router renumbering command packet 340 * return 0 on success, 1 on failure 341 */ 342static int 343rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from, 344 struct in6_addr *dst) 345{ 346 u_char ntopbuf[INET6_ADDRSTRLEN]; 347 348 /* omit rr minimal length check. hope kernel have done it. */ 349 /* rr_command length check */ 350 if ((size_t)len < (sizeof(struct icmp6_router_renum) + 351 sizeof(struct rr_pco_match))) { 352 syslog(LOG_ERR, "<%s> rr_command len %d is too short", 353 __func__, len); 354 return (1); 355 } 356 357 /* destination check. only for multicast. omit unicast check. */ 358 if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) && 359 !IN6_IS_ADDR_MC_SITELOCAL(dst)) { 360 syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal", 361 __func__, 362 inet_ntop(AF_INET6, dst, ntopbuf, sizeof(ntopbuf))); 363 return (1); 364 } 365 366 /* seqnum and segnum check */ 367 if (rro.rro_seqnum > rr->rr_seqnum) { 368 syslog(LOG_WARNING, 369 "<%s> rcvd old seqnum %d from %s", 370 __func__, (u_int32_t)ntohl(rr->rr_seqnum), 371 inet_ntop(AF_INET6, from, ntopbuf, sizeof(ntopbuf))); 372 return (1); 373 } 374 if (rro.rro_seqnum == rr->rr_seqnum && 375 (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 && 376 RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) { 377 if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0) 378 syslog(LOG_WARNING, 379 "<%s> rcvd duped segnum %d from %s", 380 __func__, rr->rr_segnum, inet_ntop(AF_INET6, from, 381 ntopbuf, sizeof(ntopbuf))); 382 return (0); 383 } 384 385 /* update seqnum */ 386 if (rro.rro_seqnum != rr->rr_seqnum) { 387 /* then must be "<" */ 388 389 /* init rro_segnum_bits */ 390 memset(rro.rro_segnum_bits, 0, 391 sizeof(rro.rro_segnum_bits)); 392 } 393 rro.rro_seqnum = rr->rr_seqnum; 394 395 return (0); 396} 397 398static void 399rr_command_input(int len, struct icmp6_router_renum *rr, 400 struct in6_addr *from, struct in6_addr *dst) 401{ 402 /* rr_command validity check */ 403 if (rr_command_check(len, rr, from, dst)) 404 goto failed; 405 if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) == 406 ICMP6_RR_FLAGS_TEST) 407 return; 408 409 /* do router renumbering */ 410 if (do_rr(len, rr)) 411 goto failed; 412 413 /* update segnum */ 414 RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum); 415 416 return; 417 418 failed: 419 syslog(LOG_ERR, "<%s> received RR was invalid", __func__); 420 return; 421} 422 423void 424rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi, 425 struct sockaddr_in6 *from, struct in6_addr *dst) 426{ 427 u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; 428 429 syslog(LOG_DEBUG, 430 "<%s> RR received from %s to %s on %s", 431 __func__, 432 inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0] ,sizeof(ntopbuf[0])), 433 inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), 434 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 435 436 /* packet validation based on Section 4.1 of RFC2894 */ 437 if ((size_t)len < sizeof(struct icmp6_router_renum)) { 438 syslog(LOG_NOTICE, 439 "<%s>: RR short message (size %d) from %s to %s on %s", 440 __func__, len, 441 inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[0], 442 sizeof(ntopbuf[0])), 443 inet_ntop(AF_INET6, &dst, ntopbuf[1], sizeof(ntopbuf[1])), 444 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 445 return; 446 } 447 448 /* 449 * If the IPv6 destination address is neither an All Routers multicast 450 * address [AARCH] nor one of the receiving router's unicast addresses, 451 * the message MUST be discarded and SHOULD be logged to network 452 * management. 453 * We rely on the kernel input routine for unicast addresses, and thus 454 * check multicast destinations only. 455 */ 456 if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && !IN6_ARE_ADDR_EQUAL( 457 &sin6_sitelocal_allrouters.sin6_addr, &pi->ipi6_addr)) { 458 syslog(LOG_NOTICE, 459 "<%s>: RR message with invalid destination (%s) " 460 "from %s on %s", 461 __func__, 462 inet_ntop(AF_INET6, &dst, ntopbuf[0], sizeof(ntopbuf[0])), 463 inet_ntop(AF_INET6, &from->sin6_addr, ntopbuf[1], 464 sizeof(ntopbuf[1])), 465 if_indextoname(pi->ipi6_ifindex, ifnamebuf)); 466 return; 467 } 468 469 rr_rcvifindex = pi->ipi6_ifindex; 470 471 switch (rr->rr_code) { 472 case ICMP6_ROUTER_RENUMBERING_COMMAND: 473 rr_command_input(len, rr, &from->sin6_addr, dst); 474 /* TODO: send reply msg */ 475 break; 476 case ICMP6_ROUTER_RENUMBERING_RESULT: 477 /* RESULT will be processed by rrenumd */ 478 break; 479 case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET: 480 /* TODO: sequence number reset */ 481 break; 482 default: 483 syslog(LOG_ERR, "<%s> received unknown code %d", 484 __func__, rr->rr_code); 485 break; 486 487 } 488 489 return; 490} 491