diff --git a/common/network/FakeInterfacePicker.h b/common/network/FakeInterfacePicker.h index 2e424cc73a..277e30997d 100644 --- a/common/network/FakeInterfacePicker.h +++ b/common/network/FakeInterfacePicker.h @@ -38,7 +38,9 @@ class FakeInterfacePicker: public InterfacePicker { m_interfaces(interfaces) { } - std::vector GetInterfaces(bool include_loopback) const { + std::vector GetInterfaces(bool include_loopback, + bool include_down) const { + (void) include_down; if (include_loopback) { return m_interfaces; } else { diff --git a/common/network/InterfacePicker.cpp b/common/network/InterfacePicker.cpp index ba796508ae..8a03844d62 100644 --- a/common/network/InterfacePicker.cpp +++ b/common/network/InterfacePicker.cpp @@ -53,7 +53,8 @@ bool InterfacePicker::ChooseInterface( const string &ip_or_name, const Options &options) const { bool found = false; - vector interfaces = GetInterfaces(options.include_loopback); + vector interfaces = GetInterfaces(options.include_loopback, + options.include_down); if (interfaces.empty()) { OLA_INFO << "No interfaces found"; @@ -110,7 +111,8 @@ bool InterfacePicker::ChooseInterface( int32_t index, const Options &options) const { bool found = false; - vector interfaces = GetInterfaces(options.include_loopback); + vector interfaces = GetInterfaces(options.include_loopback, + options.include_down); if (interfaces.empty()) { OLA_INFO << "No interfaces found"; diff --git a/common/network/InterfacePickerTest.cpp b/common/network/InterfacePickerTest.cpp index d122332414..b598a947f0 100644 --- a/common/network/InterfacePickerTest.cpp +++ b/common/network/InterfacePickerTest.cpp @@ -63,7 +63,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION(InterfacePickerTest); */ void InterfacePickerTest::testGetInterfaces() { auto_ptr picker(InterfacePicker::NewPicker()); - vector interfaces = picker->GetInterfaces(true); + vector interfaces = picker->GetInterfaces(true, true); #ifndef _WIN32 // If a Windows box is not on a network, and doesn't have it's loopback, there // may be zero interfaces present so we skip this check @@ -87,7 +87,7 @@ void InterfacePickerTest::testGetInterfaces() { */ void InterfacePickerTest::testGetLoopbackInterfaces() { auto_ptr picker(InterfacePicker::NewPicker()); - vector interfaces = picker->GetInterfaces(true); + vector interfaces = picker->GetInterfaces(true, true); #ifndef _WIN32 // If a Windows box is not on a network, and doesn't have it's loopback, there // may be zero interfaces present so we skip this check diff --git a/common/network/PosixInterfacePicker.cpp b/common/network/PosixInterfacePicker.cpp index c04c5ac05f..4ff1f2d845 100644 --- a/common/network/PosixInterfacePicker.cpp +++ b/common/network/PosixInterfacePicker.cpp @@ -70,7 +70,8 @@ using std::vector; * Return a vector of interfaces on the system. */ vector PosixInterfacePicker::GetInterfaces( - bool include_loopback) const { + bool include_loopback, + bool include_down) const { vector interfaces; #ifdef HAVE_SOCKADDR_DL_STRUCT @@ -152,7 +153,7 @@ vector PosixInterfacePicker::GetInterfaces( continue; } - if (!(ifrcopy.ifr_flags & IFF_UP)) { + if (!(ifrcopy.ifr_flags & IFF_UP) && !include_down) { OLA_DEBUG << "Skipping " << iface->ifr_name << " because it's down"; continue; diff --git a/common/network/PosixInterfacePicker.h b/common/network/PosixInterfacePicker.h index 29674f4ab8..e6016ffda3 100644 --- a/common/network/PosixInterfacePicker.h +++ b/common/network/PosixInterfacePicker.h @@ -32,7 +32,8 @@ namespace network { */ class PosixInterfacePicker: public InterfacePicker { public: - std::vector GetInterfaces(bool include_loopback) const; + std::vector GetInterfaces(bool include_loopback, + bool include_down) const; private: static const unsigned int INITIAL_IFACE_COUNT = 10; diff --git a/common/network/Socket.cpp b/common/network/Socket.cpp index dc833c9884..e354f38c2d 100644 --- a/common/network/Socket.cpp +++ b/common/network/Socket.cpp @@ -364,21 +364,34 @@ bool UDPSocket::EnableBroadcast() { } -bool UDPSocket::SetMulticastInterface(const IPV4Address &iface) { - struct in_addr addr; - addr.s_addr = iface.AsInt(); +bool UDPSocket::SetMulticastInterface(const Interface &iface) { #ifdef _WIN32 + struct in_addr addr; + addr.s_addr = htonl(iface.index()); int ok = setsockopt(m_handle.m_handle.m_fd, -#else - int ok = setsockopt(m_handle, -#endif // _WIN32 IPPROTO_IP, IP_MULTICAST_IF, reinterpret_cast(&addr), sizeof(addr)); +#else + struct ip_mreqn req = {0, 0, 0}; + req.imr_ifindex = iface.index; + int ok = setsockopt(m_handle, + IPPROTO_IP, + IP_MULTICAST_IF, + reinterpret_cast(&req), + sizeof(req)); +#endif // _WIN32 if (ok < 0) { + std::string iface_desc; + if(!iface.name.empty()) + iface_desc = iface.name + + "(" + std::to_string(iface.index) + ")"; + else + iface_desc = iface.ip_address.ToString() + + "(" + std::to_string(iface.index) + ")"; OLA_WARN << "Failed to set outgoing multicast interface to " << - iface << ": " << strerror(errno); + iface_desc << ": " << strerror(errno); return false; } return true; diff --git a/common/network/WindowsInterfacePicker.cpp b/common/network/WindowsInterfacePicker.cpp index f33e705a2c..467b85f0bd 100644 --- a/common/network/WindowsInterfacePicker.cpp +++ b/common/network/WindowsInterfacePicker.cpp @@ -38,7 +38,8 @@ using std::vector; * Return a vector of interfaces on the system. */ vector WindowsInterfacePicker::GetInterfaces( - bool include_loopback) const { + bool include_loopback, + bool include_down) const { vector interfaces; PIP_ADAPTER_INFO pAdapter = NULL; @@ -94,22 +95,23 @@ vector WindowsInterfacePicker::GetInterfaces( net = inet_addr(ipAddress->IpAddress.String); // Windows doesn't seem to have the notion of an interface being 'up' // so we check if this interface has an address assigned. - if (net) { - Interface iface; - iface.name = pAdapter->AdapterName; // IFNAME_SIZE - iface.index = pAdapter->Index; - uint8_t macaddr[MACAddress::LENGTH]; - memcpy(macaddr, pAdapter->Address, MACAddress::LENGTH); - iface.hw_address = MACAddress(macaddr); - iface.ip_address = IPV4Address(net); + if(!net && !include_down) + continue; - mask = inet_addr(ipAddress->IpMask.String); - iface.subnet_mask = IPV4Address(mask); - iface.bcast_address = IPV4Address((iface.ip_address.AsInt() & mask) | - (0xFFFFFFFF ^ mask)); + Interface iface; + iface.name = pAdapter->AdapterName; // IFNAME_SIZE + iface.index = pAdapter->Index; + uint8_t macaddr[MACAddress::LENGTH]; + memcpy(macaddr, pAdapter->Address, MACAddress::LENGTH); + iface.hw_address = MACAddress(macaddr); + iface.ip_address = IPV4Address(net); - interfaces.push_back(iface); - } + mask = inet_addr(ipAddress->IpMask.String); + iface.subnet_mask = IPV4Address(mask); + iface.bcast_address = IPV4Address((iface.ip_address.AsInt() & mask) | + (0xFFFFFFFF ^ mask)); + + interfaces.push_back(iface); } } delete[] pAdapterInfo; diff --git a/common/network/WindowsInterfacePicker.h b/common/network/WindowsInterfacePicker.h index 7460d31693..56f26fb7d9 100644 --- a/common/network/WindowsInterfacePicker.h +++ b/common/network/WindowsInterfacePicker.h @@ -32,9 +32,9 @@ namespace network { */ class WindowsInterfacePicker: public InterfacePicker { public: - std::vector GetInterfaces(bool include_loopback) const; + std::vector GetInterfaces(bool include_loopback, + bool include_down) const; }; } // namespace network } // namespace ola #endif // COMMON_NETWORK_WINDOWSINTERFACEPICKER_H_ - diff --git a/common/rdm/ResponderHelper.cpp b/common/rdm/ResponderHelper.cpp index 3b454654dc..c216a1183b 100644 --- a/common/rdm/ResponderHelper.cpp +++ b/common/rdm/ResponderHelper.cpp @@ -642,7 +642,7 @@ RDMResponse *ResponderHelper::GetListInterfaces( } vector interfaces = - network_manager->GetInterfacePicker()->GetInterfaces(false); + network_manager->GetInterfacePicker()->GetInterfaces(false, false); if (interfaces.size() == 0) { return EmptyGetResponse(request, queued_message_count); diff --git a/common/testing/MockUDPSocket.cpp b/common/testing/MockUDPSocket.cpp index 91077b21bf..f8721d5f29 100644 --- a/common/testing/MockUDPSocket.cpp +++ b/common/testing/MockUDPSocket.cpp @@ -29,6 +29,7 @@ #include #include "ola/Logging.h" +#include "ola/network/Interface.h" #include "ola/network/IPV4Address.h" #include "ola/network/NetworkUtils.h" #include "ola/testing/MockUDPSocket.h" @@ -41,6 +42,7 @@ using ola::io::IOQueue; using ola::io::IOVec; using ola::io::IOVecInterface; using ola::network::HostToNetwork; +using ola::network::Interface; using ola::network::IPV4Address; using ola::network::IPV4SocketAddress; @@ -212,23 +214,24 @@ bool MockUDPSocket::EnableBroadcast() { } -bool MockUDPSocket::SetMulticastInterface(const IPV4Address &iface) { - OLA_ASSERT_EQ(m_interface, iface); +bool MockUDPSocket::SetMulticastInterface(const Interface &iface) { + OLA_ASSERT_EQ(m_interface.ip_address, iface.ip_address); + OLA_ASSERT_EQ(m_interface.index, iface.index); return true; } -bool MockUDPSocket::JoinMulticast(const IPV4Address &iface, +bool MockUDPSocket::JoinMulticast(const IPV4Address &ip_addr, OLA_UNUSED const IPV4Address &group, OLA_UNUSED bool loop) { - OLA_ASSERT_EQ(m_interface, iface); + OLA_ASSERT_EQ(m_interface.ip_address, ip_addr); return true; } -bool MockUDPSocket::LeaveMulticast(const IPV4Address &iface, +bool MockUDPSocket::LeaveMulticast(const IPV4Address &ip_addr, OLA_UNUSED const IPV4Address &group) { - OLA_ASSERT_EQ(m_interface, iface); + OLA_ASSERT_EQ(m_interface.ip_address, ip_addr); return true; } @@ -318,7 +321,7 @@ bool MockUDPSocket::CheckNetworkParamsMatch(bool init_called, } -void MockUDPSocket::SetInterface(const IPV4Address &iface) { +void MockUDPSocket::SetInterface(const Interface &iface) { m_interface = iface; } diff --git a/include/ola/network/InterfacePicker.h b/include/ola/network/InterfacePicker.h index fb1b1311b7..37700fc624 100644 --- a/include/ola/network/InterfacePicker.h +++ b/include/ola/network/InterfacePicker.h @@ -60,9 +60,17 @@ class InterfacePicker { */ bool specific_only; + /** + * If False do not return an interface if it is down on posix or has no + * address configured on windows. + */ + bool include_down; + + Options() : include_loopback(false), - specific_only(false) { + specific_only(false), + include_down(false) { } }; @@ -86,7 +94,8 @@ class InterfacePicker { int32_t index, const Options &options = Options()) const; - virtual std::vector GetInterfaces(bool include_loopback) const = 0; + virtual std::vector GetInterfaces(bool include_loopback, + bool include_down) const = 0; static InterfacePicker *NewPicker(); }; diff --git a/include/ola/network/Socket.h b/include/ola/network/Socket.h index 44a7218a15..c851bfef4a 100644 --- a/include/ola/network/Socket.h +++ b/include/ola/network/Socket.h @@ -34,6 +34,7 @@ #include #include #include +#include #include namespace ola { @@ -196,9 +197,9 @@ class UDPSocketInterface: public ola::io::BidirectionalFileDescriptor { /** * @brief Set the outgoing interface to be used for multicast transmission. - * @param iface the address of the interface to use. + * @param iface the interface to use. */ - virtual bool SetMulticastInterface(const IPV4Address &iface) = 0; + virtual bool SetMulticastInterface(const Interface &iface) = 0; /** * @brief Join a multicast group @@ -277,7 +278,7 @@ class UDPSocket: public UDPSocketInterface { IPV4SocketAddress *source); bool EnableBroadcast(); - bool SetMulticastInterface(const IPV4Address &iface); + bool SetMulticastInterface(const Interface &iface); bool JoinMulticast(const IPV4Address &iface, const IPV4Address &group, bool multicast_loop = false); diff --git a/include/ola/testing/MockUDPSocket.h b/include/ola/testing/MockUDPSocket.h index edd5ef1766..41491af56c 100644 --- a/include/ola/testing/MockUDPSocket.h +++ b/include/ola/testing/MockUDPSocket.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -94,7 +95,7 @@ class MockUDPSocket: public ola::network::UDPSocketInterface { ssize_t *data_read, ola::network::IPV4SocketAddress *source); bool EnableBroadcast(); - bool SetMulticastInterface(const ola::network::IPV4Address &iface); + bool SetMulticastInterface(const ola::network::Interface &iface); bool JoinMulticast(const ola::network::IPV4Address &iface, const ola::network::IPV4Address &group, bool multicast_loop = false); @@ -131,7 +132,7 @@ class MockUDPSocket: public ola::network::UDPSocketInterface { uint16_t port, bool broadcast_set); - void SetInterface(const ola::network::IPV4Address &iface); + void SetInterface(const ola::network::Interface &iface); private: typedef struct { @@ -154,7 +155,7 @@ class MockUDPSocket: public ola::network::UDPSocketInterface { uint8_t m_tos; mutable std::queue m_expected_calls; mutable std::queue m_received_data; - ola::network::IPV4Address m_interface; + ola::network::Interface m_interface; bool m_discard_mode; uint8_t* IOQueueToBuffer(ola::io::IOQueue *ioqueue, diff --git a/libs/acn/E131Node.cpp b/libs/acn/E131Node.cpp index 58e8f5578f..df4f4be6b6 100644 --- a/libs/acn/E131Node.cpp +++ b/libs/acn/E131Node.cpp @@ -160,9 +160,16 @@ E131Node::~E131Node() { bool E131Node::Start() { + + + ola::network::InterfacePicker::Options opts; + opts.include_loopback = false; + opts.specific_only = m_options.force_interface; + opts.include_down = m_options.force_interface; + auto_ptr picker( ola::network::InterfacePicker::NewPicker()); - if (!picker->ChooseInterface(&m_interface, m_preferred_ip)) { + if (!picker->ChooseInterface(&m_interface, m_preferred_ip, opts)) { OLA_INFO << "Failed to find an interface"; return false; } @@ -181,7 +188,7 @@ bool E131Node::Start() { } m_socket.SetTos(m_options.dscp); - m_socket.SetMulticastInterface(m_interface.ip_address); + m_socket.SetMulticastInterface(m_interface); m_socket.SetOnData(NewCallback(&m_incoming_udp_transport, &IncomingUDPTransport::Receive)); diff --git a/libs/acn/E131Node.h b/libs/acn/E131Node.h index c8636f6c8e..010de7f365 100644 --- a/libs/acn/E131Node.h +++ b/libs/acn/E131Node.h @@ -58,6 +58,7 @@ class E131Node { : use_rev2(false), ignore_preview(true), enable_draft_discovery(false), + force_interface(false), dscp(0), port(ola::acn::ACN_PORT), source_name(ola::OLA_DEFAULT_INSTANCE_NAME) { @@ -66,6 +67,7 @@ class E131Node { bool use_rev2; /**< Use Revision 0.2 of the 2009 draft */ bool ignore_preview; /**< Ignore preview data */ bool enable_draft_discovery; /**< Enable 2014 draft discovery */ + bool force_interface; uint8_t dscp; /**< The DSCP value to tag packets with */ uint16_t port; /**< The UDP port to use, defaults to ACN_PORT */ std::string source_name; /**< The source name to use */ diff --git a/plugins/e131/E131Device.cpp b/plugins/e131/E131Device.cpp index 89b9d64e00..f9211bee34 100644 --- a/plugins/e131/E131Device.cpp +++ b/plugins/e131/E131Device.cpp @@ -57,13 +57,13 @@ using std::vector; */ E131Device::E131Device(Plugin *owner, const ola::acn::CID &cid, - string ip_addr, + string ip_or_interface, PluginAdaptor *plugin_adaptor, const E131DeviceOptions &options) : Device(owner, DEVICE_NAME), m_plugin_adaptor(plugin_adaptor), m_options(options), - m_ip_addr(ip_addr), + m_ip_or_interface(ip_or_interface), m_cid(cid) { } @@ -72,7 +72,7 @@ E131Device::E131Device(Plugin *owner, * Start this device */ bool E131Device::StartHook() { - m_node.reset(new E131Node(m_plugin_adaptor, m_ip_addr, m_options, m_cid)); + m_node.reset(new E131Node(m_plugin_adaptor, m_ip_or_interface, m_options, m_cid)); if (!m_node->Start()) { m_node.reset(); diff --git a/plugins/e131/E131Device.h b/plugins/e131/E131Device.h index e819a3144c..e385fbf814 100644 --- a/plugins/e131/E131Device.h +++ b/plugins/e131/E131Device.h @@ -52,7 +52,7 @@ class E131Device: public ola::Device { E131Device(ola::Plugin *owner, const ola::acn::CID &cid, - std::string ip_addr, + std::string ip_or_interface, class PluginAdaptor *plugin_adaptor, const E131DeviceOptions &options); @@ -74,7 +74,7 @@ class E131Device: public ola::Device { const E131DeviceOptions m_options; std::vector m_input_ports; std::vector m_output_ports; - std::string m_ip_addr; + std::string m_ip_or_interface; ola::acn::CID m_cid; void HandlePreviewMode(const ola::plugin::e131::Request *request, diff --git a/plugins/e131/E131Plugin.cpp b/plugins/e131/E131Plugin.cpp index 4bcbc2efb2..22a4b66f31 100644 --- a/plugins/e131/E131Plugin.cpp +++ b/plugins/e131/E131Plugin.cpp @@ -45,6 +45,7 @@ const char E131Plugin::DRAFT_DISCOVERY_KEY[] = "draft_discovery"; const char E131Plugin::IGNORE_PREVIEW_DATA_KEY[] = "ignore_preview"; const char E131Plugin::INPUT_PORT_COUNT_KEY[] = "input_ports"; const char E131Plugin::IP_KEY[] = "ip"; +const char E131Plugin::INTERFACE_KEY[] = "interface"; const char E131Plugin::OUTPUT_PORT_COUNT_KEY[] = "output_ports"; const char E131Plugin::PLUGIN_NAME[] = "E1.31 (sACN)"; const char E131Plugin::PLUGIN_PREFIX[] = "e131"; @@ -61,6 +62,7 @@ const unsigned int E131Plugin::DEFAULT_PORT_COUNT = 5; bool E131Plugin::StartHook() { CID cid = CID::FromString(m_preferences->GetValue(CID_KEY)); string ip_addr = m_preferences->GetValue(IP_KEY); + string interface = m_preferences->GetValue(INTERFACE_KEY); E131Device::E131DeviceOptions options; options.use_rev2 = (m_preferences->GetValue(REVISION_KEY) == REVISION_0_2); @@ -96,7 +98,15 @@ bool E131Plugin::StartHook() { OLA_WARN << "Invalid value for input_ports"; } - m_device = new E131Device(this, cid, ip_addr, m_plugin_adaptor, options); + options.force_interface = !interface.empty(); + if (!interface.empty() && !ip_addr.empty()) { + OLA_WARN << "interface=" + interface + "option overrides ip=" + ip_addr; + } + + m_device = new E131Device(this, + cid, + options.force_interface ? interface : ip_addr, + m_plugin_adaptor, options); if (!m_device->Start()) { delete m_device; diff --git a/plugins/e131/E131Plugin.h b/plugins/e131/E131Plugin.h index fed52781b3..38343ed103 100644 --- a/plugins/e131/E131Plugin.h +++ b/plugins/e131/E131Plugin.h @@ -57,6 +57,7 @@ class E131Plugin: public ola::Plugin { static const char IGNORE_PREVIEW_DATA_KEY[]; static const char INPUT_PORT_COUNT_KEY[]; static const char IP_KEY[]; + static const char INTERFACE_KEY[]; static const char OUTPUT_PORT_COUNT_KEY[]; static const char PLUGIN_NAME[]; static const char PLUGIN_PREFIX[]; diff --git a/plugins/e131/README.md b/plugins/e131/README.md index 4a07afa566..b673f93c06 100644 --- a/plugins/e131/README.md +++ b/plugins/e131/README.md @@ -25,8 +25,15 @@ Ignore preview data. The number of input ports to create up to a max of 32. `ip = [a.b.c.d|]` -The IP address or interface name to bind to. If not specified it will use -the first non-loopback interface. +The IP address or interface name to bind to. If not specified or no +matching interface is found the first "up" or configured non-loopback +interface is used. Prefer `interface` if this is not desired. + +`interface = ` +The name of the interface to bind to. Overrides the "ip" option if given. +Unlike the "ip" option this will use the specified interface whenever +possible at all, especially even if it is down. If the interface does not +exist an error is thrown rather than picking just any interface. `output_ports = [int]` The number of output ports to create up to a max of 32. diff --git a/plugins/pathport/PathportNode.cpp b/plugins/pathport/PathportNode.cpp index 1f1c62ed23..8f7afade68 100644 --- a/plugins/pathport/PathportNode.cpp +++ b/plugins/pathport/PathportNode.cpp @@ -324,7 +324,7 @@ bool PathportNode::InitNetwork() { return false; } - if (!m_socket.SetMulticastInterface(m_interface.ip_address)) { + if (!m_socket.SetMulticastInterface(m_interface)) { m_socket.Close(); return false; } diff --git a/plugins/sandnet/SandNetNode.cpp b/plugins/sandnet/SandNetNode.cpp index bee98b7aa2..47d5b515a4 100644 --- a/plugins/sandnet/SandNetNode.cpp +++ b/plugins/sandnet/SandNetNode.cpp @@ -340,13 +340,13 @@ bool SandNetNode::InitNetwork() { return false; } - if (!m_control_socket.SetMulticastInterface(m_interface.ip_address)) { + if (!m_control_socket.SetMulticastInterface(m_interface)) { m_data_socket.Close(); m_control_socket.Close(); return false; } - if (!m_data_socket.SetMulticastInterface(m_interface.ip_address)) { + if (!m_data_socket.SetMulticastInterface(m_interface)) { m_data_socket.Close(); m_control_socket.Close(); return false;