Commit c2d40e3d authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2902'

Conflicts:
	src/lib/dhcp/Makefile.am
parents 9346c7a0 454afa98
......@@ -57,12 +57,18 @@ 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, const bool use_bcast) {
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
const bool direct_response_desired) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
try {
// First call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong
IfaceMgr::instance();
// it may throw something if things go wrong.
// The 'true' value of the call to setMatchingPacketFilter imposes
// that IfaceMgr will try to use the mechanism to respond directly
// to the client which doesn't have address assigned. This capability
// may be lacking on some OSes, so there is no guarantee that server
// will be able to respond directly.
IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);
if (port) {
// open sockets only if port is non-zero. Port 0 is used
......@@ -199,9 +205,9 @@ Dhcpv4Srv::run() {
}
if (rsp) {
if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
rsp->setRemoteAddr(query->getRemoteAddr());
}
adjustRemoteAddr(query, rsp);
if (!rsp->getHops()) {
rsp->setRemotePort(DHCP4_CLIENT_PORT);
} else {
......@@ -352,19 +358,27 @@ Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// relay address
answer->setGiaddr(question->getGiaddr());
if (question->getGiaddr().toText() != "0.0.0.0") {
// relayed traffic
answer->setRemoteAddr(question->getGiaddr());
} else {
// direct traffic
answer->setRemoteAddr(question->getRemoteAddr());
}
// Let's copy client-id to response. See RFC6842.
OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
if (client_id) {
answer->addOption(client_id);
}
// If src/dest HW addresses are used by the packet filtering class
// we need to copy them as well. There is a need to check that the
// address being set is not-NULL because an attempt to set the NULL
// HW would result in exception. If these values are not set, the
// the default HW addresses (zeroed) should be generated by the
// packet filtering class when creating Ethernet header for
// outgoing packet.
HWAddrPtr src_hw_addr = question->getLocalHWAddr();
if (src_hw_addr) {
answer->setLocalHWAddr(src_hw_addr);
}
HWAddrPtr dst_hw_addr = question->getRemoteHWAddr();
if (dst_hw_addr) {
answer->setRemoteHWAddr(dst_hw_addr);
}
}
void
......@@ -517,28 +531,6 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
answer->setYiaddr(lease->addr_);
// If remote address is not set, we are dealing with a directly
// connected client requesting new lease. We can send response to
// the address assigned in the lease, but first we have to make sure
// that IfaceMgr supports responding directly to the client when
// client doesn't have address assigned to its interface yet.
if (answer->getRemoteAddr().toText() == "0.0.0.0") {
if (IfaceMgr::instance().isDirectResponseSupported()) {
answer->setRemoteAddr(lease->addr_);
} else {
// Since IfaceMgr does not support direct responses to
// clients not having IP addresses, we have to send response
// to broadcast. We don't check whether the use_bcast flag
// was set in the constructor, because this flag is only used
// by unit tests to prevent opening broadcast sockets, as
// it requires root privileges. If this function is invoked by
// unit tests, we expect that it sets broadcast address if
// direct response is not supported, so as a test can verify
// function's behavior, regardless of the use_bcast flag's value.
answer->setRemoteAddr(IOAddress("255.255.255.255"));
}
}
// IP Address Lease time (type 51)
opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
opt->setUint32(lease->valid_lft_);
......@@ -573,6 +565,60 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
}
}
void
Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg) {
// Let's create static objects representing zeroed and broadcast
// addresses. We will use them further in this function to test
// other addresses against them. Since they are static, they will
// be created only once.
static const IOAddress zero_addr("0.0.0.0");
static const IOAddress bcast_addr("255.255.255.255");
// If received relayed message, server responds to the relay address.
if (question->getGiaddr() != zero_addr) {
msg->setRemoteAddr(question->getGiaddr());
// If giaddr is 0 but client set ciaddr, server should unicast the
// response to ciaddr.
} else if (question->getCiaddr() != zero_addr) {
msg->setRemoteAddr(question->getCiaddr());
// We can't unicast the response to the client when sending NAK,
// because we haven't allocated address for him. Therefore,
// NAK is broadcast.
} else if (msg->getType() == DHCPNAK) {
msg->setRemoteAddr(bcast_addr);
// If yiaddr is set it means that we have created a lease for a client.
} else if (msg->getYiaddr() != zero_addr) {
// If the broadcast bit is set in the flags field, we have to
// send the response to broadcast address. Client may have requested it
// because it doesn't support reception of messages on the interface
// which doesn't have an address assigned. The other case when response
// must be broadcasted is when our server does not support responding
// directly to a client without address assigned.
const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
msg->setRemoteAddr(bcast_addr);
// Client cleared the broadcast bit and we support direct responses
// so we should unicast the response to a newly allocated address -
// yiaddr.
} else {
msg->setRemoteAddr(msg->getYiaddr());
}
// In most cases, we should have the remote address found already. If we
// found ourselves at this point, the rational thing to do is to respond
// to the address we got the query from.
} else {
msg->setRemoteAddr(question->getRemoteAddr());
}
}
OptionPtr
Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
uint32_t netmask = getNetmask4(subnet->get().second);
......
......@@ -45,7 +45,7 @@ namespace dhcp {
/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
class Dhcpv4Srv : public boost::noncopyable {
public:
public:
/// @brief defines if certain option may, must or must not appear
typedef enum {
......@@ -61,15 +61,22 @@ class Dhcpv4Srv : public boost::noncopyable {
/// network interaction. Will instantiate lease manager, and load
/// old or create new DUID. It is possible to specify alternate
/// port on which DHCPv4 server will listen on. That is mostly useful
/// for testing purposes.
/// for testing purposes. The Last two arguments of the constructor
/// should be left at default values for normal server operation.
/// They should be set to 'false' when creating an instance of this
/// class for unit testing because features they enable require
/// root privileges.
///
/// @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.
/// @param direct_response_desired specifies if it is desired to
/// use direct V4 traffic.
Dhcpv4Srv(uint16_t port = DHCP4_SERVER_PORT,
const char* dbconfig = "type=memfile",
const bool use_bcast = true);
const bool use_bcast = true,
const bool direct_response_desired = true);
/// @brief Destructor. Used during DHCPv4 service shutdown.
~Dhcpv4Srv();
......@@ -218,6 +225,23 @@ protected:
/// @param msg_type specifies message type
void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
/// @brief Sets remote addresses for outgoing packet.
///
/// This method sets the local and remote addresses on outgoing packet.
/// The addresses being set depend on the following conditions:
/// - has incoming packet been relayed,
/// - is direct response to a client without address supported,
/// - type of the outgoing packet,
/// - broadcast flag set in the incoming packet.
///
/// @warning This method does not check whether provided packet pointers
/// are valid. Make sure that pointers are correct before calling this
/// function.
///
/// @param question instance of a packet received by a server.
/// @param [out] msg response packet which addresses are to be adjusted.
void adjustRemoteAddr(const Pkt4Ptr& question, Pkt4Ptr& msg);
/// @brief Returns server-identifier option
///
/// @return server-id option
......@@ -272,7 +296,7 @@ protected:
/// initiate server shutdown procedure.
volatile bool shutdown_;
private:
private:
/// @brief Constructs netmask option based on subnet4
/// @param subnet subnet for which the netmask will be calculated
......
This diff is collapsed.
......@@ -34,11 +34,16 @@ libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
libb10_dhcp___la_SOURCES += option_space.cc option_space.h
libb10_dhcp___la_SOURCES += option_string.cc option_string.h
libb10_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_SOURCES += pkt_filter.h
libb10_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h
if OS_LINUX
libb10_dhcp___la_SOURCES += pkt_filter_lpf.cc pkt_filter_lpf.h
endif
libb10_dhcp___la_SOURCES += std_option_defs.h
libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -27,6 +27,10 @@ namespace dhcp {
/// @brief Hardware type that represents information from DHCPv4 packet
struct HWAddr {
public:
/// @brief Size of an ethernet hardware address.
static const size_t ETHERNET_HWADDR_LEN = 6;
/// @brief Maximum size of a hardware address.
static const size_t MAX_HWADDR_LEN = 20;
......
......@@ -58,11 +58,44 @@ Iface::Iface(const std::string& name, int ifindex)
void
Iface::closeSockets() {
for (SocketCollection::iterator sock = sockets_.begin();
sock != sockets_.end(); ++sock) {
close(sock->sockfd_);
// Close IPv4 sockets.
closeSockets(AF_INET);
// Close IPv6 sockets.
closeSockets(AF_INET6);
}
void
Iface::closeSockets(const uint16_t family) {
// Check that the correect 'family' value has been specified.
// The possible values are AF_INET or AF_INET6. Note that, in
// the current code they are used to differentiate that the
// socket is used to transmit IPv4 or IPv6 traffic. However,
// the actual family types of the sockets may be different,
// e.g. for LPF we are using raw sockets of AF_PACKET family.
//
// @todo Consider replacing the AF_INET and AF_INET6 with some
// enum which will not be confused with the actual socket type.
if ((family != AF_INET) && (family != AF_INET6)) {
isc_throw(BadValue, "Invalid socket family " << family
<< " specified when requested to close all sockets"
<< " which belong to this family");
}
// Search for the socket of the specific type.
SocketCollection::iterator sock = sockets_.begin();
while (sock != sockets_.end()) {
if (sock->family_ == family) {
// Close and delete the socket and move to the
// next one.
close(sock->sockfd_);
sockets_.erase(sock++);
} else {
// Different type of socket. Let's move
// to the next one.
++sock;
}
}
sockets_.clear();
}
std::string
......@@ -150,6 +183,14 @@ void IfaceMgr::closeSockets() {
}
}
void
IfaceMgr::closeSockets(const uint16_t family) {
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end(); ++iface) {
iface->closeSockets(family);
}
}
IfaceMgr::~IfaceMgr() {
// control_buf_ is deleted automatically (scoped_ptr)
control_buf_len_ = 0;
......@@ -157,6 +198,42 @@ IfaceMgr::~IfaceMgr() {
closeSockets();
}
bool
IfaceMgr::isDirectResponseSupported() const {
return (packet_filter_->isDirectResponseSupported());
}
void
IfaceMgr::setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
// Do not allow NULL pointer.
if (!packet_filter) {
isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
}
// Different packet filters use different socket types. It does not make
// sense to allow the change of packet filter when there are IPv4 sockets
// open because they can't be used by the receive/send functions of the
// new packet filter. Below, we check that there are no open IPv4 sockets.
// If we find at least one, we have to fail. However, caller still has a
// chance to replace the packet filter if he closes sockets explicitly.
for (IfaceCollection::const_iterator iface = ifaces_.begin();
iface != ifaces_.end(); ++iface) {
const Iface::SocketCollection& sockets = iface->getSockets();
for (Iface::SocketCollection::const_iterator sock = sockets.begin();
sock != sockets.end(); ++sock) {
if (sock->family_ == AF_INET) {
// There is at least one socket open, so we have to fail.
isc_throw(PacketFilterChangeDenied,
"it is not allowed to set new packet"
<< " filter when there are open IPv4 sockets - need"
<< " to close them first");
}
}
}
// Everything is fine, so replace packet filter.
packet_filter_ = packet_filter;
}
void IfaceMgr::stubDetectIfaces() {
string ifaceName;
const string v4addr("127.0.0.1"), v6addr("::1");
......@@ -758,7 +835,7 @@ IfaceMgr::send(const Pkt4Ptr& pkt) {
// Skip checking if packet filter is non-NULL because it has been
// already checked when packet filter was set.
return (packet_filter_->send(getSocket(*pkt), pkt));
return (packet_filter_->send(*iface, getSocket(*pkt), pkt));
}
......
......@@ -39,10 +39,10 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief IfaceMgr exception thrown when invalid packet filter object specified.
class InvalidPacketFilter : public Exception {
/// @brief Exception thrown when it is not allowed to set new Packet Filter.
class PacketFilterChangeDenied : public Exception {
public:
InvalidPacketFilter(const char* file, size_t line, const char* what) :
PacketFilterChangeDenied(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
......@@ -88,7 +88,7 @@ struct SocketInfo {
};
/// @brief represents a single network interface
/// @brief Represents a single network interface
///
/// Iface structure represents network interface with all useful
/// information, like name, interface index, MAC address and
......@@ -96,13 +96,20 @@ struct SocketInfo {
class Iface {
public:
/// maximum MAC address length (Infiniband uses 20 bytes)
/// Maximum MAC address length (Infiniband uses 20 bytes)
static const unsigned int MAX_MAC_LEN = 20;
/// type that defines list of addresses
/// Type that defines list of addresses
typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
/// type that holds a list of socket informations
/// @brief Type that holds a list of socket information.
///
/// @warning The type of the container used here must guarantee
/// that the iterators do not invalidate when erase() is called.
/// This is because, the \ref closeSockets function removes
/// elements selectively by calling erase on the element to be
/// removed and further iterates through remaining elements.
///
/// @todo: Add SocketCollectionConstIter type
typedef std::list<SocketInfo> SocketCollection;
......@@ -117,6 +124,27 @@ public:
/// @brief Closes all open sockets on interface.
void closeSockets();
/// @brief Closes all IPv4 or IPv6 sockets.
///
/// This function closes sockets of the specific 'type' and closes them.
/// The 'type' of the socket indicates whether it is used to send IPv4
/// or IPv6 packets. The allowed values of the parameter are AF_INET and
/// AF_INET6 for IPv4 and IPv6 packets respectively. It is important
/// to realize that the actual types of sockets may be different than
/// AF_INET for IPv4 packets. This is because, historically the IfaceMgr
/// always used AF_INET sockets for IPv4 traffic. This is no longer the
/// case when the Direct IPv4 traffic must be supported. In order to support
/// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET
/// family sockets on Linux.
///
/// @todo Replace the AF_INET and AF_INET6 values with an enum
/// which will not be confused with the actual socket type.
///
/// @param family type of the sockets to be closed (AF_INET or AF_INET6)
///
/// @throw BadValue if family value is different than AF_INET or AF_INET6.
void closeSockets(const uint16_t family);
/// @brief Returns full interface name as "ifname/ifindex" string.
///
/// @return string with interface name
......@@ -146,7 +174,7 @@ public:
/// @brief Sets flag_*_ fields based on bitmask value returned by OS
///
/// Note: Implementation of this method is OS-dependent as bits have
/// @note Implementation of this method is OS-dependent as bits have
/// different meaning on each OS.
///
/// @param flags bitmask value returned by OS in interface detection
......@@ -237,53 +265,53 @@ public:
const SocketCollection& getSockets() const { return sockets_; }
protected:
/// socket used to sending data
/// Socket used to send data.
SocketCollection sockets_;
/// network interface name
/// Network interface name.
std::string name_;
/// interface index (a value that uniquely indentifies an interface)
/// Interface index (a value that uniquely indentifies an interface).
int ifindex_;
/// list of assigned addresses
/// List of assigned addresses.
AddressCollection addrs_;
/// link-layer address
/// Link-layer address.
uint8_t mac_[MAX_MAC_LEN];
/// length of link-layer address (usually 6)
/// Length of link-layer address (usually 6).
size_t mac_len_;
/// hardware type
/// Hardware type.
uint16_t hardware_type_;
public:
/// @todo: Make those fields protected once we start supporting more
/// than just Linux
/// specifies if selected interface is loopback
/// Specifies if selected interface is loopback.
bool flag_loopback_;
/// specifies if selected interface is up
/// Specifies if selected interface is up.
bool flag_up_;
/// flag specifies if selected interface is running
/// (e.g. cable plugged in, wifi associated)
/// Flag specifies if selected interface is running
/// (e.g. cable plugged in, wifi associated).
bool flag_running_;
/// flag specifies if selected interface is multicast capable
/// Flag specifies if selected interface is multicast capable.
bool flag_multicast_;
/// flag specifies if selected interface is broadcast capable
/// Flag specifies if selected interface is broadcast capable.
bool flag_broadcast_;
/// interface flags (this value is as is returned by OS,
/// it may mean different things on different OSes)
/// Interface flags (this value is as is returned by OS,
/// it may mean different things on different OSes).
uint32_t flags_;
};
/// @brief handles network interfaces, transmission and reception
/// @brief Handles network interfaces, transmission and reception.
///
/// IfaceMgr is an interface manager class that detects available network
/// interfaces, configured addresses, link-local addresses, and provides
......@@ -291,7 +319,7 @@ public:
///
class IfaceMgr : public boost::noncopyable {
public:
/// defines callback used when commands are received over control session
/// Defines callback used when commands are received over control session.
typedef void (*SessionCallback) (void);
/// @brief Packet reception buffer size
......@@ -307,7 +335,7 @@ public:
// 2 maps (ifindex-indexed and name-indexed) and
// also hide it (make it public make tests easier for now)
/// type that holds a list of interfaces
/// Type that holds a list of interfaces.
typedef std::list<Iface> IfaceCollection;
/// IfaceMgr is a singleton class. This method returns reference
......@@ -324,7 +352,7 @@ public:
/// the client.
///
/// @return true if direct response is supported.
bool isDirectResponseSupported();
bool isDirectResponseSupported() const;
/// @brief Returns interface with specified interface index
///
......@@ -380,11 +408,10 @@ public:
/// @return a socket descriptor
uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
/// debugging method that prints out all available interfaces
/// Debugging method that prints out all available interfaces.
///
/// @param out specifies stream to print list of interfaces to
void
printIfaces(std::ostream& out = std::cout);
void printIfaces(std::ostream& out = std::cout);
/// @brief Sends an IPv6 packet.
///
......@@ -542,10 +569,31 @@ public:
const bool use_bcast = true);
/// @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 Dhcpv4Srv and Dhcpv6Srv classes.
void closeSockets();
/// @brief returns number of detected interfaces
/// @brief Closes all IPv4 or IPv6 sockets.
///
/// This function closes sockets of the specific 'type' and closes them.
/// The 'type' of the socket indicates whether it is used to send IPv4
/// or IPv6 packets. The allowed values of the parameter are AF_INET and
/// AF_INET6 for IPv4 and IPv6 packets respectively. It is important
/// to realize that the actual types of sockets may be different than
/// AF_INET for IPv4 packets. This is because, historically the IfaceMgr
/// always used AF_INET sockets for IPv4 traffic. This is no longer the
/// case when the Direct IPv4 traffic must be supported. In order to support
/// direct traffic, the IfaceMgr operates on raw sockets, e.g. AF_PACKET
/// family sockets on Linux.
///
/// @todo Replace the AF_INET and AF_INET6 values with an enum
/// which will not be confused with the actual socket type.
///
/// @param family type of the sockets to be closed (AF_INET or AF_INET6)
///
/// @throw BadValue if family value is different than AF_INET or AF_INET6.
void closeSockets(const uint16_t family);
/// @brief Returns number of detected interfaces.
///
/// @return number of detected interfaces
uint16_t countIfaces() { return ifaces_.size(); }
......@@ -567,18 +615,34 @@ public:
/// Packet Filters expose low-level functions handling sockets opening
/// and sending/receiving packets through those sockets. This function
/// sets custom Packet Filter (represented by a class derived from PktFilter)
/// to be used by IfaceMgr.
/// to be used by IfaceMgr. Note that there must be no IPv4 sockets open
/// when this function is called. Call closeSockets(AF_INET) to close
/// all hanging IPv4 sockets opened by the current packet filter object.
///
/// @param packet_filter new packet filter to be used by IfaceMgr to send/receive
/// packets and open sockets.
///
/// @throw InvalidPacketFilter if provided packet filter object is NULL.
void setPacketFilter(const boost::shared_ptr<PktFilter>& packet_filter) {
if (!packet_filter) {
isc_throw(InvalidPacketFilter, "NULL packet filter object specified");
}
packet_filter_ = packet_filter;
}
/// @throw PacketFilterChangeDenied if there are open IPv4 sockets
void setPacketFilter(const PktFilterPtr& packet_filter);
/// @brief Set Packet Filter object to handle send/receive packets.
///
/// This function sets Packet Filter object to be used by IfaceMgr,
/// appropriate for the current OS. Setting the argument to 'true'
/// indicates that function should set a packet filter class
/// which supports direct responses to clients having no address