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