Use getifaddrs instead of SIOCGIFCONF.

The old code is preserved in osdNetIfConf.c, which is used by the default
platform directory.

Platforms that support getifaddrs use the new code from osdNetIfAddrs.c.
This commit is contained in:
Michael Ritzert
2021-01-07 12:11:12 +01:00
committed by Michael Davidsaver
parent 43bd5ee1c2
commit 410921b5ef
8 changed files with 580 additions and 351 deletions

View File

@@ -0,0 +1 @@
#include "../osi/osdNetIfAddrs.c"

View File

@@ -0,0 +1 @@
#include "../osi/osdNetIfAddrs.c"

View File

@@ -0,0 +1 @@
#include "../osi/osdNetIfAddrs.c"

View File

@@ -1,351 +1 @@
/*************************************************************************\
* 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.
* SPDX-License-Identifier: EPICS
* EPICS Base is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Jeff Hill
* Date: 04-05-94
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "osiSock.h"
#include "epicsAssert.h"
#include "errlog.h"
#include "epicsThread.h"
#ifdef DEBUG
# define ifDepenDebugPrintf(argsInParen) printf argsInParen
#else
# define ifDepenDebugPrintf(argsInParen)
#endif
static osiSockAddr osiLocalAddrResult;
static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT;
/*
* 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 ()
*/
LIBCOM_API void epicsStdCall 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;
if ( pMatchAddr->sa.sa_family == AF_INET ) {
if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
return;
}
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_port = htons ( 0 );
pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
ellAdd ( pList, &pNewNode->node );
return;
}
}
/*
* 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;
/*
* 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 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) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
if ( pInetAddr->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;
}
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) );
/*
* 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;
}
/*
* dont use the loop back interface
*/
if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\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 ) {
osiSockAddr baddr;
status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
if ( status ) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
free ( pNewNode );
continue;
}
baddr.sa = pIfreqList->ifr_broadaddr;
if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) {
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
} else {
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
free ( pNewNode );
continue;
}
}
#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
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 ()
*/
static void osiLocalAddrOnce (void *raw)
{
SOCKET *psocket = raw;
const unsigned nelem = 100;
osiSockAddr addr;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pifreq;
struct ifreq *pIfreqListEnd;
struct ifreq *pnextifreq;
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" );
goto fail;
}
ifconf.ifc_len = nelem * sizeof ( *pIfreqList );
ifconf.ifc_req = pIfreqList;
status = socket_ioctl ( *psocket, 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 );
goto fail;
}
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 ( *psocket, 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) );
osiLocalAddrResult = addrCpy;
free ( pIfreqList );
return;
}
errlogPrintf (
"osiLocalAddr(): only loopback found\n");
fail:
/* fallback to loopback */
memset ( (void *) &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
free ( pIfreqList );
}
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
{
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
return osiLocalAddrResult;
}
#include "../osi/osdNetIfConf.c"

View File

@@ -0,0 +1 @@
#include "../osi/osdNetIfAddrs.c"

View File

@@ -0,0 +1 @@
#include "../osi/osdNetIfAddrs.c"

View File

