Files
epics-base/src/libCom/osi/os/default/osdNetIntf.c

325 lines
11 KiB
C

/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* $Revision-Id$ */
/*
* Author: Jeff Hill
* Date: 04-05-94
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define epicsExportSharedSymbols
#include "osiSock.h"
#include "epicsAssert.h"
#include "errlog.h"
#ifdef DEBUG
# define ifDepenDebugPrintf(argsInParen) printf argsInParen
#else
# define ifDepenDebugPrintf(argsInParen)
#endif
/*
* Determine the size of an ifreq structure
* Made difficult by the fact that addresses larger than the structure
* size may be returned from the kernel.
*/
static size_t ifreqSize ( struct ifreq *pifreq )
{
size_t size;
size = ifreq_size ( pifreq );
if ( size < sizeof ( *pifreq ) ) {
size = sizeof ( *pifreq );
}
return size;
}
/*
* Move to the next ifreq structure
*/
static struct ifreq * ifreqNext ( struct ifreq *pifreq )
{
struct ifreq *ifr;
ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq );
ifDepenDebugPrintf( ("ifreqNext() pifreq %p, size 0x%x, ifr 0x%p\n", pifreq, (unsigned)ifreqSize (pifreq), ifr) );
return ifr;
}
/*
* osiSockDiscoverBroadcastAddresses ()
*/
epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses
(ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr)
{
static const unsigned nelem = 100;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pIfreqListEnd;
struct ifreq *pifreq;
struct ifreq *pnextifreq;
osiSockAddrNode *pNewNode;
/*
* use pool so that we avoid using too much stack space
*
* nelem is set to the maximum interfaces
* on one machine here
*/
pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) );
if (!pIfreqList) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory to complete request\n");
return;
}
ifconf.ifc_len = nelem * sizeof(*pifreq);
ifconf.ifc_req = pIfreqList;
status = socket_ioctl (socket, SIOCGIFCONF, &ifconf);
if (status < 0 || ifconf.ifc_len == 0) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration (%d)\n", status);
free (pIfreqList);
return;
}
pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList);
pIfreqListEnd--;
for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
uint32_t current_ifreqsize;
struct sockaddr_in if_addr;
/*
* find the next ifreq
*/
pnextifreq = ifreqNext (pifreq);
/* determine ifreq size */
current_ifreqsize = ifreqSize ( pifreq );
/* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
memmove(pIfreqList, pifreq, current_ifreqsize);
ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n",
pIfreqList->ifr_name,
(unsigned)ifreq_size(pifreq),
(unsigned)current_ifreqsize));
/*
* If its not an internet interface then dont use it
*/
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );
continue;
}
if_addr = *(struct sockaddr_in *)&pIfreqList->ifr_addr;
/*
* if it isnt a wildcarded interface then look for
* an exact match
*/
if ( pMatchAddr->sa.sa_family != AF_UNSPEC ) {
if ( pMatchAddr->sa.sa_family != AF_INET ) {
continue;
}
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
if ( if_addr.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );
continue;
}
}
}
status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList );
if ( status ) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name);
continue;
}
/*
* dont bother with interfaces that have been disabled
*/
if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", pIfreqList->ifr_name) );
continue;
}
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
free ( pIfreqList );
return;
}
/*
* If this is an interface that supports
* broadcast fetch the broadcast address.
*
* Otherwise if this is a point to point
* interface then use the destination address.
*
* Otherwise CA will not query through the
* interface.
*/
if ( pIfreqList->ifr_flags & IFF_BROADCAST ) {
status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
if ( status ) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
free ( pNewNode );
continue;
}
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
}
#if defined (IFF_POINTOPOINT)
else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) {
status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList);
if ( status ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) );
free ( pNewNode );
continue;
}
pNewNode->addr.sa = pIfreqList->ifr_dstaddr;
}
#endif
#if defined(__linux__)
/* On Linux, even though the 'lo' interface doesn't set IFF_BROADCAST
* a broadcast route often exists. Assume that sending to 127.255.255.255
* reaches all local listeners.
*
* $ ip route show table local scope link dev lo
* broadcast 127.0.0.0 proto kernel src 127.0.0.1
* broadcast 127.255.255.255 proto kernel src 127.0.0.1
*/
else if ( pIfreqList->ifr_flags & IFF_LOOPBACK && if_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK) ) {
memset(&pNewNode->addr.ia, 0, sizeof(pNewNode->addr.ia));
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_addr.s_addr = htonl(0x7fffffff);
ifDepenDebugPrintf ( ( "assume loopback broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );
}
#endif
else {
ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );
free ( pNewNode );
continue;
}
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", pIfreqList->ifr_name) );
/*
* LOCK applied externally
*/
ellAdd ( pList, &pNewNode->node );
}
free ( pIfreqList );
}
/*
* osiLocalAddr ()
*/
epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket)
{
static const unsigned nelem = 100;
static char init = 0;
static osiSockAddr addr;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pifreq;
struct ifreq *pIfreqListEnd;
struct ifreq *pnextifreq;
if ( init ) {
return addr;
}
memset ( (void *) &addr, '\0', sizeof ( addr ) );
addr.sa.sa_family = AF_UNSPEC;
pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) );
if ( ! pIfreqList ) {
errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" );
return addr;
}
ifconf.ifc_len = nelem * sizeof ( *pIfreqList );
ifconf.ifc_req = pIfreqList;
status = socket_ioctl ( socket, SIOCGIFCONF, &ifconf );
if ( status < 0 || ifconf.ifc_len == 0 ) {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
errlogPrintf (
"osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n",
sockErrBuf );
free ( pIfreqList );
return addr;
}
pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req );
pIfreqListEnd--;
for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
osiSockAddr addrCpy;
uint32_t current_ifreqsize;
/*
* find the next if req
*/
pnextifreq = ifreqNext ( pifreq );
/* determine ifreq size */
current_ifreqsize = ifreqSize ( pifreq );
/* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
memmove(pIfreqList, pifreq, current_ifreqsize);
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiLocalAddr(): interface %s was not AF_INET\n", pIfreqList->ifr_name) );
continue;
}
addrCpy.sa = pIfreqList->ifr_addr;
status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList );
if ( status < 0 ) {
errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pIfreqList->ifr_name );
continue;
}
if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) {
ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s was down\n", pIfreqList->ifr_name) );
continue;
}
/*
* dont use the loop back interface
*/
if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiLocalAddr(): ignoring loopback interface: %s\n", pIfreqList->ifr_name) );
continue;
}
ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pIfreqList->ifr_name) );
init = 1;
addr = addrCpy;
break;
}
free ( pIfreqList );
return addr;
}