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 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <sys/tihdr.h> 32 #include <stropts.h> 33 #include <fcntl.h> 34 #include <syslog.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <libintl.h> 41 #include <locale.h> 42 #include <unistd.h> 43 #include <sys/varargs.h> 44 45 #include <netinet/in.h> 46 #include <sys/ethernet.h> 47 #include <sys/socket.h> 48 #include <sys/sockio.h> 49 #include <sys/sysmacros.h> 50 #include <net/if.h> 51 #include <inet/mib2.h> 52 #include <inet/ip.h> 53 #include <net/route.h> 54 #include <arpa/inet.h> 55 #include "ncaconf.h" 56 57 /* NCA does not support IPv6... */ 58 #ifndef NCA_MOD_NAME 59 #define NCA_MOD_NAME "nca" 60 #endif 61 62 #ifndef ARP_MOD_NAME 63 #define ARP_MOD_NAME "arp" 64 #endif 65 66 #define IF_SEPARATOR ':' 67 68 #define ping_prog "/usr/sbin/ping" 69 70 /* Structure to hold info about each network interface. */ 71 typedef struct nif_s { 72 char name[LIFNAMSIZ+1]; 73 struct in_addr local_addr; 74 struct in_addr router_addr; 75 uchar_t router_ether_addr[ETHERADDRL]; 76 } nif_t; 77 78 typedef struct mib_item_s { 79 struct mib_item_s *next_item; 80 int group; 81 int mib_id; 82 int length; 83 char *valp; 84 } mib_item_t; 85 86 /* The network interface array. */ 87 static nif_t *nif_list; 88 /* Number of network interface to process. */ 89 static int num_nif; 90 91 /* Interface request to IP. */ 92 static struct lifreq lifr; 93 94 /* True if syslog is to be used. */ 95 static boolean_t logging; 96 /* True if additional debugging messages are printed. */ 97 static boolean_t debug; 98 99 /* File descriptor to the routing socket. */ 100 static int rt_fd; 101 102 static void logperror(char *); 103 static void logwarn(char *, ...); 104 static void logdebug(char *, ...); 105 static int ip_domux2fd(int *, int *); 106 static void ip_plink(int, int); 107 static int find_nca_pos(int); 108 static int nca_set_nif(int, struct in_addr, uchar_t *); 109 static void nca_setup(boolean_t *); 110 static int get_if_ip_addr(void); 111 static mib_item_t *mibget(int); 112 static int ire_process(mib2_ipRouteEntry_t *, size_t, boolean_t *); 113 static int arp_process(mib2_ipNetToMediaEntry_t *, size_t, boolean_t *); 114 static int get_router_ip_addr(mib_item_t *, boolean_t *); 115 static int get_router_ether_addr(mib_item_t *, boolean_t *); 116 static int get_if_info(boolean_t *); 117 static void daemon_init(void); 118 static void daemon_work(void); 119 static void ping_them(void); 120 121 /* 122 * Print out system error messages, either to syslog or stderr. Note that 123 * syslog() should print out system error messages in the correct language 124 * used. There is no need to use gettext(). 125 */ 126 static void 127 logperror(char *str) 128 { 129 if (logging) { 130 syslog(LOG_ERR, "%s: %m\n", str); 131 } else { 132 (void) fprintf(stderr, "ncaconfd: %s: %s\n", str, 133 strerror(errno)); 134 } 135 } 136 137 /* 138 * Print out warning messages. The caller should use gettext() to have 139 * the message printed out in the correct language. 140 */ 141 /*PRINTFLIKE1*/ 142 static void 143 logwarn(char *fmt, ...) 144 { 145 va_list ap; 146 147 va_start(ap, fmt); 148 if (logging) { 149 vsyslog(LOG_WARNING, fmt, ap); 150 } else { 151 (void) fprintf(stderr, "ncaconfd: "); 152 (void) vfprintf(stderr, fmt, ap); 153 } 154 va_end(ap); 155 } 156 157 /* 158 * Print out debugging info. Note that syslogd(1M) should be configured to 159 * take ordinary debug info for it to get this kind of info. 160 */ 161 /*PRINTFLIKE1*/ 162 static void 163 logdebug(char *fmt, ...) 164 { 165 va_list ap; 166 167 va_start(ap, fmt); 168 if (logging) { 169 vsyslog(LOG_WARNING, fmt, ap); 170 } else { 171 (void) fprintf(stderr, "ncaconfd: "); 172 (void) vfprintf(stderr, fmt, ap); 173 } 174 va_end(ap); 175 } 176 177 /* 178 * Helper function for nca_setup(). It gets a fd to the lower IP 179 * stream and I_PUNLINK's the lower stream. It also initializes the 180 * global variable lifr. 181 * 182 * Param: 183 * int *udp_fd: (referenced) fd to /dev/udp (upper IP stream). 184 * int *fd: (referenced) fd to the lower IP stream. 185 * 186 * Return: 187 * -1 if operation fails, 0 otherwise. 188 */ 189 static int 190 ip_domux2fd(int *udp_fd, int *fd) 191 { 192 int ip_fd; 193 194 if ((ip_fd = open(IP_DEV_NAME, O_RDWR)) < 0) { 195 logperror("Cannot open IP"); 196 return (-1); 197 } 198 if ((*udp_fd = open(UDP_DEV_NAME, O_RDWR)) < 0) { 199 logperror("Cannot open UDP"); 200 (void) close(ip_fd); 201 return (-1); 202 } 203 if (ioctl(ip_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) { 204 logperror("ioctl(SIOCGLIFMUXID) failed"); 205 (void) close(ip_fd); 206 return (-1); 207 } 208 if (debug) { 209 logdebug("ARP_muxid %d IP_muxid %d\n", lifr.lifr_arp_muxid, 210 lifr.lifr_ip_muxid); 211 } 212 if ((*fd = ioctl(*udp_fd, _I_MUXID2FD, lifr.lifr_ip_muxid)) < 0) { 213 logperror("ioctl(_I_MUXID2FD) failed"); 214 (void) close(ip_fd); 215 (void) close(*udp_fd); 216 return (-1); 217 } 218 (void) close(ip_fd); 219 return (0); 220 } 221 222 /* 223 * Helper function for nca_setup(). It I_PLINK's back the upper and 224 * lower IP streams. Note that this function must be called after 225 * ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized 226 * and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink() 227 * must be called in pairs. 228 * 229 * Param: 230 * int udp_fd: fd to /dev/udp (upper IP stream). 231 * int fd: fd to the lower IP stream. 232 */ 233 static void 234 ip_plink(int udp_fd, int fd) 235 { 236 int mux_id; 237 238 if ((mux_id = ioctl(udp_fd, I_PLINK, fd)) < 0) { 239 logperror("ioctl(I_PLINK) failed"); 240 return; 241 } 242 if (debug > 0) { 243 logdebug("New IP_muxid %d\n", mux_id); 244 } 245 lifr.lifr_ip_muxid = mux_id; 246 if (ioctl(udp_fd, SIOCSLIFMUXID, (caddr_t)&lifr) < 0) { 247 logperror("ioctl(SIOCSLIFMUXID) failed"); 248 } 249 } 250 251 #define FOUND_NCA -1 252 #define FOUND_NONE -2 253 /* 254 * Find the proper position to insert NCA, which is just below IP. 255 * 256 * Param: 257 * int fd: fd to the lower IP stream. 258 * 259 * Return: 260 * If positive, it is the position to insert NCA. 261 * FOUND_NCA: found NCA! So skip this one for plumbing. But we 262 * still keep it in the interface list. 263 * FOUND_NONE: could not find IP or encounter other errors. Remove 264 * this interface from the list. 265 */ 266 static int 267 find_nca_pos(int fd) 268 { 269 int num_mods; 270 int i, pos; 271 struct str_list strlist; 272 boolean_t found_ip = B_FALSE; 273 boolean_t found_nca = B_FALSE; 274 275 if ((num_mods = ioctl(fd, I_LIST, NULL)) < 0) { 276 logperror("ioctl(I_LIST) failed"); 277 return (FOUND_NONE); 278 } else { 279 strlist.sl_nmods = num_mods; 280 strlist.sl_modlist = calloc(num_mods, 281 sizeof (struct str_mlist)); 282 if (strlist.sl_modlist == NULL) { 283 logperror("cannot malloc"); 284 return (FOUND_NONE); 285 } else { 286 if (ioctl(fd, I_LIST, (caddr_t)&strlist) < 0) { 287 logperror("ioctl(I_LIST) failed"); 288 } else { 289 for (i = 0; i < strlist.sl_nmods; i++) { 290 if (strcmp(IP_MOD_NAME, 291 strlist.sl_modlist[i].l_name) 292 == 0) { 293 found_ip = B_TRUE; 294 /* 295 * NCA should be just below 296 * IP. 297 */ 298 pos = i + 1; 299 } else if (strncmp(NCA_MOD_NAME, 300 strlist.sl_modlist[i].l_name, 301 strlen(NCA_MOD_NAME)) == 0) { 302 found_nca = B_TRUE; 303 } 304 } 305 } 306 free(strlist.sl_modlist); 307 } 308 } 309 if (found_nca) { 310 return (FOUND_NCA); 311 } else if (found_ip) { 312 if (debug) { 313 logdebug("NCA is at position %d in the stream.\n", pos); 314 } 315 return (pos); 316 } else { 317 if (debug) { 318 logdebug("Cannot find IP??\n"); 319 } 320 return (FOUND_NONE); 321 } 322 } 323 324 /* 325 * To set the local IP address and default router ethernet address. 326 * 327 * Param: 328 * int fd: the fd to the lower IP stream. 329 * struct in_addr local_addr: the IP address for this interface. 330 * uchar_t *ether_addr: the ethernet address of the default router for 331 * for this interface. 332 * 333 * Return: 334 * -1 if the system does not support this NCA ioctl(), 0 otherwise. 335 */ 336 static int 337 nca_set_nif(int fd, struct in_addr local_addr, uchar_t *ether_addr) 338 { 339 struct nca_set_ioctl nca_ioctl; 340 struct strioctl strioc; 341 int len; 342 uchar_t *dst; 343 344 strioc.ic_cmd = NCA_SET_IF; 345 strioc.ic_timout = INFTIM; 346 strioc.ic_len = sizeof (nca_ioctl); 347 strioc.ic_dp = (char *)&nca_ioctl; 348 349 nca_ioctl.local_addr = local_addr.s_addr; 350 dst = nca_ioctl.router_ether_addr; 351 for (len = ETHERADDRL; len > 0; len--) 352 *dst++ = *ether_addr++; 353 nca_ioctl.action = ADD_DEF_ROUTE; 354 355 if (ioctl(fd, I_STR, &strioc) < 0) { 356 logperror("ioctl(NCA_SET_IF) failed"); 357 if (errno == EINVAL) 358 return (-1); 359 } 360 return (0); 361 } 362 363 /* 364 * To setup the NCA stream. First insert NCA into the proper position. 365 * Then tell NCA the local IP address and default router by using the 366 * NCA_SET_IF ioctl. 367 * 368 * Param: 369 * boolean_t *active: (referenced) B_TRUE if NCA is setup to do active 370 * connection. If NCA does not support active connection, 371 * in return, active will be set to B_FALSE. 372 */ 373 static void 374 nca_setup(boolean_t *active) 375 { 376 int i; 377 int udp_fd; 378 int fd; 379 struct strmodconf mod; 380 /* 128 is enough because interface name can only be LIFNAMSIZ long. */ 381 char err_buf[128]; 382 383 mod.mod_name = NCA_MOD_NAME; 384 lifr.lifr_addr.ss_family = AF_INET; 385 for (i = 0; i < num_nif; i++) { 386 if (debug) { 387 logdebug("Plumbing NCA for %s\n", nif_list[i].name); 388 } 389 /* This interface does not exist according to IP. */ 390 if (nif_list[i].local_addr.s_addr == 0) { 391 continue; 392 } 393 (void) strlcpy(lifr.lifr_name, nif_list[i].name, 394 sizeof (lifr.lifr_name)); 395 396 if (ip_domux2fd(&udp_fd, &fd) < 0) { 397 continue; 398 } 399 if (ioctl(udp_fd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) { 400 (void) snprintf(err_buf, sizeof (err_buf), 401 "ioctl(I_PUNLINK) for %s failed", nif_list[i].name); 402 logperror(err_buf); 403 (void) close(udp_fd); 404 (void) close(fd); 405 continue; 406 } 407 if ((mod.pos = find_nca_pos(fd)) < 0) { 408 if (mod.pos == FOUND_NCA) { 409 if (debug) { 410 logdebug("Find NCA in the %s" 411 " stream\n", nif_list[i].name); 412 } 413 /* Just skip plumbing NCA. */ 414 goto set_nif; 415 } 416 if (debug) { 417 logdebug("Cannot find pos for %s\n", 418 nif_list[i].name); 419 } 420 goto clean_up; 421 } 422 if (ioctl(fd, _I_INSERT, (caddr_t)&mod) < 0) { 423 (void) snprintf(err_buf, sizeof (err_buf), 424 "ioctl(_I_INSERT) for %s failed", nif_list[i].name); 425 logperror(err_buf); 426 goto clean_up; 427 } 428 429 /* 430 * Only do the following if NCA is also used to make 431 * outgoing connections, and all necessary info is 432 * there. 433 */ 434 set_nif: 435 if (*active && nif_list[i].router_addr.s_addr != 0) { 436 if (nca_set_nif(fd, nif_list[i].local_addr, 437 nif_list[i].router_ether_addr) < 0) { 438 /* 439 * The system does not support this ioctl()! 440 * Skip all active stack processing but 441 * continue to plumb NCA. 442 */ 443 logwarn("NCA does not support active stack!"); 444 *active = B_FALSE; 445 } 446 } 447 clean_up: 448 ip_plink(udp_fd, fd); 449 (void) close(udp_fd); 450 (void) close(fd); 451 } 452 } 453 454 /* 455 * To get IP address of network interface from IP. 456 */ 457 static int 458 get_if_ip_addr(void) 459 { 460 int sock; 461 struct lifnum lifn; 462 struct lifconf lifc; 463 struct lifreq *lifr; 464 struct sockaddr_in *sin; 465 char *buf; 466 int num_lifr; 467 int i, j; 468 469 /* NCA only supports IPv4... */ 470 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 471 logperror(gettext("Cannot open socket")); 472 return (-1); 473 } 474 lifn.lifn_family = AF_UNSPEC; 475 lifn.lifn_flags = 0; 476 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { 477 logperror(gettext("ioctl(SIOCGLIFNUM) failed")); 478 (void) close(sock); 479 return (-1); 480 } 481 buf = (char *)calloc(lifn.lifn_count, sizeof (struct lifreq)); 482 if (buf == NULL) { 483 logperror(gettext("calloc() failed")); 484 (void) close(sock); 485 return (-1); 486 } 487 488 lifc.lifc_family = AF_UNSPEC; 489 lifc.lifc_flags = 0; 490 lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq); 491 lifc.lifc_buf = buf; 492 493 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { 494 /* 495 * NCA is set up after all the interfaces have been 496 * plumbed. So normally we should not get any error. 497 * Just abort if we encounter an error. 498 */ 499 logperror(gettext("ioctl(SIOCGLIFCONF) failed")); 500 free(buf); 501 (void) close(sock); 502 return (-1); 503 } 504 num_lifr = lifc.lifc_len / sizeof (struct lifreq); 505 /* Find the interface and copy the local IP address. */ 506 for (i = 0; i < num_nif; i++) { 507 lifr = (struct lifreq *)lifc.lifc_req; 508 for (j = num_lifr; j > 0; j--, lifr++) { 509 /* Again, NCA only supports IPv4. */ 510 if (lifr->lifr_addr.ss_family != AF_INET) 511 continue; 512 if (strncmp(nif_list[i].name, lifr->lifr_name, 513 strlen(nif_list[i].name)) == 0) { 514 sin = (struct sockaddr_in *)&lifr->lifr_addr; 515 nif_list[i].local_addr = sin->sin_addr; 516 if (debug) { 517 logdebug("IP address of %s: %s\n", 518 nif_list[i].name, 519 inet_ntoa(sin->sin_addr)); 520 } 521 break; 522 } 523 } 524 if (j == 0) { 525 /* 526 * The interface does not exist according to IP! 527 * Log a warning and go on. 528 */ 529 logwarn(gettext("Network interface %s" 530 " does not exist!\n"), nif_list[i].name); 531 /* 532 * Set local_addr to 0 so that nca_setup() will 533 * not do anything for this interface. 534 */ 535 nif_list[i].local_addr.s_addr = 0; 536 } 537 } 538 free(buf); 539 (void) close(sock); 540 return (0); 541 } 542 543 /* 544 * Get MIB2 info from IP. 545 * 546 * Param: 547 * int sd: descriptor to IP to send down mib request. 548 */ 549 static mib_item_t * 550 mibget(int sd) 551 { 552 char buf[1024]; 553 int flags; 554 int i, j, getcode; 555 struct strbuf ctlbuf, databuf; 556 /* LINTED */ 557 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; 558 /* LINTED */ 559 struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; 560 /* LINTED */ 561 struct T_error_ack *tea = (struct T_error_ack *)buf; 562 struct opthdr *req; 563 mib_item_t *first_item = (mib_item_t *)0; 564 mib_item_t *last_item = (mib_item_t *)0; 565 mib_item_t *temp; 566 567 tor->PRIM_type = T_SVR4_OPTMGMT_REQ; 568 tor->OPT_offset = sizeof (struct T_optmgmt_req); 569 tor->OPT_length = sizeof (struct opthdr); 570 tor->MGMT_flags = T_CURRENT; 571 req = (struct opthdr *)&tor[1]; 572 req->level = MIB2_IP; /* any MIB2_xxx value ok here */ 573 req->name = 0; 574 req->len = 0; 575 576 ctlbuf.buf = buf; 577 ctlbuf.len = tor->OPT_length + tor->OPT_offset; 578 flags = 0; 579 if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) { 580 logperror("mibget: putmsg(ctl) failed"); 581 goto error_exit; 582 } 583 584 /* 585 * Each reply consists of a ctl part for one fixed structure 586 * or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK, 587 * containing an opthdr structure. level/name identify the entry, 588 * len is the size of the data part of the message. 589 */ 590 req = (struct opthdr *)&toa[1]; 591 ctlbuf.maxlen = sizeof (buf); 592 j = 1; 593 for (;;) { 594 flags = 0; 595 getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags); 596 if (getcode == -1) { 597 logperror("mibget getmsg(ctl) failed"); 598 if (debug) { 599 logdebug("# level name len\n"); 600 i = 0; 601 for (last_item = first_item; last_item; 602 last_item = last_item->next_item) 603 (void) printf("%d %4d %5d %d\n", 604 ++i, 605 last_item->group, 606 last_item->mib_id, 607 last_item->length); 608 } 609 goto error_exit; 610 } 611 if (getcode == 0 && 612 ctlbuf.len >= sizeof (struct T_optmgmt_ack) && 613 toa->PRIM_type == T_OPTMGMT_ACK && 614 toa->MGMT_flags == T_SUCCESS && 615 req->len == 0) { 616 if (debug) { 617 logdebug("mibget getmsg() %d returned " 618 "EOD (level %ld, name %ld)\n", 619 j, req->level, req->name); 620 } 621 return (first_item); /* this is EOD msg */ 622 } 623 624 if (ctlbuf.len >= sizeof (struct T_error_ack) && 625 tea->PRIM_type == T_ERROR_ACK) { 626 logwarn("mibget %d gives T_ERROR_ACK: TLI_error =" 627 " 0x%lx, UNIX_error = 0x%lx\n", 628 j, tea->TLI_error, tea->UNIX_error); 629 errno = (tea->TLI_error == TSYSERR) ? 630 tea->UNIX_error : EPROTO; 631 goto error_exit; 632 } 633 634 if (getcode != MOREDATA || 635 ctlbuf.len < sizeof (struct T_optmgmt_ack) || 636 toa->PRIM_type != T_OPTMGMT_ACK || 637 toa->MGMT_flags != T_SUCCESS) { 638 logwarn("mibget getmsg(ctl) %d returned %d, " 639 "ctlbuf.len = %d, PRIM_type = %ld\n", 640 j, getcode, ctlbuf.len, toa->PRIM_type); 641 if (toa->PRIM_type == T_OPTMGMT_ACK) { 642 logwarn("T_OPTMGMT_ACK: " 643 "MGMT_flags = 0x%lx, req->len = %ld\n", 644 toa->MGMT_flags, req->len); 645 } 646 errno = ENOMSG; 647 goto error_exit; 648 } 649 650 temp = (mib_item_t *)malloc(sizeof (mib_item_t)); 651 if (!temp) { 652 logperror("mibget malloc failed"); 653 goto error_exit; 654 } 655 if (last_item) 656 last_item->next_item = temp; 657 else 658 first_item = temp; 659 last_item = temp; 660 last_item->next_item = (mib_item_t *)0; 661 last_item->group = req->level; 662 last_item->mib_id = req->name; 663 last_item->length = req->len; 664 last_item->valp = malloc((int)req->len); 665 666 databuf.maxlen = last_item->length; 667 databuf.buf = last_item->valp; 668 databuf.len = 0; 669 flags = 0; 670 getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags); 671 if (getcode == -1) { 672 logperror("mibget getmsg(data) failed"); 673 goto error_exit; 674 } else if (getcode != 0) { 675 logwarn("mibget getmsg(data) returned %d, " 676 "databuf.maxlen = %d, databuf.len = %d\n", 677 getcode, databuf.maxlen, databuf.len); 678 goto error_exit; 679 } 680 j++; 681 } 682 683 error_exit:; 684 while (first_item) { 685 last_item = first_item; 686 first_item = first_item->next_item; 687 free(last_item); 688 } 689 return (first_item); 690 } 691 692 /* 693 * Examine the IPv4 routing table for default routers. For each interface, 694 * find its default router. 695 * 696 * Param: 697 * mib2_ipRouteEntry_t *buf: the mib info buffer. 698 * size_t len: length of buffer. 699 * boolean_t *changed (referenced): set to B_TRUE if there is a change 700 * in router info. 701 * 702 * Return: 703 * number of default router found. 704 */ 705 static int 706 ire_process(mib2_ipRouteEntry_t *buf, size_t len, boolean_t *changed) 707 { 708 mib2_ipRouteEntry_t *rp; 709 mib2_ipRouteEntry_t *rp1; 710 mib2_ipRouteEntry_t *rp2; 711 struct in_addr nexthop_v4; 712 mib2_ipRouteEntry_t *endp; 713 char ifname[LIFNAMSIZ + 1]; 714 char *cp; 715 int i; 716 int ifname_len; 717 boolean_t found; 718 int num_found = 0; 719 720 if (len == 0) 721 return (0); 722 endp = buf + (len / sizeof (mib2_ipRouteEntry_t)); 723 724 for (i = 0; i < num_nif; i++) { 725 /* 726 * Loop thru the routing table entries. Process any 727 * IRE_DEFAULT ire. Ignore the others. For each such 728 * ire, get the nexthop gateway address. 729 */ 730 found = B_FALSE; 731 for (rp = buf; rp < endp; rp++) { 732 /* 733 * NCA is only interested in default routes associated 734 * with an interface. 735 */ 736 if (!(rp->ipRouteInfo.re_ire_type & IRE_DEFAULT)) { 737 continue; 738 } 739 /* Get the nexthop address. */ 740 nexthop_v4.s_addr = rp->ipRouteNextHop; 741 742 /* 743 * Right now, not all IREs have the interface name 744 * it is associated with. 745 */ 746 if (rp->ipRouteIfIndex.o_length == 0) { 747 /* 748 * We don't have the outgoing interface in 749 * this case. Get the nexthop address. Then 750 * determine the outgoing interface, by 751 * examining all interface IREs, and 752 * picking the match. 753 */ 754 for (rp1 = buf; rp1 < endp; rp1++) { 755 756 if (!(rp1->ipRouteInfo.re_ire_type & 757 IRE_INTERFACE)) { 758 continue; 759 } 760 761 /* 762 * Determine the interface IRE that 763 * matches the nexthop. i.e. 764 * (IRE addr & IRE mask) == 765 * (nexthop & IRE mask) 766 */ 767 if ((rp1->ipRouteDest & rp1->ipRouteMask) == 768 (nexthop_v4.s_addr & rp1->ipRouteMask)) { 769 /* 770 * We found the interface to go to 771 * the default router. Check the 772 * interface name. 773 */ 774 /* Can this be possible?? */ 775 if (rp1->ipRouteIfIndex.o_length == 0) 776 continue; 777 rp2 = rp1; 778 break; 779 } 780 781 } /* End inner for loop. */ 782 } else { 783 rp2 = rp; 784 } 785 786 ifname_len = MIN(rp2->ipRouteIfIndex.o_length, 787 sizeof (ifname) - 1); 788 (void) memcpy(ifname, rp2->ipRouteIfIndex.o_bytes, 789 ifname_len); 790 ifname[ifname_len] = '\0'; 791 if (ifname[0] == '\0') 792 continue; 793 cp = strchr(ifname, IF_SEPARATOR); 794 if (cp != NULL) 795 *cp = '\0'; 796 797 /* We are sure both are NULL terminated. */ 798 if (strcmp(nif_list[i].name, ifname) == 0) { 799 /* No change, do not do anything. */ 800 if (nexthop_v4.s_addr == 801 nif_list[i].router_addr.s_addr) { 802 found = B_TRUE; 803 break; 804 } 805 nif_list[i].router_addr.s_addr = 806 nexthop_v4.s_addr; 807 if (debug) { 808 logdebug("Get default" 809 " router for %s: %s\n", ifname, 810 inet_ntoa(nexthop_v4)); 811 } 812 found = B_TRUE; 813 *changed = B_TRUE; 814 break; 815 } 816 817 } 818 if (!found) { 819 /* 820 * The interface does not have a default router. 821 * Log a warning and go on. 822 */ 823 logwarn(gettext("Network interface %s" 824 " does not have a default router.\n"), 825 nif_list[i].name); 826 /* 827 * Set router_addr to 0 so that we will 828 * not do anything for this interface. 829 */ 830 nif_list[i].router_addr.s_addr = 0; 831 } else { 832 num_found++; 833 } 834 } 835 return (num_found); 836 } 837 838 /* 839 * Examine the ARP table to find ethernet address for default routers. 840 * 841 * Param: 842 * mib2_ipNetToMdeiaEntry_t *buf: the mib info buffer. 843 * size_t len: length of buffer. 844 * boolean_t *changed (referenced): set to B_TRUE if there is any change 845 * in ethernet address for any default router. 846 * 847 * Return: 848 * number of ethernet address found. 849 */ 850 static int 851 arp_process(mib2_ipNetToMediaEntry_t *buf, size_t len, boolean_t *changed) 852 { 853 mib2_ipNetToMediaEntry_t *rp; 854 mib2_ipNetToMediaEntry_t *endp; 855 int i; 856 boolean_t found; 857 int num_found = 0; 858 uchar_t *src, *dst; 859 860 if (len == 0) 861 return (0); 862 endp = buf + (len / sizeof (mib2_ipNetToMediaEntry_t)); 863 864 for (i = 0; i < num_nif; i++) { 865 /* 866 * Loop thru the arp table entries and find the ethernet 867 * address of those default routers. 868 */ 869 if (nif_list[i].router_addr.s_addr == 0) 870 continue; 871 found = B_FALSE; 872 for (rp = buf; rp < endp; rp++) { 873 if (rp->ipNetToMediaNetAddress == 874 nif_list[i].router_addr.s_addr) { 875 /* 876 * Sanity check. Make sure that this 877 * default router is only reachable thru this 878 * interface. 879 */ 880 if (rp->ipNetToMediaIfIndex.o_length != 881 strlen(nif_list[i].name) || 882 strncmp(rp->ipNetToMediaIfIndex.o_bytes, 883 nif_list[i].name, 884 rp->ipNetToMediaIfIndex.o_length) != 885 0) { 886 break; 887 } 888 /* No change, do not do anything. */ 889 if (bcmp(nif_list[i].router_ether_addr, 890 rp->ipNetToMediaPhysAddress.o_bytes, 891 ETHERADDRL) == 0) { 892 found = B_TRUE; 893 continue; 894 } 895 dst = nif_list[i].router_ether_addr; 896 src = (uchar_t *) 897 rp->ipNetToMediaPhysAddress.o_bytes; 898 for (len = ETHERADDRL; len > 0; len--) 899 *dst++ = *src++; 900 if (debug) { 901 int j; 902 uchar_t *cp; 903 char err_buf[128]; 904 905 (void) snprintf(err_buf, 906 sizeof (err_buf), 907 "Get address for %s: ", 908 inet_ntoa(nif_list[i].router_addr)); 909 cp = (uchar_t *) 910 nif_list[i].router_ether_addr; 911 for (j = 0; j < ETHERADDRL; j++) { 912 (void) sprintf(err_buf + 913 strlen(err_buf), 914 "%02x:", 0xff & cp[j]); 915 } 916 (void) sprintf(err_buf + 917 strlen(err_buf) - 1, "\n"); 918 logdebug(err_buf); 919 } 920 found = B_TRUE; 921 *changed = B_TRUE; 922 } 923 } 924 if (!found) { 925 logwarn("Cannot reach %s using %s\n", 926 inet_ntoa(nif_list[i].router_addr), 927 nif_list[i].name); 928 /* Clear this default router. */ 929 nif_list[i].router_addr.s_addr = 0; 930 } else { 931 num_found++; 932 } 933 } 934 return (num_found); 935 } 936 937 /* 938 * Get IP address of default routers for each interface. 939 * 940 * Param: 941 * mib_item_t *item: the mib info buffer. 942 * boolean_t *changed (referenced): set to B_TRUE if there is any change 943 * in router info. 944 * 945 * Return: 946 * -1 if there is no router found, 0 otherwise. 947 */ 948 static int 949 get_router_ip_addr(mib_item_t *item, boolean_t *changed) 950 { 951 int found = 0; 952 953 for (; item != NULL; item = item->next_item) { 954 /* NCA does not support IPv6... */ 955 if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_ROUTE)) 956 continue; 957 /* LINTED */ 958 found += ire_process((mib2_ipRouteEntry_t *)item->valp, 959 item->length, changed); 960 } 961 if (found == 0) 962 return (-1); 963 else 964 return (0); 965 } 966 967 /* 968 * Get Ethernet address for each default router from ARP. 969 * 970 * Param: 971 * mib_item_t *item: the mib info buffer. 972 * boolean_t *changed (referenced): set to B_TRUE if there is any change 973 * in ethernet address of router. 974 * 975 * Return: 976 * -1 if there is no ethernet address found, 0 otherwise. 977 */ 978 static int 979 get_router_ether_addr(mib_item_t *item, boolean_t *changed) 980 { 981 int found = 0; 982 983 for (; item != NULL; item = item->next_item) { 984 /* NCA does not support IPv6... */ 985 if (!(item->group == MIB2_IP && item->mib_id == MIB2_IP_MEDIA)) 986 continue; 987 /* LINTED */ 988 found += arp_process((mib2_ipNetToMediaEntry_t *)item->valp, 989 item->length, changed); 990 } 991 if (found == 0) 992 return (-1); 993 else 994 return (0); 995 } 996 997 /* 998 * Ping all default routers. It just uses system(3F) to call 999 * ping(1M) to do the job... 1000 */ 1001 static void 1002 ping_them(void) 1003 { 1004 int i; 1005 char ping_cmd[128]; 1006 1007 for (i = 0; i < num_nif; i++) { 1008 if (nif_list[i].router_addr.s_addr != 0) { 1009 (void) snprintf(ping_cmd, sizeof (ping_cmd), 1010 "%s %s > /dev/null 2>&1", 1011 ping_prog, 1012 inet_ntoa(nif_list[i].router_addr)); 1013 (void) system(ping_cmd); 1014 } 1015 } 1016 } 1017 1018 /* 1019 * To get default router info (both IP address and ethernet address) for 1020 * each configured interface from IP. 1021 * 1022 * Param: 1023 * boolean_t *changed (referenced): set to B_TRUE if there is any change 1024 * of info. 1025 * 1026 * Return: 1027 * -1 if there is any error, 0 if everything is fine. 1028 */ 1029 static int 1030 get_if_info(boolean_t *changed) 1031 { 1032 int mib_fd; 1033 mib_item_t *item; 1034 boolean_t ip_changed = B_FALSE; 1035 boolean_t ether_changed = B_FALSE; 1036 1037 if ((mib_fd = open(IP_DEV_NAME, O_RDWR)) < 0) { 1038 logperror("cannot open ip to get router info"); 1039 return (-1); 1040 } 1041 if (ioctl(mib_fd, I_PUSH, ARP_MOD_NAME) == -1) { 1042 logperror("cannot push arp"); 1043 goto err; 1044 } 1045 1046 if ((item = mibget(mib_fd)) == NULL) { 1047 goto err; 1048 } 1049 1050 if (get_router_ip_addr(item, &ip_changed) < 0) { 1051 goto err; 1052 } 1053 /* 1054 * Ping every routers to make sure that ARP has all their ethernet 1055 * addresses. 1056 */ 1057 ping_them(); 1058 /* 1059 * If the router IP address is not changed, its ethernet address 1060 * should not be changed. But just in case there is some IP 1061 * failover going on... 1062 */ 1063 if (get_router_ether_addr(item, ðer_changed) < 0) { 1064 goto err; 1065 } 1066 (void) close(mib_fd); 1067 *changed = ip_changed || ether_changed; 1068 return (0); 1069 err: 1070 (void) close(mib_fd); 1071 return (-1); 1072 } 1073 1074 /* 1075 * To remove the default router from an interface. 1076 * 1077 * Param: 1078 * struct in_addr gw_addr: the IP address of the default router to be 1079 * removed. 1080 */ 1081 static void 1082 nca_del_nif(struct in_addr gw_addr) 1083 { 1084 struct nca_set_ioctl nca_ioctl; 1085 struct strioctl strioc; 1086 int i; 1087 int udp_fd, fd; 1088 1089 /* Search for the interface for this router. */ 1090 for (i = 0; i < num_nif; i++) { 1091 if (nif_list[i].router_addr.s_addr == gw_addr.s_addr) 1092 break; 1093 } 1094 if (i == num_nif) 1095 return; 1096 1097 if (ip_domux2fd(&udp_fd, &fd) < 0) { 1098 logwarn(gettext("Removing interface %s from the" 1099 " configuration list.\n"), nif_list[i].name); 1100 nif_list[i].name[0] = 0; 1101 return; 1102 } 1103 if (ioctl(udp_fd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) { 1104 logwarn(gettext("Removing interface %s from the" 1105 " configuration list.\n"), nif_list[i].name); 1106 nif_list[i].name[0] = 0; 1107 (void) close(udp_fd); 1108 (void) close(fd); 1109 return; 1110 } 1111 1112 strioc.ic_cmd = NCA_SET_IF; 1113 strioc.ic_timout = INFTIM; 1114 strioc.ic_len = sizeof (nca_ioctl); 1115 strioc.ic_dp = (char *)&nca_ioctl; 1116 1117 nca_ioctl.local_addr = 0; 1118 (void) memset(nca_ioctl.router_ether_addr, 0, ETHERADDRL); 1119 nca_ioctl.action = DEL_DEF_ROUTE; 1120 1121 if (ioctl(fd, I_STR, &strioc) < 0) { 1122 logperror("ioctl(NCA_SET_IF) failed"); 1123 } 1124 ip_plink(udp_fd, fd); 1125 (void) close(udp_fd); 1126 (void) close(fd); 1127 1128 /* Clear the fields for this interface. */ 1129 nif_list[i].router_addr.s_addr = 0; 1130 (void) memset(nif_list[i].router_ether_addr, 0, ETHERADDRL); 1131 } 1132 1133 /* 1134 * Wait for any changes in the routing table. If there are changes to 1135 * IP address or router ethernet address, send down the info to NCA. 1136 */ 1137 static void 1138 daemon_work(void) 1139 { 1140 int n; 1141 int i; 1142 int udp_fd; 1143 int fd; 1144 int64_t msg[2048/8]; 1145 struct rt_msghdr *rtm; 1146 boolean_t changed; 1147 struct sockaddr_in *sin; 1148 struct in_addr gw_addr; 1149 uchar_t *cp; 1150 1151 /* Loop forever waiting for any routing changes. */ 1152 for (;;) { 1153 if (debug) { 1154 logdebug("Waiting to read routing info...\n"); 1155 } 1156 n = read(rt_fd, msg, sizeof (msg)); 1157 /* Don't die... Reinitialize socket and listen again. */ 1158 if (n <= 0) { 1159 if (debug) { 1160 logdebug("Routing socket read error.\n"); 1161 } 1162 (void) close(rt_fd); 1163 rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET); 1164 i = 0; 1165 while (rt_fd < 0) { 1166 if (i++ == 0) { 1167 logperror(gettext("cannot reinitialize" 1168 " routing socket")); 1169 } else if (i > 5) { 1170 logwarn(gettext("Give up on trying to" 1171 " reinitializing routing" 1172 " socket\n")); 1173 exit(1); 1174 } 1175 /* May be a transient error... */ 1176 (void) sleep(10); 1177 rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET); 1178 } 1179 } else { 1180 rtm = (struct rt_msghdr *)msg; 1181 if (rtm->rtm_version != RTM_VERSION) { 1182 logwarn(gettext("Do non understand routing" 1183 " socket info.\n")); 1184 continue; 1185 } 1186 if (debug) { 1187 logdebug("Get routing info.\n"); 1188 } 1189 switch (rtm->rtm_type) { 1190 case RTM_DELETE: 1191 case RTM_OLDDEL: 1192 sin = (struct sockaddr_in *)(rtm + 1); 1193 cp = (uchar_t *)sin; 1194 /* Only handle default route deletion. */ 1195 if ((rtm->rtm_addrs & RTA_DST) && 1196 (sin->sin_addr.s_addr == 0)) { 1197 if (!(rtm->rtm_addrs & RTA_GATEWAY)) { 1198 break; 1199 } 1200 cp += sizeof (struct sockaddr_in); 1201 /* LINTED */ 1202 sin = (struct sockaddr_in *)cp; 1203 gw_addr = sin->sin_addr; 1204 if (debug) { 1205 logdebug("Get default route " 1206 "removal notice: gw %s\n", 1207 inet_ntoa(gw_addr)); 1208 } 1209 nca_del_nif(gw_addr); 1210 } 1211 break; 1212 case RTM_ADD: 1213 case RTM_OLDADD: 1214 case RTM_CHANGE: 1215 changed = B_FALSE; 1216 if (get_if_info(&changed) < 0) { 1217 /* May be a transient error... */ 1218 (void) sleep(10); 1219 break; 1220 } 1221 /* Nothing is changed, do nothing. */ 1222 if (!changed) { 1223 if (debug) { 1224 logdebug("Get route change " 1225 "notice, but nothing is " 1226 "changed for us!"); 1227 } 1228 break; 1229 } 1230 lifr.lifr_addr.ss_family = AF_INET; 1231 for (i = 0; i < num_nif; i++) { 1232 int ret; 1233 1234 /* 1235 * If name is NULL, it means that 1236 * we have encontered some problems 1237 * when configurating the interface. 1238 * So we remove it from the list. 1239 */ 1240 if (nif_list[i].name[0] == 0 || 1241 nif_list[i].local_addr.s_addr == 0) 1242 continue; 1243 (void) strlcpy(lifr.lifr_name, 1244 nif_list[i].name, 1245 sizeof (lifr.lifr_name)); 1246 if (ip_domux2fd(&udp_fd, &fd) < 0) { 1247 logwarn(gettext("Removing" 1248 " interface %s from the" 1249 " configuration list.\n"), 1250 nif_list[i].name); 1251 nif_list[i].name[0] = 0; 1252 continue; 1253 } 1254 if (ioctl(udp_fd, I_PUNLINK, 1255 lifr.lifr_ip_muxid) < 0) { 1256 logwarn(gettext("Removing" 1257 " interface %s from the" 1258 " configuration list.\n"), 1259 nif_list[i].name); 1260 nif_list[i].name[0] = 0; 1261 (void) close(udp_fd); 1262 (void) close(fd); 1263 continue; 1264 } 1265 if (debug) { 1266 logdebug("Configuring" 1267 " %s\n", nif_list[i].name); 1268 } 1269 ret = nca_set_nif(fd, 1270 nif_list[i].local_addr, 1271 nif_list[i].router_ether_addr); 1272 ip_plink(udp_fd, fd); 1273 if (ret < 0) { 1274 /* 1275 * This should not be possible 1276 * since if NCA does not 1277 * support the ioctl, the 1278 * active flag should be 1279 * cleared already and this 1280 * function should not have 1281 * been called at all! 1282 */ 1283 logwarn("Daemon dies\n"); 1284 exit(1); 1285 } 1286 (void) close(udp_fd); 1287 (void) close(fd); 1288 } 1289 break; 1290 default: 1291 continue; 1292 } 1293 } 1294 } 1295 } 1296 1297 /* 1298 * Make us a daemon. 1299 */ 1300 static void 1301 daemon_init(void) 1302 { 1303 pid_t pid; 1304 1305 if ((pid = fork()) == -1) { 1306 /* Write directly to terminal, instead of syslog. */ 1307 (void) fprintf(stderr, gettext("ncaconfd: cannot fork: %s\n"), 1308 strerror(errno)); 1309 exit(1); 1310 } 1311 if (pid != 0) 1312 exit(0); 1313 (void) setsid(); 1314 /* Fork again so that we will never get a controlling terminal. */ 1315 if ((pid = fork()) == -1) { 1316 /* Write directly to terminal, instead of syslog. */ 1317 (void) fprintf(stderr, gettext("ncaconfd: cannot fork: %s\n"), 1318 strerror(errno)); 1319 exit(1); 1320 } 1321 if (pid != 0) 1322 exit(0); 1323 (void) chdir("/"); 1324 (void) umask(0); 1325 (void) fclose(stdin); 1326 (void) fclose(stdout); 1327 (void) fclose(stderr); 1328 } 1329 1330 int 1331 main(int argc, char **argv) 1332 { 1333 int i, j; 1334 int c; 1335 boolean_t active = B_FALSE; 1336 boolean_t as_daemon = B_TRUE; 1337 1338 if (argc == 1) { 1339 (void) fprintf(stderr, gettext("Usage: %s [-al]" 1340 " [interface1 interface2 ...]\n"), argv[0]); 1341 return (1); 1342 } 1343 1344 (void) setlocale(LC_ALL, ""); 1345 #if !defined(TEXT_DOMAIN) 1346 #define TEXT_DOMAIN "SYS_TEST" 1347 #endif 1348 (void) textdomain(TEXT_DOMAIN); 1349 1350 while ((c = getopt(argc, argv, "adcl")) != EOF) { 1351 switch (c) { 1352 case 'a': 1353 active = B_TRUE; 1354 break; 1355 case 'd': 1356 debug = B_TRUE; 1357 break; 1358 case 'c': 1359 /* Don't run as daemon. */ 1360 as_daemon = B_FALSE; 1361 break; 1362 case 'l': 1363 logging = B_TRUE; 1364 break; 1365 default: 1366 /* -d and -c are "undocumented" options. */ 1367 (void) fprintf(stderr, gettext("Usage: %s [-al]" 1368 " [interface1 interface2 ...]\n"), argv[0]); 1369 return (1); 1370 } 1371 } 1372 num_nif = argc - optind; 1373 if (num_nif == 0) { 1374 /* No network interface to proces... */ 1375 (void) fprintf(stderr, gettext("Usage: %s [-al]" 1376 " [interface1 interface2 ...]\n"), argv[0]); 1377 return (0); 1378 } 1379 nif_list = calloc(num_nif, sizeof (nif_t)); 1380 if (nif_list == NULL) { 1381 (void) fprintf(stderr, gettext("ncaconfd: Cannot malloc: %s\n"), 1382 strerror(errno)); 1383 return (1); 1384 } 1385 for (i = 0, j = optind; i < num_nif; i++, j++) { 1386 (void) strlcpy(nif_list[i].name, argv[j], LIFNAMSIZ+1); 1387 } 1388 1389 /* Get IP address info for all the intefaces. */ 1390 if (get_if_ip_addr() < 0) { 1391 if (debug) { 1392 (void) fprintf(stderr, "ncaconfd: Cannot get IP" 1393 " addresses for interfaces.\n"); 1394 } 1395 return (1); 1396 } 1397 if (logging) 1398 openlog("ncaconfd", LOG_PID, LOG_DAEMON); 1399 /* No need to run as daemon if NCA is not making active connections. */ 1400 if (active && as_daemon) 1401 daemon_init(); 1402 if (active) { 1403 boolean_t changed; 1404 1405 /* NCA does not support IPv6... */ 1406 if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) { 1407 logperror("Cannot open routing socket"); 1408 return (1); 1409 } 1410 /* 1411 * At boot up time, the default router may not have been 1412 * found. So ignore the error and check later. 1413 */ 1414 if (get_if_info(&changed) < 0) { 1415 if (debug) { 1416 (void) logwarn("Cannot get" 1417 " information from network interface.\n"); 1418 } 1419 } 1420 } 1421 /* Do the set up as daemon (if we are) to save time at boot up... */ 1422 nca_setup(&active); 1423 if (active) 1424 daemon_work(); 1425 return (0); 1426 } 1427