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