ncaconfd.c revision ff550d0e264b51131fb34e9e83163b348d916640
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stropts.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <libintl.h>
#include <locale.h>
#include <unistd.h>
#include <sys/ethernet.h>
#include <sys/sysmacros.h>
#include "ncaconf.h"
/* NCA does not support IPv6... */
#ifndef NCA_MOD_NAME
#define NCA_MOD_NAME "nca"
#endif
#ifndef ARP_MOD_NAME
#define ARP_MOD_NAME "arp"
#endif
#define IF_SEPARATOR ':'
/* Structure to hold info about each network interface. */
typedef struct nif_s {
struct in_addr local_addr;
struct in_addr router_addr;
} nif_t;
typedef struct mib_item_s {
struct mib_item_s *next_item;
int group;
int mib_id;
int length;
char *valp;
} mib_item_t;
/* The network interface array. */
/* Number of network interface to process. */
static int num_nif;
/* Interface request to IP. */
/* True if syslog is to be used. */
/* True if additional debugging messages are printed. */
/* File descriptor to the routing socket. */
static int rt_fd;
static void logperror(char *);
static void logwarn(char *, ...);
static void logdebug(char *, ...);
static int ip_domux2fd(int *, int *);
static void ip_plink(int, int);
static int find_nca_pos(int);
static int get_if_ip_addr(void);
static mib_item_t *mibget(int);
static int get_if_info(boolean_t *);
static void daemon_init(void);
static void daemon_work(void);
static void ping_them(void);
/*
* Print out system error messages, either to syslog or stderr. Note that
* syslog() should print out system error messages in the correct language
* used. There is no need to use gettext().
*/
static void
{
if (logging) {
} else {
}
}
/*
* Print out warning messages. The caller should use gettext() to have
* the message printed out in the correct language.
*/
/*PRINTFLIKE1*/
static void
{
if (logging) {
} else {
}
}
/*
* Print out debugging info. Note that syslogd(1M) should be configured to
* take ordinary debug info for it to get this kind of info.
*/
/*PRINTFLIKE1*/
static void
{
if (logging) {
} else {
}
}
/*
* Helper function for nca_setup(). It gets a fd to the lower IP
* stream and I_PUNLINK's the lower stream. It also initializes the
* global variable lifr.
*
* Param:
* int *fd: (referenced) fd to the lower IP stream.
*
* Return:
* -1 if operation fails, 0 otherwise.
*/
static int
{
int ip_fd;
logperror("Cannot open IP");
return (-1);
}
logperror("Cannot open UDP");
return (-1);
}
logperror("ioctl(SIOCGLIFMUXID) failed");
return (-1);
}
if (debug) {
}
logperror("ioctl(_I_MUXID2FD) failed");
return (-1);
}
return (0);
}
/*
* Helper function for nca_setup(). It I_PLINK's back the upper and
* lower IP streams. Note that this function must be called after
* ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized
* and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink()
* must be called in pairs.
*
* Param:
* int fd: fd to the lower IP stream.
*/
static void
{
int mux_id;
logperror("ioctl(I_PLINK) failed");
return;
}
if (debug > 0) {
}
logperror("ioctl(SIOCSLIFMUXID) failed");
}
}
#define FOUND_NCA -1
#define FOUND_NONE -2
/*
* Find the proper position to insert NCA, which is just below IP.
*
* Param:
* int fd: fd to the lower IP stream.
*
* Return:
* If positive, it is the position to insert NCA.
* FOUND_NCA: found NCA! So skip this one for plumbing. But we
* still keep it in the interface list.
* FOUND_NONE: could not find IP or encounter other errors. Remove
* this interface from the list.
*/
static int
find_nca_pos(int fd)
{
int num_mods;
int i, pos;
logperror("ioctl(I_LIST) failed");
return (FOUND_NONE);
} else {
sizeof (struct str_mlist));
logperror("cannot malloc");
return (FOUND_NONE);
} else {
logperror("ioctl(I_LIST) failed");
} else {
if (strcmp(IP_MOD_NAME,
== 0) {
/*
* NCA should be just below
* IP.
*/
pos = i + 1;
} else if (strncmp(NCA_MOD_NAME,
strlen(NCA_MOD_NAME)) == 0) {
}
}
}
}
}
if (found_nca) {
return (FOUND_NCA);
} else if (found_ip) {
if (debug) {
}
return (pos);
} else {
if (debug) {
logdebug("Cannot find IP??\n");
}
return (FOUND_NONE);
}
}
/*
* To set the local IP address and default router ethernet address.
*
* Param:
* int fd: the fd to the lower IP stream.
* struct in_addr local_addr: the IP address for this interface.
* uchar_t *ether_addr: the ethernet address of the default router for
* for this interface.
*
* Return:
* -1 if the system does not support this NCA ioctl(), 0 otherwise.
*/
static int
{
struct nca_set_ioctl nca_ioctl;
int len;
*dst++ = *ether_addr++;
logperror("ioctl(NCA_SET_IF) failed");
return (-1);
}
return (0);
}
/*
* To setup the NCA stream. First insert NCA into the proper position.
* Then tell NCA the local IP address and default router by using the
* NCA_SET_IF ioctl.
*
* Param:
* boolean_t *active: (referenced) B_TRUE if NCA is setup to do active
* connection. If NCA does not support active connection,
* in return, active will be set to B_FALSE.
*/
static void
{
int i;
int udp_fd;
int fd;
struct strmodconf mod;
/* 128 is enough because interface name can only be LIFNAMSIZ long. */
char err_buf[128];
for (i = 0; i < num_nif; i++) {
if (debug) {
}
/* This interface does not exist according to IP. */
continue;
}
continue;
}
continue;
}
if (debug) {
logdebug("Find NCA in the %s"
}
/* Just skip plumbing NCA. */
goto set_nif;
}
if (debug) {
logdebug("Cannot find pos for %s\n",
}
goto clean_up;
}
goto clean_up;
}
/*
* Only do the following if NCA is also used to make
* outgoing connections, and all necessary info is
* there.
*/
nif_list[i].router_ether_addr) < 0) {
/*
* The system does not support this ioctl()!
* Skip all active stack processing but
* continue to plumb NCA.
*/
logwarn("NCA does not support active stack!");
}
}
}
}
/*
* To get IP address of network interface from IP.
*/
static int
get_if_ip_addr(void)
{
int sock;
struct sockaddr_in *sin;
char *buf;
int num_lifr;
int i, j;
/* NCA only supports IPv4... */
return (-1);
}
lifn.lifn_flags = 0;
return (-1);
}
return (-1);
}
lifc.lifc_flags = 0;
/*
* NCA is set up after all the interfaces have been
* plumbed. So normally we should not get any error.
* Just abort if we encounter an error.
*/
return (-1);
}
/* Find the interface and copy the local IP address. */
for (i = 0; i < num_nif; i++) {
/* Again, NCA only supports IPv4. */
continue;
if (debug) {
logdebug("IP address of %s: %s\n",
}
break;
}
}
if (j == 0) {
/*
* The interface does not exist according to IP!
* Log a warning and go on.
*/
/*
* Set local_addr to 0 so that nca_setup() will
* not do anything for this interface.
*/
}
}
return (0);
}
/*
* Get MIB2 info from IP.
*
* Param:
* int sd: descriptor to IP to send down mib request.
*/
static mib_item_t *
{
char buf[1024];
int flags;
int i, j, getcode;
/* LINTED */
/* LINTED */
/* LINTED */
flags = 0;
logperror("mibget: putmsg(ctl) failed");
goto error_exit;
}
/*
* Each reply consists of a ctl part for one fixed structure
* or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
* len is the size of the data part of the message.
*/
j = 1;
for (;;) {
flags = 0;
if (getcode == -1) {
logperror("mibget getmsg(ctl) failed");
if (debug) {
logdebug("# level name len\n");
i = 0;
(void) printf("%d %4d %5d %d\n",
++i,
}
goto error_exit;
}
if (getcode == 0 &&
if (debug) {
logdebug("mibget getmsg() %d returned "
"EOD (level %ld, name %ld)\n",
}
return (first_item); /* this is EOD msg */
}
logwarn("mibget %d gives T_ERROR_ACK: TLI_error ="
" 0x%lx, UNIX_error = 0x%lx\n",
goto error_exit;
}
logwarn("mibget getmsg(ctl) %d returned %d, "
"ctlbuf.len = %d, PRIM_type = %ld\n",
logwarn("T_OPTMGMT_ACK: "
"MGMT_flags = 0x%lx, req->len = %ld\n",
}
goto error_exit;
}
if (!temp) {
logperror("mibget malloc failed");
goto error_exit;
}
if (last_item)
else
first_item = temp;
flags = 0;
if (getcode == -1) {
logperror("mibget getmsg(data) failed");
goto error_exit;
} else if (getcode != 0) {
logwarn("mibget getmsg(data) returned %d, "
"databuf.maxlen = %d, databuf.len = %d\n",
goto error_exit;
}
j++;
}
while (first_item) {
}
return (first_item);
}
/*
* Examine the IPv4 routing table for default routers. For each interface,
* find its default router.
*
* Param:
* mib2_ipRouteEntry_t *buf: the mib info buffer.
* size_t len: length of buffer.
* boolean_t *changed (referenced): set to B_TRUE if there is a change
* in router info.
*
* Return:
* number of default router found.
*/
static int
{
struct in_addr nexthop_v4;
char *cp;
int i;
int ifname_len;
int num_found = 0;
if (len == 0)
return (0);
for (i = 0; i < num_nif; i++) {
/*
* Loop thru the routing table entries. Process any
* IRE_DEFAULT ire. Ignore the others. For each such
* ire, get the nexthop gateway address.
*/
/*
* NCA is only interested in default routes associated
* with an interface.
*/
continue;
}
/* Get the nexthop address. */
/*
* Right now, not all IREs have the interface name
* it is associated with.
*/
/*
* We don't have the outgoing interface in
* this case. Get the nexthop address. Then
* determine the outgoing interface, by
* examining all interface IREs, and
* picking the match.
*/
IRE_INTERFACE)) {
continue;
}
/*
* Determine the interface IRE that
* matches the nexthop. i.e.
* (IRE addr & IRE mask) ==
* (nexthop & IRE mask)
*/
/*
* We found the interface to go to
* the default router. Check the
* interface name.
*/
/* Can this be possible?? */
continue;
break;
}
} /* End inner for loop. */
} else {
}
sizeof (ifname) - 1);
if (ifname[0] == '\0')
continue;
*cp = '\0';
/* We are sure both are NULL terminated. */
/* No change, do not do anything. */
if (nexthop_v4.s_addr ==
break;
}
if (debug) {
logdebug("Get default"
" router for %s: %s\n", ifname,
}
break;
}
}
if (!found) {
/*
* The interface does not have a default router.
* Log a warning and go on.
*/
" does not have a default router.\n"),
/*
* Set router_addr to 0 so that we will
* not do anything for this interface.
*/
} else {
num_found++;
}
}
return (num_found);
}
/*
* Examine the ARP table to find ethernet address for default routers.
*
* Param:
* mib2_ipNetToMdeiaEntry_t *buf: the mib info buffer.
* size_t len: length of buffer.
* boolean_t *changed (referenced): set to B_TRUE if there is any change
* in ethernet address for any default router.
*
* Return:
* number of ethernet address found.
*/
static int
{
int i;
int num_found = 0;
if (len == 0)
return (0);
for (i = 0; i < num_nif; i++) {
/*
* Loop thru the arp table entries and find the ethernet
* address of those default routers.
*/
continue;
if (rp->ipNetToMediaNetAddress ==
/*
* Sanity check. Make sure that this
* default router is only reachable thru this
* interface.
*/
0) {
break;
}
/* No change, do not do anything. */
ETHERADDRL) == 0) {
continue;
}
if (debug) {
int j;
char err_buf[128];
sizeof (err_buf),
"Get address for %s: ",
for (j = 0; j < ETHERADDRL; j++) {
}
}
}
}
if (!found) {
logwarn("Cannot reach %s using %s\n",
/* Clear this default router. */
} else {
num_found++;
}
}
return (num_found);
}
/*
* Get IP address of default routers for each interface.
*
* Param:
* mib_item_t *item: the mib info buffer.
* boolean_t *changed (referenced): set to B_TRUE if there is any change
* in router info.
*
* Return:
* -1 if there is no router found, 0 otherwise.
*/
static int
{
int found = 0;
/* NCA does not support IPv6... */
continue;
/* LINTED */
}
if (found == 0)
return (-1);
else
return (0);
}
/*
* Get Ethernet address for each default router from ARP.
*
* Param:
* mib_item_t *item: the mib info buffer.
* boolean_t *changed (referenced): set to B_TRUE if there is any change
* in ethernet address of router.
*
* Return:
* -1 if there is no ethernet address found, 0 otherwise.
*/
static int
{
int found = 0;
/* NCA does not support IPv6... */
continue;
/* LINTED */
}
if (found == 0)
return (-1);
else
return (0);
}
/*
* Ping all default routers. It just uses system(3F) to call
* ping(1M) to do the job...
*/
static void
ping_them(void)
{
int i;
char ping_cmd[128];
for (i = 0; i < num_nif; i++) {
}
}
}
/*
* To get default router info (both IP address and ethernet address) for
* each configured interface from IP.
*
* Param:
* boolean_t *changed (referenced): set to B_TRUE if there is any change
* of info.
*
* Return:
* -1 if there is any error, 0 if everything is fine.
*/
static int
{
int mib_fd;
logperror("cannot open ip to get router info");
return (-1);
}
logperror("cannot push arp");
goto err;
}
goto err;
}
goto err;
}
/*
* Ping every routers to make sure that ARP has all their ethernet
* addresses.
*/
ping_them();
/*
* If the router IP address is not changed, its ethernet address
* should not be changed. But just in case there is some IP
* failover going on...
*/
goto err;
}
return (0);
err:
return (-1);
}
/*
* To remove the default router from an interface.
*
* Param:
* struct in_addr gw_addr: the IP address of the default router to be
* removed.
*/
static void
{
struct nca_set_ioctl nca_ioctl;
int i;
/* Search for the interface for this router. */
for (i = 0; i < num_nif; i++) {
break;
}
if (i == num_nif)
return;
return;
}
return;
}
nca_ioctl.local_addr = 0;
logperror("ioctl(NCA_SET_IF) failed");
}
/* Clear the fields for this interface. */
}
/*
* Wait for any changes in the routing table. If there are changes to
* IP address or router ethernet address, send down the info to NCA.
*/
static void
daemon_work(void)
{
int n;
int i;
int udp_fd;
int fd;
struct sockaddr_in *sin;
/* Loop forever waiting for any routing changes. */
for (;;) {
if (debug) {
logdebug("Waiting to read routing info...\n");
}
/* Don't die... Reinitialize socket and listen again. */
if (n <= 0) {
if (debug) {
logdebug("Routing socket read error.\n");
}
i = 0;
while (rt_fd < 0) {
if (i++ == 0) {
" routing socket"));
} else if (i > 5) {
" reinitializing routing"
" socket\n"));
exit(1);
}
/* May be a transient error... */
(void) sleep(10);
}
} else {
" socket info.\n"));
continue;
}
if (debug) {
logdebug("Get routing info.\n");
}
case RTM_DELETE:
case RTM_OLDDEL:
/* Only handle default route deletion. */
break;
}
cp += sizeof (struct sockaddr_in);
/* LINTED */
if (debug) {
logdebug("Get default route "
"removal notice: gw %s\n",
}
}
break;
case RTM_ADD:
case RTM_OLDADD:
case RTM_CHANGE:
if (get_if_info(&changed) < 0) {
/* May be a transient error... */
(void) sleep(10);
break;
}
/* Nothing is changed, do nothing. */
if (!changed) {
if (debug) {
logdebug("Get route change "
"notice, but nothing is "
"changed for us!");
}
break;
}
for (i = 0; i < num_nif; i++) {
int ret;
/*
* If name is NULL, it means that
* we have encontered some problems
* when configurating the interface.
* So we remove it from the list.
*/
continue;
" interface %s from the"
" configuration list.\n"),
continue;
}
lifr.lifr_ip_muxid) < 0) {
" interface %s from the"
" configuration list.\n"),
continue;
}
if (debug) {
logdebug("Configuring"
}
nif_list[i].local_addr,
if (ret < 0) {
/*
* This should not be possible
* since if NCA does not
* support the ioctl, the
* active flag should be
* cleared already and this
* function should not have
* been called at all!
*/
logwarn("Daemon dies\n");
exit(1);
}
}
break;
default:
continue;
}
}
}
}
/*
* Make us a daemon.
*/
static void
daemon_init(void)
{
/* Write directly to terminal, instead of syslog. */
exit(1);
}
if (pid != 0)
exit(0);
(void) setsid();
/* Fork again so that we will never get a controlling terminal. */
/* Write directly to terminal, instead of syslog. */
exit(1);
}
if (pid != 0)
exit(0);
(void) chdir("/");
(void) umask(0);
}
int
{
int i, j;
int c;
if (argc == 1) {
" [interface1 interface2 ...]\n"), argv[0]);
return (1);
}
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
switch (c) {
case 'a':
break;
case 'd':
break;
case 'c':
/* Don't run as daemon. */
break;
case 'l':
break;
default:
/* -d and -c are "undocumented" options. */
" [interface1 interface2 ...]\n"), argv[0]);
return (1);
}
}
if (num_nif == 0) {
/* No network interface to proces... */
" [interface1 interface2 ...]\n"), argv[0]);
return (0);
}
return (1);
}
}
/* Get IP address info for all the intefaces. */
if (get_if_ip_addr() < 0) {
if (debug) {
" addresses for interfaces.\n");
}
return (1);
}
if (logging)
/* No need to run as daemon if NCA is not making active connections. */
daemon_init();
if (active) {
/* NCA does not support IPv6... */
logperror("Cannot open routing socket");
return (1);
}
/*
* At boot up time, the default router may not have been
* found. So ignore the error and check later.
*/
if (get_if_info(&changed) < 0) {
if (debug) {
(void) logwarn("Cannot get"
" information from network interface.\n");
}
}
}
/* Do the set up as daemon (if we are) to save time at boot up... */
if (active)
daemon_work();
return (0);
}