/*
* Copyright (c) 2001-2004 The Trustees of Indiana University.
* All rights reserved.
* Copyright (c) 1998-2001 University of Notre Dame.
* All rights reserved.
* Copyright (c) 1994-1998 The Ohio State University.
* All rights reserved.
*
* This file is part of the LAM/MPI software package. For license
* information, see the LICENSE file in the top level directory of the
* LAM/MPI source distribution.
*
* $HEADER$
*
* $Id: lamnet.c,v 6.13 2004/02/16 22:20:52 jsquyres Exp $
*
* Function: - LAM network functions
*/
#include <lam_config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#include <sys/time.h>
#if defined(__APPLE__) && defined(_LP64)
/* Apple engineering suggested using options align=power as a
workaround for a bug in OS X 10.4 (Tiger) that prevented ioctl(...,
SIOCGIFCONF, ...) from working properly in 64 bit mode on Power PC.
It turns out that the underlying issue is the size of struct
ifconf, which the kernel expects to be 12 and natural 64 bit
alignment would make 16. The same bug appears in 64 bit mode on
Intel macs, but align=power is a no-op there, so instead, use the
pack pragma to instruct the compiler to pack on 4 byte words, which
has the same effect as align=power for our needs and works on both
Intel and Power PC Macs. */
#pragma pack(push,4)
#endif
#include <net/if.h>
#if defined(__APPLE__) && defined(_LP64)
#pragma pack(pop)
#endif
#include <netinet/in.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <laminternal.h>
#include <lamnet.h>
#include <portable.h>
#include <terror.h>
#include <typical.h>
#include <lam_network.h>
#include <etc_misc.h>
#define DEFAULT_NUMBER_INTERFACES 10
#define MAX_IFCONF_SIZE 10 * 1024 * 1024
/*
* global functions
*/
int lamnet_findhosts();
int lamnet_findorig();
int lamnet_dups();
/*
* local functions
*/
static int getifaddr(uint4 **ifaddr);
/*
* lamnet_findhosts
*
* Function: - gets the network address of each host
* - "localhost" is changed to the local hostname
* Accepts: - node description array
* - array size
* - bad host index (out)
*/
int
lamnet_findhosts(struct lamnode *lamnet, int nlamnet, int *badhost)
{
int i;
char buffer[16];
if (badhost != NULL)
*badhost = -1;
/* Find INET addresses for each node */
for (i = 0; i < nlamnet; ++i) {
/* Skip empty entries */
if (lamnet[i].lnd_nodeid == NOTNODEID)
continue;
/* Get the host network address */
if (getinetaddr(lamnet[i].lnd_hname,
(unsigned char *) &lamnet[i].lnd_addr.sin_addr) != 0) {
if (badhost != NULL)
*badhost = i;
if (errno == EBADHOST)
show_help("boot", "resolve-hostname", lamnet[*badhost].lnd_hname,
NULL);
else {
snprintf(buffer, 16, "%d", errno);
show_help("boot", "resolve-unknown", buffer, NULL);
}
return(LAMERROR);
}
/* Nope; it resolved properly, so save it */
lamnet[i].lnd_addr.sin_family = AF_INET;
}
return 0;
}
/*
* lamnet_findorig
*
* Function: - finds the origin host node
* - origin is the machine this code is running on
* - calls lamnet_findhosts() if necessary
* - do matching on the network addresses
* - make sure each host is unique (no duplicates)
* Accepts: - network description array
* - array size
* Returns: - index of origin or LAMERROR
*/
int
lamnet_findorig(struct lamnode *lamnet, int nlamnet)
{
int i, j;
int nifaddr; /* # of interface addresses */
uint4 *ifaddr;
nifaddr = getifaddr(&ifaddr);
if (nifaddr < 0)
return(LAMERROR);
/*
* Loop over the host entries, comparing their addresses to
* our local host interface addresses.
*/
for (i = 0; i < nlamnet; ++i) {
/* Skip empty entries */
if (lamnet[i].lnd_nodeid == NOTNODEID)
continue;
if (inet_netof(lamnet[i].lnd_addr.sin_addr) == 127) {
free(ifaddr);
return(i);
}
for (j = 0; j < nifaddr; ++j) {
if (ifaddr[j] == lamnet[i].lnd_addr.sin_addr.s_addr) {
free(ifaddr);
return(i);
}
}
}
free(ifaddr);
errno = EINVAL;
return(LAMERROR);
}
/*
* getifaddr
*
* Function: - obtains all up interface addresses for local host
* Accepts: - INET address array (returned addresses)
* Returns: - number of addresses or LAMERROR
*/
static int
getifaddr(uint4 **ifaddr)
{
int sock;
int length;
int lastlen;
int num;
int rem;
char *p;
struct ifconf config;
struct ifreq req, *preq;
struct sockaddr_in *psin;
int ifc_len;
int successful_locate = 0;
/*
* Create an Internet socket.
*/
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
return LAMERROR;
/*
* 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(req) * DEFAULT_NUMBER_INTERFACES;
do {
config.ifc_len = ifc_len;
config.ifc_req = malloc(ifc_len);
if (config.ifc_req == NULL) {
close(sock);
return LAMERROR;
}
if (ioctl(sock, SIOCGIFCONF, &config) < 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) {
close(sock);
return LAMERROR;
}
} else {
/* if config.ifc_len is 0, we obviously aren't done. try
again... */
if (config.ifc_len == lastlen && config.ifc_len > 0) {
/* we didn't expand. we're done */
successful_locate = 1;
break;
}
lastlen = config.ifc_len;
}
/* Yes, we overflowed (or had an EINVAL on the ioctl).
. Loop back around and try again */
free(config.ifc_req);
ifc_len *= 2;
} while (ifc_len < MAX_IFCONF_SIZE);
if (0 == successful_locate) {
fprintf(stderr, "lamnet_findorig: Unable to find network interfaces.\n");
return LAMERROR;
}
/* Setup indexing */
p = (char *) config.ifc_req;
rem = config.ifc_len;
num = 0;
/* Malloc space for the return */
*ifaddr = malloc(sizeof(uint4) * (rem / sizeof(req)));
if (ifaddr == NULL) {
close(sock);
free(config.ifc_buf);
return LAMERROR;
}
/* Loop through all interfaces. */
while (rem > 0) {
preq = (struct ifreq *) p;
#if LAM_HAVE_SA_LEN
length = sizeof(struct sockaddr);
if (preq->ifr_addr.sa_len > length) {
length = preq->ifr_addr.sa_len;
}
length += sizeof(preq->ifr_name);
#else
length = sizeof(struct ifreq);
#endif
rem -= length;
p += length;
if (preq->ifr_addr.sa_family != AF_INET)
continue;
memcpy(req.ifr_name, preq->ifr_name, sizeof(req.ifr_name));
if (ioctl(sock, SIOCGIFFLAGS, &req)) {
close(sock);
free(ifaddr);
*ifaddr = NULL;
free(config.ifc_buf);
return LAMERROR;
}
if (req.ifr_flags & IFF_UP) {
psin = (struct sockaddr_in *) &(preq->ifr_addr);
(*ifaddr)[num] = psin->sin_addr.s_addr;
num++;
}
}
close(sock);
free(config.ifc_buf);
return(num);
}
syntax highlighted by Code2HTML, v. 0.9.1