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";
// These are hardcoded parameters. Currently this is a skeleton server that only
// 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);
try {
// First call to instance() will create IfaceMgr (it's a singleton)
......@@ -67,7 +67,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
if (port) {
// open sockets only if port is non-zero. Port 0 is used
// 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);
......
......@@ -66,8 +66,10 @@ class Dhcpv4Srv : public boost::noncopyable {
/// @param port specifies port number to listen on
/// @param dbconfig Lease manager configuration string. The default
/// of the "memfile" manager is used for testing.
/// @param use_bcast configure sockets to support broadcast messages.
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.
~Dhcpv4Srv();
......
......@@ -45,7 +45,16 @@ namespace {
class NakedDhcpv4Srv: public Dhcpv4Srv {
// "Naked" DHCPv4 server, exposes internal fields
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::processRequest;
......
......@@ -195,10 +195,23 @@ void IfaceMgr::stubDetectIfaces() {
addInterface(iface);
}
bool IfaceMgr::openSockets4(const uint16_t port) {
bool IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast) {
int sock;
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();
iface != ifaces_.end();
++iface) {
......@@ -219,9 +232,37 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
continue;
}
// Open socket and enable broadcast traffic
// (two last arguments enable broadcast).
// If selected interface is broadcast capable set appropriate
// options on the socket so as it can receive and send broadcast
// 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) {
isc_throw(SocketConfigError, "failed to open IPv4 socket"
<< " supporting broadcast traffic");
......
......@@ -534,10 +534,12 @@ public:
/// Will throw exception if socket creation fails.
///
/// @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.
/// @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.
/// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
......
......@@ -49,6 +49,7 @@ PktFilterInet::openSocket(const Iface& iface,
isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
#ifdef SO_BINDTODEVICE
if (receive_bcast) {
// Bind to device so as we receive traffic on a specific interface.
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, iface.getName().c_str(),
......@@ -58,6 +59,7 @@ PktFilterInet::openSocket(const Iface& iface,
<< "on socket " << sock);
}
}
#endif
if (send_bcast) {
// 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