#define DHCP4_TEST_UTILS_H
#include <gtest/gtest.h>
+#include <dhcp/iface_mgr.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
}
/// Does nothing.
- virtual int openSocket(const Iface&, const isc::asiolink::IOAddress&,
- const uint16_t, const bool, const bool) {
- return (0);
+ virtual SocketInfo openSocket(const Iface&,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port, const bool, const bool) {
+ return (SocketInfo(addr, port, 0));
}
/// Does nothing.
}
}
- SocketInfo info(sock, addr, port);
+ SocketInfo info(addr, port, sock);
iface.addSocket(info);
return (sock);
// Skip checking if the packet_filter_ is non-NULL because this check
// has been already done when packet filter object was set.
- int sock = packet_filter_->openSocket(iface, addr, port,
- receive_bcast, send_bcast);
-
- SocketInfo info(sock, addr, port);
+ SocketInfo info = packet_filter_->openSocket(iface, addr, port,
+ receive_bcast, send_bcast);
iface.addSocket(info);
- return (sock);
+ return (info.sockfd_);
}
bool
/// Holds information about socket.
struct SocketInfo {
- uint16_t sockfd_; /// socket descriptor
+
isc::asiolink::IOAddress addr_; /// bound address
uint16_t port_; /// socket port
uint16_t family_; /// IPv4 or IPv6
+ /// @brief Socket descriptor (a.k.a. primary socket).
+ int sockfd_;
+
+ /// @brief Fallback socket descriptor.
+ ///
+ /// This socket descriptor holds the handle to the fallback socket.
+ /// The fallback socket is created when there is a need for the regular
+ /// datagram socket to be bound to an IP address and port, besides
+ /// primary socket (sockfd_) which is actually used to receive and process
+ /// the DHCP messages. The fallback socket (if exists) is always associated
+ /// with the primary socket. In particular, the need for the fallback socket
+ /// arises when raw socket is a primary one. When primary socket is open,
+ /// it is bound to an interface not the address and port. The implications
+ /// include the possibility that the other process (e.g. the other instance
+ /// of DHCP server) will bind to the same address and port through which the
+ /// raw socket receives the DHCP messages.Another implication is that the
+ /// kernel, being unaware of the DHCP server operating through the raw
+ /// socket, will respond with the ICMP "Destination port unreachable"
+ /// messages when DHCP messages are only received through the raw socket.
+ /// In order to workaround the issues mentioned here, the fallback socket
+ /// should be opened so as/ the kernel is aware that the certain address
+ /// and port is in use.
+ ///
+ /// The fallback description is supposed to be set to a negative value if
+ /// the fallback socket is closed (not open).
+ int fallbackfd_;
+
/// @brief SocketInfo constructor.
///
- /// @param sockfd socket descriptor
- /// @param addr an address the socket is bound to
- /// @param port a port the socket is bound to
- SocketInfo(uint16_t sockfd, const isc::asiolink::IOAddress& addr,
- uint16_t port)
- :sockfd_(sockfd), addr_(addr), port_(port), family_(addr.getFamily()) { }
+ /// @param addr An address the socket is bound to.
+ /// @param port A port the socket is bound to.
+ /// @param sockfd Socket descriptor.
+ /// @param fallbackfd A descriptor of the fallback socket.
+ SocketInfo(const isc::asiolink::IOAddress& addr, const uint16_t port,
+ const int sockfd, const int fallbackfd = -1)
+ : addr_(addr), port_(port), family_(addr.getFamily()),
+ sockfd_(sockfd), fallbackfd_(fallbackfd) { }
+
};
/// @return true of the direct response is supported.
virtual bool isDirectResponseSupported() const = 0;
- /// @brief Open socket.
+ /// @brief Open primary and fallback socket.
///
- /// @param iface interface descriptor
- /// @param addr address on the interface to be used to send packets.
- /// @param port port number.
- /// @param receive_bcast configure socket to receive broadcast messages
+ /// A method implementation in the derived class may open one or two
+ /// sockets:
+ /// - a primary socket - used for communication with clients. DHCP messages
+ /// received using this socket are processed and the same socket is used
+ /// to send a response to the client.
+ /// - a fallback socket which is optionally opened if there is a need for
+ /// the presence of the socket which can be bound to a specific IP address
+ /// and UDP port (e.g. raw primary socket can't be). For the details, see
+ /// the documentation of @c isc::dhcp::SocketInfo.
+ ///
+ /// @param iface Interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number.
+ /// @param receive_bcast Configure socket to receive broadcast messages
/// @param send_bcast configure socket to send broadcast messages.
///
- /// @return created socket's descriptor
- virtual int openSocket(const Iface& iface,
- const isc::asiolink::IOAddress& addr,
- const uint16_t port,
- const bool receive_bcast,
- const bool send_bcast) = 0;
+ /// @return A structure describing a primary and fallback socket.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast) = 0;
/// @brief Receive packet over specified socket.
///
{
}
-int PktFilterInet::openSocket(const Iface& iface,
- const isc::asiolink::IOAddress& addr,
- const uint16_t port,
- const bool receive_bcast,
- const bool send_bcast) {
+SocketInfo
+PktFilterInet::openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast) {
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(sockaddr));
}
#endif
- return (sock);
+ SocketInfo sock_desc(addr, port, sock);
+ return (sock_desc);
}
return (false);
}
- /// @brief Open socket.
+ /// @brief Open primary and fallback socket.
///
- /// @param iface interface descriptor
- /// @param addr address on the interface to be used to send packets.
- /// @param port port number.
- /// @param receive_bcast configure socket to receive broadcast messages
- /// @param send_bcast configure socket to send broadcast messages.
+ /// @param iface Interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number.
+ /// @param receive_bcast Configure socket to receive broadcast messages
+ /// @param send_bcast Configure socket to send broadcast messages.
///
- /// @return created socket's descriptor
- virtual int openSocket(const Iface& iface,
- const isc::asiolink::IOAddress& addr,
- const uint16_t port,
- const bool receive_bcast,
- const bool send_bcast);
+ /// @return A structure describing a primary and fallback socket.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
/// @brief Receive packet over specified socket.
///
namespace isc {
namespace dhcp {
-int
+SocketInfo
PktFilterLPF::openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
const uint16_t port, const bool,
// We return negative, the proper error message will be displayed
// by the IfaceMgr ...
close(sock_check);
- return (-1);
+ return (SocketInfo(addr, port, -1));
}
close(sock_check);
<< "' to interface '" << iface.getName() << "'");
}
- return (sock);
+ SocketInfo sock_desc(addr, port, sock);
+ return (sock_desc);
}
return (true);
}
- /// @brief Open socket.
+ /// @brief Open primary and fallback socket.
///
- /// @param iface interface descriptor
- /// @param addr address on the interface to be used to send packets.
- /// @param port port number.
- /// @param receive_bcast configure socket to receive broadcast messages
- /// @param send_bcast configure socket to send broadcast messages.
+ /// @param iface Interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number.
+ /// @param receive_bcast Configure socket to receive broadcast messages
+ /// @param send_bcast Configure socket to send broadcast messages.
///
- /// @throw isc::NotImplemented always
- /// @return created socket's descriptor
- virtual int openSocket(const Iface& iface,
- const isc::asiolink::IOAddress& addr,
- const uint16_t port,
- const bool receive_bcast,
- const bool send_bcast);
+ /// @return A structure describing a primary and fallback socket.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
/// @brief Receive packet over specified socket.
///
/// (because real values are rather less than 255). Values greater
/// than 255 are not recommended because they cause warnings to be
/// reported by Valgrind when invoking close() on them.
- virtual int openSocket(const Iface&,
- const isc::asiolink::IOAddress&,
- const uint16_t,
- const bool,
- const bool) {
+ virtual SocketInfo openSocket(const Iface&,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool,
+ const bool) {
open_socket_called_ = true;
- return (255);
+ return (SocketInfo(addr, port, 255));
}
/// Does nothing
TEST_F(IfaceMgrTest, socketInfo) {
// Check that socketinfo for IPv4 socket is functional
- SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
+ SocketInfo sock1(IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7, 7);
EXPECT_EQ(7, sock1.sockfd_);
+ EXPECT_EQ(-1, sock1.fallbackfd_);
EXPECT_EQ("192.0.2.56", sock1.addr_.toText());
EXPECT_EQ(AF_INET, sock1.family_);
EXPECT_EQ(DHCP4_SERVER_PORT + 7, sock1.port_);
+ // Check that non-default value of the fallback socket descriptor is set
+ SocketInfo sock2(IOAddress("192.0.2.53"), DHCP4_SERVER_PORT + 8, 8, 10);
+ EXPECT_EQ(8, sock2.sockfd_);
+ EXPECT_EQ(10, sock2.fallbackfd_);
+ EXPECT_EQ("192.0.2.53", sock2.addr_.toText());
+ EXPECT_EQ(AF_INET, sock2.family_);
+ EXPECT_EQ(DHCP4_SERVER_PORT + 8, sock2.port_);
+
// Check that socketinfo for IPv6 socket is functional
- SocketInfo sock2(9, IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9);
- EXPECT_EQ(9, sock2.sockfd_);
- EXPECT_EQ("2001:db8:1::56", sock2.addr_.toText());
- EXPECT_EQ(AF_INET6, sock2.family_);
- EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock2.port_);
+ SocketInfo sock3(IOAddress("2001:db8:1::56"), DHCP4_SERVER_PORT + 9, 9);
+ EXPECT_EQ(9, sock3.sockfd_);
+ EXPECT_EQ(-1, sock3.fallbackfd_);
+ EXPECT_EQ("2001:db8:1::56", sock3.addr_.toText());
+ EXPECT_EQ(AF_INET6, sock3.family_);
+ EXPECT_EQ(DHCP4_SERVER_PORT + 9, sock3.port_);
// Now let's test if IfaceMgr handles socket info properly
scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
ASSERT_TRUE(loopback);
loopback->addSocket(sock1);
loopback->addSocket(sock2);
+ loopback->addSocket(sock3);
Pkt6 pkt6(DHCPV6_REPLY, 123456);
EXPECT_NO_THROW(
ifacemgr->getIface(LOOPBACK)->delSocket(7);
+ ifacemgr->getIface(LOOPBACK)->delSocket(8);
);
// It should throw again, there's no usable socket anymore.
// Try to open socket.
PktFilterInet pkt_filter;
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
// Check that socket has been opened.
ASSERT_GE(socket_, 0);
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
ASSERT_GE(socket_, 0);
// Send the packet over the socket.
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
ASSERT_GE(socket_, 0);
// Send the packet over the socket.
ASSERT_NO_THROW(pkt_filter.send(iface, socket_, pkt));
// Receive the packet.
- SocketInfo socket_info(socket_, IOAddress("127.0.0.1"), PORT);
+ SocketInfo socket_info(IOAddress("127.0.0.1"), PORT, socket_);
Pkt4Ptr rcvd_pkt = pkt_filter.receive(iface, socket_info);
// Check that the packet has been correctly received.
ASSERT_TRUE(rcvd_pkt);
// Try to open socket.
PktFilterLPF pkt_filter;
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
// Check that socket has been opened.
ASSERT_GE(socket_, 0);
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
ASSERT_GE(socket_, 0);
// Send the packet over the socket.
// Open socket. We don't check that the socket has appropriate
// options and family set because we have checked that in the
// openSocket test already.
- socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false);
+ socket_ = pkt_filter.openSocket(iface, addr, PORT, false, false).sockfd_;
ASSERT_GE(socket_, 0);
// Send the packet over the socket.