/*
* 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
*/
/*
*/
/*
* 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 <sys/sysmacros.h>
#include <libinetutil.h>
#include <libdllink.h>
#include <libgen.h>
#include <ipmp_admin.h>
#include <libipadm.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 */
/* Some useful macros */
/* Interface Cache state flags */
/* Network Cache lookup options */
/* RCM IPMP Module specific property definitions */
/* Stream module operations */
/*
* IP module data types
*/
/* Physical interface representation */
typedef struct ip_pif {
} ip_pif_t;
/* Logical interface representation */
typedef struct ip_lif
{
union {
} li_addr;
} ip_lif_t;
/* Cache element */
typedef struct ip_cache
{
} ip_cache_t;
/*
* Global cache for network interfaces
*/
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_offline(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 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_hostname(datalink_id_t);
static int if_configure_ipadm(datalink_id_t);
/* Module-Private data */
{
NULL,
NULL,
};
/*
* rcm_mod_init() - Update registrations, and return the ops structure.
*/
struct rcm_mod_ops *
rcm_mod_init(void)
{
"IP: mod_init failed: cannot get datalink handle: %s\n",
return (NULL);
}
"IP: mod_init failed: cannot get IP handle: %s\n",
dld_handle = NULL;
return (NULL);
}
/* 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 retval;
/* 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 = B_TRUE;
/* Check if the interface is under IPMP */
/*
* Even if the interface is not under IPMP, 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);
}
}
/* 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 is an IPMP interface that can be offlined.
* Request in.mpathd(1M) to offline the physical interface.
*/
/*
* 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.
*/
/* 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 in.mpathd to undo the offline */
(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);
}
/*
* interface in the same way that they would be handled
*/
if (if_configure_hostname(linkid) != 0) {
_("IP: Configuration failed (%u)\n"),
linkid);
"Failed configuring one or more IP "
"addresses");
}
/*
* Query libipadm for persistent configuration info
* and resurrect that persistent configuration.
*/
if (if_configure_ipadm(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 *
{
const char *msg;
/*
* 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);
}
_("IP: usage(%s) get link name failure(%s)\n"),
return (NULL);
}
/* TRANSLATION_NOTE: separator used between IP addresses */
sep = _(", ");
numup = 0;
numup++;
if (offline) {
msg = _("offlined");
} else if (numup == 0) {
msg = _("plumbed but down");
} else {
if (ipmp) {
msg = _("providing connectivity for IPMP group ");
bufsz += LIFGRNAMSIZ;
} else {
msg = _("hosts IP addresses: ");
}
}
_("IP: usage(%s) malloc failure(%s)\n"),
return (NULL);
}
if (ipmp) {
} else {
continue;
continue;
if (--numup > 0)
}
}
}
return (buf);
}
static boolean_t
{
void *addr;
} else {
"IP: unknown addr family %d, assuming AF_INET\n", af);
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* 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
*/
int
{
char *rsrc;
int lif_listed = 0;
return (-1);
}
if (ifspec.ifsp_lunvalid)
/* Get the interface flags */
/*
* 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
* - IFF_IPMP: IPMP meta-interfaces
*
* 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 */
/* Get the interface address for this interface */
_("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 the group name */
/* 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;
_("IP: failure opening %s socket: %s\n"),
return (-1);
}
if (status != IPADM_SUCCESS) {
return (-1);
}
}
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 size;
const char *errfmt;
}
errfmt = _("IP: %s");
} else {
errfmt = _("IP: %s(%s)");
}
}
/*
* if_cfginfo() - Save off the config info for all interfaces
*/
static int
{
int i;
/* Make a list of modules pushed and save */
_("IP: get modlist error (%s) %s\n"),
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);
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", SBIN_IFCONFIG,
"%s %s:%d inet6 configinfo\n", SBIN_IFCONFIG,
}
/* open a pipe to retrieve reconfiguration info */
_("IP: ifconfig configinfo error (%s:%d) %s\n"),
return (-1);
}
_("IP: ifconfig configinfo error (%s:%d) %s\n"),
return (-1);
}
_("IP: malloc error (%s) %s\n"),
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
{
} else {
/* Unlikely case */
"IP: Unplumb ignored (%s:%d)\n",
}
}
return (-1);
}
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;
const char *fstr;
int max_lifnum = 0;
/*
* 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
*/
fstr = "inet";
fstr = "inet6";
} else {
/* Unlikely case */
"IP: Re-plumb ignored (%s:%d)\n",
continue;
}
/* ignore logical interface instances */
continue;
} else {
}
if (!success) {
return (-1);
}
/*
* Restart DHCP if necessary.
*/
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 */
for (i = 1; i <= max_lifnum; i++) {
/* reset lif through every iteration */
/*
* Process entries in order. If the interface is
* using IPMP, only process test addresses.
*/
continue;
_("IP: Cannot addif (%s) %s\n"), lifname,
return (-1);
}
/*
* Restart DHCP if necessary.
*/
_("IP: Cannot start DHCP (%s) %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++) {
}
}
}
/*
* Attempt to offline ip_cache_t `node'; returns an IPMP error code.
*/
static int
{
int retval;
_("IP: cannot create ipmp handle: %s\n"),
return (retval);
}
if (retval != IPMP_SUCCESS) {
} else {
}
return (retval);
}
/*
* Attempt to undo the offline ip_cache_t `node'; returns an IPMP error code.
*/
static int
{
int retval;
_("IP: cannot create ipmp handle: %s\n"),
return (retval);
}
if (retval != IPMP_SUCCESS) {
_("IP: ipmp_undo_offline error: %s\n"),
} else {
}
return (retval);
}
/*
* 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 *
{
char *resource;
if (status != 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);
}
/*
*/
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) {
} else if (op == MOD_INSERT) {
} 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"),
goto fail;
}
goto fail;
}
_("IP: get_modlist(%s): I_LIST error: %s\n"),
goto fail;
}
_("IP: get_modlist(%s): %s\n"),
while (i > 0)
goto fail;
}
}
fail:
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.
*/
/*ARGSUSED*/
static int
{
char **addrlist;
return (ret);
}
return (ret);
}
/*
* ip_offlinelist()
*
* Offline IP address consumers.
*/
/*ARGSUSED*/
static int
{
char **addrlist;
return (RCM_SUCCESS);
}
!= RCM_SUCCESS) {
if (ret == RCM_FAILURE)
ret = RCM_FAILURE;
}
return (ret);
}
/*
* ip_get_addrlist() - Get the list of IP addresses on this interface (node);
* This routine malloc()s required memory for the list.
* Returns the list on success, NULL on failure.
* Call with cache_lock held.
*/
static char **
{
int i, numifs;
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);
}
return (NULL);
}
_("IP: ip_get_addrlist(%s) malloc failure(%s)\n"),
return (NULL);
}
}
node->ip_resource);
return (addrlist);
}
static void
{
int i;
return;
}
/*
* ip_consumer_notify() - Notify consumers of IP addresses coming back online.
*/
static void
{
/* 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);
}
/*
* Gets the interface name for the given linkid. Returns -1 if there is
* any error. It fills in the interface name in `ifinst' if the interface
* is not already configured. Otherwise, it puts a null string in `ifinst'.
*/
static int
{
/* Check for the interface in the cache */
/* Check if the interface is new or was not previously offlined */
(void) mutex_lock(&cache_lock);
_("IP: Skipping configured interface(%u)\n"), linkid);
(void) mutex_unlock(&cache_lock);
*ifinst = '\0';
return (0);
}
(void) mutex_unlock(&cache_lock);
len) != DLADM_STATUS_OK) {
_("IP: get %u link name failed\n"), linkid);
return (-1);
}
return (0);
}
/*
* if_configure_hostname() - Configure a physical interface after attach
*/
static int
{
linkid);
return (-1);
/* Check if the interface is already configured. */
if (ifinst[0] == '\0')
return (0);
/*
* Scan the IPv4 and IPv6 hostname files to see if (a) they exist
* and (b) if either one places the interface into an IPMP group.
*/
}
}
/*
* Configure the interface according to its hostname files.
*/
_("IP: IPv4 Post-attach failed (%s)\n"), ifinst);
goto fail;
}
_("IP: IPv6 Post-attach failed (%s)\n"), ifinst);
goto fail;
}
ifinst);
return (0);
fail:
return (-1);
}
/*
* if_configure_ipadm() - Configure a physical interface after attach
* Queries libipadm for persistent configuration information and then
* resurrects that persistent configuration.
*/
static int
{
linkid);
return (-1);
/* Check if the interface is already configured. */
if (ifinst[0] == '\0')
return (0);
if (status == IPADM_ENXIO)
goto done;
if (status != IPADM_SUCCESS) {
_("IP: IPv4 Post-attach failed (%s) Error %s\n"),
goto fail;
}
sizeof (ifinst)) == 0) {
break;
}
}
if (!found) {
return (0);
}
_("IP: IPv4 Post-attach (%s) found both "
"Ignoring ipadm config\n"), ifinst);
return (0);
}
if (status != IPADM_SUCCESS) {
_("IP: Post-attach failed (%s) Error %s\n"),
goto fail;
}
}
done:
ifinst);
return (0);
fail:
return (-1);
}
/*
* isgrouped() - Scans the given config file to see if this interface is
* using IPMP. Returns B_TRUE or B_FALSE.
*/
static boolean_t
{
_("IP: No config file(%s)\n"), cfgfile);
return (B_FALSE);
}
/*
* 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 (B_FALSE);
}
_("IP: Cannot open configuration file(%s): %s\n"), cfgfile,
return (B_FALSE);
}
_("IP: malloc failure(%s): %s\n"), cfgfile,
goto out;
}
*nlp = '\0';
goto out;
}
}
}
out:
grouped);
return (grouped);
}
/*
* if_config_inst() - Configure an interface instance as specified by the
* address family af and if it is grouped (ipmp).
*/
static int
{
_("IP: Cannot fstat file(%s)\n"), ifinst);
goto fail;
}
switch (af) {
case AF_INET:
fstr = "inet";
break;
case AF_INET6:
fstr = "inet6";
break;
default:
assert(0);
}
/*
* The hostname file exists; plumb the physical interface.
*/
goto fail;
/* Skip static configuration if the hostname file is empty */
_("IP: Zero size hostname file(%s)\n"), ifinst);
goto configured;
}
_("IP: Cannot rewind hostname file(%s): %s\n"), ifinst,
goto fail;
}
/*
* Allocate the worst-case single-line buffer sizes. A bit skanky,
* but since hostname files are small, this should suffice.
*/
goto fail;
}
goto fail;
}
/*
* For IPv4, determine whether the hostname file consists of a single
* line. We need to handle these specially since they should
* automatically be suffixed with "netmask + broadcast + up".
*/
}
_("IP: Cannot rewind hostname file(%s): %s\n"), ifinst,
goto fail;
}
/*
* Loop through the file one line at a time and feed it to ifconfig.
* weed out all of the data addresses, since those are already on the
* IPMP meta-interface.
*/
continue;
if (!ipmp) {
continue;
}
_("IP: cannot configure %s: popen \"%s\" "
goto fail;
}
}
_("IP: cannot configure %s: pclose \"%s\" "
goto fail;
}
}
/*
* Bring up the interface (it may already be up)
*
* Technically, since the boot scripts only unconditionally bring up
* IPv6 interfaces, we should only unconditionally bring up IPv6 here.
* However, if we don't bring up IPv4, and a legacy IPMP configuration
* without test addresses is being used, we will never bring the
* interface up even though we would've at boot. One fix is to check
* if the IPv4 hostname file contains data addresses that we would've
* brought up, but there's no simple way to do that. Given that it's
* rare to have persistent IP configuration for an interface that
* leaves it down, we cheap out and always bring it up for IPMP.
*/
goto fail;
/*
* For IPv4, if a DHCP configuration file exists, have DHCP configure
* the interface. As with the boot scripts, this is done after the
* hostname files are processed so that configuration in those files
* (such as IPMP group names) will be applied first.
*/
char *dhcpbuf;
goto out;
goto fail;
}
/*
* The copylist() API converts \n's to \0's, but we want them
* to be spaces.
*/
if (dhcpsize > 0) {
for (i = 0; i < dhcpsize; i++)
if (dhcpbuf[i] == '\0')
dhcpbuf[i] = ' ';
}
}
out:
return (0);
fail:
return (-1);
}
/*
* ntok() - count the number of tokens in the provided buffer.
*/
static uint_t
{
for (;;) {
cp++;
break;
do {
cp++;
ntok++;
}
return (ntok);
}
static boolean_t
{
int status;
if (stdif)
} else {
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
*/
static boolean_t
{
return (B_TRUE);
return (B_TRUE);
}
return (B_FALSE);
}