906e29ed96
This affects both trunk and 1.7 and might affect 1.6. cmr:v1.7 This commit was SVN r27562.
271 строка
8.0 KiB
C
271 строка
8.0 KiB
C
/*
|
|
* Copyright (c) 2010 Cisco Systems, Inc. All rights reserved.
|
|
* Copyright (c) 2010 Oracle and/or its affiliates. All rights reserved.
|
|
* $COPYRIGHT$
|
|
*
|
|
* Additional copyrights may follow
|
|
*
|
|
* $HEADER$
|
|
*/
|
|
|
|
#include "opal_config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "opal/constants.h"
|
|
#include "opal/util/output.h"
|
|
#include "opal/mca/if/if.h"
|
|
|
|
static int if_posix_open(void);
|
|
|
|
/* Supports all flavors of posix except those
|
|
* BSD-flavors supported elsewhere
|
|
*/
|
|
opal_if_base_component_t mca_if_posix_ipv4_component = {
|
|
/* First, the mca_component_t struct containing meta information
|
|
about the component itself */
|
|
{
|
|
OPAL_IF_BASE_VERSION_2_0_0,
|
|
|
|
/* Component name and version */
|
|
"posix_ipv4",
|
|
OPAL_MAJOR_VERSION,
|
|
OPAL_MINOR_VERSION,
|
|
OPAL_RELEASE_VERSION,
|
|
|
|
/* Component open and close functions */
|
|
if_posix_open,
|
|
NULL
|
|
},
|
|
{
|
|
/* This component is checkpointable */
|
|
MCA_BASE_METADATA_PARAM_CHECKPOINT
|
|
},
|
|
};
|
|
|
|
/* convert a netmask (in network byte order) to CIDR notation */
|
|
static int prefix (uint32_t netmask)
|
|
{
|
|
uint32_t mask = ntohl(netmask);
|
|
int plen = 0;
|
|
|
|
if (0 == mask) {
|
|
plen = 32;
|
|
} else {
|
|
while ((mask % 2) == 0) {
|
|
plen += 1;
|
|
mask /= 2;
|
|
}
|
|
}
|
|
|
|
return (32 - plen);
|
|
}
|
|
|
|
/* configure using getifaddrs(3) */
|
|
static int if_posix_open(void)
|
|
{
|
|
int sd;
|
|
int lastlen, num, rem;
|
|
char *ptr;
|
|
struct ifconf ifconf;
|
|
int ifc_len;
|
|
bool successful_locate = false;
|
|
|
|
/* Create the internet socket to test with. Must use AF_INET;
|
|
using AF_UNSPEC or AF_INET6 will cause everything to
|
|
fail. */
|
|
if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
opal_output(0, "opal_ifinit: socket() failed with errno=%d\n",
|
|
errno);
|
|
return OPAL_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Get Network Interface configuration
|
|
*
|
|
* Some notes on the behavior of ioctl(..., SIOCGIFCONF,...)
|
|
* when not enough space is allocated for all the entries.
|
|
*
|
|
* - Solaris returns -1, errno EINVAL if there is not enough
|
|
* space
|
|
* - OS X returns 0, sets .ifc_len to the space used by the
|
|
* by the entries that did fit.
|
|
* - Linux returns 0, sets .ifc_len to the space required to
|
|
* hold all the entries (although it only writes what will
|
|
* fit in the buffer of .ifc_len passed to the function).
|
|
* - FreeBSD returns 0, sets .ifc_len to 0.
|
|
*
|
|
* Everyone else seems to do one of the four.
|
|
*/
|
|
lastlen = 0;
|
|
ifc_len = sizeof(struct ifreq) * DEFAULT_NUMBER_INTERFACES;
|
|
do {
|
|
ifconf.ifc_len = ifc_len;
|
|
ifconf.ifc_req = malloc(ifc_len);
|
|
if (NULL == ifconf.ifc_req) {
|
|
close(sd);
|
|
return OPAL_ERROR;
|
|
}
|
|
|
|
/* initialize the memory so valgrind and purify won't
|
|
* complain. Since this isn't performance critical, just
|
|
* always memset.
|
|
*/
|
|
memset(ifconf.ifc_req, 0, ifconf.ifc_len);
|
|
|
|
if (ioctl(sd, SIOCGIFCONF, &ifconf) < 0) {
|
|
/* if we got an einval, we probably don't have enough
|
|
space. so we'll fall down and try to expand our
|
|
space */
|
|
if (errno != EINVAL && lastlen != 0) {
|
|
opal_output(0, "opal_ifinit: ioctl(SIOCGIFCONF) \
|
|
failed with errno=%d",
|
|
errno);
|
|
free(ifconf.ifc_req);
|
|
close(sd);
|
|
return OPAL_ERROR;
|
|
}
|
|
} else {
|
|
/* if ifc_len is 0 or different than what we set it to
|
|
at call to ioctl, try again with a bigger buffer.
|
|
else stop */
|
|
if (ifconf.ifc_len == lastlen && ifconf.ifc_len > 0) {
|
|
/* we didn't expand. we're done */
|
|
successful_locate = true;
|
|
break;
|
|
}
|
|
lastlen = ifconf.ifc_len;
|
|
}
|
|
|
|
/* Yes, we overflowed (or had an EINVAL on the ioctl).
|
|
Loop back around and try again with a bigger buffer */
|
|
free(ifconf.ifc_req);
|
|
ifc_len = (ifc_len == 0) ? 1 : ifc_len * 2;
|
|
} while (ifc_len < MAX_IFCONF_SIZE);
|
|
if (!successful_locate) {
|
|
opal_output(0, "opal_ifinit: unable to find network interfaces.");
|
|
return OPAL_ERR_FATAL;
|
|
}
|
|
|
|
/*
|
|
* Setup indexes
|
|
*/
|
|
ptr = (char*) ifconf.ifc_req;
|
|
rem = ifconf.ifc_len;
|
|
num = 0;
|
|
|
|
/* loop through all interfaces */
|
|
while (rem > 0) {
|
|
struct ifreq* ifr = (struct ifreq*) ptr;
|
|
opal_if_t *intf;
|
|
int length;
|
|
|
|
/* compute offset for entries */
|
|
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
|
length = sizeof(struct sockaddr);
|
|
|
|
if (ifr->ifr_addr.sa_len > length) {
|
|
length = ifr->ifr_addr.sa_len;
|
|
}
|
|
|
|
length += sizeof(ifr->ifr_name);
|
|
#else
|
|
length = sizeof(struct ifreq);
|
|
#endif
|
|
|
|
rem -= length;
|
|
ptr += length;
|
|
|
|
/* see if we like this entry */
|
|
if (AF_INET != ifr->ifr_addr.sa_family) {
|
|
continue;
|
|
}
|
|
|
|
if (ioctl(sd, SIOCGIFFLAGS, ifr) < 0) {
|
|
opal_output(0, "opal_ifinit: ioctl(SIOCGIFFLAGS) failed with errno=%d", errno);
|
|
continue;
|
|
}
|
|
if ((ifr->ifr_flags & IFF_UP) == 0) {
|
|
continue;
|
|
}
|
|
#ifdef IFF_SLAVE
|
|
/* Is this a slave to a load balancer or bonded channel?
|
|
If so, don't use it -- pick up the master instead */
|
|
if ((ifr->ifr_flags & IFF_SLAVE) != 0) {
|
|
continue;
|
|
}
|
|
#endif
|
|
#if 0
|
|
if (!opal_if_retain_loopback && (ifr->ifr_flags & IFF_LOOPBACK) != 0) {
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
intf = OBJ_NEW(opal_if_t);
|
|
if (NULL == intf) {
|
|
opal_output(0, "opal_ifinit: unable to allocated %lu bytes\n", (unsigned long)sizeof(opal_if_t));
|
|
free(ifconf.ifc_req);
|
|
close(sd);
|
|
return OPAL_ERR_OUT_OF_RESOURCE;
|
|
}
|
|
|
|
/* copy entry over into our data structure */
|
|
strcpy(intf->if_name, ifr->ifr_name);
|
|
intf->if_flags = ifr->ifr_flags;
|
|
|
|
/* every new address gets its own internal if_index */
|
|
intf->if_index = opal_list_get_size(&opal_if_list)+1;
|
|
|
|
/* assign the kernel index to distinguish different NICs */
|
|
#ifndef SIOCGIFINDEX
|
|
intf->if_kernel_index = intf->if_index;
|
|
#else
|
|
if (ioctl(sd, SIOCGIFINDEX, ifr) < 0) {
|
|
opal_output(0,"opal_ifinit: ioctl(SIOCGIFINDEX) failed with errno=%d", errno);
|
|
OBJ_RELEASE(intf);
|
|
continue;
|
|
}
|
|
#if defined(ifr_ifindex)
|
|
intf->if_kernel_index = ifr->ifr_ifindex;
|
|
#elif defined(ifr_index)
|
|
intf->if_kernel_index = ifr->ifr_index;
|
|
#else
|
|
intf->if_kernel_index = -1;
|
|
#endif
|
|
#endif /* SIOCGIFINDEX */
|
|
|
|
/* This call returns IPv4 addresses only. Use SIOCGLIFADDR
|
|
instead */
|
|
if (ioctl(sd, SIOCGIFADDR, ifr) < 0) {
|
|
opal_output(0, "opal_ifinit: ioctl(SIOCGIFADDR) failed with errno=%d", errno);
|
|
OBJ_RELEASE(intf);
|
|
break;
|
|
}
|
|
if (AF_INET != ifr->ifr_addr.sa_family) {
|
|
OBJ_RELEASE(intf);
|
|
continue;
|
|
}
|
|
|
|
/* based on above, we know this is an IPv4 address... */
|
|
memcpy(&intf->if_addr, &ifr->ifr_addr, sizeof(struct sockaddr_in));
|
|
|
|
if (ioctl(sd, SIOCGIFNETMASK, ifr) < 0) {
|
|
opal_output(0, "opal_ifinit: ioctl(SIOCGIFNETMASK) failed with errno=%d", errno);
|
|
OBJ_RELEASE(intf);
|
|
continue;
|
|
}
|
|
|
|
/* generate CIDR and assign to netmask */
|
|
intf->if_mask = prefix(((struct sockaddr_in*) &ifr->ifr_addr)->sin_addr.s_addr);
|
|
|
|
opal_list_append(&opal_if_list, &(intf->super));
|
|
}
|
|
free(ifconf.ifc_req);
|
|
close(sd);
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
|