From b3d58266a76bfb531b3bbc689eb587ed55aeb870 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Dec 2015 15:24:06 -0500 Subject: [PATCH] ServerContextImpl: bind to a single interface Allow config option EPICS_PVAS_INTF_ADDR_LIST specify a single interface (multiple interfaces to be handled later) Bind TCP listen and UDP sender to the interface address. For non-windows, bind a second UDP socket to the interface broadcast address. Allow dynamic broadcast port --- src/remote/blockingUDPTransport.cpp | 2 +- src/server/serverContext.cpp | 70 +++++++++++++++++++++++++---- src/server/serverContext.h | 4 +- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/remote/blockingUDPTransport.cpp b/src/remote/blockingUDPTransport.cpp index d8621b1..7caa4a4 100644 --- a/src/remote/blockingUDPTransport.cpp +++ b/src/remote/blockingUDPTransport.cpp @@ -434,7 +434,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so imreq.imr_multiaddr.s_addr = mcastAddr.ia.sin_addr.s_addr; imreq.imr_interface.s_addr = nifAddr.ia.sin_addr.s_addr; - // join multicast group on default interface + // join multicast group on the given interface int status = ::setsockopt(_channel, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&imreq, sizeof(struct ip_mreq)); if (status) diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index a747670..6ec64dc 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -138,6 +138,11 @@ void ServerContextImpl::loadConfiguration() if (debugLevel > 0) SET_LOG_LEVEL(logLevelDebug); + _ifaceAddr.ia.sin_family = AF_INET; + _ifaceAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY); + _ifaceAddr.ia.sin_port = 0; + config->getPropertyAsAddress("EPICS_PVAS_INTF_ADDR_LIST", &_ifaceAddr); + _beaconAddressList = config->getPropertyAsString("EPICS_PVA_ADDR_LIST", _beaconAddressList); _beaconAddressList = config->getPropertyAsString("EPICS_PVAS_BEACON_ADDR_LIST", _beaconAddressList); @@ -149,6 +154,7 @@ void ServerContextImpl::loadConfiguration() _serverPort = config->getPropertyAsInteger("EPICS_PVA_SERVER_PORT", _serverPort); _serverPort = config->getPropertyAsInteger("EPICS_PVAS_SERVER_PORT", _serverPort); + _ifaceAddr.ia.sin_port = htons(_serverPort); _broadcastPort = config->getPropertyAsInteger("EPICS_PVA_BROADCAST_PORT", _broadcastPort); _broadcastPort = config->getPropertyAsInteger("EPICS_PVAS_BROADCAST_PORT", _broadcastPort); @@ -158,6 +164,21 @@ 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); + } } bool ServerContextImpl::isChannelProviderNamePreconfigured() @@ -246,7 +267,7 @@ void ServerContextImpl::internalInitialize() ServerContextImpl::shared_pointer thisServerContext = shared_from_this(); - _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _serverPort, _receiveBufferSize)); + _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _ifaceAddr, _receiveBufferSize)); _serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port); // setup broadcast UDP transport @@ -265,7 +286,7 @@ void ServerContextImpl::initializeBroadcastTransport() osiSockAddr listenLocalAddress; listenLocalAddress.ia.sin_family = AF_INET; listenLocalAddress.ia.sin_port = htons(_broadcastPort); - listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY); + listenLocalAddress.ia.sin_addr.s_addr = _ifaceAddr.ia.sin_addr.s_addr; // where to send addresses SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -284,7 +305,36 @@ void ServerContextImpl::initializeBroadcastTransport() nullTransportClient, responseHandler, listenLocalAddress, PVA_PROTOCOL_REVISION, PVA_DEFAULT_PRIORITY)); + listenLocalAddress = *_broadcastTransport->getRemoteAddress(); _broadcastTransport->setSendAddresses(broadcastAddresses.get()); + _broadcastPort = ntohs(listenLocalAddress.ia.sin_port); + +#if !defined(_WIN32) + 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\n"); + } 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. + */ + _ifaceBCast.ia.sin_port = listenLocalAddress.ia.sin_port; + + responseHandler = createResponseHandler(); + _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); + } + } +#endif // set ignore address list if (!_ignoreAddressList.empty()) @@ -324,7 +374,7 @@ void ServerContextImpl::initializeBroadcastTransport() { osiSockAddr group; aToIPAddr("224.0.0.128", _broadcastPort, &group.ia); - _broadcastTransport->join(group, loAddr); + _broadcastTransport->join(group, _ifaceAddr); osiSockAddr anyAddress; anyAddress.ia.sin_family = AF_INET; @@ -356,6 +406,8 @@ void ServerContextImpl::initializeBroadcastTransport() } _broadcastTransport->start(); + if(_broadcastTransport2) + _broadcastTransport2->start(); if (_localMulticastTransport) _localMulticastTransport->start(); } @@ -455,11 +507,13 @@ void ServerContextImpl::destroy() void ServerContextImpl::internalDestroy() { // stop responding to search requests - if (_broadcastTransport.get()) - { - _broadcastTransport->close(); - _broadcastTransport.reset(); - } + if (_broadcastTransport) + _broadcastTransport->close(); + if (_broadcastTransport2) + _broadcastTransport2->close(); + _broadcastTransport.reset(); + _broadcastTransport2.reset(); + // and close local multicast transport if (_localMulticastTransport.get()) { diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 80a3987..3d22720 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -312,6 +312,8 @@ private: */ std::string _beaconAddressList; + osiSockAddr _ifaceAddr, _ifaceBCast; + /** * A space-separated list of address from which to ignore name resolution requests. * Each address must be of the form: ip.number:port or host.name:port @@ -351,7 +353,7 @@ private: /** * Broadcast transport needed for channel searches. */ - BlockingUDPTransport::shared_pointer _broadcastTransport; + BlockingUDPTransport::shared_pointer _broadcastTransport, _broadcastTransport2; /** * Local broadcast transport needed for local fan-out.