@@ -0,0 +1,222 @@
/*************************************************************************\
* 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.
* SPDX-License-Identifier: EPICS
* EPICS Base is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* This file included from various os/.../osdNetIntf.c */
#include <ctype.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "osiSock.h"
#include "epicsAssert.h"
#include "errlog.h"
#include "epicsThread.h"
#ifdef DEBUG
# define ifDepenDebugPrintf(argsInParen) printf argsInParen
#else
# define ifDepenDebugPrintf(argsInParen)
#endif
static osiSockAddr osiLocalAddrResult;
static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT;
/*
* osiSockDiscoverBroadcastAddresses ()
*/
LIBCOM_API void epicsStdCall osiSockDiscoverBroadcastAddresses
(ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr)
{
osiSockAddrNode *pNewNode;
if ( pMatchAddr->sa.sa_family == AF_INET ) {
if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
return;
}
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_port = htons ( 0 );
pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
ellAdd ( pList, &pNewNode->node );
return;
}
}
struct ifaddrs *ifaddr;
int result = getifaddrs (&ifaddr);
if ( result != 0 ) {
errlogPrintf("osiSockDiscoverBroadcastAddresses(): getifaddrs failed.\n");
return;
}
for ( struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next ) {
if ( ifa->ifa_addr == NULL ) {
continue;
}
ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s\n",
ifa->ifa_name));
/*
* If its not an internet interface then dont use it
*/
if ( ifa->ifa_addr->sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", ifa->ifa_name) );
continue;
}
/*
* 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) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) ifa->ifa_addr;
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", ifa->ifa_name) );
continue;
}
}
}
/*
* dont bother with interfaces that have been disabled
*/
if ( ! ( ifa->ifa_flags & IFF_UP ) ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", ifa->ifa_name) );
continue;
}
/*
* dont use the loop back interface
*/
if ( ifa->ifa_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", ifa->ifa_name) );
continue;
}
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
freeifaddrs ( ifaddr );
return;
}
/*
* If this is an interface that supports
* broadcast use 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 ( ifa->ifa_flags & IFF_BROADCAST ) {
osiSockAddr baddr;
baddr.sa = *ifa->ifa_broadaddr;
if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) {
pNewNode->addr.sa = *ifa->ifa_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %08x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
} else {
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %08x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
free ( pNewNode );
continue;
}
}
#if defined (IFF_POINTOPOINT)
else if ( ifa->ifa_flags & IFF_POINTOPOINT ) {
pNewNode->addr.sa = *ifa->ifa_dstaddr;
}
#endif
else {
ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", ifa->ifa_name ) );
free ( pNewNode );
continue;
}
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", ifa->ifa_name) );
/*
* LOCK applied externally
*/
ellAdd ( pList, &pNewNode->node );
}
freeifaddrs ( ifaddr );
}
/*
* osiLocalAddrOnce ()
*/
static void osiLocalAddrOnce (void *raw)
{
struct ifaddrs *ifaddr;
int result = getifaddrs (&ifaddr);
if ( result != 0 ) {
errlogPrintf("osiLocalAddrOnce(): getifaddrs failed.\n");
return;
}
for ( struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next ) {
if ( ifa->ifa_addr == NULL ) {
continue;
}
if ( ! ( ifa->ifa_flags & IFF_UP ) ) {
ifDepenDebugPrintf ( ("osiLocalAddrOnce(): net intf %s was down\n", ifa->ifa_name) );
continue;
}
if ( ifa->ifa_addr->sa_family != AF_INET ) {
ifDepenDebugPrintf ( ("osiLocalAddrOnce(): interface %s was not AF_INET\n", ifa->ifa_name) );
continue;
}
/*
* dont use the loop back interface
*/
if ( ifa->ifa_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiLocalAddrOnce(): ignoring loopback interface: %s\n", ifa->ifa_name) );
continue;
}
ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", ifa->ifa_name) );
osiLocalAddrResult.sa = *ifa->ifa_addr;
freeifaddrs ( ifaddr );
return;
}
errlogPrintf (
"osiLocalAddr(): only loopback found\n");
/* fallback to loopback */
osiSockAddr addr;
memset ( (void *) &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
freeifaddrs ( ifaddr );
}
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
{
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
return osiLocalAddrResult;
}

View File

@@ -0,0 +1,352 @@
/*************************************************************************\
* 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.
* SPDX-License-Identifier: EPICS
* EPICS Base is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* This file included from various os/.../osdNetIntf.c */
/*
* Author: Jeff Hill
* Date: 04-05-94
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "osiSock.h"
#include "epicsAssert.h"
#include "errlog.h"
#include "epicsThread.h"
#ifdef DEBUG
# define ifDepenDebugPrintf(argsInParen) printf argsInParen
#else
# define ifDepenDebugPrintf(argsInParen)
#endif
static osiSockAddr osiLocalAddrResult;
static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT;
/*
* 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 ()
*/
LIBCOM_API void epicsStdCall 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;
if ( pMatchAddr->sa.sa_family == AF_INET ) {
if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) {
pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) );
if ( pNewNode == NULL ) {
errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" );
return;
}
pNewNode->addr.ia.sin_family = AF_INET;
pNewNode->addr.ia.sin_port = htons ( 0 );
pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
ellAdd ( pList, &pNewNode->node );
return;
}
}
/*
* 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;
/*
* 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 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) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
if ( pInetAddr->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;
}
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) );
/*
* 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;
}
/*
* dont use the loop back interface
*/
if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\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 ) {
osiSockAddr baddr;
status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
if ( status ) {
errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
free ( pNewNode );
continue;
}
baddr.sa = pIfreqList->ifr_broadaddr;
if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) {
pNewNode->addr.sa = pIfreqList->ifr_broadaddr;
ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
} else {
ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) );
free ( pNewNode );
continue;
}
}
#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
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 ()
*/
static void osiLocalAddrOnce (void *raw)
{
SOCKET *psocket = raw;
const unsigned nelem = 100;
osiSockAddr addr;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pifreq;
struct ifreq *pIfreqListEnd;
struct ifreq *pnextifreq;
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" );
goto fail;
}
ifconf.ifc_len = nelem * sizeof ( *pIfreqList );
ifconf.ifc_req = pIfreqList;
status = socket_ioctl ( *psocket, 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 );
goto fail;
}
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 ( *psocket, 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) );
osiLocalAddrResult = addrCpy;
free ( pIfreqList );
return;
}
errlogPrintf (
"osiLocalAddr(): only loopback found\n");
fail:
/* fallback to loopback */
memset ( (void *) &addr, '\0', sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
osiLocalAddrResult = addr;
free ( pIfreqList );
}
LIBCOM_API osiSockAddr epicsStdCall osiLocalAddr (SOCKET socket)
{
epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket);
return osiLocalAddrResult;
}