From: Marcin Siodelski Date: Wed, 17 Dec 2014 17:48:51 +0000 (+0100) Subject: [3539] Iface configuration allows for specifying IPv4 address to listen on. X-Git-Tag: trac3504_base~23^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3fb6d907f674b95bab9f4e24888af4bb5cdcc8c8;p=thirdparty%2Fkea.git [3539] Iface configuration allows for specifying IPv4 address to listen on. --- diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index ece833361d..421d7523e5 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -40,6 +40,7 @@ using namespace std; using namespace isc::asiolink; +using namespace isc::util; using namespace isc::util::io::internal; namespace isc { @@ -149,7 +150,7 @@ void Iface::setMac(const uint8_t* mac, size_t len) { bool Iface::delAddress(const isc::asiolink::IOAddress& addr) { for (AddressCollection::iterator a = addrs_.begin(); a!=addrs_.end(); ++a) { - if (*a==addr) { + if (a->get() == addr) { addrs_.erase(a); return (true); } @@ -216,12 +217,12 @@ IfaceMgr::IfaceMgr() void Iface::addUnicast(const isc::asiolink::IOAddress& addr) { for (Iface::AddressCollection::const_iterator i = unicasts_.begin(); i != unicasts_.end(); ++i) { - if (*i == addr) { + if (i->get() == addr) { isc_throw(BadValue, "Address " << addr << " already defined on the " << name_ << " interface."); } } - unicasts_.push_back(addr); + unicasts_.push_back(OptionalValue(addr, true)); } bool @@ -233,8 +234,8 @@ Iface::getAddress4(isc::asiolink::IOAddress& address) const { addr != addrs.end(); ++addr) { // If address is IPv4, we assign it to the function argument // and return true. - if (addr->isV4()) { - address = *addr; + if (addr->get().isV4()) { + address = addr->get(); return (true); } } @@ -247,13 +248,39 @@ Iface::hasAddress(const isc::asiolink::IOAddress& address) const { const AddressCollection& addrs = getAddresses(); for (AddressCollection::const_iterator addr = addrs.begin(); addr != addrs.end(); ++addr) { - if (address == *addr) { + if (address == addr->get()) { return (true); } } return (false); } +void +Iface::addAddress(const isc::asiolink::IOAddress& addr) { + addrs_.push_back(OptionalValue(addr, OptionalValueState(false))); +} + +void +Iface::setActive(const IOAddress& address, const bool active) { + for (AddressCollection::iterator addr_it = addrs_.begin(); + addr_it != addrs_.end(); ++addr_it) { + if (address == addr_it->get()) { + addr_it->specify(active); + return; + } + } + isc_throw(BadValue, "specified address " << address << " was not" + " found on the interface " << getName()); +} + +void +Iface::setActive(const bool active) { + for (AddressCollection::iterator addr_it = addrs_.begin(); + addr_it != addrs_.end(); ++addr_it) { + addr_it->specify(active); + } +} + void IfaceMgr::closeSockets() { for (IfaceCollection::iterator iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) { @@ -393,7 +420,7 @@ IfaceMgr::hasOpenSocket(const IOAddress& addr) const { iface->getAddresses().begin(); addr_it != iface->getAddresses().end(); ++addr_it) { - if (addr == *addr_it) { + if (addr == addr_it->get()) { return (true); } } @@ -488,7 +515,7 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, ++addr) { // Skip all but V4 addresses. - if (!addr->isV4()) { + if (!addr->get().isV4()) { continue; } @@ -616,7 +643,7 @@ IfaceMgr::openSockets6(const uint16_t port, ++addr) { // Skip all but V6 addresses. - if (!addr->isV6()) { + if (!addr->get().isV6()) { continue; } @@ -625,7 +652,7 @@ IfaceMgr::openSockets6(const uint16_t port, // with interface with 2 global addresses, we would bind 3 sockets // (one for link-local and two for global). That would result in // getting each message 3 times. - if (!addr->isV6LinkLocal()){ + if (!addr->get().isV6LinkLocal()){ continue; } @@ -663,7 +690,7 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) { for (Iface::AddressCollection::const_iterator addr = addrs.begin(); addr != addrs.end(); ++addr) { - out << " " << addr->toText(); + out << " " << addr->get().toText(); } out << endl; } @@ -743,7 +770,7 @@ int IfaceMgr::openSocketFromIface(const std::string& ifname, Iface::AddressCollection addrs = iface->getAddresses(); Iface::AddressCollection::iterator addr_it = addrs.begin(); while (addr_it != addrs.end()) { - if (addr_it->getFamily() == family) { + if (addr_it->get().getFamily() == family) { // We have interface and address so let's open socket. // This may cause isc::Unexpected exception. return (openSocket(iface->getName(), *addr_it, port, false)); @@ -787,7 +814,7 @@ int IfaceMgr::openSocketFromAddress(const IOAddress& addr, // on detected interfaces. If it does, we have // address and interface detected so we can open // socket. - if (*addr_it == addr) { + if (addr_it->get() == addr) { // Open socket using local interface, address and port. // This may cause isc::Unexpected exception. return (openSocket(iface->getName(), *addr_it, port, false)); diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 8917920e54..15a6b83e4f 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -150,7 +151,8 @@ public: static const unsigned int MAX_MAC_LEN = 20; /// Type that defines list of addresses - typedef std::vector AddressCollection; + typedef + std::list > AddressCollection; /// @brief Type that holds a list of socket information. /// @@ -291,9 +293,30 @@ public: /// configure address on actual network interface. /// /// @param addr address to be added - void addAddress(const isc::asiolink::IOAddress& addr) { - addrs_.push_back(addr); - } + void addAddress(const isc::asiolink::IOAddress& addr); + + /// @brief Activates or deactivates address for the interface. + /// + /// This method marks a specified address on the interface active or + /// inactive. If the address is marked inactive, the + /// @c IfaceMgr::openSockets4 method will NOT open socket for this address. + /// + /// @param address An address which should be activated, deactivated. + /// @param active A boolean flag which indicates that the specified address + /// should be active (if true) or inactive (if false). + /// + /// @throw BadValue if specified address doesn't exist for the interface. + void setActive(const isc::asiolink::IOAddress& address, const bool active); + + /// @brief Activates or deactivates all addresses for the interface. + /// + /// This method marks all addresses on the interface active or inactive. + /// If the address is marked inactive, the @c IfaceMgr::openSockets4 + /// method will NOT open socket for this address. + /// + /// @param active A boolean flag which indicates that the addresses + /// should be active (if true) or inactive (if false). + void setActive(const bool active); /// @brief Deletes an address from an interface. /// diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.cc b/src/lib/dhcp/tests/iface_mgr_test_config.cc index f95bfdbc4e..719c4a502f 100644 --- a/src/lib/dhcp/tests/iface_mgr_test_config.cc +++ b/src/lib/dhcp/tests/iface_mgr_test_config.cc @@ -107,6 +107,7 @@ IfaceMgrTestConfig::createIfaces() { // eth1 addIface("eth1", 2); addAddress("eth1", IOAddress("192.0.2.3")); + addAddress("eth1", IOAddress("192.0.2.5")); addAddress("eth1", IOAddress("fe80::3a60:77ff:fed5:abcd")); } @@ -143,7 +144,7 @@ IfaceMgrTestConfig::setIfaceFlags(const std::string& name, bool IfaceMgrTestConfig::socketOpen(const std::string& iface_name, - const int family) const { + const int family) const { Iface* iface = IfaceMgr::instance().getIface(iface_name); if (iface == NULL) { isc_throw(Unexpected, "No such interface '" << iface_name << "'"); @@ -159,6 +160,25 @@ IfaceMgrTestConfig::socketOpen(const std::string& iface_name, return (false); } +bool +IfaceMgrTestConfig::socketOpen(const std::string& iface_name, + const std::string& address) const { + Iface* iface = IfaceMgr::instance().getIface(iface_name); + if (iface == NULL) { + isc_throw(Unexpected, "No such interface '" << iface_name << "'"); + } + + const Iface::SocketCollection& sockets = iface->getSockets(); + for (Iface::SocketCollection::const_iterator sock = sockets.begin(); + sock != sockets.end(); ++sock) { + if ((sock->family_ == AF_INET) && + (sock->addr_ == IOAddress(address))) { + return (true); + } + } + return (false); +} + bool IfaceMgrTestConfig::unicastOpen(const std::string& iface_name) const { Iface* iface = IfaceMgr::instance().getIface(iface_name); diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.h b/src/lib/dhcp/tests/iface_mgr_test_config.h index 6c597d7aad..582f1c6db0 100644 --- a/src/lib/dhcp/tests/iface_mgr_test_config.h +++ b/src/lib/dhcp/tests/iface_mgr_test_config.h @@ -235,6 +235,14 @@ public: /// @param family One of: AF_INET or AF_INET6 bool socketOpen(const std::string& iface_name, const int family) const; + /// @brief Checks is socket is opened on the interface and bound to a + /// specified address. + /// + /// @param iface_name Interface name. + /// @param address Address to which the socket is bound. + bool socketOpen(const std::string& iface_name, + const std::string & address) const; + /// @brief Checks if unicast socket is opened on interface. /// /// @param iface_name Interface name. diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index bd99b4b202..2aecd276f5 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -289,7 +289,7 @@ public: iface->getAddresses().begin(); addr_it != iface->getAddresses().end(); ++addr_it) { - if (*addr_it == IOAddress(addr)) { + if (addr_it->get() == IOAddress(addr)) { return (true); } } @@ -1501,7 +1501,7 @@ TEST_F(IfaceMgrTest, openSockets4IfaceDown) { // Expecting that the socket is open on eth1 because it was up, running // and active. - EXPECT_EQ(1, IfaceMgr::instance().getIface("eth1")->getSockets().size()); + EXPECT_EQ(2, IfaceMgr::instance().getIface("eth1")->getSockets().size()); // Never open socket on loopback interface. EXPECT_TRUE(IfaceMgr::instance().getIface("lo")->getSockets().empty()); @@ -2072,7 +2072,7 @@ TEST_F(IfaceMgrTest, iface) { addrs = iface->getAddresses(); ASSERT_EQ(1, addrs.size()); - EXPECT_EQ("192.0.2.6", addrs.at(0).toText()); + EXPECT_EQ("192.0.2.6", addrs.begin()->get().toText()); // No such address, should return false. EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9"))); @@ -2332,7 +2332,7 @@ checkIfAddrs(const Iface & iface, struct ifaddrs *& ifptr) { for (Iface::AddressCollection::const_iterator a = iface.getAddresses().begin(); a != iface.getAddresses().end(); ++ a) { - if(a->isV4() && (*a) == addrv4) { + if(a->get().isV4() && (a->get()) == addrv4) { return (true); } } @@ -2349,7 +2349,7 @@ checkIfAddrs(const Iface & iface, struct ifaddrs *& ifptr) { for(Iface::AddressCollection::const_iterator a = iface.getAddresses().begin(); a != iface.getAddresses().end(); ++ a) { - if(a->isV6() && (*a) == addrv6) { + if (a->get().isV6() && (a->get() == addrv6)) { return (true); } } diff --git a/src/lib/dhcpsrv/cfg_iface.cc b/src/lib/dhcpsrv/cfg_iface.cc index 1d38754e2d..ebb771da83 100644 --- a/src/lib/dhcpsrv/cfg_iface.cc +++ b/src/lib/dhcpsrv/cfg_iface.cc @@ -37,7 +37,7 @@ CfgIface::closeSockets() const { bool CfgIface::equals(const CfgIface& other) const { return (iface_set_ == other.iface_set_ && - unicast_map_ == other.unicast_map_ && + address_map_ == other.address_map_ && wildcard_used_ == other.wildcard_used_); } @@ -70,6 +70,10 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port, } else if (family == AF_INET) { iface->inactive4_ = false; + ExplicitAddressMap::const_iterator addr = address_map_.find(iface->getName()); + if (addr != address_map_.end()) { + iface->setActive(addr->second, true); + } } else { iface->inactive6_ = false; @@ -79,8 +83,8 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port, // Select unicast sockets. It works only for V6. Ignore for V4. if (family == AF_INET6) { - for (UnicastMap::const_iterator unicast = unicast_map_.begin(); - unicast != unicast_map_.end(); ++unicast) { + for (ExplicitAddressMap::const_iterator unicast = address_map_.begin(); + unicast != address_map_.end(); ++unicast) { Iface* iface = IfaceMgr::instance().getIface(unicast->first); if (iface == NULL) { isc_throw(Unexpected, @@ -130,13 +134,23 @@ CfgIface::setState(const uint16_t family, const bool inactive, for (IfaceMgr::IfaceCollection::iterator iface = ifaces.begin(); iface != ifaces.end(); ++iface) { Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName()); + bool iface_inactive = iface_ptr->flag_loopback_ ? + loopback_inactive : inactive; if (family == AF_INET) { - iface_ptr->inactive4_ = iface_ptr->flag_loopback_ ? - loopback_inactive : inactive; + iface_ptr->inactive4_ = iface_inactive; } else { - iface_ptr->inactive6_ = iface_ptr->flag_loopback_ ? - loopback_inactive : inactive; + iface_ptr->inactive6_ = iface_inactive; } + + // Activate/deactivate all addresses. + const Iface::AddressCollection addresses = iface_ptr->getAddresses(); + for (Iface::AddressCollection::const_iterator addr_it = + addresses.begin(); addr_it != addresses.end(); ++addr_it) { + if (addr_it->get().getFamily() == family) { + iface_ptr->setActive(addr_it->get(), !iface_inactive); + } + } + } } @@ -169,18 +183,6 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { << "' doesn't exist in the system"); } - // If interface has already been specified. - if (iface_set_.find(name) != iface_set_.end()) { - isc_throw(DuplicateIfaceName, "interface '" << name - << "' has already been specified"); - - } - - // All ok, add interface. - LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE) - .arg(name); - iface_set_.insert(name); - } else if (wildcard_used_) { isc_throw(DuplicateIfaceName, "the wildcard interface '" << ALL_IFACES_KEYWORD << "' can only be specified once"); @@ -192,14 +194,10 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { } - } else if (family == AF_INET) { - isc_throw(InvalidIfaceName, "unicast addresses in the format of: " - "iface-name/unicast-addr_stress can only be specified for" - " IPv6 addr_stress family"); - } else { - // The interface name includes the unicast addr_stress, so we split - // interface name and the unicast addr_stress to two variables. + // The interface name includes the address on which the socket should + // be opened, we we need to split interface name and the address to + // two variables. name = util::str::trim(iface_name.substr(0, pos)); addr_str = util::str::trim(iface_name.substr(pos + 1)); @@ -210,10 +208,10 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { " interface configuration"); } - // Unicast addr_stress following the interface name must not be empty. + // An address following the interface name must not be empty. if (addr_str.empty()) { isc_throw(InvalidIfaceName, - "empty unicast addr_stress specified in the interface" + "empty address specified in the interface" << " configuration"); } @@ -222,8 +220,8 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { if (name == ALL_IFACES_KEYWORD) { isc_throw(InvalidIfaceName, "wildcard interface name '" << ALL_IFACES_KEYWORD - << "' must not be used in conjunction with a" - " unicast addr_stress"); + << "' must not be used in conjunction with an" + " address"); } @@ -239,17 +237,26 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { // is invalid. IOAddress addr(addr_str); - // Check that the address is a valid unicast address. - if (!addr.isV6() || addr.isV6LinkLocal() || addr.isV6Multicast()) { - isc_throw(InvalidIfaceName, "address '" << addr << "' is not" - " a valid IPv6 unicast address"); - } + // Validate V6 address. + if (family == AF_INET6) { + // Check that the address is a valid unicast address. + if (!addr.isV6() || addr.isV6LinkLocal() || addr.isV6Multicast()) { + isc_throw(InvalidIfaceName, "address '" << addr << "' is not" + " a valid IPv6 unicast address"); + } - // There are valid cases where link local address can be specified to - // receive unicast traffic, e.g. sent by relay agent. - if (addr.isV6LinkLocal()) { - LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_UNICAST_LINK_LOCAL) - .arg(addr.toText()).arg(name); + // There are valid cases where link local address can be specified to + // receive unicast traffic, e.g. sent by relay agent. + if (addr.isV6LinkLocal()) { + LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_UNICAST_LINK_LOCAL) + .arg(addr.toText()).arg(name); + } + + } else { + if (!addr.isV4()) { + isc_throw(InvalidIfaceName, "address '" << addr << "' is not" + " a valid IPv4 address"); + } } // Interface must have this address assigned. @@ -261,17 +268,41 @@ CfgIface::use(const uint16_t family, const std::string& iface_name) { // Insert address and the interface to the collection of unicast // addresses. - if (unicast_map_.find(name) != unicast_map_.end()) { + if (address_map_.find(name) != address_map_.end()) { isc_throw(DuplicateIfaceName, "must not specify unicast address '" << addr << "' for interface '" << name << "' " "because other unicast address has already been" " specified for this interface"); } - LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_UNICAST) - .arg(addr.toText()).arg(name); - unicast_map_.insert(std::pair(name, addr)); + + if (family == AF_INET6) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_UNICAST) + .arg(addr.toText()).arg(name); + + } else { + LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_USE_ADDRESS) + .arg(addr.toText()).arg(name); + } + address_map_.insert(std::pair(name, addr)); } + // If interface name was explicitly specified and we're not parsing + // a unicast IPv6 address, add the interface to the interface set. + if ((name != ALL_IFACES_KEYWORD) && + ((family == AF_INET) || ((family == AF_INET6) && addr_str.empty()))) { + // If interface has already been specified. + if (iface_set_.find(name) != iface_set_.end()) { + isc_throw(DuplicateIfaceName, "interface '" << name + << "' has already been specified"); + } + + // Log that we're listening on the specific interface and that the + // address is not explicitly specified. + if (addr_str.empty()) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_ADD_IFACE).arg(name); + } + iface_set_.insert(name); + } } } // end of isc::dhcp namespace diff --git a/src/lib/dhcpsrv/cfg_iface.h b/src/lib/dhcpsrv/cfg_iface.h index 44fa041cde..c88fa0bbf7 100644 --- a/src/lib/dhcpsrv/cfg_iface.h +++ b/src/lib/dhcpsrv/cfg_iface.h @@ -193,14 +193,13 @@ private: /// @brief A set of interface names specified by the user. IfaceSet iface_set_; - /// @brief A map of interfaces and unicast addresses. - typedef std::map UnicastMap; + /// @brief A map of interfaces and addresses to which the server + /// should bind sockets. + typedef std::map ExplicitAddressMap; - /// @brief A map which holds the pairs of interface names and unicast - /// addresses for which the unicast sockets should be opened. - /// - /// This is only used for V6 family. - UnicastMap unicast_map_; + /// @brief A map which holds the pairs of interface names and addresses + /// for which the sockets should be opened. + ExplicitAddressMap address_map_; /// @brief A booolean value which indicates that the wildcard interface name /// has been specified (*). diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 7d7088d6d5..0ce7940728 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -142,6 +142,10 @@ This warning message is logged when user specified a link-local address to receive unicast traffic. The warning message is issued because it is an uncommon use. +% DHCPSRV_CFGMGR_USE_ADDRESS listening on address %1, on interface %2 +A message issued when server is configured to listen on the explicitly specified +IP address on the given interface. + % DHCPSRV_CLOSE_DB closing currently open %1 database This is a debug message, issued when the DHCP server closes the currently open lease database. It is issued at program shutdown and whenever diff --git a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc index f83a32f888..7dee356466 100644 --- a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc @@ -42,6 +42,14 @@ public: /// @param family One of: AF_INET or AF_INET6 bool socketOpen(const std::string& iface_name, const int family) const; + /// @brief Checks if socket is opened on the specified interface and bound + /// to a specific IPv4 address. + /// + /// @param iface_name Interface name. + /// @param address Address that the socket should be bound to. + bool socketOpen(const std::string& iface_name, + const std::string& address) const; + /// @brief Checks if unicast socket is opened on interface. /// /// @param iface_name Interface name. @@ -57,6 +65,15 @@ CfgIfaceTest::socketOpen(const std::string& iface_name, const int family) const { return (iface_mgr_test_config_.socketOpen(iface_name, family)); } + +bool +CfgIfaceTest::socketOpen(const std::string& iface_name, + const std::string& address) const { + return (iface_mgr_test_config_.socketOpen(iface_name, address)); +} + + + bool CfgIfaceTest::unicastOpen(const std::string& iface_name) const { return (iface_mgr_test_config_.unicastOpen(iface_name)); @@ -99,7 +116,38 @@ TEST_F(CfgIfaceTest, explicitNamesV4) { EXPECT_FALSE(socketOpen("eth0", AF_INET)); EXPECT_TRUE(socketOpen("eth1", AF_INET)); EXPECT_FALSE(socketOpen("lo", AF_INET)); +} +// This test checks that it is possible to specify an interface and address +// on this interface to which the socket should be bound. The sockets should +// not be opened on other addresses on this interface. +TEST_F(CfgIfaceTest, explicitNamesAndAddressesV4) { + CfgIface cfg; + ASSERT_NO_THROW(cfg.use(AF_INET, "eth0/10.0.0.1")); + ASSERT_NO_THROW(cfg.use(AF_INET, "eth1/192.0.2.3")); + ASSERT_THROW(cfg.use(AF_INET, "eth1/192.0.2.5"), DuplicateIfaceName); + + // Open sockets on specified interfaces and addresses. + cfg.openSockets(AF_INET, DHCP4_SERVER_PORT); + + EXPECT_TRUE(socketOpen("eth0", "10.0.0.1")); + EXPECT_TRUE(socketOpen("eth1", "192.0.2.3")); + EXPECT_FALSE(socketOpen("eth1", "192.0.2.5")); + + // Close all sockets and make sure they are really closed. + cfg.closeSockets(); + ASSERT_FALSE(socketOpen("eth0", "10.0.0.1")); + ASSERT_FALSE(socketOpen("eth1", "192.0.2.3")); + ASSERT_FALSE(socketOpen("eth1", "192.0.2.5")); + + // Now check that the socket can be bound to a different address on + // eth1. + ASSERT_NO_THROW(cfg.use(AF_INET, "eth1/192.0.2.5")); + ASSERT_THROW(cfg.use(AF_INET, "eth1/192.0.2.3"), DuplicateIfaceName); + + EXPECT_FALSE(socketOpen("eth0", "10.0.0.1")); + EXPECT_FALSE(socketOpen("eth1", "192.0.2.3")); + EXPECT_TRUE(socketOpen("eth1", "192.0.2.5")); } // This test checks that the interface names can be explicitly selected @@ -118,7 +166,7 @@ TEST_F(CfgIfaceTest, explicitNamesV6) { EXPECT_TRUE(socketOpen("eth1", AF_INET6)); EXPECT_FALSE(socketOpen("lo", AF_INET6)); - // No IPv4 sockets should be present because we wanted IPv4 sockets. + // No IPv4 sockets should be present because we wanted IPv6 sockets. EXPECT_FALSE(socketOpen("eth0", AF_INET)); EXPECT_FALSE(socketOpen("eth1", AF_INET)); EXPECT_FALSE(socketOpen("lo", AF_INET));