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
logperror(char * str)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
logwarn(char * fmt,...)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
logdebug(char * fmt,...)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
ip_domux2fd(int * udp_fd,int * fd)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
ip_plink(int udp_fd,int fd)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
find_nca_pos(int fd)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
nca_set_nif(int fd,struct in_addr local_addr,uchar_t * ether_addr)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
nca_setup(boolean_t * active)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
get_if_ip_addr(void)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 *
mibget(int sd)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
ire_process(mib2_ipRouteEntry_t * buf,size_t len,boolean_t * changed)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
arp_process(mib2_ipNetToMediaEntry_t * buf,size_t len,boolean_t * changed)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
get_router_ip_addr(mib_item_t * item,boolean_t * changed)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
get_router_ether_addr(mib_item_t * item,boolean_t * changed)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
ping_them(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
get_if_info(boolean_t * changed)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
nca_del_nif(struct in_addr gw_addr)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
daemon_work(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
daemon_init(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
main(int argc,char ** argv)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