mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[master] Merge branch 'trac3437'
This commit is contained in:
@@ -8,6 +8,13 @@
|
||||
# Kea is told to listen on eth0 interface only.
|
||||
"interfaces": [ "eth0" ],
|
||||
|
||||
# We need to specify lease type. As of May 2014, three backends are supported:
|
||||
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
|
||||
# any prior set up.
|
||||
"lease-database": {
|
||||
"type": "memfile"
|
||||
},
|
||||
|
||||
# Addresses will be assigned with preferred and valid lifetimes
|
||||
# being 3000 and 4000, respectively. Client is told to start
|
||||
# renewing after 1000 seconds. If the server does not repond
|
||||
|
@@ -90,10 +90,9 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
||||
try {
|
||||
// Open sockets only if port is non-zero. Port 0 is used for testing
|
||||
// purposes in two cases:
|
||||
// - when non-socket related testing is performed
|
||||
// - when the particular test supplies its own packet filtering class.
|
||||
// Port 0 is used for testing purposes where we don't open broadcast
|
||||
// capable sockets. So, set the packet filter handling direct traffic
|
||||
// only if we are in non-test mode.
|
||||
if (port) {
|
||||
// First call to instance() will create IfaceMgr (it's a singleton)
|
||||
// it may throw something if things go wrong.
|
||||
@@ -103,13 +102,6 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast,
|
||||
// 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);
|
||||
|
||||
// Create error handler. This handler will be called every time
|
||||
// the socket opening operation fails. We use this handler to
|
||||
// log a warning.
|
||||
isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
|
||||
boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
|
||||
IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
|
||||
}
|
||||
|
||||
// Instantiate allocation engine
|
||||
|
@@ -122,21 +122,12 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
|
||||
|
||||
// Initialize objects required for DHCP server operation.
|
||||
try {
|
||||
// Port 0 is used for testing purposes. It means that the server should
|
||||
// not open any sockets at all. Some tests, e.g. configuration parser,
|
||||
// require Dhcpv6Srv object, but they don't really need it to do
|
||||
// anything. This speed up and simplifies the tests.
|
||||
if (port > 0) {
|
||||
if (IfaceMgr::instance().countIfaces() == 0) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
|
||||
return;
|
||||
}
|
||||
// Create error handler. This handler will be called every time
|
||||
// the socket opening operation fails. We use this handler to
|
||||
// log a warning.
|
||||
isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
|
||||
boost::bind(&Dhcpv6Srv::ifaceMgrSocket6ErrorHandler, _1);
|
||||
IfaceMgr::instance().openSockets6(port_, error_handler);
|
||||
// Port 0 is used for testing purposes where in most cases we don't
|
||||
// rely on the physical interfaces. Therefore, it should be possible
|
||||
// to create an object even when there are no usable interfaces.
|
||||
if ((port > 0) && (IfaceMgr::instance().countIfaces() == 0)) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
|
||||
return;
|
||||
}
|
||||
|
||||
string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
|
||||
|
@@ -344,9 +344,22 @@ IfaceMgr::hasOpenSocket(const IOAddress& addr) const {
|
||||
const Iface::SocketCollection& sockets = iface->getSockets();
|
||||
for (Iface::SocketCollection::const_iterator sock = sockets.begin();
|
||||
sock != sockets.end(); ++sock) {
|
||||
// Check if the socket address matches the specified address.
|
||||
// Check if the socket address matches the specified address or
|
||||
// if address is unspecified (in6addr_any).
|
||||
if (sock->addr_ == addr) {
|
||||
return (true);
|
||||
} else if (sock->addr_ == IOAddress("::")) {
|
||||
// Handle the case that the address is unspecified (any).
|
||||
// In this case, we should check if the specified address
|
||||
// belongs to any of the interfaces.
|
||||
for (Iface::AddressCollection::const_iterator addr_it =
|
||||
iface->getAddresses().begin();
|
||||
addr_it != iface->getAddresses().end();
|
||||
++addr_it) {
|
||||
if (addr == *addr_it) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,9 +558,9 @@ IfaceMgr::openSockets6(const uint16_t port,
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run OS-specific function to open a socket on link-local address
|
||||
// and join multicast group (non-Linux OSes), or open two sockets and
|
||||
// bind one to link-local, another one to multicast address.
|
||||
// Run OS-specific function to open a socket capable of receiving
|
||||
// packets sent to All_DHCP_Relay_Agents_and_Servers multicast
|
||||
// address.
|
||||
if (openMulticastSocket(*iface, *addr, port, error_handler)) {
|
||||
++count;
|
||||
}
|
||||
@@ -776,17 +789,6 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
|
||||
return IOAddress(local_address);
|
||||
}
|
||||
|
||||
int
|
||||
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
|
||||
const bool join_multicast) {
|
||||
// Assuming that packet filter is not NULL, because its modifier checks it.
|
||||
SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
|
||||
join_multicast);
|
||||
iface.addSocket(info);
|
||||
|
||||
return (info.sockfd_);
|
||||
}
|
||||
|
||||
int
|
||||
IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr,
|
||||
const uint16_t port, const bool receive_bcast,
|
||||
|
@@ -898,7 +898,10 @@ public:
|
||||
/// @brief Checks if there is a socket open and bound to an address.
|
||||
///
|
||||
/// This function checks if one of the sockets opened by the IfaceMgr is
|
||||
/// bound to the IP address specified as the method parameter.
|
||||
/// bound to the IP address specified as the method parameter. If the
|
||||
/// socket is bound to the port (and address is unspecified), the
|
||||
/// method will check if the address passed in the argument is configured
|
||||
/// on an interface.
|
||||
///
|
||||
/// @param addr Address of the socket being searched.
|
||||
///
|
||||
@@ -1020,11 +1023,10 @@ private:
|
||||
|
||||
/// @brief Open an IPv6 socket with multicast support.
|
||||
///
|
||||
/// This function opens socket(s) to allow reception of the DHCPv6 sent
|
||||
/// to multicast address. It opens an IPv6 socket, binds it to link-local
|
||||
/// address and joins multicast group (on non-Linux systems) or opens two
|
||||
/// IPv6 sockets and binds one of them to link-local address and another
|
||||
/// one to multicast address (on Linux systems).
|
||||
/// This function opens a socket capable of receiving messages sent to
|
||||
/// the All_DHCP_Relay_Agents_and_Servers (ff02::1:2) multicast address.
|
||||
/// The socket is bound to the in6addr_any address and the IPV6_JOIN_GROUP
|
||||
/// option is set to point to the ff02::1:2 multicast address.
|
||||
///
|
||||
/// @note This function is intended to be called internally by the
|
||||
/// @c IfaceMgr::openSockets6. It is not intended to be called from any
|
||||
|
@@ -156,10 +156,9 @@ IfaceMgr::openMulticastSocket(Iface& iface,
|
||||
const uint16_t port,
|
||||
IfaceMgrErrorMsgCallback error_handler) {
|
||||
try {
|
||||
// This should open a socket, bound it to link-local address
|
||||
// This should open a socket, bind it to link-local address
|
||||
// and join multicast group.
|
||||
openSocket(iface.getName(), addr, port,
|
||||
iface.flag_multicast_);
|
||||
openSocket(iface.getName(), addr, port, iface.flag_multicast_);
|
||||
|
||||
} catch (const Exception& ex) {
|
||||
IFACEMGR_ERROR(SocketConfigError, error_handler,
|
||||
@@ -172,6 +171,20 @@ IfaceMgr::openMulticastSocket(Iface& iface,
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
|
||||
const bool join_multicast) {
|
||||
// On BSD, we bind the socket to in6addr_any and join multicast group
|
||||
// to receive multicast traffic. So, if the multicast is requested,
|
||||
// replace the address specified by the caller with the "unspecified"
|
||||
// address.
|
||||
IOAddress actual_address = join_multicast ? IOAddress("::") : addr;
|
||||
SocketInfo info = packet_filter6_->openSocket(iface, actual_address, port,
|
||||
join_multicast);
|
||||
iface.addSocket(info);
|
||||
return (info.sockfd_);
|
||||
}
|
||||
|
||||
|
||||
} // end of isc::dhcp namespace
|
||||
} // end of dhcp namespace
|
||||
|
@@ -557,8 +557,8 @@ IfaceMgr::openMulticastSocket(Iface& iface,
|
||||
|
||||
}
|
||||
|
||||
// To receive multicast traffic, Linux requires binding socket to
|
||||
// the multicast address.
|
||||
// In order to receive multicast traffic another socket is opened
|
||||
// and bound to the multicast address.
|
||||
|
||||
/// @todo The DHCPv6 requires multicast so we may want to think
|
||||
/// whether we want to open the socket on a multicast-incapable
|
||||
@@ -588,6 +588,17 @@ IfaceMgr::openMulticastSocket(Iface& iface,
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
|
||||
const bool join_multicast) {
|
||||
// Assuming that packet filter is not NULL, because its modifier checks it.
|
||||
SocketInfo info = packet_filter6_->openSocket(iface, addr, port,
|
||||
join_multicast);
|
||||
iface.addSocket(info);
|
||||
|
||||
return (info.sockfd_);
|
||||
}
|
||||
|
||||
} // end of isc::dhcp namespace
|
||||
} // end of isc namespace
|
||||
|
||||
|
@@ -176,6 +176,16 @@ IfaceMgr::openMulticastSocket(Iface& iface,
|
||||
return (true);
|
||||
}
|
||||
|
||||
int
|
||||
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
|
||||
const bool join_multicast) {
|
||||
IOAddress actual_address = join_multicast ? IOAddress("::") : addr;
|
||||
SocketInfo info = packet_filter6_->openSocket(iface, actual_address, port,
|
||||
join_multicast);
|
||||
iface.addSocket(info);
|
||||
return (info.sockfd_);
|
||||
}
|
||||
|
||||
} // end of isc::dhcp namespace
|
||||
} // end of dhcp namespace
|
||||
|
||||
|
@@ -112,14 +112,14 @@ network operations. In particlar, it provides information about existing
|
||||
network interfaces See isc::dhcp::IfaceMgr::Iface class and
|
||||
isc::dhcp::IfaceMgr::detectIfaces() and isc::dhcp::IfaceMgr::getIface().
|
||||
|
||||
Currently there is interface detection is implemented in Linux only. There
|
||||
are plans to implement such support for other OSes, but they remain low
|
||||
priority for now.
|
||||
Currently there is interface detection is implemented in Linux and BSD.
|
||||
There are plans to implement such support for other OSes, but they
|
||||
remain low priority for now.
|
||||
|
||||
Generic parts of the code are isc::dhcp::IfaceMgr class in
|
||||
src/lib/dhcp/iface_mgr.cc file. OS-specific code is located in separate
|
||||
files, e.g. iface_mgr_linux.cc. Such separation should be maintained when
|
||||
additional code will be developed.
|
||||
files, e.g. iface_mgr_linux.cc, iface_mgr_bsd. Such separation should be
|
||||
maintained when additional code will be developed.
|
||||
|
||||
For systems that interface detection is not supported on, there is a stub
|
||||
mechanism implemented. It assumes that interface name is read from a text
|
||||
@@ -132,7 +132,7 @@ should bind to. In theory this mechanism also supports IPv4, but it was
|
||||
never tested. The code currently supports only a single interface defined
|
||||
that way.
|
||||
|
||||
Another useful methods are dedicated to transmission
|
||||
Other useful methods are dedicated to transmission
|
||||
(isc::dhcp::IfaceMgr::send(), 2 overloads) and reception
|
||||
(isc::dhcp::IfaceMgr::receive4() and isc::dhcp::IfaceMgr::receive6()).
|
||||
Note that receive4() and receive6() methods may return NULL, e.g.
|
||||
@@ -196,7 +196,7 @@ the \ref isc::dhcp::IfaceMgr logic.
|
||||
The \ref isc::dhcp::IfaceMgr::openSockets6 function examines configuration
|
||||
of detected interfaces for their availability to listen DHCPv6 traffic. For
|
||||
all running interfaces (except local loopback) it will try to open a socket
|
||||
and bind it to the link local or global unicast address. The socket will
|
||||
and bind it to the link-local or global unicast address. The socket will
|
||||
not be opened on the interface which is down or for which it was explicitly
|
||||
specified that it should not be used to listen to DHCPv6 messages. There is
|
||||
a substantial amount of logic in this function that has to be unit tested for
|
||||
|
@@ -41,21 +41,27 @@ PktFilter::openFallbackSocket(const isc::asiolink::IOAddress& addr,
|
||||
|
||||
if (bind(sock, reinterpret_cast<struct sockaddr*>(&addr4),
|
||||
sizeof(addr4)) < 0) {
|
||||
// Get the error message immediately after the bind because the
|
||||
// invocation to close() below would override the errno.
|
||||
char* errmsg = strerror(errno);
|
||||
// Remember to close the socket if we failed to bind it.
|
||||
close(sock);
|
||||
isc_throw(SocketConfigError, "failed to bind fallback socket to"
|
||||
" address " << addr << ", port " << port
|
||||
<< ", reason: " << strerror(errno)
|
||||
<< ", reason: " << errmsg
|
||||
<< " - is another DHCP server running?");
|
||||
}
|
||||
|
||||
// Set socket to non-blocking mode. This is to prevent the read from the
|
||||
// fallback socket to block message processing on the primary socket.
|
||||
if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
|
||||
// Get the error message immediately after the bind because the
|
||||
// invocation to close() below would override the errno.
|
||||
char* errmsg = strerror(errno);
|
||||
close(sock);
|
||||
isc_throw(SocketConfigError, "failed to set SO_NONBLOCK option on the"
|
||||
" fallback socket, bound to " << addr << ", port "
|
||||
<< port << ", reason: " << strerror(errno));
|
||||
<< port << ", reason: " << errmsg);
|
||||
}
|
||||
// Successfully created and bound a fallback socket. Return a descriptor.
|
||||
return (sock);
|
||||
|
@@ -125,10 +125,9 @@ public:
|
||||
|
||||
/// @brief Joins IPv6 multicast group on a socket.
|
||||
///
|
||||
/// Socket must be created and bound to an address. Note that this
|
||||
/// address is different than the multicast address. For example DHCPv6
|
||||
/// server should bind its socket to link-local address (fe80::1234...)
|
||||
/// and later join ff02::1:2 multicast group.
|
||||
/// This function joins the socket to the specified multicast group.
|
||||
/// The socket descriptor must point to a valid socket bound to the
|
||||
/// in6addr_any prior to calling this function.
|
||||
///
|
||||
/// @param sock A socket descriptor (socket must be bound).
|
||||
/// @param ifname An interface name (for link-scoped multicast groups).
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2014 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
|
||||
@@ -38,11 +38,20 @@ PktFilterInet6::openSocket(const Iface& iface,
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
addr6.sin6_family = AF_INET6;
|
||||
addr6.sin6_port = htons(port);
|
||||
if (addr.toText() != "::1") {
|
||||
// sin6_scope_id must be set to interface index for link-local addresses.
|
||||
// For unspecified addresses we set the scope id to the interface index
|
||||
// to handle the case when the IfaceMgr is opening a socket which will
|
||||
// join the multicast group. Such socket is bound to in6addr_any.
|
||||
if (addr.isV6Multicast() ||
|
||||
(addr.isV6LinkLocal() && (addr != IOAddress("::1"))) ||
|
||||
(addr == IOAddress("::"))) {
|
||||
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
|
||||
}
|
||||
|
||||
memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
|
||||
// Copy the address if it has been specified.
|
||||
if (addr != IOAddress("::")) {
|
||||
memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
|
||||
}
|
||||
#ifdef HAVE_SA_LEN
|
||||
addr6.sin6_len = sizeof(addr6);
|
||||
#endif
|
||||
@@ -60,13 +69,18 @@ PktFilterInet6::openSocket(const Iface& iface,
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *)&flag, sizeof(flag)) < 0) {
|
||||
close(sock);
|
||||
isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
|
||||
isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on IPv6"
|
||||
" socket.");
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
|
||||
// Get the error message immediately after the bind because the
|
||||
// invocation to close() below would override the errno.
|
||||
char* errmsg = strerror(errno);
|
||||
close(sock);
|
||||
isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
|
||||
<< "/port=" << port);
|
||||
isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to "
|
||||
<< addr.toText() << "/port=" << port
|
||||
<< ": " << errmsg);
|
||||
}
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
// RFC3542 - a new way
|
||||
@@ -169,6 +183,17 @@ PktFilterInet6::receive(const SocketInfo& socket_info) {
|
||||
isc_throw(SocketReadError, "failed to receive data");
|
||||
}
|
||||
|
||||
// Filter out packets sent to global unicast address (not link local and
|
||||
// not multicast) if the socket is set to listen multicast traffic and
|
||||
// is bound to in6addr_any. The traffic sent to global unicast address is
|
||||
// received via dedicated socket.
|
||||
IOAddress local_addr = IOAddress::fromBytes(AF_INET6,
|
||||
reinterpret_cast<const uint8_t*>(&to_addr));
|
||||
if ((socket_info.addr_ == IOAddress("::")) &&
|
||||
!(local_addr.isV6Multicast() || local_addr.isV6LinkLocal())) {
|
||||
return (Pkt6Ptr());
|
||||
}
|
||||
|
||||
// Let's create a packet.
|
||||
Pkt6Ptr pkt;
|
||||
try {
|
||||
|
@@ -36,8 +36,10 @@ public:
|
||||
|
||||
/// @brief Opens a socket.
|
||||
///
|
||||
/// This function open an IPv6 socket on an interface and binds it to a
|
||||
/// specified UDP port and IP address.
|
||||
/// This function opens an IPv6 socket on an interface and binds it to a
|
||||
/// specified UDP port and IP address. The @c addr value may be set to
|
||||
/// "::" in which case the address is unspecified and the socket is
|
||||
/// bound to in6addr_any.
|
||||
///
|
||||
/// @param iface Interface descriptor.
|
||||
/// @param addr Address on the interface to be used to send packets.
|
||||
@@ -62,6 +64,10 @@ public:
|
||||
/// must first check that there is any message on the socket (using
|
||||
/// select function) prior to calling this function.
|
||||
///
|
||||
/// If the message is received through the socket bound to "any"
|
||||
/// (in6addr_any) address this function will drop this message if it has
|
||||
/// been sent to an address other than multicast or link-local.
|
||||
///
|
||||
/// @param socket_info A structure holding socket information.
|
||||
///
|
||||
/// @return A pointer to received message.
|
||||
|
@@ -99,14 +99,16 @@ public:
|
||||
virtual SocketInfo openSocket(const Iface& iface,
|
||||
const isc::asiolink::IOAddress& addr,
|
||||
const uint16_t port,
|
||||
const bool,
|
||||
const bool join_multicast,
|
||||
const bool) {
|
||||
// Check if there is any other socket bound to the specified address
|
||||
// and port on this interface.
|
||||
const Iface::SocketCollection& sockets = iface.getSockets();
|
||||
for (Iface::SocketCollection::const_iterator socket = sockets.begin();
|
||||
socket != sockets.end(); ++socket) {
|
||||
if ((socket->addr_ == addr) && (socket->port_ == port)) {
|
||||
if (((socket->addr_ == addr) ||
|
||||
((socket->addr_ == IOAddress("::")) && join_multicast)) &&
|
||||
socket->port_ == port) {
|
||||
isc_throw(SocketConfigError, "test socket bind error");
|
||||
}
|
||||
}
|
||||
@@ -243,6 +245,16 @@ public:
|
||||
sock != sockets.end(); ++sock) {
|
||||
if (sock->addr_ == IOAddress(addr)) {
|
||||
return (true);
|
||||
} else if ((sock->addr_ == IOAddress("::")) &&
|
||||
(IOAddress(addr).isV6LinkLocal())) {
|
||||
for (Iface::AddressCollection::const_iterator addr_it =
|
||||
iface->getAddresses().begin();
|
||||
addr_it != iface->getAddresses().end();
|
||||
++addr_it) {
|
||||
if (*addr_it == IOAddress(addr)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
@@ -1906,10 +1918,10 @@ TEST_F(IfaceMgrTest, openSocket6ErrorHandler) {
|
||||
ASSERT_TRUE(filter);
|
||||
ASSERT_NO_THROW(ifacemgr.setPacketFilter(filter));
|
||||
|
||||
// Open socket on eth0.
|
||||
// Open multicast socket on eth0.
|
||||
ASSERT_NO_THROW(ifacemgr.openSocket("eth0",
|
||||
IOAddress("fe80::3a60:77ff:fed5:cdef"),
|
||||
DHCP6_SERVER_PORT));
|
||||
DHCP6_SERVER_PORT, true));
|
||||
|
||||
// Install an error handler before trying to open sockets. This handler
|
||||
// should be called when the IfaceMgr fails to open socket on eth0.
|
||||
|
Reference in New Issue
Block a user