1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #include "defs.h" 36 37 #define IF_SEPARATOR ':' 38 39 struct interface *ifnet; 40 41 static int setup_listen_sock(int ifindex); 42 static void addrouteforif(struct interface *ifp); 43 static void resetup_listen_sock(struct interface *, int); 44 45 /* 46 * This is called at startup and after that, every CHECK_INTERVAL seconds or 47 * when a SIGHUP is received. 48 */ 49 void 50 initifs(void) 51 { 52 static char *buf = NULL; 53 static uint_t maxbufsize = 0; 54 int bufsize; 55 int numifs; 56 struct lifnum lifn; 57 struct lifconf lifc; 58 struct lifreq lifr; 59 struct lifreq *lifrp; 60 int n; 61 struct interface ifs; 62 struct interface *ifp; 63 int netmaskchange = 0; 64 boolean_t changes = _B_FALSE; 65 66 lifn.lifn_family = AF_INET6; 67 lifn.lifn_flags = 0; 68 if (ioctl(iocsoc, SIOCGLIFNUM, (char *)&lifn) < 0) { 69 syslog(LOG_ERR, "initifs: ioctl (get interface numbers): %m"); 70 return; 71 } 72 numifs = lifn.lifn_count; 73 bufsize = numifs * sizeof (struct lifreq); 74 75 if (buf == NULL || bufsize > maxbufsize) { 76 if (buf != NULL) 77 free(buf); 78 maxbufsize = bufsize; 79 buf = (char *)malloc(maxbufsize); 80 if (buf == NULL) { 81 syslog(LOG_ERR, "initifs: out of memory"); 82 return; 83 } 84 } 85 86 lifc.lifc_family = AF_INET6; 87 lifc.lifc_flags = 0; 88 lifc.lifc_len = bufsize; 89 lifc.lifc_buf = buf; 90 if (ioctl(iocsoc, SIOCGLIFCONF, (char *)&lifc) < 0) { 91 syslog(LOG_ERR, 92 "initifs: ioctl (get interface configuration): %m"); 93 return; 94 } 95 96 /* 97 * Mark all of the currently known interfaces in order to determine 98 * which of the these interfaces no longer exist. 99 */ 100 for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) 101 ifp->int_flags |= RIP6_IFF_MARKED; 102 lifrp = lifc.lifc_req; 103 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { 104 bzero((char *)&ifs, sizeof (ifs)); 105 (void) strncpy(lifr.lifr_name, lifrp->lifr_name, 106 sizeof (lifr.lifr_name)); 107 if (ioctl(iocsoc, SIOCGLIFFLAGS, (char *)&lifr) < 0) { 108 syslog(LOG_ERR, 109 "initifs: ioctl (get interface flags): %m"); 110 continue; 111 } 112 if (!(lifr.lifr_flags & IFF_IPV6) || 113 !(lifr.lifr_flags & IFF_MULTICAST) || 114 (lifr.lifr_flags & IFF_LOOPBACK)) 115 continue; 116 117 ifp = if_ifwithname(lifr.lifr_name); 118 if (ifp != NULL) 119 ifp->int_flags &= ~RIP6_IFF_MARKED; 120 if (lifr.lifr_flags & IFF_POINTOPOINT) 121 ifs.int_flags |= RIP6_IFF_POINTOPOINT; 122 if (lifr.lifr_flags & IFF_NORTEXCH) 123 ifs.int_flags |= RIP6_IFF_NORTEXCH; 124 if (lifr.lifr_flags & IFF_PRIVATE) 125 ifs.int_flags |= RIP6_IFF_PRIVATE; 126 if (lifr.lifr_flags & IFF_UP) { 127 ifs.int_flags |= RIP6_IFF_UP; 128 } else { 129 if (ifp != NULL) { 130 if (ifp->int_flags & RIP6_IFF_UP) { 131 /* 132 * If there is an transition from up to 133 * down for an exisiting interface, 134 * increment the counter. 135 */ 136 ifp->int_transitions++; 137 changes = _B_TRUE; 138 } 139 if_purge(ifp); 140 } 141 continue; 142 } 143 144 if (ifs.int_flags & RIP6_IFF_POINTOPOINT) { 145 /* 146 * For point-to-point interfaces, retrieve both the 147 * local and the remote addresses. 148 */ 149 if (ioctl(iocsoc, SIOCGLIFADDR, (char *)&lifr) < 0) { 150 syslog(LOG_ERR, 151 "initifs: ioctl (get interface address): " 152 "%m"); 153 continue; 154 } 155 ifs.int_addr = 156 ((struct sockaddr_in6 *)&lifr.lifr_addr)->sin6_addr; 157 if (ioctl(iocsoc, SIOCGLIFDSTADDR, (char *)&lifr) < 0) { 158 syslog(LOG_ERR, 159 "initifs: ioctl (get destination address): " 160 "%m"); 161 continue; 162 } 163 ifs.int_dstaddr = ((struct sockaddr_in6 *) 164 &lifr.lifr_dstaddr)->sin6_addr; 165 ifs.int_prefix_length = IPV6_ABITS; 166 } else { 167 /* 168 * For other interfaces, retreieve the prefix (including 169 * the prefix length. 170 */ 171 if (ioctl(iocsoc, SIOCGLIFSUBNET, (char *)&lifr) < 0) { 172 syslog(LOG_ERR, 173 "initifs: ioctl (get subnet prefix): %m"); 174 continue; 175 } 176 /* 177 * This should never happen but check for it in any case 178 * since the kernel stores it as an signed integer. 179 */ 180 if (lifr.lifr_addrlen < 0 || 181 lifr.lifr_addrlen > IPV6_ABITS) { 182 syslog(LOG_ERR, 183 "initifs: ioctl (get subnet prefix) " 184 "returned invalid prefix length of %d", 185 lifr.lifr_addrlen); 186 continue; 187 } 188 ifs.int_prefix_length = lifr.lifr_addrlen; 189 ifs.int_addr = ((struct sockaddr_in6 *) 190 &lifr.lifr_subnet)->sin6_addr; 191 } 192 193 if (ioctl(iocsoc, SIOCGLIFMETRIC, (char *)&lifr) < 0 || 194 lifr.lifr_metric < 0) 195 ifs.int_metric = 1; 196 else 197 ifs.int_metric = lifr.lifr_metric + 1; 198 199 if (ioctl(iocsoc, SIOCGLIFINDEX, (char *)&lifr) < 0) { 200 syslog(LOG_ERR, "initifs: ioctl (get index): %m"); 201 continue; 202 } 203 ifs.int_ifindex = lifr.lifr_index; 204 205 if (ioctl(iocsoc, SIOCGLIFMTU, (char *)&lifr) < 0) { 206 syslog(LOG_ERR, "initifs: ioctl (get mtu): %m"); 207 continue; 208 } 209 210 /* 211 * If the interface's recorded MTU doesn't make sense, use 212 * IPV6_MIN_MTU instead. 213 */ 214 if (lifr.lifr_mtu < IPV6_MIN_MTU) 215 ifs.int_mtu = IPV6_MIN_MTU; 216 else 217 ifs.int_mtu = lifr.lifr_mtu; 218 219 if (ifp != NULL) { 220 /* 221 * RIP6_IFF_NORTEXCH flag change by itself shouldn't 222 * cause an if_purge() call, which also purges all the 223 * routes heard off this interface. So, let's suppress 224 * changes of RIP6_IFF_NORTEXCH in the following 225 * comparisons. 226 */ 227 if (ifp->int_prefix_length == ifs.int_prefix_length && 228 ((ifp->int_flags | RIP6_IFF_NORTEXCH) == 229 (ifs.int_flags | RIP6_IFF_NORTEXCH)) && 230 ifp->int_metric == ifs.int_metric && 231 ifp->int_ifindex == ifs.int_ifindex) { 232 /* 233 * Now let's make sure we capture the latest 234 * value of RIP6_IFF_NORTEXCH flag. 235 */ 236 if (ifs.int_flags & RIP6_IFF_NORTEXCH) 237 ifp->int_flags |= RIP6_IFF_NORTEXCH; 238 else 239 ifp->int_flags &= ~RIP6_IFF_NORTEXCH; 240 241 if (!(ifp->int_flags & RIP6_IFF_POINTOPOINT) && 242 IN6_ARE_ADDR_EQUAL(&ifp->int_addr, 243 &ifs.int_addr)) 244 continue; 245 if ((ifp->int_flags & RIP6_IFF_POINTOPOINT) && 246 IN6_ARE_ADDR_EQUAL(&ifp->int_dstaddr, 247 &ifs.int_dstaddr)) 248 continue; 249 } 250 if_purge(ifp); 251 if (ifp->int_prefix_length != ifs.int_prefix_length) 252 netmaskchange = 1; 253 ifp->int_addr = ifs.int_addr; 254 ifp->int_dstaddr = ifs.int_dstaddr; 255 ifp->int_metric = ifs.int_metric; 256 /* 257 * If there is an transition from down to up for an 258 * exisiting interface, increment the counter. 259 */ 260 if (!(ifp->int_flags & RIP6_IFF_UP) && 261 (ifs.int_flags & RIP6_IFF_UP)) 262 ifp->int_transitions++; 263 ifp->int_flags |= ifs.int_flags; 264 ifp->int_prefix_length = ifs.int_prefix_length; 265 266 /* 267 * If the interface index has changed, we may need to 268 * set up the listen socket again. 269 */ 270 if (ifp->int_ifindex != ifs.int_ifindex) { 271 if (ifp->int_sock != -1) { 272 resetup_listen_sock(ifp, 273 ifs.int_ifindex); 274 } 275 ifp->int_ifindex = ifs.int_ifindex; 276 } 277 278 ifp->int_mtu = ifs.int_mtu; 279 } else { 280 char *cp; 281 int log_num; 282 283 ifp = (struct interface *) 284 malloc(sizeof (struct interface)); 285 if (ifp == NULL) { 286 syslog(LOG_ERR, "initifs: out of memory"); 287 return; 288 } 289 *ifp = ifs; 290 ifp->int_name = ifp->int_ifbase = NULL; 291 ifp->int_name = 292 (char *)malloc((size_t)strlen(lifr.lifr_name) + 1); 293 if (ifp->int_name == NULL) { 294 free(ifp); 295 syslog(LOG_ERR, "initifs: out of memory"); 296 return; 297 } 298 (void) strcpy(ifp->int_name, lifr.lifr_name); 299 ifp->int_ifbase = 300 (char *)malloc((size_t)strlen(lifr.lifr_name) + 1); 301 if (ifp->int_ifbase == NULL) { 302 free(ifp->int_name); 303 free(ifp); 304 syslog(LOG_ERR, "initifs: out of memory"); 305 return; 306 } 307 (void) strcpy(ifp->int_ifbase, lifr.lifr_name); 308 cp = (char *)index(ifp->int_ifbase, IF_SEPARATOR); 309 if (cp != NULL) { 310 /* 311 * Verify that the value following the separator 312 * is an integer greater than zero (the only 313 * possible value for a logical interface). 314 */ 315 log_num = atoi((char *)(cp + 1)); 316 if (log_num <= 0) { 317 free(ifp->int_ifbase); 318 free(ifp->int_name); 319 free(ifp); 320 syslog(LOG_ERR, 321 "initifs: interface name %s could " 322 "not be parsed", ifp->int_name); 323 return; 324 } 325 *cp = '\0'; 326 } else { 327 log_num = 0; 328 } 329 if (log_num == 0) { 330 ifp->int_sock = 331 setup_listen_sock(ifp->int_ifindex); 332 } else { 333 ifp->int_sock = -1; 334 } 335 ifp->int_next = ifnet; 336 ifnet = ifp; 337 traceinit(ifp); 338 } 339 addrouteforif(ifp); 340 changes = _B_TRUE; 341 } 342 343 /* 344 * Any remaining interfaces that are still marked and which were in an 345 * up state (RIP6_IFF_UP) need to removed from the routing table. 346 */ 347 for (ifp = ifnet; ifp != NULL; ifp = ifp->int_next) { 348 if ((ifp->int_flags & (RIP6_IFF_MARKED | RIP6_IFF_UP)) == 349 (RIP6_IFF_MARKED | RIP6_IFF_UP)) { 350 if_purge(ifp); 351 ifp->int_flags &= ~RIP6_IFF_MARKED; 352 changes = _B_TRUE; 353 } 354 } 355 if (netmaskchange) 356 rtchangeall(); 357 if (supplier & changes) 358 dynamic_update((struct interface *)NULL); 359 } 360 361 static void 362 addrouteforif(struct interface *ifp) 363 { 364 struct rt_entry *rt; 365 struct in6_addr *dst; 366 367 if (ifp->int_flags & RIP6_IFF_POINTOPOINT) 368 dst = &ifp->int_dstaddr; 369 else 370 dst = &ifp->int_addr; 371 372 rt = rtlookup(dst, ifp->int_prefix_length); 373 374 if (rt != NULL) { 375 if (rt->rt_state & RTS_INTERFACE) 376 return; 377 rtdelete(rt); 378 } 379 rtadd(dst, &ifp->int_addr, ifp->int_prefix_length, ifp->int_metric, 0, 380 _B_TRUE, ifp); 381 } 382 383 static int 384 setup_listen_sock(int ifindex) 385 { 386 int sock; 387 struct sockaddr_in6 sin6; 388 uint_t hops; 389 struct ipv6_mreq allrouters_mreq; 390 int on = 1; 391 int off = 0; 392 int recvsize; 393 394 sock = socket(AF_INET6, SOCK_DGRAM, 0); 395 if (sock == -1) 396 goto sock_fail; 397 398 if (setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, (char *)&ifindex, 399 sizeof (ifindex)) < 0) { 400 syslog(LOG_ERR, 401 "setup_listen_sock: setsockopt: IPV6_BOUND_IF: %m"); 402 goto sock_fail; 403 } 404 405 hops = IPV6_MAX_HOPS; 406 if (setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hops, 407 sizeof (hops)) < 0) { 408 syslog(LOG_ERR, 409 "setup_listen_sock: setsockopt: IPV6_UNICAST_HOPS: %m"); 410 goto sock_fail; 411 } 412 413 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, 414 sizeof (hops)) < 0) { 415 syslog(LOG_ERR, 416 "setup_listen_sock: setsockopt: IPV6_MULTICAST_HOPS: %m"); 417 goto sock_fail; 418 } 419 420 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&off, 421 sizeof (off)) < 0) { 422 syslog(LOG_ERR, 423 "setup_listen_sock: setsockopt: IPV6_MULTICAST_LOOP: %m"); 424 goto sock_fail; 425 } 426 427 allrouters_mreq.ipv6mr_multiaddr = allrouters_in6; 428 allrouters_mreq.ipv6mr_interface = ifindex; 429 if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, 430 (char *)&allrouters_mreq, sizeof (allrouters_mreq)) < 0) { 431 if (errno != EADDRINUSE) { 432 syslog(LOG_ERR, 433 "setup_listen_sock: setsockopt: " 434 "IPV6_JOIN_GROUP: %m"); 435 goto sock_fail; 436 } 437 } 438 439 if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, (char *)&on, 440 sizeof (off)) < 0) { 441 syslog(LOG_ERR, 442 "setup_listen_sock: setsockopt: IPV6_RECVHOPLIMIT: %m"); 443 goto sock_fail; 444 } 445 446 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, 447 sizeof (on)) < 0) { 448 syslog(LOG_ERR, 449 "setup_listen_sock: setsockopt: SO_REUSEADDR: %m"); 450 goto sock_fail; 451 } 452 453 recvsize = RCVBUFSIZ; 454 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&recvsize, 455 sizeof (int)) < 0) { 456 syslog(LOG_ERR, "setup_listen_sock: setsockopt: SO_RCVBUF: %m"); 457 goto sock_fail; 458 } 459 460 bzero((char *)&sin6, sizeof (sin6)); 461 sin6.sin6_family = AF_INET6; 462 sin6.sin6_port = rip6_port; 463 if (bind(sock, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) { 464 syslog(LOG_ERR, "setup_listen_sock: bind: %m"); 465 goto sock_fail; 466 } 467 468 poll_ifs_num++; 469 if (poll_ifs == NULL) { 470 poll_ifs = (struct pollfd *) 471 malloc(max_poll_ifs * sizeof (struct pollfd)); 472 } else if (poll_ifs_num > max_poll_ifs) { 473 max_poll_ifs *= 2; 474 poll_ifs = (struct pollfd *)realloc((char *)poll_ifs, 475 max_poll_ifs * sizeof (struct pollfd)); 476 } 477 if (poll_ifs == NULL) { 478 syslog(LOG_ERR, "setup_listen_sock: out of memory"); 479 goto sock_fail; 480 } 481 482 poll_ifs[poll_ifs_num - 1].fd = sock; 483 poll_ifs[poll_ifs_num - 1].events = POLLIN; 484 return (sock); 485 486 sock_fail: 487 if (sock > 0) 488 (void) close(sock); 489 return (-1); 490 } 491 492 /* 493 * resetup_listen_sock is primarily used in the case where a tunnel was 494 * plumbed, unplumbed, then plumbed again. This would cause the binding set by 495 * IPV6_BOUND_IF to be useless, and sends to the associated socket will be 496 * transmitted on the wrong interface. resetup_listen_sock 497 * closes the socket, 498 * removes the socket from poll_ifs[] 499 * plugs the hole in poll_ifs[] 500 * calls setup_listen_sock to set up the socket again 501 */ 502 void 503 resetup_listen_sock(struct interface *ifp, int newindex) 504 { 505 int i; 506 507 (void) close(ifp->int_sock); 508 509 /* Remove socket from poll_ifs[]. */ 510 for (i = poll_ifs_num - 1; i >= 0; i--) { 511 512 if (poll_ifs[i].fd == ifp->int_sock) { 513 514 poll_ifs[i].fd = 0; 515 poll_ifs[i].events = 0; 516 517 /* 518 * Remove hole in poll_ifs. Possibly exchange 519 * poll_ifs[i] with poll_ifs[poll_ifs_num-1]. 520 */ 521 if (i != poll_ifs_num - 1) { 522 poll_ifs[i] = poll_ifs[poll_ifs_num - 1]; 523 poll_ifs[poll_ifs_num - 1].fd = 0; 524 poll_ifs[poll_ifs_num - 1].events = 0; 525 } 526 poll_ifs_num--; 527 528 /* Now set everything up again. */ 529 ifp->int_sock = setup_listen_sock(newindex); 530 break; 531 } 532 } 533 } 534