diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 05acf7a..ba06d17 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -258,14 +258,12 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom, // TODO DoS attack? const bool responseRequired = (QOS_REPLY_REQUIRED & qosCode) != 0; - // TODO bloom filter or similar server selection (by GUID) - // - // locally broadcast if unicast (qosCode & 0x80 == 0x80) + // locally broadcast if unicast (qosCode & 0x80 == 0x80) via UDP // if ((qosCode & 0x80) == 0x80) { - BlockingUDPTransport::shared_pointer bt = _context->getBroadcastTransport(); + BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast(transport); if (bt && bt->hasLocalMulticastAddress()) { // clear unicast flag diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index 44f4cff..f30bd35 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -33,7 +33,6 @@ ServerContextImpl::ServerContextImpl(): _serverPort(PVA_SERVER_PORT), _receiveBufferSize(MAX_TCP_RECV), _timer(), - _broadcastTransport(), _beaconEmitter(), _acceptor(), _transportRegistry(), @@ -166,20 +165,19 @@ void ServerContextImpl::loadConfiguration() _channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames); _channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames); - _ifaceBCast.ia.sin_family = AF_UNSPEC; - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { - ELLLIST alist = ELLLIST_INIT; - SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); - if(sock) { - osiSockDiscoverBroadcastAddresses(&alist, sock, &_ifaceAddr); - epicsSocketDestroy(sock); - } - if(ellCount(&alist)>0) { - osiSockAddrNode *node = (osiSockAddrNode*)ellFirst(&alist); - _ifaceBCast = node->addr; - } - ellFree(&alist); + SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if (!sock) { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); + return; } + + if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr) || _ifaceList.size() == 0) + { + THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport, no interfaces available."); + return; + } + + epicsSocketDestroy(sock); } bool ServerContextImpl::isChannelProviderNamePreconfigured() @@ -269,201 +267,230 @@ void ServerContextImpl::internalInitialize() // setup broadcast UDP transport initializeBroadcastTransport(); - // TODO introduce a constant + // TODO introduce "tcp" a constant _beaconEmitter.reset(new BeaconEmitter("tcp", _broadcastTransport, thisServerContext)); } void ServerContextImpl::initializeBroadcastTransport() { - // setup UDP transport - try - { - // where to bind (listen) address - osiSockAddr listenLocalAddress; - listenLocalAddress.ia.sin_family = AF_INET; - listenLocalAddress.ia.sin_port = htons(_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = _ifaceAddr.ia.sin_addr.s_addr; + TransportClient::shared_pointer nullTransportClient; + auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); + + osiSockAddr anyAddress; + anyAddress.ia.sin_family = AF_INET; + anyAddress.ia.sin_port = htons(0); + anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + _broadcastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + anyAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + + // + // set broadcast (send) addresses - where to send beacons + // + + InetAddrVector autoBCastAddr; + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + if (node.ifaceBCast.ia.sin_family != AF_UNSPEC) + { + node.ifaceBCast.ia.sin_port = htons(_broadcastPort); + autoBCastAddr.push_back(node.ifaceBCast); + } + } + + // + // set beacon (broadcast) address list + // - TransportClient::shared_pointer nullTransportClient; + // set broadcast address list + if (!_beaconAddressList.empty()) + { + // if auto is true, add it to specified list + if (!_autoBeaconAddressList) + autoBCastAddr.clear(); - auto_ptr broadcastConnector(new BlockingUDPConnector(true, true, true)); - _broadcastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - listenLocalAddress, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - listenLocalAddress = *_broadcastTransport->getRemoteAddress(); - _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); - _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; + auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, &autoBCastAddr)); + if (list.get() && list->size()) + { + _broadcastTransport->setSendAddresses(list.get()); + } + else // TODO or no fallback at all + { + // fallback + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } + } + else + { + // fallback + // set default (auto) address list + _broadcastTransport->setSendAddresses(&autoBCastAddr); + } - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { - if(_ifaceBCast.ia.sin_family == AF_UNSPEC || - _ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { - LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(_ifaceBCast, false).c_str()); + + // debug output for broadcast addresses + InetAddrVector* blist = _broadcastTransport->getSendAddresses(); + if (!blist || !blist->size()) + LOG(logLevelWarn, + "No beacon broadcast addresses found or specified!"); + else + for (size_t i = 0; i < blist->size(); i++) + LOG(logLevelDebug, + "Beacon broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); + + // + // set ignore address list + // + auto_ptr ignoreAddressList; + if (!_ignoreAddressList.empty()) + { + // we do not care about the port + ignoreAddressList.reset(getSocketAddressList(_ignoreAddressList, 0, NULL)); + } + + + + // TODO configurable local NIF, address + osiSockAddr loAddr; + getLoopbackNIF(loAddr, "", 0); + + for (IfaceNodeVector::const_iterator iter = _ifaceList.begin(); iter != _ifaceList.end(); iter++) + { + ifaceNode node = *iter; + + LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s, index %d.", + inetAddressToString(node.ifaceAddr, false).c_str(), + inetAddressToString(node.ifaceBCast, false).c_str(), + node.ifaceIndex); + try + { + // where to bind (listen) address + // TODO opt copy + osiSockAddr listenLocalAddress; + listenLocalAddress.ia.sin_family = AF_INET; + listenLocalAddress.ia.sin_port = htons(_broadcastPort); + listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr; + + BlockingUDPTransport::shared_pointer transport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + listenLocalAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *transport->getRemoteAddress(); + + if (ignoreAddressList.get() && ignoreAddressList->size()) + transport->setIgnoredAddresses(ignoreAddressList.get()); + + + BlockingUDPTransport::shared_pointer transport2; + + if(node.ifaceBCast.ia.sin_family == AF_UNSPEC || + node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) { + LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceBCast, false).c_str()); + } + #if !defined(_WIN32) + else + { + /* An oddness of BSD sockets (not winsock) is that binding to + * INADDR_ANY will receive unicast and broadcast, but binding to + * a specific interface address receives only unicast. The trick + * is to bind a second socket to the interface broadcast address, + * which will then receive only broadcasts. + */ + + // TODO opt copy + osiSockAddr bcastAddress; + bcastAddress.ia.sin_family = AF_INET; + bcastAddress.ia.sin_port = htons(_broadcastPort); + bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr; + + transport2 = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + bcastAddress, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + /* The other wrinkle is that nothing should be sent from this second + * socket. So replies are made through the unicast socket. + */ + transport2->setReplyTransport(transport); + + if (ignoreAddressList.get() && ignoreAddressList->size()) + transport2->setIgnoredAddresses(ignoreAddressList.get()); + } + #endif + + // TODO set ignore list on transport and transport2 + + + // + // Setup local broadcasting + // + // Each network interface gets its own multicast group on a local interface. + // Multicast address is determined by prefix 224.0.0.128 + NIF index + // + + osiSockAddr group; + int lastAddr = 128 + node.ifaceIndex; + std::ostringstream o; + // TODO configurable prefix and base + o << "224.0.0." << lastAddr; + aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); + + transport->setMutlicastNIF(loAddr, true); + transport->setLocalMulticastAddress(group); + + BlockingUDPTransport::shared_pointer localMulticastTransport; + + if (true) + { + try + { + // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address + localMulticastTransport = static_pointer_cast(broadcastConnector->connect( + nullTransportClient, _responseHandler, + group, PVA_PROTOCOL_REVISION, + PVA_DEFAULT_PRIORITY)); + localMulticastTransport->join(group, loAddr); + + LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", + inetAddressToString(listenLocalAddress, false).c_str(), + inetAddressToString(loAddr, false).c_str(), + inetAddressToString(group).c_str()); + } + catch (std::exception& ex) + { + LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); + } } -#if !defined(_WIN32) else { - /* An oddness of BSD sockets (not winsock) is that binding to - * INADDR_ANY will receive unicast and broadcast, but binding to - * a specific interface address receives only unicast. The trick - * is to bind a second socket to the interface broadcast address, - * which will then receive only broadcasts. - */ - _broadcastTransport2 = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - _ifaceBCast, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - /* The other wrinkle is that nothing should be sent from this second - * socket. So replies are made through the unicast socket. - */ - _broadcastTransport2->setReplyTransport(_broadcastTransport); + LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); } + + transport->start(); + if(transport2) + transport2->start(); + if (localMulticastTransport) + localMulticastTransport->start(); + + _udpTransports.push_back(transport); + _udpTransports.push_back(transport2); + _udpTransports.push_back(localMulticastTransport); + } -#endif - - - SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (socket == INVALID_SOCKET) + catch (std::exception& e) + { + THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); + } + catch (...) { THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); } - - - auto_ptr broadcastAddresses; - if(_ifaceAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) - { - InetAddrVector * v = new InetAddrVector; - v->push_back(_ifaceBCast); - broadcastAddresses.reset(v); - } - else - { - // all the interfaces - broadcastAddresses.reset(getBroadcastAddresses(socket, _broadcastPort)); - } - - int ifIndex = discoverInterfaceIndex(socket, &listenLocalAddress); - if (ifIndex == -1) - { - LOG(logLevelWarn, "Unable to find interface index for %s.", inetAddressToString(listenLocalAddress, false).c_str()); - // TODO fallback - } - - epicsSocketDestroy(socket); - - - // set default (auto) address list - _broadcastTransport->setSendAddresses(broadcastAddresses.get()); - - // set broadcast address list - if (!_beaconAddressList.empty()) - { - // if auto is true, add it to specified list - InetAddrVector* appendList = NULL; - if (_autoBeaconAddressList == true) - { - appendList = _broadcastTransport->getSendAddresses(); - } - - auto_ptr list(getSocketAddressList(_beaconAddressList, _broadcastPort, appendList)); - if (list.get() != NULL && list->size() > 0) - { - _broadcastTransport->setSendAddresses(list.get()); - } - } - - - // debug output for broadcast addresses - InetAddrVector* blist = _broadcastTransport->getSendAddresses(); - if (!blist || !blist->size()) - LOG(logLevelWarn, - "No broadcast addresses found or specified!"); - else - for (size_t i = 0; i < blist->size(); i++) - LOG(logLevelDebug, - "Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str()); - - // - // set ignore address list - // - if (!_ignoreAddressList.empty()) - { - // we do not care about the port - auto_ptr list(getSocketAddressList(_ignoreAddressList, 0, NULL)); - if (list.get() != NULL && list->size() > 0) - { - _broadcastTransport->setIgnoredAddresses(list.get()); - } - } - - // - // Setup local broadcasting - // - // Each network interface gets its own multicast group on a local interface. - // Multicast address is determined by prefix 224.0.0.124 + NIF index - // - - // TODO configurable local NIF, address - osiSockAddr loAddr; - getLoopbackNIF(loAddr, "", 0); - - osiSockAddr group; - int lastAddr = 128 + ifIndex; - std::ostringstream o; - // TODO configurable prefix and base - o << "224.0.0." << lastAddr; - aToIPAddr(o.str().c_str(), _broadcastPort, &group.ia); - - _broadcastTransport->setMutlicastNIF(loAddr, true); - _broadcastTransport->setLocalMulticastAddress(group); - - if (true) - { - try - { - // NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address - _localMulticastTransport = static_pointer_cast(broadcastConnector->connect( - nullTransportClient, _responseHandler, - group, PVA_PROTOCOL_REVISION, - PVA_DEFAULT_PRIORITY)); - _localMulticastTransport->join(group, loAddr); - /* used for sending - _localMulticastTransport->setMutlicastNIF(loAddr, true); - InetAddrVector sendAddressList; - sendAddressList.push_back(group); - _localMulticastTransport->setSendAddresses(&sendAddressList); - */ - - LOG(logLevelDebug, "Local multicast for %s enabled on %s/%s.", - inetAddressToString(listenLocalAddress, false).c_str(), - inetAddressToString(loAddr, false).c_str(), - inetAddressToString(group).c_str()); - } - catch (std::exception& ex) - { - LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what()); - } - } - else - { - LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled."); - } - - _broadcastTransport->start(); - if(_broadcastTransport2) - _broadcastTransport2->start(); - if (_localMulticastTransport) - _localMulticastTransport->start(); - } - catch (std::exception& e) - { - THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e); - } - catch (...) - { - THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport"); - } + } } void ServerContextImpl::run(int32 seconds) @@ -552,34 +579,32 @@ void ServerContextImpl::destroy() void ServerContextImpl::internalDestroy() { // stop responding to search requests - if (_broadcastTransport) - _broadcastTransport->close(); - if (_broadcastTransport2) - _broadcastTransport2->close(); - _broadcastTransport.reset(); - _broadcastTransport2.reset(); + for (BlockingUDPTransportVector::const_iterator iter = _udpTransports.begin(); + iter != _udpTransports.end(); iter++) + (*iter)->close(); + _udpTransports.clear(); - // and close local multicast transport - if (_localMulticastTransport.get()) + // stop emitting beacons + if (_beaconEmitter) { - _localMulticastTransport->close(); - _localMulticastTransport.reset(); + _beaconEmitter->destroy(); + _beaconEmitter.reset(); + } + + // close UDP sent transport + if (_broadcastTransport) + { + _broadcastTransport->close(); + _broadcastTransport.reset(); } // stop accepting connections - if (_acceptor.get()) + if (_acceptor) { _acceptor->destroy(); _acceptor.reset(); } - // stop emitting beacons - if (_beaconEmitter.get()) - { - _beaconEmitter->destroy(); - _beaconEmitter.reset(); - } - // this will also destroy all channels destroyAllTransports(); } @@ -732,12 +757,7 @@ osiSockAddr* ServerContextImpl::getServerInetAddress() BlockingUDPTransport::shared_pointer ServerContextImpl::getBroadcastTransport() { - return _broadcastTransport; -} - -BlockingUDPTransport::shared_pointer ServerContextImpl::getLocalMulticastTransport() -{ - return _localMulticastTransport; + return _broadcastTransport; } ChannelProviderRegistry::shared_pointer ServerContextImpl::getChannelProviderRegistry() diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 4e92ecc..e75f42b 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -145,8 +145,6 @@ public: virtual void newServerDetected(); - BlockingUDPTransport::shared_pointer getLocalMulticastTransport(); - epicsTimeStamp& getStartTime(); @@ -256,13 +254,13 @@ public: */ osiSockAddr* getServerInetAddress(); - /** - * Broadcast transport. - * @return broadcast transport. - */ - BlockingUDPTransport::shared_pointer getBroadcastTransport(); + /** + * Broadcast (UDP send) transport. + * @return broadcast transport. + */ + BlockingUDPTransport::shared_pointer getBroadcastTransport(); - /** + /** * Get channel provider registry implementation used by this instance. * @return channel provider registry used by this instance. */ @@ -310,7 +308,12 @@ private: */ std::string _beaconAddressList; - osiSockAddr _ifaceAddr, _ifaceBCast; + /** + * List of used NIF. + */ + IfaceNodeVector _ifaceList; + + osiSockAddr _ifaceAddr; /** * A space-separated list of address from which to ignore name resolution requests. @@ -349,14 +352,15 @@ private: epics::pvData::Timer::shared_pointer _timer; /** - * Broadcast transport needed for channel searches. + * UDP transports needed to receive channel searches. */ - BlockingUDPTransport::shared_pointer _broadcastTransport, _broadcastTransport2; + typedef std::vector BlockingUDPTransportVector; + BlockingUDPTransportVector _udpTransports; /** - * Local broadcast transport needed for local fan-out. + * UDP socket used to sending. */ - BlockingUDPTransport::shared_pointer _localMulticastTransport; + BlockingUDPTransport::shared_pointer _broadcastTransport; /** * Beacon emitter. diff --git a/src/utils/inetAddressUtil.cpp b/src/utils/inetAddressUtil.cpp index 27cbde5..640f82d 100644 --- a/src/utils/inetAddressUtil.cpp +++ b/src/utils/inetAddressUtil.cpp @@ -248,8 +248,7 @@ static struct ifreq * ifreqNext ( struct ifreq *pifreq ) return ifr; } -int discoverInterfaceIndex - (SOCKET socket, const osiSockAddr *pMatchAddr) +int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr) { static const unsigned nelem = 100; int status; @@ -258,16 +257,7 @@ int discoverInterfaceIndex struct ifreq *pIfreqListEnd; struct ifreq *pifreq; struct ifreq *pnextifreq; - - /* - * check if pMatchAddr is valid - */ - if ( pMatchAddr->sa.sa_family == AF_UNSPEC || - pMatchAddr->sa.sa_family != AF_INET || - pMatchAddr->ia.sin_addr.s_addr == htonl(INADDR_ANY) ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): invalid pMatchAddr\n"); - return -1; - } + int match; /* * use pool so that we avoid using too much stack space @@ -277,7 +267,7 @@ int discoverInterfaceIndex */ pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); if (!pIfreqList) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): no memory to complete request\n"); + errlogPrintf ("discoverInterfaces(): no memory to complete request\n"); return -1; } @@ -285,7 +275,8 @@ int discoverInterfaceIndex ifconf.ifc_req = pIfreqList; status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); if (status < 0 || ifconf.ifc_len == 0) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): unable to fetch network interface configuration\n"); + /*ifDepenDebugPrintf(("discoverInterfaces(): status: 0x08x, ifconf.ifc_len: %d\n", status, ifconf.ifc_len));*/ + errlogPrintf ("discoverInterfaces(): unable to fetch network interface configuration\n"); free (pIfreqList); return -1; } @@ -306,10 +297,16 @@ int discoverInterfaceIndex /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ memmove(pIfreqList, pifreq, current_ifreqsize); + /*ifDepenDebugPrintf (("discoverInterfaces(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n", + pIfreqList->ifr_name, + ifreq_size(pifreq), + current_ifreqsize));*/ + /* * If its not an internet interface then dont use it */ if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );*/ continue; } @@ -317,38 +314,110 @@ int discoverInterfaceIndex * if it isnt a wildcarded interface then look for * an exact match */ - struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; - if ( pInetAddr->sin_addr.s_addr == pMatchAddr->ia.sin_addr.s_addr ) { + match = 0; + if ( pMatchAddr && 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 ( ("discoverInterfaces(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );*/ + continue; + } + else + match = 1; + } + } - unsigned int index = if_nametoindex(pIfreqList->ifr_name); - if ( !index ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - free (pIfreqList); - return -1; - } + status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); + if ( status ) { + errlogPrintf ("discoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } - free (pIfreqList); - return index; + /* + * dont bother with interfaces that have been disabled + */ + if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" was down\n", pIfreqList->ifr_name) );*/ + continue; + } - /* - status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); - if ( status ) { - errlogPrintf ("osiSockDiscoverInterfaceIndex(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); - free (pIfreqList); - return -1; - } + /* + * dont use the loop back interface, unless it maches pMatchAddr + */ + if (!match) { + if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { + /*ifDepenDebugPrintf ( ("discoverInterfaces(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );*/ + continue; + } + } - free (pIfreqList); - return pIfreqList->ifr_ifindex; - */ + ifaceNode node; + node.ifaceAddr.sa = pIfreqList->ifr_addr; + + /* + * 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 ("discoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); + continue; + } + node.ifaceBCast.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 ( ("discoverInterfaces(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) );*/ + continue; + } + node.ifaceBCast.sa = pIfreqList->ifr_dstaddr; + } +#endif + else { + /*ifDepenDebugPrintf ( ( "discoverInterfaces(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );*/ + continue; } - } - /* not found */ - free ( pIfreqList ); - return -1; -} + unsigned int index = if_nametoindex(pIfreqList->ifr_name); + if ( !index ) { + errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + + node.ifaceIndex = index; + + /* + status = socket_ioctl ( socket, SIOCGIFINDEX, pIfreqList ); + if ( status ) { + errlogPrintf ("discoverInterfaces(): net intf index fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + + node.ifaceIndex = pIfreqList->ifr_ifindex; + */ + + /*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" found\n", pIfreqList->ifr_name) );*/ + + list.push_back(node); + } + + free ( pIfreqList ); + return 0; + } } diff --git a/src/utils/inetAddressUtil.h b/src/utils/inetAddressUtil.h index b46b736..7c83983 100644 --- a/src/utils/inetAddressUtil.h +++ b/src/utils/inetAddressUtil.h @@ -42,6 +42,13 @@ namespace pvAccess { */ epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort); + struct ifaceNode { + int ifaceIndex; + osiSockAddr ifaceAddr, ifaceBCast; + }; + typedef std::vector IfaceNodeVector; + epicsShareFunc int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr = 0); + /** * Returns NIF index for given interface address, or -1 on failure. */