From 1adff7f6c77487c697d8a98d4eb041f8d72006fc Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Wed, 28 Jun 2000 16:33:51 +0000 Subject: [PATCH] updated to support new BSD network interface query API --- src/ca/if_depen.c | 465 +++++++++++++++++++++++----------------------- 1 file changed, 231 insertions(+), 234 deletions(-) diff --git a/src/ca/if_depen.c b/src/ca/if_depen.c index 9d263cb8b..b8d7e7193 100644 --- a/src/ca/if_depen.c +++ b/src/ca/if_depen.c @@ -34,7 +34,6 @@ * (and not N-1 interfaces) */ - static char *sccsId = "@(#) $Id$"; #include "iocinf.h" @@ -45,263 +44,261 @@ static char *sccsId = "@(#) $Id$"; * a higher level. */ - +#ifdef DEBUG +# define ifDepenDebugPrintf(argsInParen) printf argsInParen +#else +# define ifDepenDebugPrintf(argsInParen) +#endif + /* - * local_addr() - * - * A valid non-loopback local address is required in the - * beacon message in certain situations where - * there are beacon repeaters and there are addresses - * in the EPICS_CA_ADDRESS_LIST for which we dont have - * a strictly correct local server address on a multi-interface - * system. In this situation we use the first valid non-loopback local - * address found in the beacon message. + * Move to the next ifreq structure + * Made difficult by the fact that addresses larger than the structure + * size may be returned from the kernel. */ -int local_addr(int s, struct sockaddr_in *plcladdr) +static struct ifreq * ifreqNext (struct ifreq *pifreq) { - int status; - struct ifconf ifconf; - struct ifreq ifreq[25]; - struct ifreq *pifreq; - static struct sockaddr_in addr; - static char init = FALSE; - struct sockaddr_in *tmpaddr; + unsigned int size; - if (init){ - *plcladdr = addr; - return OK; - } - - /* - * get the addr of the first interface found - * (report inconsistent interfaces however) - */ - ifconf.ifc_len = sizeof ifreq; - ifconf.ifc_req = ifreq; - status = socket_ioctl(s, SIOCGIFCONF, &ifconf); - if (status < 0 || ifconf.ifc_len == 0) { - ca_printf( - "CAC: SIOCGIFCONF ioctl failed because \"%s\"\n", - SOCKERRSTR); - ifconf.ifc_len = 0; - } - -#ifdef DEBUG - ca_printf("CAC: %d net intf found\n", ifconf.ifc_len/sizeof(*pifreq)); +#if ( defined (BSD) && ( BSD >= 44 ) ) || defined ( SOCKADDR_HAS_LEN ) + size = pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name); + if (size < sizeof(*pifreq)) + size = sizeof(*pifreq); +#else + size = sizeof(*pifreq); #endif - - for ( pifreq = ifconf.ifc_req; - ((size_t)ifconf.ifc_len) >= sizeof(*pifreq); - pifreq++, ifconf.ifc_len -= sizeof(*pifreq)) { - - status = socket_ioctl(s, SIOCGIFFLAGS, pifreq); - if (status == ERROR){ - ca_printf("CAC: net intf flags fetch for %s failed\n", pifreq->ifr_name); - continue; - } - - if (!(pifreq->ifr_flags & IFF_UP)) { -#ifdef DEBUG - ca_printf("CAC: net intf %s was down\n", pifreq->ifr_name); -#endif - continue; - } - -#ifdef DEBUG - ca_printf("CAC: net intf %s found\n", pifreq->ifr_name); -#endif - - /* - * dont use the loop back interface - */ - if (pifreq->ifr_flags & IFF_LOOPBACK) { - continue; - } - - status = socket_ioctl(s, SIOCGIFADDR, pifreq); - if (status == ERROR){ -#ifdef DEBUG - ca_printf("CAC: could not obtain addr for %s\n", pifreq->ifr_name); -#endif - continue; - } - - if (pifreq->ifr_addr.sa_family != AF_INET){ -#ifdef DEBUG - ca_printf("CAC: interface %s was not AF_INET\n", pifreq->ifr_name); -#endif - continue; - } - - tmpaddr = (struct sockaddr_in *) &pifreq->ifr_addr; - - init = TRUE; - addr = *tmpaddr; - break; - } - - if(!init){ - return ERROR; - } - - *plcladdr = addr; - return OK; + return (struct ifreq *)(size + (char *)pifreq); } - - /* - * caDiscoverInterfaces() - * - * This routine is provided with the address of an ELLLIST, a socket - * a destination port number, and a match address. When the - * routine returns there will be one additional inet address - * (a caAddrNode) in the list for each inet interface found that - * is up and isnt a loop back interface (match addr is INADDR_ANY) - * or it matches the specified interface (match addr isnt INADDR_ANY). - * If the interface supports broadcast then I add its broadcast - * address to the list. If the interface is a point to - * point link then I add the destination address of the point to - * point link to the list. In either case I set the port number - * in the address node to the port supplied in the argument - * list. - * - * LOCK should be applied here for (pList) - * (this is also called from the server) + * caDiscoverInterfaces () */ -void epicsShareAPI caDiscoverInterfaces - (ELLLIST *pList, int socket, unsigned short port, struct in_addr matchAddr) +epicsShareFunc void epicsShareAPI caDiscoverInterfaces + (ELLLIST *pList, SOCKET socket, unsigned short port, struct in_addr matchAddr) { - struct sockaddr_in localAddr; - struct sockaddr_in *pInetAddr; - caAddrNode *pNode; - int status; - struct ifconf ifconf; - struct ifreq *pIfreqList; - struct ifreq *pifreq; - unsigned nelem; + static const unsigned nelem = 100; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pIfreqListEnd; + struct ifreq *pifreq; + struct ifreq *pnextifreq; + caAddrNode *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 ("osiSockDiscoverInterfaces(): 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 ("osiSockDiscoverInterfaces(): unable to fetch network interface configuration\n"); + free (pIfreqList); + return; + } + + pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList); + pIfreqListEnd--; - /* - * use pool so that we avoid using to much stack space - * under vxWorks - * - * nelem is set to the maximum interfaces - * on one machine here - */ - nelem = 100; - pIfreqList = (struct ifreq *)calloc(nelem, sizeof(*pifreq)); - if(!pIfreqList){ - return; - } + for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { - ifconf.ifc_len = nelem*sizeof(*pifreq); - ifconf.ifc_req = pIfreqList; - status = socket_ioctl(socket, SIOCGIFCONF, &ifconf); - if (status < 0 || ifconf.ifc_len == 0) { - free(pIfreqList); - return; - } + /* + * find the next if req + */ + pnextifreq = ifreqNext (pifreq); - nelem = ifconf.ifc_len/sizeof(struct ifreq); - for (pifreq = pIfreqList; pifreq<(pIfreqList+nelem); pifreq++){ - status = socket_ioctl(socket, SIOCGIFFLAGS, pifreq); - if (status){ - continue; - } + /* + * If its not an internet interface then dont use it + */ + if ( pifreq->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): interface \"%s\" was not AF_INET\n", pifreq->ifr_name) ); + continue; + } - /* - * dont bother with interfaces that have been disabled - */ - if (!(pifreq->ifr_flags & IFF_UP)) { - continue; - } + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + if ( matchAddr.s_addr != htonl (INADDR_ANY) ) { + struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pifreq->ifr_addr; + if ( pInetAddr->sin_addr.s_addr != matchAddr.s_addr ) { + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): net intf \"%s\" didnt match\n", pifreq->ifr_name) ); + continue; + } + } - /* - * dont use the loop back interface - */ - if (pifreq->ifr_flags & IFF_LOOPBACK) { - continue; - } + status = socket_ioctl ( socket, SIOCGIFFLAGS, pifreq ); + if ( status ) { + errlogPrintf ("osiSockDiscoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pifreq->ifr_name); + continue; + } + + /* + * dont bother with interfaces that have been disabled + */ + if ( ! ( pifreq->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): net intf \"%s\" was down\n", pifreq->ifr_name) ); + continue; + } - /* - * Fetch the local address for this interface - */ - status = socket_ioctl(socket, SIOCGIFADDR, pifreq); - if (status){ - continue; - } + /* + * dont use the loop back interface + */ + if ( pifreq->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): ignoring loopback interface: \"%s\"\n", pifreq->ifr_name) ); + continue; + } - /* - * If its not an internet inteface - * then dont use it. - */ - if (pifreq->ifr_addr.sa_family != AF_INET) { - continue; - } + pNewNode = (caAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + errlogPrintf ( "osiSockDiscoverInterfaces(): no memory available for configuration\n" ); + free ( pIfreqList ); + return; + } - /* - * save the interface's IP address - */ - pInetAddr = (struct sockaddr_in *)&pifreq->ifr_addr; - localAddr = *pInetAddr; + /* + * 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 ( pifreq->ifr_flags & IFF_BROADCAST ) { + status = socket_ioctl (socket, SIOCGIFBRDADDR, pifreq); + if ( status ) { + errlogPrintf ("osiSockDiscoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pifreq->ifr_name); + free ( pNewNode ); + continue; + } + pNewNode->destAddr.sa = pifreq->ifr_broadaddr; + } + else if ( pifreq->ifr_flags & IFF_POINTOPOINT ) { + status = socket_ioctl ( socket, SIOCGIFDSTADDR, pifreq); + if ( status ) { + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): net intf \"%s\": pt to pt addr fetch fail\n", pifreq->ifr_name) ); + free ( pNewNode ); + continue; + } + pNewNode->destAddr.sa = pifreq->ifr_dstaddr; + } + else { + errlogPrintf ( "osiSockDiscoverInterfaces(): net intf \"%s\": not pt to pt or bcast?\n", pifreq->ifr_name ); + free ( pNewNode ); + continue; + } - /* - * if it isnt a wildcarded interface then look for - * an exact match - */ - if (matchAddr.s_addr != htonl(INADDR_ANY)) { - if (pInetAddr->sin_addr.s_addr != matchAddr.s_addr) { - continue; - } - } + if ( pNewNode->destAddr.sa.sa_family == AF_INET ) { + pNewNode->destAddr.in.sin_port = htons ( port ); + } - /* - * 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 (pifreq->ifr_flags & IFF_BROADCAST) { - status = socket_ioctl( - socket, - SIOCGIFBRDADDR, - pifreq); - if (status){ - continue; - } - } - else if(pifreq->ifr_flags & IFF_POINTOPOINT){ - status = socket_ioctl( - socket, - SIOCGIFDSTADDR, - pifreq); - if (status){ - continue; - } - } - else{ - continue; - } - - pNode = (caAddrNode *) calloc(1,sizeof(*pNode)); - if(!pNode){ - continue; - } - - pNode->destAddr.in = *pInetAddr; - pNode->destAddr.in.sin_port = htons(port); - pNode->srcAddr.in = localAddr; + ifDepenDebugPrintf ( ("osiSockDiscoverInterfaces(): net intf \"%s\" found\n", pifreq->ifr_name) ); /* * LOCK applied externally */ - ellAdd(pList, &pNode->node); - } + ellAdd ( pList, &pNewNode->node ); + } - free(pIfreqList); + free ( pIfreqList ); } + +/* + * osiLocalAddr () + */ +int local_addr (SOCKET socket, struct sockaddr_in *plcladdr) +{ + static const unsigned nelem = 100; + static char init = 0; + static caAddr addr; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pifreq; + struct ifreq *pIfreqListEnd; + struct ifreq *pnextifreq; + if ( init ) { + *plcladdr = addr.in; + return OK; + } + + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.in.sin_family = AF_UNSPEC; + + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) ); + if ( ! pIfreqList ) { + errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" ); + return ERROR; + } + + ifconf.ifc_len = nelem * sizeof ( *pIfreqList ); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl ( socket, SIOCGIFCONF, &ifconf ); + if ( status < 0 || ifconf.ifc_len == 0 ) { + errlogPrintf ( + "CAC: SIOCGIFCONF ioctl failed because %d\n", + SOCKERRNO ); + free ( pIfreqList ); + return ERROR; + } + + pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req ); + pIfreqListEnd--; + + for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + caAddr addrCpy; + + /* + * find the next if req + */ + pnextifreq = ifreqNext ( pifreq ); + + if ( pifreq->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("local_addr: interface %s was not AF_INET\n", pifreq->ifr_name) ); + continue; + } + + addrCpy.sa = pifreq->ifr_addr; + + status = socket_ioctl ( socket, SIOCGIFFLAGS, pifreq ); + if ( status < 0 ) { + errlogPrintf ( "local_addr: net intf flags fetch for %s failed\n", pifreq->ifr_name ); + continue; + } + + if ( ! ( pifreq->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("local_addr: net intf %s was down\n", pifreq->ifr_name) ); + continue; + } + + /* + * dont use the loop back interface + */ + if ( pifreq->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("local_addr: ignoring loopback interface: %s\n", pifreq->ifr_name) ); + continue; + } + + ifDepenDebugPrintf ( ("local_addr: net intf %s found\n", pifreq->ifr_name) ); + + init = 1; + addr = addrCpy; + break; + } + + free ( pIfreqList ); + + *plcladdr = addr.in; + return OK; +}