face: bugfix of MulticastUdpFace receiving packets from all available NICs on Linux
Now on Linux the creation of MulticastUdpFace requires
CAP_NET_RAW capability if the name of the interface is specified
Change-Id: Iff53035371fb26c6ee40e1065a0935e5ed16dc60
Refs: #1475
diff --git a/README.md b/README.md
index 04aadb5..dc46009 100644
--- a/README.md
+++ b/README.md
@@ -90,3 +90,12 @@
sudo chgrp admin /dev/bpf*
sudo chmod g+rw /dev/bpf*
+
+## UDP multicast support in multi-homed Linux machines
+
+The UDP configuration file section contains settings for unicast and multicast UDP
+faces. If the Linux box is equipped with multiple network interfaces with multicast
+capabilities, the settings for multicast faces will **NOT** work without root
+or setting the appropriate permissions:
+
+ sudo setcap cap_net_raw=eip /full/path/nfd
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index cac94a2..e97e9bb 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -27,6 +27,10 @@
#include "core/resolver.hpp"
#include "core/network-interface.hpp"
+#if defined(__linux__)
+#include <sys/socket.h>
+#endif
+
namespace nfd {
using namespace boost::asio;
@@ -163,9 +167,10 @@
shared_ptr<MulticastUdpFace>
UdpFactory::createMulticastFace(const udp::Endpoint& localEndpoint,
- const udp::Endpoint& multicastEndpoint)
+ const udp::Endpoint& multicastEndpoint,
+ const std::string& networkInterfaceName /* "" */)
{
- //checking if the local and musticast endpoint are already in use for a multicast face
+ //checking if the local and multicast endpoint are already in use for a multicast face
shared_ptr<MulticastUdpFace> multicastFace = findMulticastFace(localEndpoint);
if (static_cast<bool>(multicastFace)) {
if (multicastFace->getMulticastGroup() == multicastEndpoint)
@@ -224,6 +229,24 @@
throw Error(msg.str());
}
+#if defined(__linux__)
+ //On linux system, if there are more than one MulticastUdpFace for the same multicast group but
+ //bound on different network interfaces, the socket has to be bound with the specific interface
+ //using SO_BINDTODEVICE, otherwise the face will receive packets also from other interfaces.
+ //Without SO_BINDTODEVICE every MulticastUdpFace that have joined the same multicast group
+ //on different interfaces will receive the same packet.
+ //This applies only on linux, for OS X the ip::multicast::join_group is enough to get
+ //the desired behaviour
+ if (!networkInterfaceName.empty()) {
+ if (::setsockopt(clientSocket->native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
+ networkInterfaceName.c_str(), networkInterfaceName.size()+1) == -1){
+ throw Error("Cannot bind multicast face to " + networkInterfaceName
+ + " make sure you have CAP_NET_RAW capability" );
+ }
+ }
+
+#endif
+
clientSocket->set_option(ip::multicast::enable_loopback(false));
multicastFace = make_shared<MulticastUdpFace>(boost::cref(clientSocket), localEndpoint);
@@ -237,13 +260,15 @@
shared_ptr<MulticastUdpFace>
UdpFactory::createMulticastFace(const std::string& localIp,
const std::string& multicastIp,
- const std::string& multicastPort)
+ const std::string& multicastPort,
+ const std::string& networkInterfaceName /* "" */)
{
return createMulticastFace(UdpResolver::syncResolve(localIp,
multicastPort),
UdpResolver::syncResolve(multicastIp,
- multicastPort));
+ multicastPort),
+ networkInterfaceName);
}
void
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 8d0494c..fa146ad 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -115,6 +115,13 @@
* If an unicast face is already active on the same local NIC and port, the
* creation fails and an exception is thrown
*
+ * \param networkInterfaceName name of the network interface on which the face will be bound
+ * (Used only on multihomed linux machine with more than one MulticastUdpFace for
+ * the same multicast group. If specified, will requires CAP_NET_RAW capability)
+ * An empty string can be provided in other system or in linux machine with only one
+ * MulticastUdpFace per multicast group
+ *
+ *
* \returns always a valid pointer to a MulticastUdpFace object, an exception
* is thrown if it cannot be created.
*
@@ -125,12 +132,14 @@
*/
shared_ptr<MulticastUdpFace>
createMulticastFace(const udp::Endpoint& localEndpoint,
- const udp::Endpoint& multicastEndpoint);
+ const udp::Endpoint& multicastEndpoint,
+ const std::string& networkInterfaceName = "");
shared_ptr<MulticastUdpFace>
createMulticastFace(const std::string& localIp,
const std::string& multicastIp,
- const std::string& multicastPort);
+ const std::string& multicastPort,
+ const std::string& networkInterfaceName = "");
// from Factory
virtual void
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 79822f7..8397253 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -498,6 +498,7 @@
if (useMcast && enableV4)
{
+ std::list<shared_ptr<NetworkInterfaceInfo> > ipv4MulticastInterfaces;
for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i = nicList.begin();
i != nicList.end();
++i)
@@ -505,13 +506,35 @@
const shared_ptr<NetworkInterfaceInfo>& nic = *i;
if (nic->isUp() && nic->isMulticastCapable() && !nic->ipv4Addresses.empty())
{
- shared_ptr<MulticastUdpFace> newFace =
- factory->createMulticastFace(nic->ipv4Addresses[0].to_string(),
- mcastGroup, mcastPort);
-
- addCreatedFaceToForwarder(newFace);
+ ipv4MulticastInterfaces.push_back(nic);
}
}
+
+ bool isNicNameNecessary = false;
+
+#if defined(__linux__)
+ if (ipv4MulticastInterfaces.size() > 1)
+ {
+ //On Linux, if we have more than one MulticastUdpFace we need to specify
+ //the name of the interface
+ isNicNameNecessary = true;
+ }
+#endif
+
+ for (std::list<shared_ptr<NetworkInterfaceInfo> >::const_iterator i =
+ ipv4MulticastInterfaces.begin();
+ i != ipv4MulticastInterfaces.end();
+ ++i)
+ {
+ const shared_ptr<NetworkInterfaceInfo>& nic = *i;
+ shared_ptr<MulticastUdpFace> newFace;
+ newFace = factory->createMulticastFace(nic->ipv4Addresses[0].to_string(),
+ mcastGroup,
+ mcastPort,
+ isNicNameNecessary ? nic->name : "");
+
+ addCreatedFaceToForwarder(newFace);
+ }
}
}
}
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 22aae5a..ace10e1 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -66,7 +66,12 @@
; UDP multicast settings
; NFD creates one UDP multicast face per NIC
-
+ ;
+ ; In multi-homed Linux machines these settings will NOT work without
+ ; root or settings the appropriate permissions:
+ ;
+ ; sudo setcap cap_net_raw=eip /full/path/nfd
+ ;
mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
mcast_port 56363 ; UDP multicast port number
mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)