ip_anon_rcm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2002-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* RCM module to prevent plumbed IP addresses from being removed.
*/
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <thread.h>
#include <synch.h>
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cladm.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip6.h>
#include <inet/ip.h>
#include <inet/ip6.h>
#include "rcm_module.h"
#include "ifaddrlist.h"
#define SUNW_IP "SUNW_ip/"
#define IP_REG_SIZE (9 + INET6_ADDRSTRLEN)
#define IP_ANON_USAGE gettext("Plumbed IP Address")
#define IP_SUSPEND_ERR gettext("Plumbed IP Addresses cannot be suspended")
#define IP_OFFLINE_ERR gettext("Invalid operation: IP cannot be offlined")
#define IP_REMOVE_ERR gettext("Invalid operation: IP cannot be removed")
#define IP_REG_FAIL gettext("Registration Failed")
#define IP_NO_CLUSTER gettext("Could not read cluster network addresses")
#define IP_FLAG_NEW 0x00
#define IP_FLAG_REG 0x01
#define IP_FLAG_CL 0x02
#define IP_FLAG_IGNORE 0x04
#define IP_FLAG_DELETE 0x08
static int ip_anon_register(rcm_handle_t *);
static int ip_anon_unregister(rcm_handle_t *);
static int ip_anon_getinfo(rcm_handle_t *, char *, id_t, uint_t,
char **, char **, nvlist_t *, rcm_info_t **);
static int ip_anon_suspend(rcm_handle_t *, char *, id_t,
timespec_t *, uint_t, char **, rcm_info_t **);
static int ip_anon_resume(rcm_handle_t *, char *, id_t, uint_t,
char **, rcm_info_t **);
static int ip_anon_offline(rcm_handle_t *, char *, id_t, uint_t,
char **, rcm_info_t **);
static int ip_anon_online(rcm_handle_t *, char *, id_t, uint_t,
char **, rcm_info_t **);
static int ip_anon_remove(rcm_handle_t *, char *, id_t, uint_t,
char **, rcm_info_t **);
static int exclude_ipv4(cladm_netaddrs_t exclude_addrs,
ipaddr_t address);
static int exclude_ipv6(cladm_netaddrs_t exclude_addrs,
uint32_t address[4]);
typedef struct ip_status {
int flags;
char device[IP_REG_SIZE];
struct ip_status *next;
} ip_status_t;
static ip_status_t *findreg(char *reg);
static ip_status_t *addreg(char *reg);
static int deletereg(ip_status_t *entry);
static ip_status_t *ip_list = NULL;
static mutex_t ip_list_lock;
static struct rcm_mod_ops ip_anon_ops =
{
RCM_MOD_OPS_VERSION,
ip_anon_register,
ip_anon_unregister,
ip_anon_getinfo,
ip_anon_suspend,
ip_anon_resume,
ip_anon_offline,
ip_anon_online,
ip_anon_remove,
NULL,
NULL,
NULL
};
struct rcm_mod_ops *
rcm_mod_init()
{
return (&ip_anon_ops);
}
const char *
rcm_mod_info()
{
return ("RCM IP address module %I%");
}
int
rcm_mod_fini()
{
ip_status_t *tlist;
/* free the registration list */
(void) mutex_lock(&ip_list_lock);
while (ip_list != NULL) {
tlist = ip_list->next;
free(ip_list);
ip_list = tlist;
}
(void) mutex_unlock(&ip_list_lock);
(void) mutex_destroy(&ip_list_lock);
return (RCM_SUCCESS);
}
static int
ip_anon_register(rcm_handle_t *hdl)
{
int bootflags;
struct ifaddrlist *al = NULL, *al6 = NULL;
char errbuf[ERRBUFSIZE] = "", errbuf6[ERRBUFSIZE] = "";
char treg[IP_REG_SIZE], tstr[IP_REG_SIZE];
cladm_netaddrs_t exclude_addrs;
int num_ifs, num_ifs6, i, ret;
uint32_t num_exclude_addrs = 0;
ip_status_t *tlist, *tentry;
(void) mutex_lock(&ip_list_lock);
rcm_log_message(RCM_DEBUG, "ip_anon: registration refresh.\n");
exclude_addrs.cladm_num_netaddrs = 0;
if (_cladm(CL_INITIALIZE, CL_GET_BOOTFLAG, &bootflags) != 0) {
rcm_log_message(RCM_ERROR,
gettext("unable to check cluster status\n"));
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
rcm_log_message(RCM_DEBUG,
"ip_anon: cladm bootflags=%d\n", bootflags);
if (bootflags == 3) {
/* build the exclusion list */
if ((ret = _cladm(CL_CONFIG, CL_GET_NUM_NETADDRS,
&num_exclude_addrs)) == 0) {
exclude_addrs.cladm_num_netaddrs = num_exclude_addrs;
if (num_exclude_addrs == 0)
rcm_log_message(RCM_DEBUG,
"ip_anon: no addresses excluded\n");
else {
if ((exclude_addrs.cladm_netaddrs_array =
malloc(sizeof (cladm_netaddr_entry_t) *
(num_exclude_addrs))) == NULL) {
rcm_log_message(RCM_ERROR,
gettext("out of memory\n"));
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
if ((ret = _cladm(CL_CONFIG,
CL_GET_NETADDRS, &exclude_addrs))
!= 0) {
rcm_log_message(RCM_ERROR,
IP_NO_CLUSTER);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
}
} else {
if ((ret != 0) && (errno == EINVAL)) {
rcm_log_message(RCM_DEBUG,
"no _cladm() backend to get addrs\n");
} else {
rcm_log_message(RCM_ERROR, IP_NO_CLUSTER);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
}
rcm_log_message(RCM_DEBUG,
"cladm returned %d errno=%d\n", ret, errno);
rcm_log_message(RCM_DEBUG,
"ip_anon: num exclude addrs: %d\n",
exclude_addrs.cladm_num_netaddrs);
/* print the exclusion list for debugging purposes */
for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) {
(void) strcpy(treg, "<UNKNOWN>");
(void) strcpy(tstr, "<UNKNOWN>");
if (exclude_addrs.cladm_netaddrs_array[i].\
cl_ipversion == IPV4_VERSION) {
(void) inet_ntop(AF_INET,
&exclude_addrs.cladm_netaddrs_array[i].
cl_ipv_un.cl_ipv4.ipv4_netaddr,
treg, INET_ADDRSTRLEN);
(void) inet_ntop(AF_INET,
&exclude_addrs.cladm_netaddrs_array[i].
cl_ipv_un.cl_ipv4.ipv4_netmask,
tstr, INET_ADDRSTRLEN);
}
if (exclude_addrs.cladm_netaddrs_array[i].\
cl_ipversion == IPV6_VERSION) {
(void) inet_ntop(AF_INET6,
&exclude_addrs.cladm_netaddrs_array[i].
cl_ipv_un.cl_ipv6.ipv6_netaddr,
treg, INET6_ADDRSTRLEN);
(void) inet_ntop(AF_INET6,
&exclude_addrs.cladm_netaddrs_array[i].
cl_ipv_un.cl_ipv6.ipv6_netmask,
tstr, INET6_ADDRSTRLEN);
}
rcm_log_message(RCM_DEBUG, "IPV%d: %s %s\n",
exclude_addrs.cladm_netaddrs_array[i].
cl_ipversion, treg, tstr);
}
}
/* obtain a list of all IPv4 and IPv6 addresses in the system */
rcm_log_message(RCM_DEBUG,
"ip_anon: obtaining list of IPv4 addresses.\n");
num_ifs = ifaddrlist(&al, AF_INET, errbuf);
if ((num_ifs == -1) && (errno != 0) && (errno != ENOENT) &&
(errno != EINVAL)) {
rcm_log_message(RCM_ERROR,
gettext("cannot get IPv4 address list errno=%d (%s)\n"),
errno, errbuf);
free(al);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
rcm_log_message(RCM_DEBUG,
"ip_anon: obtaining list of IPv6 addresses.\n");
num_ifs6 = ifaddrlist(&al6, AF_INET6, errbuf6);
if ((num_ifs6 == -1) && (errno != 0) && (errno != ENOENT) &&
(errno != EINVAL)) {
rcm_log_message(RCM_ERROR,
gettext("cannot get IPv6 address list errno=%d (%s)\n"),
errno, errbuf6);
free(al);
free(al6);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
/* check the state of outstanding registrations against the list */
rcm_log_message(RCM_DEBUG,
"ip_anon: checking outstanding registrations.\n");
tlist = ip_list;
while (tlist != NULL) {
tlist->flags |= IP_FLAG_DELETE;
tlist = tlist->next;
}
/* IPv4 */
rcm_log_message(RCM_DEBUG, "ip_anon: checking IPv4 addresses.\n");
for (i = 0; i < num_ifs; i++) {
(void) inet_ntop(AF_INET, &al[i].addr.addr, tstr,
INET_ADDRSTRLEN);
(void) strcpy(treg, SUNW_IP);
(void) strcat(treg, tstr);
if ((tlist = findreg(treg)) == NULL)
tlist = addreg(treg);
else
tlist->flags &= (~IP_FLAG_DELETE);
if (tlist == NULL) {
rcm_log_message(RCM_ERROR,
gettext("out of memory\n"));
free(al);
free(al6);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
if (exclude_ipv4(exclude_addrs, al[i].addr.addr.s_addr))
tlist->flags |= IP_FLAG_CL;
}
/* IPv6 */
rcm_log_message(RCM_DEBUG, "ip_anon: checking IPv6 addresses.\n");
for (i = 0; i < num_ifs6; i++) {
(void) inet_ntop(AF_INET6, &al6[i].addr.addr, tstr,
INET6_ADDRSTRLEN);
(void) strcpy(treg, SUNW_IP);
(void) strcat(treg, tstr);
if ((tlist = findreg(treg)) == NULL)
tlist = addreg(treg);
else
tlist->flags &= (~IP_FLAG_DELETE);
if (tlist == NULL) {
rcm_log_message(RCM_ERROR,
gettext("out of memory\n"));
free(al);
free(al6);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
}
if (exclude_ipv6(exclude_addrs, al6[i].addr.addr6._S6_un.\
_S6_u32))
tlist->flags |= IP_FLAG_CL;
}
rcm_log_message(RCM_DEBUG, "ip_anon: updating reg. state.\n");
/* examine the list of ip address registrations and their state */
tlist = ip_list;
while (tlist != NULL) {
tentry = tlist;
tlist = tlist->next;
if (tentry->flags & IP_FLAG_DELETE) {
if (tentry->flags & IP_FLAG_REG) {
rcm_log_message(RCM_DEBUG,
"ip_anon: unregistering interest in %s\n",
tentry->device);
if (rcm_unregister_interest(hdl,
tentry->device, 0) != 0) {
rcm_log_message(RCM_ERROR,
gettext("failed to unregister"));
}
}
(void) deletereg(tentry);
} else if (!(tentry->flags & IP_FLAG_IGNORE)) {
/*
* If the registration is not a clustered devices and
* not already registered, then RCM doesn't
* currently know about it.
*/
if (!(tentry->flags & IP_FLAG_CL) &&
!(tentry->flags & IP_FLAG_REG)) {
tentry->flags |= IP_FLAG_REG;
rcm_log_message(RCM_DEBUG,
"ip_anon: registering interest in %s\n",
tentry->device);
if (rcm_register_interest(hdl,
tentry->device, 0, NULL) !=
RCM_SUCCESS) {
rcm_log_message(RCM_ERROR,
IP_REG_FAIL);
free(al);
free(al6);
(void) mutex_unlock(&ip_list_lock);
return (RCM_FAILURE);
} else {
rcm_log_message(RCM_DEBUG,
"ip_anon: registered %s\n",
tentry->device);
}
}
/*
* If the entry is registered and clustered, then
* the configuration has been changed and it
* should be unregistered.
*/
if ((tentry->flags & IP_FLAG_REG) &
(tentry->flags & IP_FLAG_CL)) {
rcm_log_message(RCM_DEBUG,
"ip_anon: unregistering in %s\n",
tentry->device);
if (rcm_unregister_interest(hdl,
tentry->device, 0) != 0) {
rcm_log_message(RCM_ERROR,
gettext("failed to unregister"));
}
tentry->flags &= (~IP_FLAG_REG);
}
}
}
tlist = ip_list;
while (tlist != NULL) {
rcm_log_message(RCM_DEBUG, "ip_anon: %s (%Xh)\n",
tlist->device, tlist->flags);
tlist = tlist->next;
}
rcm_log_message(RCM_DEBUG, "ip_anon: registration complete.\n");
free(al);
free(al6);
(void) mutex_unlock(&ip_list_lock);
return (RCM_SUCCESS);
}
static int
ip_anon_unregister(rcm_handle_t *hdl)
{
ip_status_t *tlist;
(void) mutex_lock(&ip_list_lock);
tlist = ip_list;
while (tlist != NULL) {
if ((tlist->flags & IP_FLAG_REG)) {
if (rcm_unregister_interest(hdl,
tlist->device, 0) != 0) {
rcm_log_message(RCM_ERROR,
gettext("failed to unregister"));
}
tlist->flags &= (~IP_FLAG_REG);
}
tlist = tlist->next;
}
(void) mutex_unlock(&ip_list_lock);
return (RCM_SUCCESS);
}
/*ARGSUSED*/
static int
ip_anon_getinfo(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
char **infostr, char **errstr, nvlist_t *props, rcm_info_t **dependent)
{
assert(rsrcname != NULL && infostr != NULL);
if ((*infostr = strdup(IP_ANON_USAGE)) == NULL)
rcm_log_message(RCM_ERROR, gettext("strdup failure\n"));
return (RCM_SUCCESS);
}
/*ARGSUSED*/
static int
ip_anon_suspend(rcm_handle_t *hdl, char *rsrcname, id_t id,
timespec_t *interval, uint_t flags, char **errstr,
rcm_info_t **dependent)
{
if ((*errstr = strdup(IP_SUSPEND_ERR)) == NULL)
rcm_log_message(RCM_ERROR, gettext("strdup failure\n"));
return (RCM_FAILURE);
}
/*ARGSUSED*/
static int
ip_anon_resume(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
char **errstr, rcm_info_t **dependent)
{
return (RCM_SUCCESS);
}
/*ARGSUSED*/
static int
ip_anon_offline(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
char **errstr, rcm_info_t **dependent)
{
if ((*errstr = strdup(IP_OFFLINE_ERR)) == NULL)
rcm_log_message(RCM_ERROR, gettext("strdup failure\n"));
return (RCM_FAILURE);
}
/*ARGSUSED*/
static int
ip_anon_online(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
char **errstr, rcm_info_t **dependent)
{
return (RCM_SUCCESS);
}
/*ARGSUSED*/
static int
ip_anon_remove(rcm_handle_t *hdl, char *rsrcname, id_t id, uint_t flags,
char **errstr, rcm_info_t **dependent)
{
if ((*errstr = strdup(IP_REMOVE_ERR)) == NULL)
rcm_log_message(RCM_ERROR, gettext("strdup failure\n"));
return (RCM_FAILURE);
}
/*
* Call with ip_list_lock held.
*/
static ip_status_t *
findreg(char *reg)
{
ip_status_t *tlist;
int done;
tlist = ip_list;
done = 0;
while ((tlist != NULL) && (!done)) {
if (strcmp(tlist->device, reg) == 0)
done = 1;
else
tlist = tlist->next;
}
return (tlist);
}
static ip_status_t *
addreg(char *reg)
{
ip_status_t *tlist, *tentry;
tentry = (ip_status_t *)malloc(sizeof (ip_status_t));
if (tentry == NULL)
return (tentry);
tentry->flags = IP_FLAG_NEW;
tentry->next = NULL;
(void) strcpy(tentry->device, reg);
if (ip_list == NULL)
ip_list = tentry;
else {
tlist = ip_list;
while (tlist->next != NULL)
tlist = tlist->next;
tlist->next = tentry;
}
return (tentry);
}
static int
deletereg(ip_status_t *entry)
{
ip_status_t *tlist;
if (entry == NULL)
return (-1);
if (entry == ip_list) {
ip_list = ip_list->next;
free(entry);
} else {
tlist = ip_list;
while ((tlist->next != NULL) && (tlist->next != entry))
tlist = tlist->next;
if (tlist->next != entry)
return (-1);
tlist->next = entry->next;
free(entry);
}
return (0);
}
static int
exclude_ipv4(cladm_netaddrs_t exclude_addrs, ipaddr_t address)
{
int i;
char taddr[IP_REG_SIZE], tmask[IP_REG_SIZE], tmatch[IP_REG_SIZE];
ipaddr_t ipv4_netaddr, ipv4_netmask;
(void) inet_ntop(AF_INET, &address, taddr, INET_ADDRSTRLEN);
rcm_log_message(RCM_DEBUG, "ip_anon: exclude_ipv4 (%s, %d)\n",
taddr, exclude_addrs.cladm_num_netaddrs);
/*
* If this falls in the exclusion list, the IP_FLAG_CL
* bit should be set for the adapter.
*/
for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) {
if (exclude_addrs.cladm_netaddrs_array[i].\
cl_ipversion == IPV4_VERSION) {
ipv4_netaddr = exclude_addrs.\
cladm_netaddrs_array[i].cl_ipv_un.cl_ipv4.\
ipv4_netaddr;
ipv4_netmask = exclude_addrs.\
cladm_netaddrs_array[i].cl_ipv_un.cl_ipv4.\
ipv4_netmask;
(void) inet_ntop(AF_INET, &ipv4_netaddr, tmatch,
INET_ADDRSTRLEN);
(void) inet_ntop(AF_INET, &ipv4_netmask, tmask,
INET_ADDRSTRLEN);
if ((address & ipv4_netmask) == ipv4_netaddr) {
rcm_log_message(RCM_DEBUG,
"ip_anon: matched %s:%s => %s\n",
taddr, tmask, tmatch);
return (1);
}
}
}
rcm_log_message(RCM_DEBUG, "ip_anon: no match for %s\n",
taddr);
return (0);
}
static int
exclude_ipv6(cladm_netaddrs_t exclude_addrs, uint32_t address[4])
{
int i, j, numequal;
uint32_t addr[4], ipv6_netaddr[4], ipv6_netmask[4];
char taddr[IP_REG_SIZE], tmask[IP_REG_SIZE], tmatch[IP_REG_SIZE];
(void) inet_ntop(AF_INET6, address, taddr, INET6_ADDRSTRLEN);
/*
* If this falls in the exclusion list, the IP_FLAG_CL
* bit should be set for the adapter.
*/
for (i = 0; i < exclude_addrs.cladm_num_netaddrs; i++) {
if (exclude_addrs.cladm_netaddrs_array[i].\
cl_ipversion == IPV6_VERSION) {
numequal = 0;
for (j = 0; j < 4; j++) {
ipv6_netaddr[j] = exclude_addrs.\
cladm_netaddrs_array[i].\
cl_ipv_un.cl_ipv6.ipv6_netaddr[j];
ipv6_netmask[j] = exclude_addrs.\
cladm_netaddrs_array[i].\
cl_ipv_un.cl_ipv6.ipv6_netmask[j];
addr[j] = address[j] & ipv6_netmask[j];
if (addr[j] == ipv6_netaddr[j])
numequal++;
}
(void) inet_ntop(AF_INET6, ipv6_netaddr, tmatch,
INET6_ADDRSTRLEN);
(void) inet_ntop(AF_INET6, ipv6_netmask, tmask,
INET6_ADDRSTRLEN);
if (numequal == 4)
return (1);
}
}
rcm_log_message(RCM_DEBUG, "ip_anon: no match for %s\n",
taddr);
return (0);
}
/*
* The code below is taken from usr/src/cmd/cmd-inet/common/ifaddrlist.c
*/
/*
* Copyright (c) 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* Construct the interface list with given address family.
* If it fails, returns -1 and an error message in *errbuf;
* otherwise, returns number of interfaces, and the interface list in *ipaddrp.
*/
int
ifaddrlist(struct ifaddrlist **ipaddrp, int family, char *errbuf)
{
int fd;
struct lifreq *lifrp, *lifend;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
struct ifaddrlist *al;
struct lifconf lifc;
struct lifreq *ibuf, lifr;
char device[LIFNAMSIZ + 1];
struct ifaddrlist *ifaddrlist;
struct lifnum lifn;
int lifc_flags = 0;
int count;
if (family != AF_INET && family != AF_INET6) {
(void) sprintf(errbuf, "invalid address family");
return (-1);
}
fd = socket(family, SOCK_DGRAM, 0);
if (fd < 0) {
(void) snprintf(errbuf, ERRBUFSIZE, "socket: %s",
strerror(errno));
return (-1);
}
/* determine the number of interfaces */
lifn.lifn_family = family;
lifn.lifn_flags = lifc_flags;
if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0) {
(void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFNUM: %s",
strerror(errno));
(void) close(fd);
return (-1);
}
/* allocate memory for the determined number of interfaces */
ifaddrlist = calloc((size_t)lifn.lifn_count,
(size_t)sizeof (struct ifaddrlist));
if (ifaddrlist == NULL) {
(void) snprintf(errbuf, ERRBUFSIZE, "calloc: %s",
strerror(errno));
(void) close(fd);
return (-1);
}
ibuf = calloc((size_t)lifn.lifn_count, (size_t)sizeof (struct lifreq));
if (ibuf == NULL) {
(void) snprintf(errbuf, ERRBUFSIZE, "calloc: %s",
strerror(errno));
free(ifaddrlist);
(void) close(fd);
return (-1);
}
/* pull out the interface list from the kernel */
lifc.lifc_family = family;
lifc.lifc_len = (int)(lifn.lifn_count * sizeof (struct lifreq));
lifc.lifc_buf = (caddr_t)ibuf;
lifc.lifc_flags = lifc_flags;
if (ioctl(fd, SIOCGLIFCONF, (char *)&lifc) < 0 ||
lifc.lifc_len < sizeof (struct lifreq)) {
(void) snprintf(errbuf, ERRBUFSIZE, "SIOCGLIFCONF: %s",
strerror(errno));
free(ifaddrlist);
free(ibuf);
(void) close(fd);
return (-1);
}
lifrp = ibuf;
/*LINTED*/
lifend = (struct lifreq *)((char *)ibuf + lifc.lifc_len);
al = ifaddrlist;
count = 0;
/* let's populate the interface entries in the ifaddrlist */
for (; lifrp < lifend; lifrp++) {
/*
* Need a template to preserve address info that is
* used below to locate the next entry. (Otherwise,
* SIOCGLIFFLAGS stomps over it because the requests
* are returned in a union.)
*/
(void) strncpy(lifr.lifr_name, lifrp->lifr_name,
sizeof (lifr.lifr_name));
if (ioctl(fd, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
if (errno == ENXIO) {
continue;
}
(void) snprintf(errbuf, ERRBUFSIZE,
"SIOCGLIFFLAGS: %.*s: %s",
(int)sizeof (lifr.lifr_name), lifr.lifr_name,
strerror(errno));
free(ifaddrlist);
free(ibuf);
(void) close(fd);
return (-1);
}
al->flags = lifr.lifr_flags;
/* get the interface address */
(void) strncpy(device, lifr.lifr_name, sizeof (device));
device[sizeof (device) - 1] = '\0';
if (ioctl(fd, SIOCGLIFADDR, (char *)&lifr) < 0) {
(void) snprintf(errbuf, ERRBUFSIZE,
"SIOCGLIFADDR: %s: %s", device, strerror(errno));
free(ifaddrlist);
free(ibuf);
(void) close(fd);
return (-1);
}
if (family == AF_INET) {
sin = (struct sockaddr_in *)&lifr.lifr_addr;
al->addr.addr = sin->sin_addr;
} else {
sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
al->addr.addr6 = sin6->sin6_addr;
}
(void) strncpy(al->device, device, sizeof (device));
/* get the interface index */
if (ioctl(fd, SIOCGLIFINDEX, (char *)&lifr) < 0) {
(void) snprintf(errbuf, ERRBUFSIZE,
"SIOCGLIFADDR: %s: %s", device, strerror(errno));
free(ifaddrlist);
free(ibuf);
(void) close(fd);
return (-1);
}
al->index = lifr.lifr_index;
++al;
++count;
}
free(ibuf);
(void) close(fd);
*ipaddrp = ifaddrlist;
return (count);
}