local multicast revised for multiple NIF (server side only)
This commit is contained in:
@@ -194,6 +194,19 @@ namespace epics {
|
||||
_sendTo = sendTo;
|
||||
}
|
||||
|
||||
virtual void setLocalMulticastAddress(const osiSockAddr& sendTo) {
|
||||
_localMulticastAddressEnabled = true;
|
||||
_localMulticastAddress = sendTo;
|
||||
}
|
||||
|
||||
virtual bool hasLocalMulticastAddress() const {
|
||||
return _localMulticastAddressEnabled;
|
||||
}
|
||||
|
||||
virtual const osiSockAddr& getLocalMulticastAddress() const {
|
||||
return _localMulticastAddress;
|
||||
}
|
||||
|
||||
virtual void flushSerializeBuffer() {
|
||||
// noop
|
||||
}
|
||||
@@ -366,6 +379,12 @@ namespace epics {
|
||||
osiSockAddr _sendTo;
|
||||
bool _sendToEnabled;
|
||||
|
||||
/**
|
||||
* Local multicast address.
|
||||
*/
|
||||
osiSockAddr _localMulticastAddress;
|
||||
bool _localMulticastAddressEnabled;
|
||||
|
||||
/**
|
||||
* Receive buffer.
|
||||
*/
|
||||
|
||||
@@ -389,6 +389,9 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
|
||||
(target == inetAddressType_broadcast_multicast && _isSendAddressUnicast[i]))
|
||||
continue;
|
||||
|
||||
LOG(logLevelDebug, "Sending to %d bytes to %s.",
|
||||
buffer->getRemaining(), inetAddressToString((*_sendAddresses)[i]).c_str());
|
||||
|
||||
int retval = sendto(_channel, buffer->getArray(),
|
||||
buffer->getLimit(), 0, &((*_sendAddresses)[i].sa),
|
||||
sizeof(sockaddr));
|
||||
@@ -460,7 +463,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
throw std::runtime_error(
|
||||
string("Failed to set multicast network inteface '") +
|
||||
string("Failed to set multicast network interface '") +
|
||||
inetAddressToString(nifAddr, false) + "': " + errStr);
|
||||
}
|
||||
|
||||
@@ -473,7 +476,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
|
||||
char errStr[64];
|
||||
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
|
||||
throw std::runtime_error(
|
||||
string("Failed to enable multicast loopback on network inteface '") +
|
||||
string("Failed to enable multicast loopback on network interface '") +
|
||||
inetAddressToString(nifAddr, false) + "': " + errStr);
|
||||
}
|
||||
|
||||
|
||||
@@ -4468,10 +4468,24 @@ namespace epics {
|
||||
*/
|
||||
bool initializeUDPTransport() {
|
||||
|
||||
// where to bind (listen) address
|
||||
osiSockAddr listenLocalAddress;
|
||||
listenLocalAddress.ia.sin_family = AF_INET;
|
||||
listenLocalAddress.ia.sin_port = htons(m_broadcastPort);
|
||||
listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// quary broadcast addresses of all IFs
|
||||
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
|
||||
if (socket == INVALID_SOCKET) return false;
|
||||
auto_ptr<InetAddrVector> broadcastAddresses(getBroadcastAddresses(socket, m_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 broadcast address list
|
||||
@@ -4497,12 +4511,6 @@ namespace epics {
|
||||
LOG(logLevelDebug,
|
||||
"Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str());
|
||||
|
||||
// where to bind (listen) address
|
||||
osiSockAddr listenLocalAddress;
|
||||
listenLocalAddress.ia.sin_family = AF_INET;
|
||||
listenLocalAddress.ia.sin_port = htons(m_broadcastPort);
|
||||
listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
ClientContextImpl::shared_pointer thisPointer = shared_from_this();
|
||||
|
||||
TransportClient::shared_pointer nullTransportClient;
|
||||
@@ -4540,8 +4548,13 @@ namespace epics {
|
||||
{
|
||||
try
|
||||
{
|
||||
int lastAddr = 128 + ifIndex;
|
||||
std::ostringstream o;
|
||||
// TODO configurable
|
||||
o << "224.0.0." << lastAddr;
|
||||
|
||||
//osiSockAddr group;
|
||||
aToIPAddr("224.0.0.128", m_broadcastPort, &m_localBroadcastAddress.ia);
|
||||
aToIPAddr(o.str().c_str(), m_broadcastPort, &m_localBroadcastAddress.ia);
|
||||
m_broadcastTransport->join(m_localBroadcastAddress, loAddr);
|
||||
|
||||
// NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST
|
||||
|
||||
@@ -263,10 +263,10 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
|
||||
//
|
||||
// locally broadcast if unicast (qosCode & 0x80 == 0x80)
|
||||
//
|
||||
if (0)
|
||||
if ((qosCode & 0x80) == 0x80)
|
||||
{
|
||||
BlockingUDPTransport::shared_pointer bt = _context->getLocalMulticastTransport();
|
||||
if (bt)
|
||||
BlockingUDPTransport::shared_pointer bt = _context->getBroadcastTransport();
|
||||
if (bt && bt->hasLocalMulticastAddress())
|
||||
{
|
||||
// clear unicast flag
|
||||
payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80));
|
||||
@@ -277,7 +277,7 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
|
||||
|
||||
payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip()
|
||||
|
||||
bt->send(payloadBuffer);
|
||||
bt->send(payloadBuffer, bt->getLocalMulticastAddress());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +291,14 @@ void ServerContextImpl::initializeBroadcastTransport()
|
||||
THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport");
|
||||
}
|
||||
auto_ptr<InetAddrVector> broadcastAddresses(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);
|
||||
|
||||
TransportClient::shared_pointer nullTransportClient;
|
||||
@@ -360,50 +368,48 @@ void ServerContextImpl::initializeBroadcastTransport()
|
||||
}
|
||||
|
||||
|
||||
// setup local broadcasting
|
||||
//
|
||||
// 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
|
||||
{
|
||||
osiSockAddr group;
|
||||
|
||||
// TODO there should be different mcast groups
|
||||
// one for all interfaces, and then one per interface
|
||||
|
||||
// if received on specific NIF, then it's resent to speicfic mcast address
|
||||
// if received on any NIF, then it should be resent to specific mcast address (calculate from receive from and mask)
|
||||
// if interested for all the NIFs, then it should join to all specific mcast addresses
|
||||
|
||||
// --- server
|
||||
// UDP bind on broadcast port + mcast as above
|
||||
|
||||
// -- client
|
||||
// UDP bind to broadcast port + mcast as above
|
||||
|
||||
|
||||
aToIPAddr("224.0.0.128", _broadcastPort, &group.ia);
|
||||
_broadcastTransport->join(group, _ifaceAddr);
|
||||
|
||||
osiSockAddr anyAddress;
|
||||
anyAddress.ia.sin_family = AF_INET;
|
||||
anyAddress.ia.sin_port = htons(0);
|
||||
anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// NOTE: localMulticastTransport is not started (no read is called on a socket)
|
||||
// NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address
|
||||
_localMulticastTransport = static_pointer_cast<BlockingUDPTransport>(broadcastConnector->connect(
|
||||
nullTransportClient, _responseHandler,
|
||||
anyAddress, PVA_PROTOCOL_REVISION,
|
||||
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 enabled on %s using network interface %s.",
|
||||
inetAddressToString(group).c_str(), inetAddressToString(loAddr, false).c_str());
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -212,5 +212,144 @@ int getLoopbackNIF(osiSockAddr &loAddr, string const & localNIF, unsigned short
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// copy of base-3.14.12.4/src/libCom/osi/os/default/osdNetIntf.c
|
||||
// TODO support windows (see osi/os/WIN32/osdNetIntf.c)
|
||||
|
||||
#include <osiSock.h>
|
||||
//#include <epicsAssert.h>
|
||||
#include <errlog.h>
|
||||
|
||||
/*
|
||||
* 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 );
|
||||
return ifr;
|
||||
}
|
||||
|
||||
int discoverInterfaceIndex
|
||||
(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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 ("osiSockDiscoverInterfaceIndex(): no memory to complete request\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifconf.ifc_len = nelem * sizeof(*pifreq);
|
||||
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");
|
||||
free (pIfreqList);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* If its not an internet interface then dont use it
|
||||
*/
|
||||
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 ) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
free (pIfreqList);
|
||||
return index;
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
free (pIfreqList);
|
||||
return pIfreqList->ifr_ifindex;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* not found */
|
||||
free ( pIfreqList );
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,11 @@ namespace pvAccess {
|
||||
*/
|
||||
epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort);
|
||||
|
||||
/**
|
||||
* Returns NIF index for given interface address, or -1 on failure.
|
||||
*/
|
||||
epicsShareFunc int discoverInterfaceIndex(SOCKET socket, const osiSockAddr *pMatchAddr);
|
||||
|
||||
/**
|
||||
* Encode IPv4 address as IPv6 address.
|
||||
* @param buffer byte-buffer where to put encoded data.
|
||||
|
||||
Reference in New Issue
Block a user