mgmt: Create UDP multicast faces according to whitelist/blacklist
refs #1712
Change-Id: Ia957bb1a3a3a0108d06716bfb25ecd29c6952d62
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index d4ee086..8ea503c 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -66,6 +66,13 @@
// mcast yes
// mcast_group 224.0.23.170
// mcast_port 56363
+ // whitelist
+ // {
+ // *
+ // }
+ // blacklist
+ // {
+ // }
// }
uint16_t port = 6363;
@@ -116,6 +123,12 @@
else if (key == "mcast_port") {
mcastConfig.group.port(ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp"));
}
+ else if (key == "whitelist") {
+ mcastConfig.netifPredicate.parseWhitelist(value);
+ }
+ else if (key == "blacklist") {
+ mcastConfig.netifPredicate.parseBlacklist(value);
+ }
else {
BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option face_system.udp." + key));
}
@@ -167,6 +180,9 @@
NFD_LOG_INFO("changing multicast group from " << m_mcastConfig.group <<
" to " << mcastConfig.group);
}
+ else if (m_mcastConfig.netifPredicate != mcastConfig.netifPredicate) {
+ NFD_LOG_INFO("changing whitelist/blacklist");
+ }
else {
// There's no configuration change, but we still need to re-apply configuration because
// netifs may have changed.
@@ -456,7 +472,8 @@
auto capableNetifRange = context.listNetifs() |
boost::adaptors::filtered([this] (const NetworkInterfaceInfo& netif) {
return netif.isUp() && netif.isMulticastCapable() &&
- !netif.ipv4Addresses.empty();
+ !netif.ipv4Addresses.empty() &&
+ m_mcastConfig.netifPredicate(netif);
});
bool needIfname = false;
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 1e1a6ab..9b68448 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -199,6 +199,7 @@
{
bool isEnabled = false;
udp::Endpoint group = udp::getDefaultMulticastGroup();
+ NetworkInterfacePredicate netifPredicate;
};
MulticastConfig m_mcastConfig;
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 58ef855..b1b704a 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -128,6 +128,20 @@
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)
+
+ ; Whitelist and blacklist can contain, in no particular order:
+ ; interface names (e.g., ifname eth0),
+ ; mac addresses (e.g., ether 85:3b:4d:d3:5f:c2),
+ ; subnets (e.g., subnet 192.0.2.0/24, note that only IPv4 is supported here),
+ ; or a wildcard (*) that matches all interfaces.
+
+ whitelist
+ {
+ *
+ }
+ blacklist
+ {
+ }
}
; The ether section contains settings of Ethernet faces and channels.
@@ -161,7 +175,7 @@
@IF_HAVE_LIBPCAP@ mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
@IF_HAVE_LIBPCAP@ mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
@IF_HAVE_LIBPCAP@
- @IF_HAVE_LIBPCAP@ ; Whitelist and blacklist can contain, in no particular order,
+ @IF_HAVE_LIBPCAP@ ; Whitelist and blacklist can contain, in no particular order:
@IF_HAVE_LIBPCAP@ ; interface names (e.g., ifname eth0),
@IF_HAVE_LIBPCAP@ ; mac addresses (e.g., ether 85:3b:4d:d3:5f:c2),
@IF_HAVE_LIBPCAP@ ; subnets (e.g., subnet 192.0.2.0/24, note that only IPv4 is supported here),
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index ac468cb..265bf97 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -28,6 +28,8 @@
#include "factory-test-common.hpp"
#include "face-system-fixture.hpp"
#include "tests/limited-io.hpp"
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/range/algorithm.hpp>
namespace nfd {
namespace face {
@@ -133,6 +135,15 @@
return this->listUdpMcastFaces().size();
}
+ /** \brief determine whether a UDP multicast face is created on \p netif
+ */
+ static bool
+ isFaceOnNetif(const Face& face, const NetworkInterfaceInfo& netif)
+ {
+ auto ip = boost::asio::ip::address_v4::from_string(face.getLocalUri().getHost());
+ return boost::count(netif.ipv4Addresses, ip) > 0;
+ }
+
protected:
/** \brief MulticastUdpTransport-capable network interfaces
*/
@@ -231,6 +242,100 @@
FaceUri("udp4://239.66.30.2:7012"));
}
+BOOST_FIXTURE_TEST_CASE(Whitelist, UdpMcastConfigFixture)
+{
+#ifdef __linux__
+ // need superuser privilege for creating multicast faces on linux
+ SKIP_IF_NOT_SUPERUSER();
+#endif // __linux__
+ SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1);
+
+ std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ udp
+ {
+ whitelist
+ {
+ ifname %ifname
+ }
+ }
+ }
+ )CONFIG";
+ boost::replace_first(CONFIG, "%ifname", netifs.front().name);
+
+ parseConfig(CONFIG, false);
+ auto udpMcastFaces = this->listUdpMcastFaces();
+ BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), 1);
+ BOOST_CHECK(isFaceOnNetif(*udpMcastFaces.front(), netifs.front()));
+}
+
+BOOST_FIXTURE_TEST_CASE(Blacklist, UdpMcastConfigFixture)
+{
+#ifdef __linux__
+ // need superuser privilege for creating multicast faces on linux
+ SKIP_IF_NOT_SUPERUSER();
+#endif // __linux__
+ SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1);
+
+ std::string CONFIG = R"CONFIG(
+ face_system
+ {
+ udp
+ {
+ blacklist
+ {
+ ifname %ifname
+ }
+ }
+ }
+ )CONFIG";
+ boost::replace_first(CONFIG, "%ifname", netifs.front().name);
+
+ parseConfig(CONFIG, false);
+ auto udpMcastFaces = this->listUdpMcastFaces();
+ BOOST_CHECK_EQUAL(udpMcastFaces.size(), netifs.size() - 1);
+ BOOST_CHECK_EQUAL(boost::count_if(udpMcastFaces, [=] (const Face* face) {
+ return isFaceOnNetif(*face, netifs.front());
+ }), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(ChangePredicate, UdpMcastConfigFixture)
+{
+#ifdef __linux__
+ // need superuser privilege for creating multicast faces on linux
+ SKIP_IF_NOT_SUPERUSER();
+#endif // __linux__
+ SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(2);
+
+ std::string CONFIG1 = R"CONFIG(
+ face_system
+ {
+ udp
+ {
+ whitelist
+ {
+ ifname %ifname
+ }
+ }
+ }
+ )CONFIG";
+ std::string CONFIG2 = CONFIG1;
+ boost::replace_first(CONFIG1, "%ifname", netifs.front().name);
+ boost::replace_first(CONFIG2, "%ifname", netifs.back().name);
+
+ parseConfig(CONFIG1, false);
+ auto udpMcastFaces = this->listUdpMcastFaces();
+ BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), 1);
+ BOOST_CHECK(isFaceOnNetif(*udpMcastFaces.front(), netifs.front()));
+
+ parseConfig(CONFIG2, false);
+ g_io.poll();
+ udpMcastFaces = this->listUdpMcastFaces();
+ BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), 1);
+ BOOST_CHECK(isFaceOnNetif(*udpMcastFaces.front(), netifs.back()));
+}
+
BOOST_AUTO_TEST_CASE(Omitted)
{
const std::string CONFIG = R"CONFIG(