ip_rcm.c revision 648495d6a097a39eaebc7e56d25f2463ff3bba65
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This RCM module adds support to the RCM framework for IP managed
* interfaces.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <synch.h>
#include <libintl.h>
#include <errno.h>
#include <fcntl.h>
#include <stropts.h>
#include <strings.h>
#include <libdevinfo.h>
#include <sys/systeminfo.h>
#include <netdb.h>
#include <libinetutil.h>
#include <libdllink.h>
#include <ipmp_mpathd.h>
#include "rcm_module.h"
/*
* Definitions
*/
#ifndef lint
#define _(x) gettext(x)
#else
#define _(x) x
#endif
/* Some generic well-knowns and defaults used in this module */
/* ifconfig(1M) */
/* Physical interface flags mask */
/* Some useful macros */
#ifndef MAX
#define MAX(a, b) (((a) > (b))?(a):(b))
#endif /* MAX */
#ifndef ISSPACE
#endif
#ifndef ISEOL
#endif
#ifndef STREQ
#endif
#ifndef ADDSPACE
#endif
/* Interface Cache state flags */
/* Network Cache lookup options */
/* RCM IPMP Module specific property definitions */
/* in.mpathd(1M) specifics */
/* Stream module operations */
#define MOD_INSERT 0 /* Insert a mid-stream module */
/*
* in.mpathd(1M) message passing formats
*/
typedef struct mpathd_cmd {
/* Message passing values for MI_SETOINDEX */
} mpathd_cmd_t;
/* This is needed since mpathd checks message size for offline */
typedef struct mpathd_unoffline {
typedef struct mpathd_response {
/*
* IP module data types
*/
/* Physical interface representation */
typedef struct ip_pif {
} ip_pif_t;
/* Logical interface representation */
typedef struct ip_lif
{
union {
struct sockaddr_storage storage;
} li_addr;
int li_modcnt; /* # of modules */
char *li_reconfig; /* Reconfiguration string */
} ip_lif_t;
/* Cache element */
typedef struct ip_cache
{
char *ip_resource; /* resource name */
int ip_cachestate; /* cache state flags */
} ip_cache_t;
/*
* Global cache for network interfaces
*/
static ip_cache_t cache_head;
static ip_cache_t cache_tail;
static mutex_t cache_lock;
static int events_registered = 0;
/*
* RCM module interface prototypes
*/
static int ip_register(rcm_handle_t *);
static int ip_unregister(rcm_handle_t *);
char **, char **, nvlist_t *, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, rcm_info_t **);
char **, nvlist_t *, rcm_info_t **);
/* Module private routines */
static void free_cache();
static int update_cache(rcm_handle_t *);
static void cache_remove(ip_cache_t *);
static void free_node(ip_cache_t *);
static void cache_insert(ip_cache_t *);
static char *ip_usage(ip_cache_t *);
static int ip_ipmp_undo_offline(ip_cache_t *);
static int if_unplumb(ip_cache_t *);
static int if_replumb(ip_cache_t *);
static void ip_log_err(ip_cache_t *, char **, char *);
static char *get_link_resource(const char *);
static void clr_cfg_state(ip_pif_t *);
static int mpathd_send_cmd(mpathd_cmd_t *);
static int connect_to_mpathd(int);
static int modop(char *, char *, int, char);
static int get_modlist(char *, ip_lif_t *);
static int ip_domux2fd(int *, int *, int *, struct lifreq *);
rcm_info_t **);
rcm_info_t **);
static char **ip_get_addrlist(ip_cache_t *);
static void ip_free_addrlist(char **);
uint_t, rcm_info_t **);
static int if_configure(datalink_id_t);
static int isgrouped(char *);
static int if_ipmp_config(char *, int, int);
static int if_mpathd_configure(char *, char *, int, int);
static char *get_mpathd_dest(char *, int);
static int if_getcount(int);
static void tokenize(char *, char **, char *, int *);
/* Module-Private data */
static struct rcm_mod_ops ip_ops =
{
NULL,
NULL,
};
/*
* rcm_mod_init() - Update registrations, and return the ops structure.
*/
struct rcm_mod_ops *
rcm_mod_init(void)
{
/* Return the ops vectors */
return (&ip_ops);
}
/*
* rcm_mod_info() - Return a string describing this module.
*/
const char *
rcm_mod_info(void)
{
return ("IP Multipathing module version 1.23");
}
/*
* rcm_mod_fini() - Destroy the network interfaces cache.
*/
int
rcm_mod_fini(void)
{
free_cache();
(void) mutex_destroy(&cache_lock);
return (RCM_SUCCESS);
}
/*
* ip_register() - Make sure the cache is properly sync'ed, and its
* registrations are in order.
*/
static int
{
/* Guard against bad arguments */
if (update_cache(hd) < 0)
return (RCM_FAILURE);
/*
* Need to register interest in all new resources
* getting attached, so we get attach event notifications
*/
if (!events_registered) {
!= RCM_SUCCESS) {
_("IP: failed to register %s\n"),
return (RCM_FAILURE);
} else {
}
}
return (RCM_SUCCESS);
}
/*
* ip_unregister() - Walk the cache, unregistering all the networks.
*/
static int
{
/* Guard against bad arguments */
/* Walk the cache, unregistering everything */
(void) mutex_lock(&cache_lock);
while (probe != &cache_tail) {
!= RCM_SUCCESS) {
/* unregister failed for whatever reason */
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
}
(void) mutex_unlock(&cache_lock);
/*
* Need to unregister interest in all new resources
*/
if (events_registered) {
!= RCM_SUCCESS) {
_("IP: failed to unregister %s\n"),
return (RCM_FAILURE);
} else {
}
}
return (RCM_SUCCESS);
}
/*
* ip_offline() - Offline an interface.
*/
static int
{
int detachable = 0;
int nofailover = 0;
int ipmp = 0;
/* Guard against bad arguments */
/* Lock the cache and lookup the resource */
(void) mutex_lock(&cache_lock);
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/* Establish default detachability criteria */
detachable++;
}
/* Check if the interface is an IPMP grouped interface */
ipmp++;
}
nofailover++;
}
/*
* Even if the interface is not in an IPMP group, it's possible that
* it's still okay to offline it as long as there are higher-level
* failover mechanisms for the addresses it owns (e.g., clustering).
* In this case, ip_offlinelist() will return RCM_SUCCESS, and we
* charge on.
*/
if (!ipmp && !detachable) {
/* Inform consumers of IP addresses being offlined */
RCM_SUCCESS) {
"IP: consumers agree on detach");
} else {
"Device consumers prohibit offline");
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
}
/*
* Cannot remove an IPMP interface if IFF_NOFAILOVER is set.
*/
if (ipmp && nofailover) {
/* Interface is part of an IPMP group, and cannot failover */
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* Check if it's a query */
rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/* Check detachability, save configuration if detachable */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/* standalone detachable device */
if (!ipmp) {
if (if_unplumb(node) < 0) {
"Failed to unplumb the device");
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* This an IPMP interface that can be failed over.
* Request in.mpathd(1M) to failover the physical interface.
*/
/* Failover to "any", let mpathd determine best failover candidate */
/*
* Odds are that in.mpathd(1M) could not offline the device
* because it was the last interface in the group. However,
* it's possible that it's still okay to offline it as long as
* there are higher-level failover mechanisms for the
* addresses it owns (e.g., clustering). In this case,
* ip_offlinelist() will return RCM_SUCCESS, and we charge on.
*
* TODO: change ip_ipmp_offline() to return the actual failure
* from in.mpathd so that we can verify that it did indeed
* fail with IPMP_EMINRED.
*/
if (!detachable) {
/* Inform consumers of IP addresses being offlined */
depend_info) == RCM_SUCCESS) {
"IP: consumers agree on detach");
} else {
"Device consumers prohibit offline");
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
}
}
if (if_unplumb(node) < 0) {
_("IP: Unplumb failed (%s)\n"),
/* Request mpathd to undo the offline */
if (ip_ipmp_undo_offline(node) < 0) {
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* ip_undo_offline() - Undo offline of a previously offlined device.
*/
/*ARGSUSED*/
static int
{
/* Guard against bad arguments */
(void) mutex_lock(&cache_lock);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* Check if no attempt should be made to online the device here */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/* Check if the interface was previously offlined */
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* re-plumb failed */
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* Inform consumers about IP addresses being un-offlined */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* ip_get_info() - Gather usage information for this resource.
*/
/*ARGSUSED*/
int
{
char *infostr;
/* Guard against bad arguments */
(void) mutex_lock(&cache_lock);
if (!node) {
_("IP: get_info(%s) unrecognized resource\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* most likely malloc failure */
_("IP: get_info(%s) malloc failure\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* Set usage property, infostr will be freed by caller */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* ip_suspend() - Nothing to do, always okay
*/
/*ARGSUSED*/
static int
{
/* Guard against bad arguments */
return (RCM_SUCCESS);
}
/*
* ip_resume() - Nothing to do, always okay
*/
/*ARGSUSED*/
static int
{
/* Guard against bad arguments */
return (RCM_SUCCESS);
}
/*
* ip_remove() - remove a resource from cache
*/
/*ARGSUSED*/
static int
{
/* Guard against bad arguments */
(void) mutex_lock(&cache_lock);
if (!node) {
_("IP: remove(%s) unrecognized resource\n"), rsrc);
(void) mutex_unlock(&cache_lock);
return (RCM_FAILURE);
}
/* remove the cached entry for the resource */
(void) mutex_unlock(&cache_lock);
return (RCM_SUCCESS);
}
/*
* ip_notify_event - Project private implementation to receive new resource
* events. It intercepts all new resource events. If the
* new resource is a network resource, pass up a notify
* for it too. The new resource need not be cached, since
* it is done at register again.
*/
/*ARGSUSED*/
static int
{
_("IP: unrecognized event for %s\n"), rsrc);
return (RCM_FAILURE);
}
/* Update cache to reflect latest interfaces */
if (update_cache(hd) < 0) {
return (RCM_FAILURE);
}
_("IP: cannot get linkid\n"));
return (RCM_FAILURE);
}
if (if_configure(linkid) != 0) {
_("IP: Configuration failed (%u)\n"),
linkid);
"Failed configuring one or more IP "
"addresses");
}
/* Notify all IP address consumers */
}
}
"IP: notify_event: device configuration complete\n");
return (RCM_SUCCESS);
}
/*
* ip_usage - Determine the usage of a device. Call with cache_lock held.
* The returned buffer is owned by caller, and the caller
* must free it up when done.
*/
static char *
{
int numifs;
char *buf;
char *linkidstr;
const char *fmt;
char *sep;
char link[MAXLINKNAMELEN];
char addrstr[INET6_ADDRSTRLEN];
char errmsg[DLADM_STRSIZE];
int offline = 0;
/*
* Note that node->ip_resource is in the form of SUNW_datalink/<linkid>
*/
errno = 0;
_("IP: usage(%s) parse linkid failure (%s)\n"),
return (NULL);
}
sizeof (link))) != DLADM_STATUS_OK) {
_("IP: usage(%s) get link name failure(%s)\n"),
return (NULL);
}
/* TRANSLATION_NOTE: separator used between IP addresses */
sep = _(", ");
numifs = 0;
numifs++;
}
}
offline++;
}
fmt = _("%1$s hosts IP addresses: ");
} else if (offline) {
fmt = _("%1$s offlined");
} else {
fmt = _("%1$s plumbed but down");
}
/* space for addresses and separators, plus message */
_("IP: usage(%s) malloc failure(%s)\n"),
return (NULL);
}
return (buf);
}
void *addr;
int af;
/* ignore interfaces not up */
continue;
}
} else {
"IP: unknown addr family %d, assuming AF_INET\n",
af);
}
continue;
}
numifs--;
if (numifs > 0) {
}
}
return (buf);
}
/*
* Cache management routines, all cache management functions should be
* be called with cache_lock held.
*/
/*
* cache_lookup() - Get a cache node for a resource.
* Call with cache lock held.
*
* This ensures that the cache is consistent with the system state and
* returns a pointer to the cache element corresponding to the resource.
*/
static ip_cache_t *
{
/* drop lock since update locks cache again */
(void) mutex_unlock(&cache_lock);
(void) update_cache(hd);
(void) mutex_lock(&cache_lock);
}
while (probe != &cache_tail) {
if (probe->ip_resource &&
"IP: cache lookup success(%s)\n", rsrc);
return (probe);
}
}
return (NULL);
}
/*
* free_node - Free a node from the cache
* Call with cache_lock held.
*/
static void
{
if (node) {
if (node->ip_resource) {
}
/* free the pif */
if (pif) {
/* free logical interfaces */
while (lif) {
}
}
}
}
/*
* cache_insert - Insert a resource node in cache
* Call with the cache_lock held.
*/
static void
{
node->ip_resource);
/* insert at the head for best performance */
}
/*
* cache_remove() - Remove a resource node from cache.
* Call with the cache_lock held.
*/
static void
{
node->ip_resource);
}
/*
* update_pif() - Update physical interface properties
* Call with cache_lock held
*/
/*ARGSUSED*/
static int
{
char *rsrc;
struct sockaddr_storage ifaddr;
int lif_listed = 0;
return (-1);
}
if (ifspec.ifsp_lunvalid)
/* Get the interface flags */
_("IP: SIOCGLIFFLAGS(%s): %s\n"),
return (-1);
}
/*
* Ignore interfaces that are always incapable of DR:
* - IFF_VIRTUAL: e.g., loopback and vni
* - IFF_POINTOPOINT: e.g., sppp and ip.tun
* - !IFF_MULTICAST: e.g., ip.6to4tun
*
* Note: The !IFF_MULTICAST check can be removed once iptun is
* implemented as a datalink.
*/
if (!(ifflags & IFF_MULTICAST) ||
return (0);
}
/* Get the interface group name for this interface */
_("IP: SIOCGLIFGROUPNAME(%s): %s\n"),
return (-1);
}
/* copy the group name */
sizeof (pif.pi_grpname));
/* Get the interface address for this interface */
_("IP: SIOCGLIFADDR(%s): %s\n"),
return (-1);
}
_("IP: get_link_resource(%s) failed\n"),
return (-1);
}
} else {
/* malloc errors are bad */
return (-1);
}
}
/* Check if lifs need to be updated */
"IP: refreshing lifs for %s, ifnum=%d\n",
/* refresh lif properties */
lif_listed++;
break;
}
}
}
/* we created it, so clean it up */
}
return (-1);
}
/* Save interface name */
}
/* save pif properties */
sizeof (pif.pi_grpname));
/* add lif, if this is a lif and it is not in cache */
if (!lif_listed) {
return (-1);
}
/* save lif properties */
/* insert us at the head of the lif list */
}
}
probe->ip_resource);
return (0);
}
/*
* update_ipifs() - Determine all network interfaces in the system
* Call with cache_lock held
*/
static int
{
int sock;
char *buf;
int i;
_("IP: failure opening %s socket: %s\n"),
return (-1);
}
lifn.lifn_flags = 0;
_("IP: SIOCLGIFNUM failed: %s\n"),
return (-1);
}
return (-1);
}
lifc.lifc_flags = 0;
_("IP: SIOCGLIFCONF failed: %s\n"),
return (-1);
}
/* now we need to search for active interfaces */
for (i = 0; i < lifn.lifn_count; i++) {
lifrp++;
}
return (0);
}
/*
* update_cache() - Update cache with latest interface info
*/
static int
{
int rv;
int i;
(void) mutex_lock(&cache_lock);
/* first we walk the entire cache, marking each entry stale */
while (probe != &cache_tail) {
}
}
}
(void) mutex_unlock(&cache_lock);
return (-1);
}
(void) mutex_unlock(&cache_lock);
return (-1);
}
/* unregister devices that are not offlined and still in cache */
while (probe != &cache_tail) {
/* clear stale lifs */
for (i = 0; i < IP_MAX_MODS; i++) {
}
} else {
}
}
}
0);
probe->ip_resource);
continue;
}
continue;
}
if (rv != RCM_SUCCESS) {
_("IP: failed to register %s\n"),
probe->ip_resource);
(void) mutex_unlock(&cache_lock);
return (-1);
} else {
probe->ip_resource);
}
}
(void) mutex_unlock(&cache_lock);
return (0);
}
/*
* free_cache() - Empty the cache
*/
static void
{
(void) mutex_lock(&cache_lock);
while (probe != &cache_tail) {
}
(void) mutex_unlock(&cache_lock);
}
/*
* ip_log_err() - RCM error log wrapper
*/
static void
{
int len;
const char *errfmt;
char *error;
}
errfmt = _("IP: %s");
}
} else {
errfmt = _("IP: %s(%s)");
}
}
}
/*
* if_cfginfo() - Save off the config info for all interfaces
*/
static int
{
int i;
char buf[MAX_RECONFIG_SIZE];
/* Make a list of modules pushed and save */
_("IP: get modlist error (%s) %s\n"),
(void) clr_cfg_state(pif);
return (-1);
}
if (!force) {
/* Look if unknown modules have been inserted */
lif->li_modules[i],
i, MOD_CHECK) == -1) {
_("IP: module %s@%d\n"),
lif->li_modules[i], i);
(void) clr_cfg_state(pif);
return (-1);
}
}
}
/* Last module is the device driver, so ignore that */
"IP: modremove Pos = %d, Module = %s \n",
i, lif->li_modules[i]);
i, MOD_REMOVE) == -1) {
lif->li_modules[i],
i, MOD_INSERT) == -1) {
/* Gross error */
_("IP: if_cfginfo"
"(%s) %s\n"),
return (-1);
}
i++;
}
_("IP: if_cfginfo(%s): modremove "
lif->li_modules[i],
return (-1);
}
}
}
/* Save reconfiguration information */
"%s %s:%d configinfo\n", USR_SBIN_IFCONFIG,
"%s %s:%d inet6 configinfo\n", USR_SBIN_IFCONFIG,
}
/* open a pipe to retrieve reconfiguration info */
_("IP: ifconfig configinfo error (%s:%d) %s\n"),
(void) clr_cfg_state(pif);
return (-1);
}
_("IP: ifconfig configinfo error (%s:%d) %s\n"),
(void) clr_cfg_state(pif);
return (-1);
}
_("IP: malloc error (%s) %s\n"),
(void) clr_cfg_state(pif);
return (-1);
}
"IP: if_cfginfo: reconfig string(%s:%d) = %s\n",
}
return (0);
}
/*
* if_unplumb() - Unplumb the interface
* Save off the modlist, ifconfig options and unplumb.
* Fail, if an unknown module lives between IP and driver and
* force is not set
* Call with cache_lock held
*/
static int
{
ipv4++;
ipv6++;
} else {
/* Unlikely case */
"IP: Unplumb ignored (%s:%d)\n",
continue;
}
}
/* Unplumb the physical interface */
if (ipv4) {
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Cannot unplumb (%s) %s\n"),
return (-1);
}
}
if (ipv6) {
"IP: if_unplumb: ifconfig %s inet6 unplumb\n",
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Cannot unplumb (%s) %s\n"),
return (-1);
}
}
node->ip_resource);
return (0);
}
/*
* if_replumb() - Undo previous unplumb i.e. plumb back the physical interface
* instances and the logical interfaces in order, restoring
* all ifconfig options
* Call with cache_lock held
*/
static int
{
int i;
/*
* Be extra careful about bringing up the interfaces in the
* correct order:
* - First plumb in the physical interface instances
* - modinsert the necessary modules@pos
* - Next, add the logical interfaces being careful about
* the order, (follow the cached interface number li_ifnum order)
*/
/*
* Make a first pass to plumb in physical interfaces and get a count
* of the max logical interfaces
*/
}
}
} else {
/* Unlikely case */
"IP: Re-plumb ignored (%s:%d)\n",
continue;
}
"%s %s\n", USR_SBIN_IFCONFIG,
lif->li_reconfig);
"%s %s inet plumb group %s\n",
"%s %s inet6 plumb group %s\n",
}
"IP: if_replumb: %s\n", syscmd);
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Cannot plumb (%s) %s\n"),
return (-1);
}
/* modinsert modules in order, ignore driver(last) */
"IP: modinsert: Pos = %d Mod = %s\n",
i, lif->li_modules[i]);
MOD_INSERT) == -1) {
_("IP: modinsert error(%s)\n"),
return (-1);
}
}
}
}
/* Now, add all the logical interfaces in the correct order */
/* reset lif through every iteration */
/* Plumb in the logical interface */
"%s %s\n", USR_SBIN_IFCONFIG,
lif->li_reconfig);
"IP: if_replumb: %s\n", syscmd);
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Cannot addif (%s:%d) "
"%s\n"),
return (-1);
}
}
}
}
node->ip_resource);
return (0);
}
/*
* clr_cfg_state() - Cleanup after errors in unplumb
*/
static void
{
int i;
for (i = 0; i < IP_MAX_MODS; i++) {
}
}
}
/*
* ip_ipmp_offline() - Failover from if_from to if_to using a
* minimum redudancy of min_red. This uses IPMPs
* "offline" mechanism to achieve the failover.
*/
static int
{
return (-1);
}
sizeof (mpdcmd.cmd_movetoif));
} else {
}
if (mpathd_send_cmd(&mpdcmd) < 0) {
_("IP: mpathd offline error: %s\n"),
return (-1);
}
return (0);
}
/*
* ip_ipmp_undo_offline() - Undo prior offline of the interface.
* This uses IPMPs "undo offline" feature.
*/
static int
{
if (mpathd_send_cmd(&mpdcmd) < 0) {
_("IP: mpathd error: %s\n"),
return (-1);
}
return (0);
}
/*
* get_link_resource() - Convert a link name (e.g., net0, hme1000) into a
* dynamically allocated string containing the associated link resource
* name ("SUNW_datalink/<linkid>").
*/
static char *
get_link_resource(const char *link)
{
char errmsg[DLADM_STRSIZE];
char *resource;
!= DLADM_STATUS_OK) {
goto fail;
}
if (!(flags & DLADM_OPT_ACTIVE)) {
goto fail;
}
return (NULL);
}
return (resource);
fail:
_("IP: get_link_resource for %s error(%s)\n"),
return (NULL);
}
/*
* if_get_flags() - Return the cached physical interface flags
* Call with cache_lock held
*/
static uint64_t
{
}
}
return (0);
}
/*
* mpathd_send_cmd() - Sends the command to in.mpathd.
*/
static int
{
struct mpathd_response mpr;
int i;
int s;
for (i = 0; i < MPATHD_MAX_RETRIES; i++) {
s = connect_to_mpathd(AF_INET);
if (s == -1) {
s = connect_to_mpathd(AF_INET6);
if (s == -1) {
_("IP: Cannot talk to mpathd\n"));
return (-1);
}
}
switch (mpd->cmd_command) {
case MI_OFFLINE :
sizeof (mpathd_cmd_t)) {
_("IP: mpathd write: %s\n"),
(void) close(s);
return (-1);
}
break;
case MI_SETOINDEX :
sizeof (mpathd_cmd_t)) {
_("IP: mpathd write: %s\n"),
(void) close(s);
return (-1);
}
break;
case MI_UNDO_OFFLINE:
/* mpathd checks for exact size of the message */
sizeof (mpathd_unoffline_t)) {
_("IP: mpathd write: %s\n"),
(void) close(s);
return (-1);
}
break;
default :
_("IP: unsupported mpathd command\n"));
(void) close(s);
return (-1);
}
/* Read the result from mpathd */
sizeof (struct mpathd_response)) {
(void) close(s);
return (-1);
}
(void) close(s);
if (mpr.resp_mpathd_err == 0) {
"IP: mpathd_send_cmd success\n");
return (0); /* Successful */
}
(void) sleep(1);
"IP: mpathd retrying\n");
continue; /* Retry */
}
_("IP: mpathd_send_cmd error: %s\n"),
"Minimum redundancy not met\n"));
} else {
_("IP: mpathd_send_cmd error\n"));
}
/* retry */
}
_("IP: mpathd_send_cmd failed %d retries\n"), MPATHD_MAX_RETRIES);
return (-1);
}
/*
* Returns -1 on failure. Returns the socket file descriptor on
* success.
*/
static int
connect_to_mpathd(int family)
{
int s;
struct sockaddr_storage ss;
int addrlen;
int ret;
int on;
if (s < 0) {
return (-1);
}
/*
* Need to bind to a privelged port. For non-root, this
* will fail. in.mpathd verifies that only commands coming
* from priveleged ports succeed so that the ordinary user
* can't issue offline commands.
*/
on = 1;
sizeof (on)) < 0) {
_("IP: mpathd setsockopt: TCP_ANONPRIVBIND: %s\n"),
return (-1);
}
switch (family) {
case AF_INET:
addrlen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
addrlen = sizeof (struct sockaddr_in6);
break;
}
if (ret != 0) {
return (-1);
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
if (ret != 0) {
if (errno == ECONNREFUSED) {
/* in.mpathd is not running, start it */
_("IP: mpathd exec: %s\n"),
return (-1);
}
}
if (ret != 0) {
return (-1);
}
}
on = 0;
sizeof (on)) < 0) {
_("IP: mpathd setsockopt TCP_ANONPRIVBIND: %s\n"),
return (-1);
}
return (s);
}
/*
*/
static int
{
/* Nothing to do with "ip", "arp" */
return (0);
}
/*
* No known good modules (yet) apart from ip and arp
* which are handled above
*/
return (-1);
}
if (op == MOD_REMOVE) {
pos);
} else if (op == MOD_INSERT) {
pos);
} else {
_("IP: modop(%s): unknown operation\n"), name);
return (-1);
}
return (-1);
}
return (0);
}
/*
* get_modlist() - return a list of pushed mid-stream modules
* Required memory is malloced to construct the list,
* Caller must free this memory list
* Call with cache_lock held
*/
static int
{
int mux_fd;
int muxid_fd;
int fd;
int i;
int num_mods;
return (-1);
}
_("IP: get_modlist(%s): I_LIST(%s) \n"),
return (-1);
}
return (-1);
}
_("IP: get_modlist(%s): I_LIST error: %s\n"),
return (-1);
}
lif->li_modules[i] =
_("IP: get_modlist(%s): %s\n"),
return (-1);
}
}
}
/*
* ip_domux2fd() - Helper function for mod*() functions
* Stolen from ifconfig.c
*/
static int
{
int muxid_fd;
char *udp_dev_name;
} else {
}
return (-1);
}
return (-1);
}
_("IP: ip_domux2fd: SIOCGLIFMUXID(%s): %s\n"),
return (-1);
}
"IP: ip_domux2fd: ARP_muxid %d IP_muxid %d\n",
_("IP: ip_domux2fd: _I_MUXID2FD(%s): %s\n"),
return (-1);
}
_("IP: ip_domux2fd: I_PUNLINK(%s): %s\n"),
return (-1);
}
/* Note: mux_fd and muxid_fd are closed in ip_plink below */
return (0);
}
/*
* ip_plink() - Helper function for mod*() functions.
* Stolen from ifconfig.c
*/
static int
{
int mux_id;
return (-1);
}
_("IP: ip_plink SIOCSLIFMUXID(%s): %s\n"),
return (-1);
}
return (0);
}
/*
* ip_onlinelist()
*
* Notify online to IP address consumers.
*/
static int
{
char **addrlist;
int ret = RCM_SUCCESS;
return (ret);
}
return (ret);
}
/*
* ip_offlinelist()
*
* Offline IP address consumers.
*/
static int
{
char **addrlist;
int ret = RCM_SUCCESS;
return (RCM_SUCCESS);
}
!= RCM_SUCCESS) {
if (ret == RCM_FAILURE)
ret = RCM_FAILURE;
}
return (ret);
}
/*
* ip_get_addrlist() - Compile list of IP addresses hosted on this NIC (node)
* This routine malloc() required memeory for the list
* Returns list on success, NULL if failed
* Call with cache_lock held.
*/
static char **
{
int numifs;
char addrstr[INET6_ADDRSTRLEN];
void *addr;
int af;
int i;
node->ip_resource);
numifs = 0;
numifs++;
}
/*
* Allocate space for resource names list; add 1 and use calloc()
* so that the list is NULL-terminated.
*/
_("IP: ip_get_addrlist(%s) malloc failure(%s)\n"),
return (NULL);
}
} else {
"IP: unknown addr family %d, assuming AF_INET\n",
af);
}
return (NULL);
}
== NULL) {
_("IP: ip_get_addrlist(%s) malloc failure(%s)\n"),
return (NULL);
}
}
node->ip_resource);
return (addrlist);
}
static void
ip_free_addrlist(char **addrlist)
{
int i;
return;
}
/*
* ip_consumer_notify() - Notify consumers of IP addresses coming back online.
*/
static void
{
char cached_name[RCM_LINK_RESOURCE_MAX];
/* Check for the interface in the cache */
(void) mutex_lock(&cache_lock);
linkid);
(void) mutex_unlock(&cache_lock);
return;
}
/*
* Inform anonymous consumers about IP addresses being
* onlined
*/
(void) mutex_unlock(&cache_lock);
return;
}
/*
* if_configure() - Configure a physical interface after attach
*/
static int
{
char ifinst[MAXLINKNAMELEN];
char cfgfile[MAXPATHLEN];
char cached_name[RCM_LINK_RESOURCE_MAX];
int af = 0;
int ipmp = 0;
/* Check for the interface in the cache */
/* Check if the interface is new or was previously offlined */
(void) mutex_lock(&cache_lock);
_("IP: Skipping configured interface(%u)\n"), linkid);
(void) mutex_unlock(&cache_lock);
return (0);
}
(void) mutex_unlock(&cache_lock);
sizeof (ifinst)) != DLADM_STATUS_OK) {
_("IP: get %u link name failed\n"), linkid);
return (-1);
}
/* Scan IPv4 configuration first */
af |= CONFIG_AF_INET;
ipmp++;
}
}
/* Scan IPv6 configuration details */
af |= CONFIG_AF_INET6;
ipmp++;
}
}
if (af & CONFIG_AF_INET) {
_("IP: IPv4 Post-attach failed (%s)\n"), ifinst);
return (-1);
}
}
if (af & CONFIG_AF_INET6) {
_("IP: IPv6 Post-attach failed(%s)\n"), ifinst);
return (-1);
}
}
return (0);
}
/*
* isgrouped() - Scans the given config file to see if this is a grouped
* interface
* Returns non-zero if true; 0 if false
*/
static int
{
int ntok;
int group = 0;
return (0);
_("IP: No config file(%s)\n"), cfgfile);
return (0);
}
/*
* We also ignore single-byte config files because the file should
* always be newline-terminated, so we know there's nothing of
* interest. Further, a single-byte file would cause the fgets() loop
* below to spin forever.
*/
_("IP: Empty config file(%s)\n"), cfgfile);
return (0);
}
_("IP: Cannot open configuration file(%s): %s\n"), cfgfile,
return (0);
}
_("IP: calloc failure(%s): %s\n"), cfgfile,
return (0);
}
if (*buf == '\0')
continue;
while (ntok) {
group++;
}
}
ntok--;
}
}
if (group <= 0) {
cfgfile);
return (0);
} else {
cfgfile);
return (1);
}
}
/*
* if_ipmp_config() - Configure an interface instance as specified by the
* address family af and if it is grouped (ipmp).
*/
static int
{
char *buf;
int nofailover = 0;
int newattach = 0;
int cmdvalid = 0;
int ntok;
int n;
int stdif = 0;
return (0);
if (af & CONFIG_AF_INET) {
ifinst);
} else if (af & CONFIG_AF_INET6) {
ifinst);
} else {
return (0); /* nothing to do */
}
grpcmd[0] = '\0';
"IP: No config file(%s)\n", ifinst);
return (0);
}
/* Config file exists, plumb in the physical interface */
if (af & CONFIG_AF_INET6) {
if (if_getcount(AF_INET6) == 0) {
/*
* Configure software loopback driver if this is the
* first IPv6 interface plumbed
*/
newattach++;
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Cannot plumb (%s) %s\n"),
return (-1);
}
}
} else {
if (if_getcount(AF_INET) == 0) {
newattach++;
}
}
if (rcm_exec_cmd(syscmd) != 0) {
return (-1);
}
/* Check if config file is empty, if so, nothing else to do */
"IP: Zero size config file(%s)\n", ifinst);
return (0);
}
return (-1);
}
return (-1);
}
/* a single line with one token implies a classical if */
if (ntok == 1) {
stdif++;
}
}
return (-1);
}
/*
* Process the config command
* This loop also handles multiple logical interfaces that may
* be configured on a single line
*/
nofailover = 0;
cmdvalid = 0;
if (*buf == '\0')
continue;
if (ntok <= 0)
continue;
/* Reset the config command */
/* No parsing if this is first interface of its kind */
if (newattach) {
/* Classic if */
}
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: Error: %s (%s): %s\n"),
}
continue;
}
/* Parse the tokens to determine nature of the interface */
for (n = 0; n < ntok; n++) {
/* Handle pathological failover cases */
nofailover++;
nofailover--;
/* group attribute requires special processing */
"%s %s %s %s %s", USR_SBIN_IFCONFIG,
n++; /* skip next token */
continue;
}
}
/* Execute buffered command ? */
(n == (ntok -1))) {
/* config command complete ? */
if (n == (ntok -1)) {
cmdvalid++;
}
if (!cmdvalid) {
cmdvalid++;
continue;
}
/* Classic if ? */
}
if (nofailover > 0) {
"IP: Interim exec: %s\n", syscmd);
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: %s fail(%s): %s\n"),
}
} else {
/* Have mpathd configure the address */
_("IP: %s fail(%s): %s\n"),
}
}
/* Reset config command */
fstr);
nofailover = 0;
cmdvalid = 0;
}
/*
* Note: No explicit command validation is required
* since ifconfig to does it for us
*/
cmdvalid++;
}
}
/*
* The group name needs to be set after all the test/nofailover
* addresses have been configured. Otherwise, if IPMP detects that the
* interface is failed, the addresses will be moved to a working
* interface before the '-failover' flag can be set.
*/
if (grpcmd[0] != '\0') {
if (rcm_exec_cmd(grpcmd) != 0) {
}
}
return (0);
}
/*
* if_mpathd_configure() - Determine configuration disposition of the interface
*/
static int
{
int ntok;
char *addr;
char *from_lifname;
int n;
if (ntok <= 0)
return (0);
for (n = 0; n < ntok; n++) {
return (-1);
} else
break;
}
}
/* Check std. commands or no failed over address */
"IP: No failed-over host, exec %s\n", syscmd);
if (rcm_exec_cmd(syscmd) != 0) {
_("IP: %s failed(%s): %s\n"),
return (-1);
}
return (0);
}
/* Check for non-IPMP failover scenarios */
/* Address already hosted on another NIC, return */
"IP: Non-IPMP failed-over host(%s): %s\n",
return (0);
}
/*
* Valid failed-over host; have mpathd set the original index
*/
if (af & CONFIG_AF_INET6) {
} else {
}
/* Send command to in.mpathd(1M) */
"IP: Attempting setoindex from (%s) to (%s) ....\n",
if (mpathd_send_cmd(&mpdcmd) < 0) {
"IP: mpathd set original index unsuccessful: %s\n",
return (-1);
}
"IP: setoindex success (%s) to (%s)\n",
return (0);
}
/*
* get_mpathd_dest() - Return current destination for lif; caller is
* responsible to free memory allocated for address
*/
static char *
{
int sock;
char *buf;
int i;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
char addrstr[INET6_ADDRSTRLEN];
char ifaddr[INET6_ADDRSTRLEN];
int err;
return (NULL);
}
if (family & CONFIG_AF_INET6) {
} else {
}
_("IP: failure opening %s socket: %s\n"),
return (NULL);
}
lifn.lifn_flags = 0;
_("IP: SIOCLGIFNUM failed: %s\n"),
return (NULL);
}
return (NULL);
}
lifc.lifc_flags = 0;
_("IP: SIOCGLIFCONF failed: %s\n"),
return (NULL);
}
/* Filter out prefix address from netmask */
}
/* Check for aliases */
if (hp) {
/* Restore original address and use it */
*prefix = '\0';
}
}
}
/* now search the interfaces */
/* Get the interface address for this interface */
return (NULL);
}
continue;
}
} else {
continue;
}
}
/* Allocate memory to hold interface name */
return (NULL);
}
/* Copy the interface name */
/*
* (void) memcpy(ifname, lifrp->lifr_name,
* sizeof (ifname));
* ifname[sizeof (ifname) - 1] = '\0';
*/
break;
}
}
addr);
else
return (ifname);
}
static int
if_getcount(int af)
{
int sock;
_("IP: failure opening %s socket: %s\n"),
return (-1);
}
lifn.lifn_flags = 0;
_("IP: SIOCLGIFNUM failed: %s\n"),
return (-1);
}
return (lifn.lifn_count);
}
/*
* tokenize() - turn a command line into tokens; caller is responsible to
* provide enough memory to hold all tokens
*/
static void
{
char *cp;
char *sp;
cp++;
break;
do {
*sp++ = '\0';
}
}