Commit 43ad3049 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[991] Broadcast options are enabled on sockets conditionally.

parent 63df2f7c
...@@ -57,7 +57,7 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid"; ...@@ -57,7 +57,7 @@ static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
// These are hardcoded parameters. Currently this is a skeleton server that only // These are hardcoded parameters. Currently this is a skeleton server that only
// grants those options and a single, fixed, hardcoded lease. // grants those options and a single, fixed, hardcoded lease.
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) { Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port); LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
try { try {
// First call to instance() will create IfaceMgr (it's a singleton) // First call to instance() will create IfaceMgr (it's a singleton)
...@@ -67,7 +67,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) { ...@@ -67,7 +67,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
if (port) { if (port) {
// open sockets only if port is non-zero. Port 0 is used // open sockets only if port is non-zero. Port 0 is used
// for non-socket related testing. // for non-socket related testing.
IfaceMgr::instance().openSockets4(port); IfaceMgr::instance().openSockets4(port, use_bcast);
} }
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE); string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
......
...@@ -66,8 +66,10 @@ class Dhcpv4Srv : public boost::noncopyable { ...@@ -66,8 +66,10 @@ class Dhcpv4Srv : public boost::noncopyable {
/// @param port specifies port number to listen on /// @param port specifies port number to listen on
/// @param dbconfig Lease manager configuration string. The default /// @param dbconfig Lease manager configuration string. The default
/// of the "memfile" manager is used for testing. /// of the "memfile" manager is used for testing.
/// @param use_bcast configure sockets to support broadcast messages.
Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT, Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT,
const char* dbconfig = "type=memfile"); const char* dbconfig = "type=memfile",
const bool use_bcast = true);
/// @brief Destructor. Used during DHCPv4 service shutdown. /// @brief Destructor. Used during DHCPv4 service shutdown.
~Dhcpv4Srv(); ~Dhcpv4Srv();
......
...@@ -45,7 +45,16 @@ namespace { ...@@ -45,7 +45,16 @@ namespace {
class NakedDhcpv4Srv: public Dhcpv4Srv { class NakedDhcpv4Srv: public Dhcpv4Srv {
// "Naked" DHCPv4 server, exposes internal fields // "Naked" DHCPv4 server, exposes internal fields
public: public:
NakedDhcpv4Srv(uint16_t port = 0):Dhcpv4Srv(port) { }
/// @brief Constructor.
///
/// It disables configuration of broadcast options on
/// sockets that are opened by the Dhcpv4Srv constructor.
/// Setting broadcast options requires root privileges
/// which is not the case when running unit tests.
NakedDhcpv4Srv(uint16_t port = 0)
: Dhcpv4Srv(port, "type=memfile", false) {
}
using Dhcpv4Srv::processDiscover; using Dhcpv4Srv::processDiscover;
using Dhcpv4Srv::processRequest; using Dhcpv4Srv::processRequest;
......
...@@ -195,10 +195,23 @@ void IfaceMgr::stubDetectIfaces() { ...@@ -195,10 +195,23 @@ void IfaceMgr::stubDetectIfaces() {
addInterface(iface); addInterface(iface);
} }
bool IfaceMgr::openSockets4(const uint16_t port) { bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
int sock; int sock;
int count = 0; int count = 0;
// This option is used to bind sockets to particular interfaces.
// This is currently the only way to discover on which interface
// the broadcast packet has been received. If this option is
// not supported then only one interface should be confugured
// to listen for broadcast traffic.
#ifdef SO_BINDTODEVICE
const bool bind_to_device = true;
#else
const bool bind_to_device = false;
#endif
int bcast_num = 0;
for (IfaceCollection::iterator iface = ifaces_.begin(); for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end(); iface != ifaces_.end();
++iface) { ++iface) {
...@@ -219,9 +232,37 @@ bool IfaceMgr::openSockets4(const uint16_t port) { ...@@ -219,9 +232,37 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
continue; continue;
} }
// Open socket and enable broadcast traffic // If selected interface is broadcast capable set appropriate
// (two last arguments enable broadcast). // options on the socket so as it can receive and send broadcast
sock = openSocket(iface->getName(), *addr, port, true, true); // messages.
if (iface->flag_broadcast_ && use_bcast) {
// If our OS supports binding socket to a device we can listen
// for broadcast messages on multiple interfaces. Otherwise we
// bind to INADDR_ANY address but we can do it only once. Thus,
// if one socket has been bound we can't do it any further.
if (!bind_to_device && bcast_num > 0) {
isc_throw(SocketConfigError, "SO_BINDTODEVICE socket option is"
<< " not supported on this OS; therefore, DHCP"
<< " server can only listen broadcast traffic on"
<< " a single interface");
} else {
// We haven't open any broadcast sockets yet, so we can
// open at least one more.
sock = openSocket(iface->getName(), *addr, port, true, true);
// Binding socket to an interface is not supported so we can't
// open any more broadcast sockets. Increase the number of
// opened broadcast sockets.
if (!bind_to_device) {
++bcast_num;
}
}
} else {
// Not broadcast capable, do not set broadcast flags.
sock = openSocket(iface->getName(), *addr, port, false, false);
}
if (sock < 0) { if (sock < 0) {
isc_throw(SocketConfigError, "failed to open IPv4 socket" isc_throw(SocketConfigError, "failed to open IPv4 socket"
<< " supporting broadcast traffic"); << " supporting broadcast traffic");
......
...@@ -534,10 +534,12 @@ public: ...@@ -534,10 +534,12 @@ public:
/// Will throw exception if socket creation fails. /// Will throw exception if socket creation fails.
/// ///
/// @param port specifies port number (usually DHCP4_SERVER_PORT) /// @param port specifies port number (usually DHCP4_SERVER_PORT)
/// @param use_bcast configure sockets to support broadcast messages.
/// ///
/// @throw SocketOpenFailure if tried and failed to open socket. /// @throw SocketOpenFailure if tried and failed to open socket.
/// @return true if any sockets were open /// @return true if any sockets were open
bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT); bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT,
const bool use_bcast = true);
/// @brief Closes all open sockets. /// @brief Closes all open sockets.
/// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes. /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
......
...@@ -49,6 +49,7 @@ PktFilterInet::openSocket(const Iface& iface, ...@@ -49,6 +49,7 @@ PktFilterInet::openSocket(const Iface& iface,
isc_throw(SocketConfigError, "Failed to create UDP6 socket."); isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
} }
#ifdef SO_BINDTODEVICE
if (receive_bcast) { if (receive_bcast) {
// Bind to device so as we receive traffic on a specific interface. // Bind to device so as we receive traffic on a specific interface.
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(), if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(),
...@@ -58,6 +59,7 @@ PktFilterInet::openSocket(const Iface& iface, ...@@ -58,6 +59,7 @@ PktFilterInet::openSocket(const Iface& iface,
<< "on socket " << sock); << "on socket " << sock);
} }
} }
#endif
if (send_bcast) { if (send_bcast) {
// Enable sending to broadcast address. // Enable sending to broadcast address.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment