8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.lib / ncaconfd / ncaconfd.c
blob06972f53dc1bc28f1e9c2ab2f7e0468b8a07c7a1
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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>
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"
57 /* NCA does not support IPv6... */
58 #ifndef NCA_MOD_NAME
59 #define NCA_MOD_NAME "nca"
60 #endif
62 #ifndef ARP_MOD_NAME
63 #define ARP_MOD_NAME "arp"
64 #endif
66 #define IF_SEPARATOR ':'
68 #define ping_prog "/usr/sbin/ping"
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;
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;
86 /* The network interface array. */
87 static nif_t *nif_list;
88 /* Number of network interface to process. */
89 static int num_nif;
91 /* Interface request to IP. */
92 static struct lifreq lifr;
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;
99 /* File descriptor to the routing socket. */
100 static int rt_fd;
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);
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().
126 static void
127 logperror(char *str)
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));
138 * Print out warning messages. The caller should use gettext() to have
139 * the message printed out in the correct language.
141 /*PRINTFLIKE1*/
142 static void
143 logwarn(char *fmt, ...)
145 va_list ap;
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);
154 va_end(ap);
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.
161 /*PRINTFLIKE1*/
162 static void
163 logdebug(char *fmt, ...)
165 va_list ap;
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);
174 va_end(ap);
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.
182 * Param:
183 * int *udp_fd: (referenced) fd to /dev/udp (upper IP stream).
184 * int *fd: (referenced) fd to the lower IP stream.
186 * Return:
187 * -1 if operation fails, 0 otherwise.
189 static int
190 ip_domux2fd(int *udp_fd, int *fd)
192 int ip_fd;
194 if ((ip_fd = open(IP_DEV_NAME, O_RDWR)) < 0) {
195 logperror("Cannot open IP");
196 return (-1);
198 if ((*udp_fd = open(UDP_DEV_NAME, O_RDWR)) < 0) {
199 logperror("Cannot open UDP");
200 (void) close(ip_fd);
201 return (-1);
203 if (ioctl(ip_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
204 logperror("ioctl(SIOCGLIFMUXID) failed");
205 (void) close(ip_fd);
206 return (-1);
208 if (debug) {
209 logdebug("ARP_muxid %d IP_muxid %d\n", lifr.lifr_arp_muxid,
210 lifr.lifr_ip_muxid);
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);
218 (void) close(ip_fd);
219 return (0);
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.
229 * Param:
230 * int udp_fd: fd to /dev/udp (upper IP stream).
231 * int fd: fd to the lower IP stream.
233 static void
234 ip_plink(int udp_fd, int fd)
236 int mux_id;
238 if ((mux_id = ioctl(udp_fd, I_PLINK, fd)) < 0) {
239 logperror("ioctl(I_PLINK) failed");
240 return;
242 if (debug > 0) {
243 logdebug("New IP_muxid %d\n", mux_id);
245 lifr.lifr_ip_muxid = mux_id;
246 if (ioctl(udp_fd, SIOCSLIFMUXID, (caddr_t)&lifr) < 0) {
247 logperror("ioctl(SIOCSLIFMUXID) failed");
251 #define FOUND_NCA -1
252 #define FOUND_NONE -2
254 * Find the proper position to insert NCA, which is just below IP.
256 * Param:
257 * int fd: fd to the lower IP stream.
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.
266 static int
267 find_nca_pos(int fd)
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;
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;
295 * NCA should be just below
296 * IP.
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;
306 free(strlist.sl_modlist);
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);
315 return (pos);
316 } else {
317 if (debug) {
318 logdebug("Cannot find IP??\n");
320 return (FOUND_NONE);
325 * To set the local IP address and default router ethernet address.
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.
333 * Return:
334 * -1 if the system does not support this NCA ioctl(), 0 otherwise.
336 static int
337 nca_set_nif(int fd, struct in_addr local_addr, uchar_t *ether_addr)
339 struct nca_set_ioctl nca_ioctl;
340 struct strioctl strioc;
341 int len;
342 uchar_t *dst;
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;
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;
355 if (ioctl(fd, I_STR, &strioc) < 0) {
356 logperror("ioctl(NCA_SET_IF) failed");
357 if (errno == EINVAL)
358 return (-1);
360 return (0);
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.
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.
373 static void
374 nca_setup(boolean_t *active)
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];
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);
389 /* This interface does not exist according to IP. */
390 if (nif_list[i].local_addr.s_addr == 0) {
391 continue;
393 (void) strlcpy(lifr.lifr_name, nif_list[i].name,
394 sizeof (lifr.lifr_name));
396 if (ip_domux2fd(&udp_fd, &fd) < 0) {
397 continue;
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;
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);
413 /* Just skip plumbing NCA. */
414 goto set_nif;
416 if (debug) {
417 logdebug("Cannot find pos for %s\n",
418 nif_list[i].name);
420 goto clean_up;
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;
430 * Only do the following if NCA is also used to make
431 * outgoing connections, and all necessary info is
432 * there.
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) {
439 * The system does not support this ioctl()!
440 * Skip all active stack processing but
441 * continue to plumb NCA.
443 logwarn("NCA does not support active stack!");
444 *active = B_FALSE;
447 clean_up:
448 ip_plink(udp_fd, fd);
449 (void) close(udp_fd);
450 (void) close(fd);
455 * To get IP address of network interface from IP.
457 static int
458 get_if_ip_addr(void)
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;
469 /* NCA only supports IPv4... */
470 if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
471 logperror(gettext("Cannot open socket"));
472 return (-1);
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);
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);
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;
493 if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) {
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.
499 logperror(gettext("ioctl(SIOCGLIFCONF) failed"));
500 free(buf);
501 (void) close(sock);
502 return (-1);
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));
521 break;
524 if (j == 0) {
526 * The interface does not exist according to IP!
527 * Log a warning and go on.
529 logwarn(gettext("Network interface %s"
530 " does not exist!\n"), nif_list[i].name);
532 * Set local_addr to 0 so that nca_setup() will
533 * not do anything for this interface.
535 nif_list[i].local_addr.s_addr = 0;
538 free(buf);
539 (void) close(sock);
540 return (0);
544 * Get MIB2 info from IP.
546 * Param:
547 * int sd: descriptor to IP to send down mib request.
549 static mib_item_t *
550 mibget(int sd)
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;
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;
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;
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.
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);
609 goto error_exit;
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);
621 return (first_item); /* this is EOD msg */
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;
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);
646 errno = ENOMSG;
647 goto error_exit;
650 temp = (mib_item_t *)malloc(sizeof (mib_item_t));
651 if (!temp) {
652 logperror("mibget malloc failed");
653 goto error_exit;
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);
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;
680 j++;
683 error_exit:;
684 while (first_item) {
685 last_item = first_item;
686 first_item = first_item->next_item;
687 free(last_item);
689 return (first_item);
693 * Examine the IPv4 routing table for default routers. For each interface,
694 * find its default router.
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.
702 * Return:
703 * number of default router found.
705 static int
706 ire_process(mib2_ipRouteEntry_t *buf, size_t len, boolean_t *changed)
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;
720 if (len == 0)
721 return (0);
722 endp = buf + (len / sizeof (mib2_ipRouteEntry_t));
724 for (i = 0; i < num_nif; i++) {
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.
730 found = B_FALSE;
731 for (rp = buf; rp < endp; rp++) {
733 * NCA is only interested in default routes associated
734 * with an interface.
736 if (!(rp->ipRouteInfo.re_ire_type & IRE_DEFAULT)) {
737 continue;
739 /* Get the nexthop address. */
740 nexthop_v4.s_addr = rp->ipRouteNextHop;
743 * Right now, not all IREs have the interface name
744 * it is associated with.
746 if (rp->ipRouteIfIndex.o_length == 0) {
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.
754 for (rp1 = buf; rp1 < endp; rp1++) {
756 if (!(rp1->ipRouteInfo.re_ire_type &
757 IRE_INTERFACE)) {
758 continue;
762 * Determine the interface IRE that
763 * matches the nexthop. i.e.
764 * (IRE addr & IRE mask) ==
765 * (nexthop & IRE mask)
767 if ((rp1->ipRouteDest & rp1->ipRouteMask) ==
768 (nexthop_v4.s_addr & rp1->ipRouteMask)) {
770 * We found the interface to go to
771 * the default router. Check the
772 * interface name.
774 /* Can this be possible?? */
775 if (rp1->ipRouteIfIndex.o_length == 0)
776 continue;
777 rp2 = rp1;
778 break;
781 } /* End inner for loop. */
782 } else {
783 rp2 = rp;
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';
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;
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));
812 found = B_TRUE;
813 *changed = B_TRUE;
814 break;
818 if (!found) {
820 * The interface does not have a default router.
821 * Log a warning and go on.
823 logwarn(gettext("Network interface %s"
824 " does not have a default router.\n"),
825 nif_list[i].name);
827 * Set router_addr to 0 so that we will
828 * not do anything for this interface.
830 nif_list[i].router_addr.s_addr = 0;
831 } else {
832 num_found++;
835 return (num_found);
839 * Examine the ARP table to find ethernet address for default routers.
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.
847 * Return:
848 * number of ethernet address found.
850 static int
851 arp_process(mib2_ipNetToMediaEntry_t *buf, size_t len, boolean_t *changed)
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;
860 if (len == 0)
861 return (0);
862 endp = buf + (len / sizeof (mib2_ipNetToMediaEntry_t));
864 for (i = 0; i < num_nif; i++) {
866 * Loop thru the arp table entries and find the ethernet
867 * address of those default routers.
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) {
876 * Sanity check. Make sure that this
877 * default router is only reachable thru this
878 * interface.
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;
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;
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];
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]);
916 (void) sprintf(err_buf +
917 strlen(err_buf) - 1, "\n");
918 logdebug(err_buf);
920 found = B_TRUE;
921 *changed = B_TRUE;
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++;
934 return (num_found);
938 * Get IP address of default routers for each interface.
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.
945 * Return:
946 * -1 if there is no router found, 0 otherwise.
948 static int
949 get_router_ip_addr(mib_item_t *item, boolean_t *changed)
951 int found = 0;
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);
961 if (found == 0)
962 return (-1);
963 else
964 return (0);
968 * Get Ethernet address for each default router from ARP.
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.
975 * Return:
976 * -1 if there is no ethernet address found, 0 otherwise.
978 static int
979 get_router_ether_addr(mib_item_t *item, boolean_t *changed)
981 int found = 0;
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);
991 if (found == 0)
992 return (-1);
993 else
994 return (0);
998 * Ping all default routers. It just uses system(3F) to call
999 * ping(1M) to do the job...
1001 static void
1002 ping_them(void)
1004 int i;
1005 char ping_cmd[128];
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);
1019 * To get default router info (both IP address and ethernet address) for
1020 * each configured interface from IP.
1022 * Param:
1023 * boolean_t *changed (referenced): set to B_TRUE if there is any change
1024 * of info.
1026 * Return:
1027 * -1 if there is any error, 0 if everything is fine.
1029 static int
1030 get_if_info(boolean_t *changed)
1032 int mib_fd;
1033 mib_item_t *item;
1034 boolean_t ip_changed = B_FALSE;
1035 boolean_t ether_changed = B_FALSE;
1037 if ((mib_fd = open(IP_DEV_NAME, O_RDWR)) < 0) {
1038 logperror("cannot open ip to get router info");
1039 return (-1);
1041 if (ioctl(mib_fd, I_PUSH, ARP_MOD_NAME) == -1) {
1042 logperror("cannot push arp");
1043 goto err;
1046 if ((item = mibget(mib_fd)) == NULL) {
1047 goto err;
1050 if (get_router_ip_addr(item, &ip_changed) < 0) {
1051 goto err;
1054 * Ping every routers to make sure that ARP has all their ethernet
1055 * addresses.
1057 ping_them();
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...
1063 if (get_router_ether_addr(item, &ether_changed) < 0) {
1064 goto err;
1066 (void) close(mib_fd);
1067 *changed = ip_changed || ether_changed;
1068 return (0);
1069 err:
1070 (void) close(mib_fd);
1071 return (-1);
1075 * To remove the default router from an interface.
1077 * Param:
1078 * struct in_addr gw_addr: the IP address of the default router to be
1079 * removed.
1081 static void
1082 nca_del_nif(struct in_addr gw_addr)
1084 struct nca_set_ioctl nca_ioctl;
1085 struct strioctl strioc;
1086 int i;
1087 int udp_fd, fd;
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;
1094 if (i == num_nif)
1095 return;
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;
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;
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;
1117 nca_ioctl.local_addr = 0;
1118 (void) memset(nca_ioctl.router_ether_addr, 0, ETHERADDRL);
1119 nca_ioctl.action = DEL_DEF_ROUTE;
1121 if (ioctl(fd, I_STR, &strioc) < 0) {
1122 logperror("ioctl(NCA_SET_IF) failed");
1124 ip_plink(udp_fd, fd);
1125 (void) close(udp_fd);
1126 (void) close(fd);
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);
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.
1137 static void
1138 daemon_work(void)
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;
1151 /* Loop forever waiting for any routing changes. */
1152 for (;;) {
1153 if (debug) {
1154 logdebug("Waiting to read routing info...\n");
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");
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);
1175 /* May be a transient error... */
1176 (void) sleep(10);
1177 rt_fd = socket(PF_ROUTE, SOCK_RAW, AF_INET);
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;
1186 if (debug) {
1187 logdebug("Get routing info.\n");
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;
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));
1209 nca_del_nif(gw_addr);
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;
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!");
1228 break;
1230 lifr.lifr_addr.ss_family = AF_INET;
1231 for (i = 0; i < num_nif; i++) {
1232 int ret;
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.
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;
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;
1265 if (debug) {
1266 logdebug("Configuring"
1267 " %s\n", nif_list[i].name);
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) {
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!
1283 logwarn("Daemon dies\n");
1284 exit(1);
1286 (void) close(udp_fd);
1287 (void) close(fd);
1289 break;
1290 default:
1291 continue;
1298 * Make us a daemon.
1300 static void
1301 daemon_init(void)
1303 pid_t pid;
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);
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);
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);
1331 main(int argc, char **argv)
1333 int i, j;
1334 int c;
1335 boolean_t active = B_FALSE;
1336 boolean_t as_daemon = B_TRUE;
1338 if (argc == 1) {
1339 (void) fprintf(stderr, gettext("Usage: %s [-al]"
1340 " [interface1 interface2 ...]\n"), argv[0]);
1341 return (1);
1344 (void) setlocale(LC_ALL, "");
1345 #if !defined(TEXT_DOMAIN)
1346 #define TEXT_DOMAIN "SYS_TEST"
1347 #endif
1348 (void) textdomain(TEXT_DOMAIN);
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);
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);
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);
1385 for (i = 0, j = optind; i < num_nif; i++, j++) {
1386 (void) strlcpy(nif_list[i].name, argv[j], LIFNAMSIZ+1);
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");
1395 return (1);
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;
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);
1411 * At boot up time, the default router may not have been
1412 * found. So ignore the error and check later.
1414 if (get_if_info(&changed) < 0) {
1415 if (debug) {
1416 (void) logwarn("Cannot get"
1417 " information from network interface.\n");
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);