+2168. [bug] andrei
+ Fixed interface redetection which had stopped working since
+ Kea 2.3.6.
+ (Gitlab #3017, #3145)
+
2167. [bug] razvan
Fixed a race condition in free lease queue allocator.
(Gitlab #3111, #3143)
}
}
+/// @brief Process a DHCPv4 confguration and return an answer stating if the
+/// configuration is valid, or specifying details about the error otherwise.
+///
+/// @param config_set the configuration being processed
isc::data::ConstElementPtr
processDhcp4Config(isc::data::ConstElementPtr config_set) {
// Before starting any subnet operations, let's reset the subnet-id counter,
parser.parse(hr_identifiers);
}
- ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
- if (ifaces_config) {
- parameter_name = "interfaces-config";
- IfacesConfigParser parser(AF_INET, true);
- CfgIfacePtr cfg_iface = srv_config->getCfgIface();
- parser.parse(cfg_iface, ifaces_config);
- }
-
ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
if (sanity_checks) {
parameter_name = "sanity-checks";
}
}
} else {
- string parameter_name;
- ElementPtr mutable_cfg;
-
// disable multi-threading (it will be applied by new configuration)
// this must be done in order to properly handle MT to ST transition
// when 'multi-threading' structure is missing from new config and
TimerMgr::instance()->unregisterTimers();
server.discardPackets();
server.getCBControl()->reset();
+ }
+ if (status_code == CONTROL_RESULT_SUCCESS) {
+ string parameter_name;
+ ElementPtr mutable_cfg;
try {
-
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
if (ifaces_config) {
parameter_name = "interfaces-config";
- IfacesConfigParser parser(AF_INET, false);
+ IfacesConfigParser parser(AF_INET, check_only);
CfgIfacePtr cfg_iface = srv_config->getCfgIface();
cfg_iface->reset();
parser.parse(cfg_iface, ifaces_config);
class NakedControlledDhcpv4Srv: public ControlledDhcpv4Srv {
// "Naked" DHCPv4 server, exposes internal fields
public:
- NakedControlledDhcpv4Srv():ControlledDhcpv4Srv(0) {
+ NakedControlledDhcpv4Srv() : ControlledDhcpv4Srv(0) {
CfgMgr::instance().setFamily(AF_INET);
}
/// @brief Path to the UNIX socket being used to communicate with the server
std::string socket_path_;
+ /// @brief List of interfaces (defaults to "*").
+ std::string interfaces_;
+
/// @brief Pointer to the tested server object
boost::shared_ptr<NakedControlledDhcpv4Srv> server_;
/// @brief Default constructor
///
/// Sets socket path to its default value.
- CtrlChannelDhcpv4SrvTest() {
+ CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") {
const char* env = getenv("KEA_SOCKET_TEST_DIR");
if (env) {
socket_path_ = string(env) + "/kea4.sock";
socket_path_ = sandbox.join("kea4.sock");
}
reset();
+ IfaceMgr::instance().setTestMode(false);
+ IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
+ IfaceMgr::instancePtr().get(), ph::_1));
}
/// @brief Destructor
CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND);
server_.reset();
+ IfaceMgr::instance().setTestMode(false);
+ IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
+ IfaceMgr::instancePtr().get(), ph::_1));
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().detectIfaces();
};
/// @brief Returns pointer to the server's IO service.
std::string header =
"{"
" \"interfaces-config\": {"
- " \"interfaces\": [ \"*\" ]"
+ " \"interfaces\": [";
+
+ std::string body = "]"
" },"
" \"expired-leases-processing\": {"
" \"reclaim-timer-wait-time\": 60,"
// Fill in the socket-name value with socket_path_ to
// make the actual configuration text.
- std::string config_txt = header + socket_path_ + footer;
-
+ std::string config_txt = header + interfaces_ + body + socket_path_ + footer;
ASSERT_NO_THROW(server_.reset(new NakedControlledDhcpv4Srv()));
ConstElementPtr config;
ASSERT_GT(isc::config::CommandMgr::instance().getControlSocketFD(), -1);
}
-
/// @brief Reset hooks data
///
/// Resets the data for the hooks-related portion of the test by ensuring
sendUnixCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
- // socket name (/tmp/kea-<value-changing-each-time>/kea6.sock), so the
+ // socket name (/tmp/kea-<value-changing-each-time>/kea4.sock), so the
// hash will be different each time. As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
EXPECT_FALSE(fileExists(socket_path_));
// With no command channel, should still receive the response. The config contains random
- // socket name (/tmp/kea-<value-changing-each-time>/kea6.sock), so the
+ // socket name (/tmp/kea-<value-changing-each-time>/kea4.sock), so the
// hash will be different each time. As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
sendUnixCommand(os.str(), response);
// Verify the configuration was successful. The config contains random
- // socket name (/tmp/kea-<value-changing-each-time>/kea6.sock), so the
+ // socket name (/tmp/kea-<value-changing-each-time>/kea4.sock), so the
// hash will be different each time. As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
sendUnixCommand("{ \"command\": \"config-write\", "
"\"arguments\": { \"filename\": \"test2.json\" } }", response);
+
checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test2.json");
::remove("test2.json");
}
// This command should reload test8.json config.
sendUnixCommand("{ \"command\": \"config-reload\" }", response);
+
+ // Verify the configuration was successful. The config contains random
+ // socket name (/tmp/kea-<value-changing-each-time>/kea4.sock), so the
+ // hash will be different each time. As such, we can do simplified checks:
+ // - verify the "result": 0 is there
+ // - verify the "text": "Configuration successful." is there
+ EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
+ EXPECT_NE(response.find("\"text\": \"Configuration successful.\""), std::string::npos);
+
+ // Check that the config was indeed applied.
+ const Subnet4Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
+ EXPECT_EQ(2, subnets->size());
+
+ ::remove("test8.json");
+}
+
+// Tests if config-reload attempts to reload a file and reports that the
+// file is loaded correctly.
+TEST_F(CtrlChannelDhcpv4SrvTest, configReloadDetectInterfaces) {
+ interfaces_ = "\"eth0\"";
+ IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+ "11:22:33:44:55:66");
+ auto detectIfaces = [&](bool update_only) {
+ if (!update_only) {
+ eth0->addAddress(IOAddress("10.0.0.1"));
+ eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ eth0->addAddress(IOAddress("2001:db8:1::1"));
+ IfaceMgr::instance().addInterface(eth0);
+ }
+ return (false);
+ };
+ IfaceMgr::instance().setDetectCallback(detectIfaces);
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().detectIfaces();
+ createUnixChannelServer();
+ std::string response;
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ server_->setConfigFile("test8.json");
+
+ // Ok, enough fooling around. Let's create a valid config.
+ const std::string cfg_txt =
+ "{ \"Dhcp4\": {"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ \"eth1\" ]"
+ " },"
+ " \"subnet4\": ["
+ " { \"id\": 1, \"subnet\": \"192.0.2.0/24\" },"
+ " { \"id\": 2, \"subnet\": \"192.0.3.0/24\" }"
+ " ],"
+ " \"valid-lifetime\": 4000,"
+ " \"lease-database\": {"
+ " \"type\": \"memfile\", \"persist\": false }"
+ "} }";
+ ofstream f("test8.json", ios::trunc);
+ f << cfg_txt;
+ f.close();
+
+ IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+ "AA:BB:CC:DD:EE:FF");
+ auto detectUpdateIfaces = [&](bool update_only) {
+ if (!update_only) {
+ eth1->addAddress(IOAddress("192.0.2.3"));
+ eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+ eth1->addAddress(IOAddress("3001:db8:100::1"));
+ IfaceMgr::instance().addInterface(eth1);
+ }
+ return (false);
+ };
+ IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+ // This command should reload test8.json config.
+ sendUnixCommand("{ \"command\": \"config-reload\" }", response);
+
// Verify the configuration was successful. The config contains random
- // socket name (/tmp/kea-<value-changing-each-time>/kea6.sock), so the
+ // socket name (/tmp/kea-<value-changing-each-time>/kea4.sock), so the
// hash will be different each time. As such, we can do simplified checks:
// - verify the "result": 0 is there
// - verify the "text": "Configuration successful." is there
}
}
+/// @brief Process a DHCPv6 confguration and return an answer stating if the
+/// configuration is valid, or specifying details about the error otherwise.
+///
+/// @param config_set the configuration being processed
isc::data::ConstElementPtr
processDhcp6Config(isc::data::ConstElementPtr config_set) {
// Before starting any subnet operations, let's reset the subnet-id counter,
parser.parse(cfg, server_id);
}
- ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
- if (ifaces_config) {
- parameter_name = "interfaces-config";
- IfacesConfigParser parser(AF_INET6, true);
- CfgIfacePtr cfg_iface = srv_config->getCfgIface();
- parser.parse(cfg_iface, ifaces_config);
- }
-
ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
if (sanity_checks) {
parameter_name = "sanity-checks";
}
}
} else {
- string parameter_name;
- ElementPtr mutable_cfg;
-
// disable multi-threading (it will be applied by new configuration)
// this must be done in order to properly handle MT to ST transition
// when 'multi-threading' structure is missing from new config and
TimerMgr::instance()->unregisterTimers();
server.discardPackets();
server.getCBControl()->reset();
+ }
+ if (status_code == CONTROL_RESULT_SUCCESS) {
+ string parameter_name;
+ ElementPtr mutable_cfg;
try {
-
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
if (ifaces_config) {
parameter_name = "interfaces-config";
- IfacesConfigParser parser(AF_INET6, false);
+ IfacesConfigParser parser(AF_INET6, check_only);
CfgIfacePtr cfg_iface = srv_config->getCfgIface();
cfg_iface->reset();
parser.parse(cfg_iface, ifaces_config);
class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
// "Naked" DHCPv6 server, exposes internal fields
public:
- NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) {
+ NakedControlledDhcpv6Srv() : ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) {
CfgMgr::instance().setFamily(AF_INET6);
}
reset();
};
-
/// @brief Reset hooks data
///
/// Resets the data for the hooks-related portion of the test by ensuring
/// @brief Path to the UNIX socket being used to communicate with the server
std::string socket_path_;
+ /// @brief List of interfaces (defaults to "*").
+ std::string interfaces_;
+
/// @brief Pointer to the tested server object
boost::shared_ptr<NakedControlledDhcpv6Srv> server_;
/// @brief Default constructor
///
/// Sets socket path to its default value.
- CtrlChannelDhcpv6SrvTest() {
+ CtrlChannelDhcpv6SrvTest() : interfaces_("\"*\"") {
const char* env = getenv("KEA_SOCKET_TEST_DIR");
if (env) {
socket_path_ = string(env) + "/kea6.sock";
socket_path_ = sandbox.join("/kea6.sock");
}
reset();
+ IfaceMgr::instance().setTestMode(false);
+ IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
+ IfaceMgr::instancePtr().get(), ph::_1));
}
/// @brief Destructor
~CtrlChannelDhcpv6SrvTest() {
server_.reset();
reset();
+ IfaceMgr::instance().setTestMode(false);
+ IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces,
+ IfaceMgr::instancePtr().get(), ph::_1));
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().detectIfaces();
};
/// @brief Returns pointer to the server's IO service.
std::string header =
"{"
" \"interfaces-config\": {"
- " \"interfaces\": [ \"*\" ]"
+ " \"interfaces\": [";
+
+ std::string body = "]"
" },"
" \"expired-leases-processing\": {"
" \"reclaim-timer-wait-time\": 60,"
// Fill in the socket-name value with socket_path_ to
// make the actual configuration text.
- std::string config_txt = header + socket_path_ + footer;
-
+ std::string config_txt = header + interfaces_ + body + socket_path_ + footer;
ASSERT_NO_THROW(server_.reset(new NakedControlledDhcpv6Srv()));
ConstElementPtr config;
sendUnixCommand("{ \"command\": \"config-write\", "
"\"arguments\": { \"filename\": \"test2.json\" } }", response);
+
checkConfigWrite(response, CONTROL_RESULT_SUCCESS, "test2.json");
::remove("test2.json");
}
::remove("test8.json");
}
+// Tests if config-reload attempts to reload a file and reports that the
+// file is loaded correctly.
+TEST_F(CtrlChannelDhcpv6SrvTest, configReloadDetectInterfaces) {
+ interfaces_ = "\"eth0\"";
+ IfacePtr eth0 = IfaceMgrTestConfig::createIface("eth0", ETH0_INDEX,
+ "11:22:33:44:55:66");
+ auto detectIfaces = [&](bool update_only) {
+ if (!update_only) {
+ eth0->addAddress(IOAddress("10.0.0.1"));
+ eth0->addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ eth0->addAddress(IOAddress("2001:db8:1::1"));
+ IfaceMgr::instance().addInterface(eth0);
+ }
+ return (false);
+ };
+ IfaceMgr::instance().setDetectCallback(detectIfaces);
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().detectIfaces();
+ createUnixChannelServer();
+ std::string response;
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ server_->setConfigFile("test8.json");
+
+ // Ok, enough fooling around. Let's create a valid config.
+ const std::string cfg_txt =
+ "{ \"Dhcp6\": {"
+ " \"interfaces-config\": {"
+ " \"interfaces\": [ \"eth1\" ]"
+ " },"
+ " \"subnet6\": ["
+ " { \"subnet\": \"2001:db8:1::/64\", \"id\": 1 },"
+ " { \"subnet\": \"2001:db8:2::/64\", \"id\": 2 }"
+ " ],"
+ " \"lease-database\": {"
+ " \"type\": \"memfile\", \"persist\": false }"
+ "} }";
+ ofstream f("test8.json", ios::trunc);
+ f << cfg_txt;
+ f.close();
+
+ IfacePtr eth1 = IfaceMgrTestConfig::createIface("eth1", ETH1_INDEX,
+ "AA:BB:CC:DD:EE:FF");
+ auto detectUpdateIfaces = [&](bool update_only) {
+ if (!update_only) {
+ eth1->addAddress(IOAddress("192.0.2.3"));
+ eth1->addAddress(IOAddress("fe80::3a60:77ff:fed5:abcd"));
+ eth1->addAddress(IOAddress("3001:db8:100::1"));
+ IfaceMgr::instance().addInterface(eth1);
+ }
+ return (false);
+ };
+ IfaceMgr::instance().setDetectCallback(detectUpdateIfaces);
+
+ // This command should reload test8.json config.
+ sendUnixCommand("{ \"command\": \"config-reload\" }", response);
+
+ // Verify the configuration was successful. The config contains random
+ // socket name (/tmp/kea-<value-changing-each-time>/kea6.sock), so the
+ // hash will be different each time. As such, we can do simplified checks:
+ // - verify the "result": 0 is there
+ // - verify the "text": "Configuration successful." is there
+ EXPECT_NE(response.find("\"result\": 0"), std::string::npos);
+ EXPECT_NE(response.find("\"text\": \"Configuration successful.\""), std::string::npos);
+
+ // Check that the config was indeed applied.
+ const Subnet6Collection* subnets =
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
+ EXPECT_EQ(2, subnets->size());
+
+ ::remove("test8.json");
+}
+
// This test verifies that disable DHCP service command performs sanity check on
// parameters.
TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableBadParam) {
// used for generating DUID-LLT.
if (identifier.empty()) {
isc_throw(Unexpected, "unable to find suitable interface for "
- " generating a DUID-LLT");
+ "generating a DUID-LLT");
}
}
using namespace isc::util;
using namespace isc::util::io;
using namespace isc::util::io::internal;
+namespace ph = std::placeholders;
namespace isc {
namespace dhcp {
IfaceMgr::IfaceMgr()
: packet_filter_(new PktFilterInet()),
packet_filter6_(new PktFilterInet6()),
- test_mode_(false),
- allow_loopback_(false) {
+ test_mode_(false), allow_loopback_(false) {
// Ensure that PQMs have been created to guarantee we have
// default packet queues in place.
isc_throw(Unexpected, "Failed to create PacketQueueManagers: " << ex.what());
}
+ detect_callback_ = std::bind(&IfaceMgr::checkDetectIfaces, this, ph::_1);
+
try {
// required for sending/receiving packets
return (false);
}
-void IfaceMgr::stubDetectIfaces() {
- string ifaceName;
- const string v4addr("127.0.0.1"), v6addr("::1");
-
- // This is a stub implementation for interface detection. Actual detection
- // is faked by detecting loopback interface (lo or lo0). It will eventually
- // be removed once we have actual implementations for all supported systems.
-
- if (if_nametoindex("lo") > 0) {
- ifaceName = "lo";
- // this is Linux-like OS
- } else if (if_nametoindex("lo0") > 0) {
- ifaceName = "lo0";
- // this is BSD-like OS
- } else {
- // we give up. What OS is this, anyway? Solaris? Hurd?
- isc_throw(NotImplemented,
- "Interface detection on this OS is not supported.");
- }
-
- IfacePtr iface(new Iface(ifaceName, if_nametoindex(ifaceName.c_str())));
- iface->flag_up_ = true;
- iface->flag_running_ = true;
-
- // Note that we claim that this is not a loopback. iface_mgr tries to open a
- // socket on all interfaces that are up, running and not loopback. As this is
- // the only interface we were able to detect, let's pretend this is a normal
- // interface.
- iface->flag_loopback_ = false;
- iface->flag_multicast_ = true;
- iface->flag_broadcast_ = true;
- iface->setHWType(HWTYPE_ETHERNET);
-
- iface->addAddress(IOAddress(v4addr));
- iface->addAddress(IOAddress(v6addr));
- addInterface(iface);
-}
-
bool
IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast,
IfaceMgrErrorMsgCallback error_handler,
return (getIfaceInternal(ifindex, MultiThreadingMgr::instance().getMode()));
}
-
IfacePtr
IfaceCollection::getIface(const std::string& ifname) {
return (getIfaceInternal(ifname, MultiThreadingMgr::instance().getMode()));
isc_throw(IfaceNotFound, "Tried to find socket for non-existent interface");
}
-
const Iface::SocketCollection& socket_collection = iface->getSockets();
Iface::SocketCollection::const_iterator candidate = socket_collection.end();
return errors_;
}
+bool
+IfaceMgr::checkDetectIfaces(bool update_only) {
+ if (test_mode_ && update_only) {
+ return (false);
+ }
+ return (true);
+}
+
} // end of namespace isc::dhcp
} // end of namespace isc
/// @param addr address to be removed.
///
/// @return true if removal was successful (address was in collection),
- /// false otherwise
+ /// false otherwise
bool delAddress(const isc::asiolink::IOAddress& addr);
/// @brief Adds socket descriptor to an interface.
///
class IfaceMgr : public boost::noncopyable {
public:
- /// Defines callback used when data is received over external sockets.
+ /// @brief Defines callback used when data is received over external sockets.
+ ///
/// @param fd socket descriptor of the ready socket
typedef std::function<void (int fd)> SocketCallback;
+ /// @brief Defines callback used when detecting interfaces.
+ ///
+ /// @param update_only Only add interfaces that do not exist and update
+ /// existing interfaces.
+ ///
+ /// @return true if callback exited with no issue and @ref detectIfaces
+ /// should continue with specific system calls, false otherwise.
+ typedef std::function<bool (bool)> DetectCallback;
+
/// Keeps callback information for external sockets.
struct SocketCallbackInfo {
/// Socket descriptor of the external socket.
/// @c PktFilter class to mimic socket operation on these interfaces.
void clearIfaces();
+ /// @brief Set a callback to perform operations before executing specific
+ /// system calls.
+ ///
+ /// @param cb The callback used before executing specific system calls.
+ void setDetectCallback(const DetectCallback& cb) {
+ detect_callback_ = cb;
+ }
+
+ /// @brief Check if the specific system calls used to detect interfaces
+ /// should be executed.
+ ///
+ /// @param update_only Only add interfaces that do not exist and update
+ /// existing interfaces.
+ ///
+ /// @return true if the specific system calls should be executed, false
+ /// otherwise causing the @ref detectIfaces to return immediately.
+ bool checkDetectIfaces(bool update_only);
+
/// @brief Detects network interfaces.
///
- /// This method will eventually detect available interfaces. For now
- /// it offers stub implementation. First interface name and link-local
- /// IPv6 address is read from interfaces.txt file.
+ /// If the @ref detect_callback_ returns true, the specific system calls are
+ /// executed, otherwise the @ref detectIfaces will return immediately.
///
/// @param update_only Only add interfaces that do not exist and update
/// existing interfaces.
int openSocketFromRemoteAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
-
/// @brief Opens IPv6 sockets on detected interfaces.
///
/// This method opens sockets only on interfaces which have the
/// @return Pkt6 object representing received packet (or null)
Pkt6Ptr receive6Indirect(uint32_t timeout_sec, uint32_t timeout_usec = 0);
-
- /// @brief Stub implementation of network interface detection.
- ///
- /// This implementations reads a single line from interfaces.txt file
- /// and pretends to detect such interface. First interface name and
- /// link-local IPv6 address or IPv4 address is read from the
- /// interfaces.txt file.
- void stubDetectIfaces();
-
/// @brief List of available interfaces
IfaceCollection ifaces_;
getLocalAddress(const isc::asiolink::IOAddress& remote_addr,
const uint16_t port);
-
/// @brief Open an IPv6 socket with multicast support.
///
/// This function opens a socket capable of receiving messages sent to
/// @brief Indicates if the IfaceMgr is in the test mode.
bool test_mode_;
+ /// @brief Detect callback used to perform actions before system dependent
+ /// function calls.
+ ///
+ /// If the @ref detect_callback_ returns true, the specific system calls are
+ /// executed, otherwise the @ref detectIfaces will return immediately.
+ DetectCallback detect_callback_;
+
/// @brief Allows to use loopback
bool allow_loopback_;
/// This is a BSD specific interface detection method.
void
IfaceMgr::detectIfaces(bool update_only) {
- if (isTestMode() && update_only) {
- return;
+ if (detect_callback_) {
+ if (!detect_callback_(update_only)) {
+ return;
+ }
}
struct ifaddrs* iflist = 0;// The whole interface list
/// Uses the socket-based netlink protocol to retrieve the list of interfaces
/// from the Linux kernel.
void IfaceMgr::detectIfaces(bool update_only) {
- if (isTestMode() && update_only) {
- return;
+ if (detect_callback_) {
+ if (!detect_callback_(update_only)) {
+ return;
+ }
}
// Copies of netlink messages about links will be stored here.
/// only, as earlier versions did not support getifaddrs() API.
void
IfaceMgr::detectIfaces(bool update_only) {
- if (isTestMode() && update_only) {
- return;
+ if (detect_callback_) {
+ if (!detect_callback_(update_only)) {
+ return;
+ }
}
struct ifaddrs* iflist = 0;// The whole interface list
}
void
-IfaceMgrTestConfig::addIface(const std::string& name, const unsigned int ifindex) {
+IfaceMgrTestConfig::addIface(const std::string& name,
+ const unsigned int ifindex) {
IfaceMgr::instance().addInterface(createIface(name, ifindex));
}
IfacePtr
-IfaceMgrTestConfig::createIface(const std::string &name, const unsigned int ifindex) {
+IfaceMgrTestConfig::createIface(const std::string& name,
+ const unsigned int ifindex,
+ const std::string& mac) {
IfacePtr iface(new Iface(name, ifindex));
if (name == "lo") {
iface->flag_loopback_ = true;
iface->flag_up_ = true;
iface->flag_running_ = true;
- // Set MAC address to 08:08:08:08:08:08.
- std::vector<uint8_t> mac_vec(6, 8);
+ // Set MAC address.
+ HWAddr hwaddr = HWAddr::fromText(mac);
+ std::vector<uint8_t> mac_vec = hwaddr.hwaddr_;
iface->setMac(&mac_vec[0], mac_vec.size());
iface->setHWType(HTYPE_ETHER);
return (false);
}
-}
-}
-}
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
/// - multicast always to true
/// - broadcast always to false
///
- /// If one needs to modify the default flag settings, the setIfaceFlags
- /// function should be used.
- ///
/// @param name A name of the interface to be created.
/// @param ifindex An index of the interface to be created.
+ /// @param mac The mac of the interface.
///
/// @return An object representing interface.
- static IfacePtr createIface(const std::string& name, const unsigned int ifindex);
+ static IfacePtr createIface(const std::string& name,
+ const unsigned int ifindex,
+ const std::string& mac = "08:08:08:08:08:08");
/// @brief Creates a default (example) set of fake interfaces.
void createIfaces();
/// @param iface_name Interface name.
bool unicastOpen(const std::string& iface_name) const;
-
private:
/// @brief Currently used packet filter for DHCPv4.
PktFilterPtr packet_filter4_;
PktFilter6Ptr packet_filter6_;
};
-};
-};
-};
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
#endif // IFACE_MGR_TEST_CONFIG_H
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
-#include <dhcp/tests/pkt_captures.h>
-#include <string>
#include <asiolink/io_address.h>
+#include <dhcp/tests/pkt_captures.h>
#include <util/encode/hex.h>
+#include <string>
-/// @file wireshark.cc
+/// @file pkt_captures4.cc
///
/// @brief contains packet captures imported from Wireshark
///
#include <config.h>
#include <dhcp/pkt6.h>
-#include <util/encode/hex.h>
#include <dhcp/tests/pkt_captures.h>
+#include <util/encode/hex.h>
#include <string>
/// @file pkt_captures6.cc
if (element.first == "interfaces") {
parseInterfacesList(cfg, element.second);
continue;
-
}
if (element.first == "dhcp-socket-type") {