BIND 10 master, updated. a6a35e9970e4937924c1e33c01f6bd7eaf1ed994 Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Dec 9 09:02:46 UTC 2011
The branch, master has been updated
via a6a35e9970e4937924c1e33c01f6bd7eaf1ed994 (commit)
via 86a4ce45115dab4d3978c36dd2dbe07edcac02ac (commit)
via 14a64484a3159a142f1b83a9830ac389a52f6a35 (commit)
via 146203239c50d2a00069986944d4ec168f17b31f (commit)
via 660cf410c0fb41587b977df992879f5dff934c19 (commit)
via b09fdcc6b45d4580b138cc9f59bfc051bd6ad360 (commit)
via 4d97ef5cdb4833a7a36b6679c16338505b07d4e3 (commit)
via 424f32864efcd2c647c6e5303125b6a8afb421ea (commit)
via a26b979adb54baabdf939ed1a7852b2ee9b8b93c (commit)
via cbe600decbef4db82cb3b070e03b5702540af4aa (commit)
via 936511f6e114f26bf86497466a7f61ef467bf5ad (commit)
via 5a2d958780a4a671cd8df9080d99ff95dd16772d (commit)
via 075e3787986676c7491f157931b6f7da1773db0a (commit)
via 7d2f07481169780071bf564223a20a219b550385 (commit)
via d5e189cf1573446503a4fafa3e909db60eb04623 (commit)
via 0b6937d0e075e1192c41891ae138532f2c733b47 (commit)
via 5371b694b6cc564c3f1899a935769dd024f38e56 (commit)
via 837002896937febe208c141912fc4f8c3beaa2ab (commit)
from c8dc421c5cad0f3296174b44f8deccfb69dec43f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a6a35e9970e4937924c1e33c01f6bd7eaf1ed994
Merge: c8dc421c5cad0f3296174b44f8deccfb69dec43f 86a4ce45115dab4d3978c36dd2dbe07edcac02ac
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Fri Dec 9 10:01:01 2011 +0100
Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10
Conflicts:
ChangeLog
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 5 +
src/bin/dhcp6/.gitignore | 1 +
src/bin/dhcp6/dhcp6_srv.cc | 30 ++-
src/bin/dhcp6/dhcp6_srv.h | 11 +-
src/bin/dhcp6/iface_mgr.cc | 436 ++++++++++++++++++++---------
src/bin/dhcp6/iface_mgr.h | 269 +++++++++++++++---
src/bin/dhcp6/tests/Makefile.am | 3 -
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 23 +-
src/bin/dhcp6/tests/iface_mgr_unittest.cc | 264 ++++++++++++++----
src/lib/dhcp/pkt4.cc | 47 ++--
src/lib/dhcp/pkt4.h | 24 ++-
src/lib/dhcp/tests/option_unittest.cc | 2 +
src/lib/dhcp/tests/pkt4_unittest.cc | 13 +
13 files changed, 843 insertions(+), 285 deletions(-)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 2583fdf..e8f57ba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+341. [func] tomek
+ libdhcp++: Support for handling both IPv4 and IPv6 added.
+ Also added support for binding IPv4 sockets.
+ (Trac #1238, git 86a4ce45115dab4d3978c36dd2dbe07edcac02ac)
+
340. [build] jelte
Fixed several linker issues related to recent gcc versions, botan
and gtest.
diff --git a/src/bin/dhcp6/.gitignore b/src/bin/dhcp6/.gitignore
index 6a6060b..e4e8f2d 100644
--- a/src/bin/dhcp6/.gitignore
+++ b/src/bin/dhcp6/.gitignore
@@ -7,3 +7,4 @@ Makefile.in
b10-dhcp6
spec_config.h
spec_config.h.pre
+tests/dhcp6_unittests
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index ba5afec..d5a969f 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -12,26 +12,32 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include "dhcp/dhcp6.h"
-#include "dhcp/pkt6.h"
-#include "dhcp6/iface_mgr.h"
-#include "dhcp6/dhcp6_srv.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/option6_iaaddr.h"
-#include "asiolink/io_address.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp6/iface_mgr.h>
+#include <dhcp6/dhcp6_srv.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <asiolink/io_address.h>
+#include <exceptions/exceptions.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
-Dhcpv6Srv::Dhcpv6Srv() {
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
+
+//void Dhcpv6Srv::Dhcpv6Srv_impl(uint16_t port) {
cout << "Initialization" << endl;
- // first call to instance() will create IfaceMgr (it's a singleton)
- // it may throw something if things go wrong
+ // First call to instance() will create IfaceMgr (it's a singleton).
+ // It may throw something if things go wrong.
IfaceMgr::instance();
+ // Now try to open IPv6 sockets on detected interfaces.
+ IfaceMgr::instance().openSockets(port);
+
/// @todo: instantiate LeaseMgr here once it is imlpemented.
setServerID();
@@ -41,6 +47,8 @@ Dhcpv6Srv::Dhcpv6Srv() {
Dhcpv6Srv::~Dhcpv6Srv() {
cout << "DHCPv6 Srv shutdown." << endl;
+
+ IfaceMgr::instance().closeSockets();
}
bool
@@ -49,7 +57,7 @@ Dhcpv6Srv::run() {
boost::shared_ptr<Pkt6> query; // client's message
boost::shared_ptr<Pkt6> rsp; // server's response
- query = IfaceMgr::instance().receive();
+ query = IfaceMgr::instance().receive6();
if (query) {
if (!query->unpack()) {
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 4daef3a..bcc7818 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -17,8 +17,9 @@
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
-#include "dhcp/pkt6.h"
-#include "dhcp/option.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/option.h>
#include <iostream>
namespace isc {
@@ -41,10 +42,12 @@ public:
/// In particular, creates IfaceMgr that will be responsible for
/// network interaction. Will instantiate lease manager, and load
/// old or create new DUID.
- Dhcpv6Srv();
+ ///
+ /// @param port port on will all sockets will listen
+ Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
/// @brief Destructor. Used during DHCPv6 service shutdown.
- ~Dhcpv6Srv();
+ virtual ~Dhcpv6Srv();
/// @brief Returns server-intentifier option
///
diff --git a/src/bin/dhcp6/iface_mgr.cc b/src/bin/dhcp6/iface_mgr.cc
index a96db07..60dac63 100644
--- a/src/bin/dhcp6/iface_mgr.cc
+++ b/src/bin/dhcp6/iface_mgr.cc
@@ -18,9 +18,9 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-#include "dhcp/dhcp6.h"
-#include "dhcp6/iface_mgr.h"
-#include "exceptions/exceptions.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp6/iface_mgr.h>
+#include <exceptions/exceptions.h>
using namespace std;
using namespace isc;
@@ -79,6 +79,30 @@ IfaceMgr::Iface::getPlainMac() const {
return (tmp.str());
}
+bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
+
+ // Let's delete all addresses that match. It really shouldn't matter
+ // if we delete first or all, as the OS should allow to add a single
+ // address to an interface only once. If OS allows multiple instances
+ // of the same address added, we are in deep problems anyway.
+ size_t size = addrs_.size();
+ addrs_.erase(remove(addrs_.begin(), addrs_.end(), addr), addrs_.end());
+ return (addrs_.size() < size);
+}
+
+bool IfaceMgr::Iface::delSocket(uint16_t sockfd) {
+ list<SocketInfo>::iterator sock = sockets_.begin();
+ while (sock!=sockets_.end()) {
+ if (sock->sockfd_ == sockfd) {
+ close(sockfd);
+ sockets_.erase(sock);
+ return (true); //socket found
+ }
+ ++sock;
+ }
+ return (false); // socket not found
+}
+
IfaceMgr::IfaceMgr()
:control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_])
@@ -95,9 +119,6 @@ IfaceMgr::IfaceMgr()
detectIfaces();
- if (!openSockets()) {
- isc_throw(Unexpected, "Failed to open/bind sockets.");
- }
} catch (const std::exception& ex) {
cout << "IfaceMgr creation failed:" << ex.what() << endl;
@@ -109,7 +130,23 @@ IfaceMgr::IfaceMgr()
}
}
+void IfaceMgr::closeSockets() {
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+
+ for (SocketCollection::iterator sock = iface->sockets_.begin();
+ sock != iface->sockets_.end(); ++sock) {
+ cout << "Closing socket " << sock->sockfd_ << endl;
+ close(sock->sockfd_);
+ }
+ iface->sockets_.clear();
+ }
+
+}
+
IfaceMgr::~IfaceMgr() {
+ closeSockets();
+
// control_buf_ is deleted automatically (scoped_ptr)
control_buf_len_ = 0;
}
@@ -139,8 +176,8 @@ IfaceMgr::detectIfaces() {
Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
IOAddress addr(linkLocal);
- iface.addrs_.push_back(addr);
- ifaces_.push_back(iface);
+ iface.addAddress(addr);
+ addInterface(iface);
interfaces.close();
} catch (const std::exception& ex) {
// TODO: deallocate whatever memory we used
@@ -154,51 +191,55 @@ IfaceMgr::detectIfaces() {
}
}
-bool
-IfaceMgr::openSockets() {
- int sock;
+void
+IfaceMgr::openSockets(uint16_t port) {
+ int sock1, sock2;
+
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
+ AddressCollection addrs = iface->getAddresses();
- for (Addr6Lst::iterator addr=iface->addrs_.begin();
- addr!=iface->addrs_.end();
+ for (AddressCollection::iterator addr = addrs.begin();
+ addr != addrs.end();
++addr) {
- sock = openSocket(iface->name_, *addr,
- DHCP6_SERVER_PORT);
- if (sock<0) {
- cout << "Failed to open unicast socket." << endl;
- return (false);
+ sock1 = openSocket(iface->getName(), *addr, port);
+ if (sock1 < 0) {
+ isc_throw(Unexpected, "Failed to open unicast socket on "
+ << " interface " << iface->getFullName());
}
- sendsock_ = sock;
-
- sock = openSocket(iface->name_,
- IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
- DHCP6_SERVER_PORT);
- if (sock<0) {
- cout << "Failed to open multicast socket." << endl;
- close(sendsock_);
- return (false);
+
+ if ( !joinMcast(sock1, iface->getName(),
+ string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+ close(sock1);
+ isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ << " multicast group.");
+ }
+
+ // this doesn't work too well on NetBSD
+ sock2 = openSocket(iface->getName(),
+ IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+ port);
+ if (sock2 < 0) {
+ isc_throw(Unexpected, "Failed to open multicast socket on "
+ << " interface " << iface->getFullName());
+ iface->delSocket(sock1); // delete previously opened socket
}
- recvsock_ = sock;
}
}
-
- return (true);
}
void
IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
- for (IfaceLst::const_iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
+ for (IfaceCollection::const_iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
out << "Detected interface " << iface->getFullName() << endl;
- out << " " << iface->addrs_.size() << " addr(s):" << endl;
- for (Addr6Lst::const_iterator addr=iface->addrs_.begin();
- addr != iface->addrs_.end();
- ++addr) {
+ out << " " << iface->getAddresses().size() << " addr(s):" << endl;
+ const AddressCollection addrs = iface->getAddresses();
+
+ for (AddressCollection::const_iterator addr = addrs.begin();
+ addr != addrs.end(); ++addr) {
out << " " << addr->toText() << endl;
}
out << " mac: " << iface->getPlainMac() << endl;
@@ -207,11 +248,11 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
IfaceMgr::Iface*
IfaceMgr::getIface(int ifindex) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
- if (iface->ifindex_ == ifindex)
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+ if (iface->getIndex() == ifindex) {
return (&(*iface));
+ }
}
return (NULL); // not found
@@ -219,29 +260,87 @@ IfaceMgr::getIface(int ifindex) {
IfaceMgr::Iface*
IfaceMgr::getIface(const std::string& ifname) {
- for (IfaceLst::iterator iface=ifaces_.begin();
- iface!=ifaces_.end();
- ++iface) {
- if (iface->name_ == ifname)
+ for (IfaceCollection::iterator iface = ifaces_.begin();
+ iface != ifaces_.end(); ++iface) {
+ if (iface->getName() == ifname) {
return (&(*iface));
+ }
}
return (NULL); // not found
}
int
-IfaceMgr::openSocket(const std::string& ifname,
- const IOAddress& addr,
+IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
int port) {
- struct sockaddr_in6 addr6;
+ Iface* iface = getIface(ifname);
+ if (!iface) {
+ isc_throw(BadValue, "There is no " << ifname << " interface present.");
+ }
+ switch (addr.getFamily()) {
+ case AF_INET:
+ return openSocket4(*iface, addr, port);
+ case AF_INET6:
+ return openSocket6(*iface, addr, port);
+ default:
+ isc_throw(BadValue, "Failed to detect family of address: "
+ << addr.toText());
+ }
+}
+
+int
+IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+
+ cout << "Creating UDP4 socket on " << iface.getFullName()
+ << " " << addr.toText() << "/port=" << port << endl;
+
+ struct sockaddr_in addr4;
+ memset(&addr4, 0, sizeof(sockaddr));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = htons(port);
+ memcpy(&addr4.sin_addr, addr.getAddress().to_v4().to_bytes().data(),
+ sizeof(addr4.sin_addr));
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ isc_throw(Unexpected, "Failed to create UDP6 socket.");
+ }
+
+ if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
+ close(sock);
+ isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ << "/port=" << port);
+ }
+
+ // If there is no support for IP_PKTINFO, we are really out of luck.
+ // It will be difficult to understand, where this packet came from.
+#if defined(IP_PKTINFO)
+ int flag = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
+ close(sock);
+ isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+ }
+#endif
+
+ cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
+ addr.toText() << "/port=" << port << endl;
- cout << "Creating socket on " << ifname << "/" << addr.toText()
- << "/port=" << port << endl;
+ iface.addSocket(SocketInfo(sock, addr, port));
+ return (sock);
+}
+
+int
+IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+
+ cout << "Creating UDP6 socket on " << iface.getFullName()
+ << " " << addr.toText() << "/port=" << port << endl;
+
+ struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
- addr6.sin6_scope_id = if_nametoindex(ifname.c_str());
+ addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
memcpy(&addr6.sin6_addr,
addr.getAddress().to_v6().to_bytes().data(),
@@ -255,61 +354,58 @@ IfaceMgr::openSocket(const std::string& ifname,
// make a socket
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
- cout << "Failed to create UDP6 socket." << endl;
- return (-1);
+ isc_throw(Unexpected, "Failed to create UDP6 socket.");
}
- /* Set the REUSEADDR option so that we don't fail to start if
- we're being restarted. */
+ // Set the REUSEADDR option so that we don't fail to start if
+ // we're being restarted.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
- cout << "Can't set SO_REUSEADDR option on dhcpv6 socket." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
}
if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
- cout << "Failed to bind socket " << sock << " to " << addr.toText()
- << "/port=" << port << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ << "/port=" << port);
}
#ifdef IPV6_RECVPKTINFO
- /* RFC3542 - a new way */
+ // RFC3542 - a new way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
- cout << "setsockopt: IPV6_RECVPKTINFO failed." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
}
#else
- /* RFC2292 - an old way */
+ // RFC2292 - an old way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
- cout << "setsockopt: IPV6_PKTINFO: failed." << endl;
close(sock);
- return (-1);
+ isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
}
#endif
// multicast stuff
-
if (addr.getAddress().to_v6().is_multicast()) {
// both mcast (ALL_DHCP_RELAY_AGENTS_AND_SERVERS and ALL_DHCP_SERVERS)
// are link and site-scoped, so there is no sense to join those groups
// with global addresses.
- if ( !joinMcast( sock, ifname,
+ if ( !joinMcast( sock, iface.getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
close(sock);
- return (-1);
+ isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ << " multicast group.");
}
}
- cout << "Created socket " << sock << " on " << ifname << "/" <<
+ cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
addr.toText() << "/port=" << port << endl;
+ iface.addSocket(SocketInfo(sock, addr, port));
+
return (sock);
}
@@ -345,16 +441,19 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
int result;
struct in6_pktinfo *pktinfo;
struct cmsghdr *cmsg;
+
+ Iface* iface = getIface(pkt->iface_);
+ if (!iface) {
+ isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
+ << pkt->iface_ << ") specified.");
+ }
+
memset(&control_buf_[0], 0, control_buf_len_);
- /*
- * Initialize our message header structure.
- */
+ // Initialize our message header structure.
memset(&m, 0, sizeof(m));
- /*
- * Set the target address we're sending to.
- */
+ // Set the target address we're sending to.
sockaddr_in6 to;
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
@@ -367,24 +466,20 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
m.msg_name = &to;
m.msg_namelen = sizeof(to);
- /*
- * Set the data buffer we're sending. (Using this wacky
- * "scatter-gather" stuff... we only have a single chunk
- * of data to send, so we declare a single vector entry.)
- */
+ // Set the data buffer we're sending. (Using this wacky
+ // "scatter-gather" stuff... we only have a single chunk
+ // of data to send, so we declare a single vector entry.)
v.iov_base = (char *) &pkt->data_[0];
v.iov_len = pkt->data_len_;
m.msg_iov = &v;
m.msg_iovlen = 1;
- /*
- * Setting the interface is a bit more involved.
- *
- * We have to create a "control message", and set that to
- * define the IPv6 packet information. We could set the
- * source address if we wanted, but we can safely let the
- * kernel decide what that should be.
- */
+ // Setting the interface is a bit more involved.
+ //
+ // We have to create a "control message", and set that to
+ // define the IPv6 packet information. We could set the
+ // source address if we wanted, but we can safely let the
+ // kernel decide what that should be.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
cmsg = CMSG_FIRSTHDR(&m);
@@ -396,14 +491,12 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
pktinfo->ipi6_ifindex = pkt->ifindex_;
m.msg_controllen = cmsg->cmsg_len;
- result = sendmsg(sendsock_, &m, 0);
+ result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
cout << "Send packet failed." << endl;
}
- cout << "Sent " << result << " bytes." << endl;
-
- cout << "Sent " << pkt->data_len_ << " bytes over "
- << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
+ cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
+ << " on " << iface->getFullName() << " interface: "
<< " dst=" << pkt->remote_addr_.toText()
<< ", src=" << pkt->local_addr_.toText()
<< endl;
@@ -411,8 +504,24 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
return (result);
}
+bool
+IfaceMgr::send(boost::shared_ptr<Pkt4>& )
+{
+ /// TODO: Implement this (ticket #1240)
+ isc_throw(NotImplemented, "Pkt4 send not implemented yet.");
+}
+
+
+boost::shared_ptr<Pkt4>
+IfaceMgr::receive4() {
+ isc_throw(NotImplemented, "Pkt4 reception not implemented yet.");
+
+ // TODO: To be implemented (ticket #1239)
+ return (boost::shared_ptr<Pkt4>()); // NULL
+}
+
boost::shared_ptr<Pkt6>
-IfaceMgr::receive() {
+IfaceMgr::receive6() {
struct msghdr m;
struct iovec v;
int result;
@@ -442,49 +551,66 @@ IfaceMgr::receive() {
memset(&from, 0, sizeof(from));
memset(&to_addr, 0, sizeof(to_addr));
- /*
- * Initialize our message header structure.
- */
+ // Initialize our message header structure.
memset(&m, 0, sizeof(m));
- /*
- * Point so we can get the from address.
- */
+ // Point so we can get the from address.
m.msg_name = &from;
m.msg_namelen = sizeof(from);
- /*
- * Set the data buffer we're receiving. (Using this wacky
- * "scatter-gather" stuff... but we that doesn't really make
- * sense for us, so we use a single vector entry.)
- */
+ // Set the data buffer we're receiving. (Using this wacky
+ // "scatter-gather" stuff... but we that doesn't really make
+ // sense for us, so we use a single vector entry.)
v.iov_base = (void*)&pkt->data_[0];
v.iov_len = pkt->data_len_;
m.msg_iov = &v;
m.msg_iovlen = 1;
- /*
- * Getting the interface is a bit more involved.
- *
- * We set up some space for a "control message". We have
- * previously asked the kernel to give us packet
- * information (when we initialized the interface), so we
- * should get the destination address from that.
- */
+ // Getting the interface is a bit more involved.
+ //
+ // We set up some space for a "control message". We have
+ // previously asked the kernel to give us packet
+ // information (when we initialized the interface), so we
+ // should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
- result = recvmsg(recvsock_, &m, 0);
+ /// TODO: Need to move to select() and pool over
+ /// all available sockets. For now, we just take the
+ /// first interface and use first socket from it.
+ IfaceCollection::const_iterator iface = ifaces_.begin();
+ if (iface == ifaces_.end()) {
+ isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+ }
+ SocketCollection::const_iterator s = iface->sockets_.begin();
+ const SocketInfo* candidate = 0;
+ while (s != iface->sockets_.end()) {
+ if (s->addr_.getAddress().to_v6().is_multicast()) {
+ candidate = &(*s);
+ break;
+ }
+ if (!candidate) {
+ candidate = &(*s); // it's not multicast, but it's better than none
+ }
+ ++s;
+ }
+ if (!candidate) {
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any sockets open.");
+ }
+
+ cout << "Trying to receive over socket " << candidate->sockfd_ << " bound to "
+ << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
+ << iface->getFullName() << endl;
+ result = recvmsg(candidate->sockfd_, &m, 0);
if (result >= 0) {
- /*
- * If we did read successfully, then we need to loop
- * through the control messages we received and
- * find the one with our destination address.
- *
- * We also keep a flag to see if we found it. If we
- * didn't, then we consider this to be an error.
- */
+ // If we did read successfully, then we need to loop
+ // through the control messages we received and
+ // find the one with our destination address.
+ //
+ // We also keep a flag to see if we found it. If we
+ // didn't, then we consider this to be an error.
int found_pktinfo = 0;
cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
@@ -520,7 +646,7 @@ IfaceMgr::receive() {
Iface* received = getIface(pkt->ifindex_);
if (received) {
- pkt->iface_ = received->name_;
+ pkt->iface_ = received->getName();
} else {
cout << "Received packet over unknown interface (ifindex="
<< pkt->ifindex_ << ")." << endl;
@@ -539,4 +665,60 @@ IfaceMgr::receive() {
return (pkt);
}
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
+ Iface* iface = getIface(pkt.iface_);
+ if (!iface) {
+ isc_throw(BadValue, "Tried to find socket for non-existent interface "
+ << pkt.iface_);
+ }
+
+ SocketCollection::const_iterator s;
+ for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+ if (s->family_ != AF_INET6) {
+ // don't use IPv4 sockets
+ continue;
+ }
+ if (s->addr_.getAddress().to_v6().is_multicast()) {
+ // don't use IPv6 sockets bound to multicast address
+ continue;
+ }
+ /// TODO: Add more checks here later. If remote address is
+ /// not link-local, we can't use link local bound socket
+ /// to send data.
+
+ return (s->sockfd_);
+ }
+
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any suitable IPv6 sockets open.");
+}
+
+uint16_t
+IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+ Iface* iface = getIface(pkt.getIface());
+ if (!iface) {
+ isc_throw(BadValue, "Tried to find socket for non-existent interface "
+ << pkt.getIface());
+ }
+
+ SocketCollection::const_iterator s;
+ for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
+ if (s->family_ != AF_INET) {
+ // don't use IPv4 sockets
+ continue;
+ }
+ /// TODO: Add more checks here later. If remote address is
+ /// not link-local, we can't use link local bound socket
+ /// to send data.
+
+ return (s->sockfd_);
+ }
+
+ isc_throw(Unexpected, "Interface " << iface->getFullName()
+ << " does not have any suitable IPv4 sockets open.");
+}
+
+
+
}
diff --git a/src/bin/dhcp6/iface_mgr.h b/src/bin/dhcp6/iface_mgr.h
index 249c7ef..0aa2592 100644
--- a/src/bin/dhcp6/iface_mgr.h
+++ b/src/bin/dhcp6/iface_mgr.h
@@ -19,8 +19,9 @@
#include <boost/shared_ptr.hpp>
#include <boost/scoped_array.hpp>
#include <boost/noncopyable.hpp>
-#include "asiolink/io_address.h"
-#include "dhcp/pkt6.h"
+#include <asiolink/io_address.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/pkt6.h>
namespace isc {
@@ -34,26 +35,119 @@ namespace dhcp {
class IfaceMgr : public boost::noncopyable {
public:
/// type that defines list of addresses
- typedef std::list<isc::asiolink::IOAddress> Addr6Lst;
+ typedef std::vector<isc::asiolink::IOAddress> AddressCollection;
/// maximum MAC address length (Infiniband uses 20 bytes)
static const unsigned int MAX_MAC_LEN = 20;
+ /// 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 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()) { }
+ };
+
+ /// type that holds a list of socket informations
+ typedef std::list<SocketInfo> SocketCollection;
+
/// @brief represents a single network interface
///
/// Iface structure represents network interface with all useful
/// information, like name, interface index, MAC address and
/// list of assigned addresses
- struct Iface {
- /// constructor
+ class Iface {
+ public:
+ /// @brief Iface constructor.
+ ///
+ /// Creates Iface object that represents network interface.
+ ///
+ /// @param name name of the interface
+ /// @param ifindex interface index (unique integer identifier)
Iface(const std::string& name, int ifindex);
- /// returns full interface name in format ifname/ifindex
+ /// @brief Returns full interface name as "ifname/ifindex" string.
+ ///
+ /// @return string with interface name
std::string getFullName() const;
- /// returns link-layer address a plain text
+ /// @brief Returns link-layer address a plain text.
+ ///
+ /// @return MAC address as a plain text (string)
std::string getPlainMac() const;
+ /// @brief Returns interface index.
+ ///
+ /// @return interface index
+ uint16_t getIndex() const { return ifindex_; }
+
+ /// @brief Returns interface name.
+ ///
+ /// @return interface name
+ std::string getName() const { return name_; };
+
+ /// @brief Returns all interfaces available on an interface.
+ ///
+ /// Care should be taken to not use this collection after Iface object
+ /// ceases to exist. That is easy in most cases as Iface objects are
+ /// created by IfaceMgr that is a singleton an is expected to be
+ /// available at all time. We may revisit this if we ever decide to
+ /// implement dynamic interface detection, but such fancy feature would
+ /// mostly be useful for clients with wifi/vpn/virtual interfaces.
+ ///
+ /// @return collection of addresses
+ const AddressCollection& getAddresses() const { return addrs_; }
+
+ /// @brief Adds an address to an interface.
+ ///
+ /// This only adds an address to collection, it does not physically
+ /// configure address on actual network interface.
+ ///
+ /// @param addr address to be added
+ void addAddress(const isc::asiolink::IOAddress& addr) {
+ addrs_.push_back(addr);
+ }
+
+ /// @brief Deletes an address from an interface.
+ ///
+ /// This only deletes address from collection, it does not physically
+ /// remove address configuration from actual network interface.
+ ///
+ /// @param addr address to be removed.
+ ///
+ /// @return true if removal was successful (address was in collection),
+ /// false otherwise
+ bool delAddress(const isc::asiolink::IOAddress& addr);
+
+ /// @brief Adds socket descriptor to an interface.
+ ///
+ /// @param socket SocketInfo structure that describes socket.
+ void addSocket(const SocketInfo& sock)
+ { sockets_.push_back(sock); }
+
+ /// @brief Closes socket.
+ ///
+ /// Closes socket and removes corresponding SocketInfo structure
+ /// from an interface.
+ ///
+ /// @param socket descriptor to be closed/removed.
+ /// @return true if there was such socket, false otherwise
+ bool delSocket(uint16_t sockfd);
+
+ /// socket used to sending data
+ /// TODO: this should be protected
+ SocketCollection sockets_;
+
+ protected:
/// network interface name
std::string name_;
@@ -61,19 +155,13 @@ public:
int ifindex_;
/// list of assigned addresses
- Addr6Lst addrs_;
+ AddressCollection addrs_;
/// link-layer address
uint8_t mac_[MAX_MAC_LEN];
/// length of link-layer address (usually 6)
int mac_len_;
-
- /// socket used to sending data
- int sendsock_;
-
- /// socket used for receiving data
- int recvsock_;
};
// TODO performance improvement: we may change this into
@@ -81,7 +169,7 @@ public:
// also hide it (make it public make tests easier for now)
/// type that holds a list of interfaces
- typedef std::list<Iface> IfaceLst;
+ typedef std::list<Iface> IfaceCollection;
/// IfaceMgr is a singleton class. This method returns reference
/// to its sole instance.
@@ -109,27 +197,63 @@ public:
Iface*
getIface(const std::string& ifname);
+ /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+ ///
+ /// This method takes Pkt6 (see overloaded implementation that takes
+ /// Pkt4) and chooses appropriate socket to send it. This method
+ /// may throw BadValue if specified packet does not have outbound
+ /// interface specified, no such interface exists, or specified
+ /// interface does not have any appropriate sockets open.
+ ///
+ /// @param pkt a packet to be transmitted
+ ///
+ /// @return a socket descriptor
+ uint16_t getSocket(const isc::dhcp::Pkt6& pkt);
+
+ /// @brief Return most suitable socket for transmitting specified IPv6 packet.
+ ///
+ /// This method takes Pkt4 (see overloaded implementation that takes
+ /// Pkt6) and chooses appropriate socket to send it. This method
+ /// may throw BadValue if specified packet does not have outbound
+ /// interface specified, no such interface exists, or specified
+ /// interface does not have any appropriate sockets open.
+ ///
+ /// @param pkt a packet to be transmitted
+ ///
+ /// @return a socket descriptor
+ uint16_t getSocket(const isc::dhcp::Pkt4& pkt);
+
/// 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);
- /// @brief Sends a packet.
+ /// @brief Sends an IPv6 packet.
///
- /// Sends a packet. All parameters for actual transmission are specified in
+ /// Sends an IPv6 packet. All parameters for actual transmission are specified in
/// Pkt6 structure itself. That includes destination address, src/dst port
/// and interface over which data will be sent.
///
/// @param pkt packet to be sent
///
/// @return true if sending was successful
- bool
- send(boost::shared_ptr<Pkt6>& pkt);
+ bool send(boost::shared_ptr<Pkt6>& pkt);
- /// @brief Tries to receive packet over open sockets.
+ /// @brief Sends an IPv4 packet.
///
- /// Attempts to receive a single packet of any of the open sockets.
+ /// Sends an IPv4 packet. All parameters for actual transmission are specified
+ /// in Pkt4 structure itself. That includes destination address, src/dst
+ /// port and interface over which data will be sent.
+ ///
+ /// @param pkt a packet to be sent
+ ///
+ /// @return true if sending was successful
+ bool send(boost::shared_ptr<Pkt4>& pkt);
+
+ /// @brief Tries to receive IPv6 packet over open IPv6 sockets.
+ ///
+ /// Attempts to receive a single IPv6 packet of any of the open IPv6 sockets.
/// If reception is successful and all information about its sender
/// are obtained, Pkt6 object is created and returned.
///
@@ -138,7 +262,49 @@ public:
/// (e.g. remove expired leases)
///
/// @return Pkt6 object representing received packet (or NULL)
- boost::shared_ptr<Pkt6> receive();
+ boost::shared_ptr<Pkt6> receive6();
+
+ /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
+ ///
+ /// Attempts to receive a single IPv4 packet of any of the open IPv4 sockets.
+ /// If reception is successful and all information about its sender
+ /// are obtained, Pkt4 object is created and returned.
+ ///
+ /// TODO Start using select() and add timeout to be able
+ /// to not wait infinitely, but rather do something useful
+ /// (e.g. remove expired leases)
+ ///
+ /// @return Pkt4 object representing received packet (or NULL)
+ boost::shared_ptr<Pkt4> receive4();
+
+ /// Opens UDP/IP socket and binds it to address, interface and port.
+ ///
+ /// Specific type of socket (UDP/IPv4 or UDP/IPv6) depends on passed addr
+ /// family.
+ ///
+ /// @param ifname name of the interface
+ /// @param addr address to be bound.
+ /// @param port UDP port.
+ ///
+ /// Method will throw if socket creation, socket binding or multicast
+ /// join fails.
+ ///
+ /// @return socket descriptor, if socket creation, binding and multicast
+ /// group join were all successful.
+ int openSocket(const std::string& ifname,
+ const isc::asiolink::IOAddress& addr, int port);
+
+ /// Opens IPv6 sockets on detected interfaces.
+ ///
+ /// Will throw exception if socket creation fails.
+ ///
+ /// @param port specifies port number (usually DHCP6_SERVER_PORT)
+ void openSockets(uint16_t port);
+
+
+ /// @brief Closes all open sockets.
+ /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
+ void closeSockets();
// don't use private, we need derived classes in tests
protected:
@@ -146,11 +312,44 @@ protected:
/// @brief Protected constructor.
///
/// Protected constructor. This is a singleton class. We don't want
- /// anyone to create instances of IfaceMgr. Use instance() method
+ /// anyone to create instances of IfaceMgr. Use instance() method instead.
IfaceMgr();
~IfaceMgr();
+ /// @brief Opens IPv4 socket.
+ ///
+ /// Please do not use this method directly. Use openSocket instead.
+ ///
+ /// This method may throw exception if socket creation fails.
+ ///
+ /// @param iface reference to interface structure.
+ /// @param addr an address the created socket should be bound to
+ /// @param port a port that created socket should be bound to
+ ///
+ /// @return socket descriptor
+ int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+
+ /// @brief Opens IPv6 socket.
+ ///
+ /// Please do not use this method directly. Use openSocket instead.
+ ///
+ /// This method may throw exception if socket creation fails.
+ ///
+ /// @param iface reference to interface structure.
+ /// @param addr an address the created socket should be bound to
+ /// @param port a port that created socket should be bound to
+ ///
+ /// @return socket descriptor
+ int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+
+ /// @brief Adds an interface to list of known interfaces.
+ ///
+ /// @param iface reference to Iface object.
+ void addInterface(const Iface& iface) {
+ ifaces_.push_back(iface);
+ }
+
/// @brief Detects network interfaces.
///
/// This method will eventually detect available interfaces. For now
@@ -159,24 +358,11 @@ protected:
void
detectIfaces();
- ///
- /// Opens UDP/IPv6 socket and binds it to address, interface and port.
- ///
- /// @param ifname name of the interface
- /// @param addr address to be bound.
- /// @param port UDP port.
- ///
- /// @return socket descriptor, if socket creation, binding and multicast
- /// group join were all successful. -1 otherwise.
- int openSocket(const std::string& ifname,
- const isc::asiolink::IOAddress& addr,
- int port);
-
// TODO: having 2 maps (ifindex->iface and ifname->iface would)
// probably be better for performance reasons
/// List of available interfaces
- IfaceLst ifaces_;
+ IfaceCollection ifaces_;
/// a pointer to a sole instance of this class (a singleton)
static IfaceMgr * instance_;
@@ -184,8 +370,9 @@ protected:
// TODO: Also keep this interface on Iface once interface detection
// is implemented. We may need it e.g. to close all sockets on
// specific interface
- int recvsock_; // TODO: should be fd_set eventually, but we have only
- int sendsock_; // 2 sockets for now. Will do for until next release
+ //int recvsock_; // TODO: should be fd_set eventually, but we have only
+ //int sendsock_; // 2 sockets for now. Will do for until next release
+
// we can't use the same socket, as receiving socket
// is bound to multicast address. And we all know what happens
// to people who try to use multicast as source address.
@@ -197,9 +384,6 @@ protected:
boost::scoped_array<char> control_buf_;
private:
- /// Opens sockets on detected interfaces.
- bool
- openSockets();
/// creates a single instance of this class (a singleton implementation)
static void
@@ -221,6 +405,7 @@ private:
bool
joinMcast(int sock, const std::string& ifname,
const std::string& mcast);
+
};
}; // namespace isc::dhcp
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 985368e..f766840 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -25,8 +25,6 @@ check-local:
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
AM_CPPFLAGS += -I$(top_srcdir)/src/bin
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/dhcp6/tests\"
@@ -58,7 +56,6 @@ dhcp6_unittests_LDADD += $(SQLITE_LIBS)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 72e48e4..50f37af 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -34,7 +34,7 @@ namespace test {
class NakedDhcpv6Srv: public Dhcpv6Srv {
// "naked" Interface Manager, exposes internal fields
public:
- NakedDhcpv6Srv() { }
+ NakedDhcpv6Srv():Dhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
boost::shared_ptr<Pkt6>
processSolicit(boost::shared_ptr<Pkt6>& request) {
@@ -53,30 +53,27 @@ public:
};
TEST_F(Dhcpv6SrvTest, basic) {
- // there's almost no code now. What's there provides echo capability
- // that is just a proof of concept and will be removed soon
- // No need to thoroughly test it
-
// srv has stubbed interface detection. It will read
// interfaces.txt instead. It will pretend to have detected
// fe80::1234 link-local address on eth0 interface. Obviously
// an attempt to bind this socket will fail.
- EXPECT_NO_THROW( {
- Dhcpv6Srv * srv = new Dhcpv6Srv();
-
- delete srv;
- });
+ Dhcpv6Srv* srv = 0;
+ ASSERT_NO_THROW( {
+ // open an unpriviledged port
+ srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+ });
+ delete srv;
}
TEST_F(Dhcpv6SrvTest, Solicit_basic) {
NakedDhcpv6Srv * srv = 0;
- EXPECT_NO_THROW( srv = new NakedDhcpv6Srv(); );
+ ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
boost::shared_array<uint8_t> clntDuid(new uint8_t[32]);
- for (int i=0; i<32; i++)
- clntDuid[i] = 100+i;
+ for (int i = 0; i < 32; i++)
+ clntDuid[i] = 100 + i;
boost::shared_ptr<Pkt6> sol =
boost::shared_ptr<Pkt6>(new Pkt6(DHCPV6_SOLICIT,
diff --git a/src/bin/dhcp6/tests/iface_mgr_unittest.cc b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
index f126e6a..3cc2ae8 100644
--- a/src/bin/dhcp6/tests/iface_mgr_unittest.cc
+++ b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
@@ -20,9 +20,10 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-#include "io_address.h"
-#include "dhcp/pkt6.h"
-#include "dhcp6/iface_mgr.h"
+#include <asiolink/io_address.h>
+#include <dhcp/pkt6.h>
+#include <dhcp6/iface_mgr.h>
+#include <dhcp/dhcp4.h>
using namespace std;
using namespace isc;
@@ -39,16 +40,7 @@ class NakedIfaceMgr: public IfaceMgr {
// "naked" Interface Manager, exposes internal fields
public:
NakedIfaceMgr() { }
- IfaceLst & getIfacesLst() { return ifaces_; }
- void setSendSock(int sock) { sendsock_ = sock; }
- void setRecvSock(int sock) { recvsock_ = sock; }
-
- int openSocket(const std::string& ifname,
- const isc::asiolink::IOAddress& addr,
- int port) {
- return IfaceMgr::openSocket(ifname, addr, port);
- }
-
+ IfaceCollection & getIfacesLst() { return ifaces_; }
};
// dummy class for now, but this will be expanded when needed
@@ -56,6 +48,13 @@ class IfaceMgrTest : public ::testing::Test {
public:
IfaceMgrTest() {
}
+
+ void createLoInterfacesTxt() {
+ unlink(INTERFACE_FILE);
+ fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
+ fakeifaces << LOOPBACK << " ::1";
+ fakeifaces.close();
+ }
};
// We need some known interface to work reliably. Loopback interface
@@ -109,6 +108,7 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
while (true) {
pkt = ifacemgr->receive();
+ cout << "// this code is autogenerated. Do NOT edit." << endl;
cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
cout << " Pkt6* pkt;" << endl;
@@ -183,10 +183,10 @@ TEST_F(IfaceMgrTest, getIface) {
cout << "There are " << ifacemgr->getIfacesLst().size()
<< " interfaces." << endl;
- for (IfaceMgr::IfaceLst::iterator iface=ifacemgr->getIfacesLst().begin();
+ for (IfaceMgr::IfaceCollection::iterator iface=ifacemgr->getIfacesLst().begin();
iface != ifacemgr->getIfacesLst().end();
++iface) {
- cout << " " << iface->name_ << "/" << iface->ifindex_ << endl;
+ cout << " " << iface->getFullName() << endl;
}
@@ -195,15 +195,15 @@ TEST_F(IfaceMgrTest, getIface) {
// ASSERT_NE(NULL, tmp); is not supported. hmmmm.
ASSERT_TRUE( tmp != NULL );
- EXPECT_STREQ( "en3", tmp->name_.c_str() );
- EXPECT_EQ(5, tmp->ifindex_);
+ EXPECT_EQ( "en3", tmp->getName() );
+ EXPECT_EQ(5, tmp->getIndex());
// check that interface can be retrieved by name
tmp = ifacemgr->getIface("lo1");
ASSERT_TRUE( tmp != NULL );
- EXPECT_STREQ( "lo1", tmp->name_.c_str() );
- EXPECT_EQ(1, tmp->ifindex_);
+ EXPECT_EQ( "lo1", tmp->getName() );
+ EXPECT_EQ(1, tmp->getIndex());
// check that non-existing interfaces are not returned
EXPECT_EQ(static_cast<void*>(NULL), ifacemgr->getIface("wifi0") );
@@ -231,58 +231,51 @@ TEST_F(IfaceMgrTest, detectIfaces) {
IfaceMgr::Iface * eth0 = ifacemgr->getIface("eth0");
// there should be one address
- EXPECT_EQ(1, eth0->addrs_.size());
+ IfaceMgr::AddressCollection addrs = eth0->getAddresses();
+ ASSERT_EQ(1, addrs.size());
- IOAddress * addr = &(*eth0->addrs_.begin());
- ASSERT_TRUE( addr != NULL );
+ IOAddress addr = *addrs.begin();
- EXPECT_STREQ( "fe80::1234", addr->toText().c_str() );
+ EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
delete ifacemgr;
}
-// TODO: disabled due to other naming on various systems
-// (lo in Linux, lo0 in BSD systems)
-// Fix for this is available on 1186 branch, will reenable
-// this test once 1186 is merged
-TEST_F(IfaceMgrTest, DISABLED_sockets) {
+TEST_F(IfaceMgrTest, sockets6) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
+ createLoInterfacesTxt();
+
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
IOAddress loAddr("::1");
+ Pkt6 pkt6(128);
+ pkt6.iface_ = LOOPBACK;
+
// bind multicast socket to port 10547
int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
EXPECT_GT(socket1, 0); // socket > 0
+ EXPECT_EQ(socket1, ifacemgr->getSocket(pkt6));
+
// bind unicast socket to port 10548
int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10548);
EXPECT_GT(socket2, 0);
- // expect success. This address/port is already bound, but
- // we are using SO_REUSEADDR, so we can bind it twice
- int socket3 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
-
- // rebinding succeeds on Linux, fails on BSD
- // TODO: add OS-specific defines here (or modify code to
- // behave the same way on all OSes, but that may not be
- // possible
- // EXPECT_GT(socket3, 0); // socket > 0
-
- // we now have 3 sockets open at the same time. Looks good.
+ // removed code for binding socket twice to the same address/port
+ // as it caused problems on some platforms (e.g. Mac OS X)
close(socket1);
close(socket2);
- close(socket3);
delete ifacemgr;
}
// TODO: disabled due to other naming on various systems
// (lo in Linux, lo0 in BSD systems)
-TEST_F(IfaceMgrTest, DISABLED_socketsMcast) {
+TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
@@ -311,27 +304,21 @@ TEST_F(IfaceMgrTest, DISABLED_socketsMcast) {
delete ifacemgr;
}
-// TODO: disabled due to other naming on various systems
-// (lo in Linux, lo0 in BSD systems)
-// Fix for this is available on 1186 branch, will reenable
-// this test once 1186 is merged
-TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
+TEST_F(IfaceMgrTest, sendReceive6) {
+
// testing socket operation in a portable way is tricky
// without interface detection implemented
+ createLoInterfacesTxt();
- fstream fakeifaces(INTERFACE_FILE, ios::out|ios::trunc);
- fakeifaces << LOOPBACK << " ::1";
- fakeifaces.close();
-
- NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
// let's assume that every supported OS have lo interface
IOAddress loAddr("::1");
- int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
- int socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
-
- ifacemgr->setSendSock(socket2);
- ifacemgr->setRecvSock(socket1);
+ int socket1 = 0, socket2 = 0;
+ EXPECT_NO_THROW(
+ socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
+ socket2 = ifacemgr->openSocket(LOOPBACK, loAddr, 10546);
+ );
boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
@@ -349,7 +336,7 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
EXPECT_EQ(true, ifacemgr->send(sendPkt));
- rcvPkt = ifacemgr->receive();
+ rcvPkt = ifacemgr->receive6();
ASSERT_TRUE( rcvPkt ); // received our own packet
@@ -359,7 +346,168 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
rcvPkt->data_len_) );
EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
- EXPECT_EQ(rcvPkt->remote_port_, 10546);
+
+ // since we opened 2 sockets on the same interface and none of them is multicast,
+ // none is preferred over the other for sending data, so we really should not
+ // assume the one or the other will always be choosen for sending data. Therefore
+ // we should accept both values as source ports.
+ EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
+
+ delete ifacemgr;
+}
+
+TEST_F(IfaceMgrTest, socket4) {
+
+ createLoInterfacesTxt();
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+
+ // Let's assume that every supported OS have lo interface.
+ IOAddress loAddr("127.0.0.1");
+ // Use unprivileged port (it's convenient for running tests as non-root).
+ int socket1 = 0;
+
+ EXPECT_NO_THROW(
+ socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, DHCP4_SERVER_PORT + 10000);
+ );
+
+ EXPECT_GT(socket1, 0);
+
+ Pkt4 pkt(DHCPDISCOVER, 1234);
+ pkt.setIface(LOOPBACK);
+
+ // Expect that we get the socket that we just opened.
+ EXPECT_EQ(socket1, ifacemgr->getSocket(pkt));
+
+ close(socket1);
+
+ delete ifacemgr;
+}
+
+// Test the Iface structure itself
+TEST_F(IfaceMgrTest, iface) {
+ IfaceMgr::Iface* iface = 0;
+ EXPECT_NO_THROW(
+ iface = new IfaceMgr::Iface("eth0",1);
+ );
+
+ EXPECT_EQ("eth0", iface->getName());
+ EXPECT_EQ(1, iface->getIndex());
+ EXPECT_EQ("eth0/1", iface->getFullName());
+
+ // Let's make a copy of this address collection.
+ IfaceMgr::AddressCollection addrs = iface->getAddresses();
+
+ EXPECT_EQ(0, addrs.size());
+
+ IOAddress addr1("192.0.2.6");
+ iface->addAddress(addr1);
+
+ addrs = iface->getAddresses();
+ ASSERT_EQ(1, addrs.size());
+ EXPECT_EQ("192.0.2.6", addrs.at(0).toText());
+
+ // No such address, should return false.
+ EXPECT_FALSE(iface->delAddress(IOAddress("192.0.8.9")));
+
+ // This address is present, delete it!
+ EXPECT_TRUE(iface->delAddress(IOAddress("192.0.2.6")));
+
+ // Not really necessary, previous reference still points to the same
+ // collection. Let's do it anyway, as test code may serve as example
+ // usage code as well.
+ addrs = iface->getAddresses();
+
+ EXPECT_EQ(0, addrs.size());
+
+ EXPECT_NO_THROW(
+ delete iface;
+ );
+}
+
+TEST_F(IfaceMgrTest, socketInfo) {
+
+ // check that socketinfo for IPv4 socket is functional
+ IfaceMgr::SocketInfo sock1(7, IOAddress("192.0.2.56"), DHCP4_SERVER_PORT + 7);
+ EXPECT_EQ(7, sock1.sockfd_);
+ 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 socketinfo for IPv6 socket is functional
+ IfaceMgr::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_);
+
+ // now let's test if IfaceMgr handles socket info properly
+ createLoInterfacesTxt();
+ NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+ IfaceMgr::Iface* loopback = ifacemgr->getIface(LOOPBACK);
+ ASSERT_TRUE(loopback);
+ loopback->addSocket(sock1);
+ loopback->addSocket(sock2);
+
+ Pkt6 pkt6(100);
+
+ // pkt6 dos not have interface set yet
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ BadValue
+ );
+
+ // try to send over non-existing interface
+ pkt6.iface_ = "nosuchinterface45";
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ BadValue
+ );
+
+ // this will work
+ pkt6.iface_ = LOOPBACK;
+ EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
+
+ bool deleted = false;
+ EXPECT_NO_THROW(
+ deleted = ifacemgr->getIface(LOOPBACK)->delSocket(9);
+ );
+ EXPECT_EQ(true, deleted);
+
+ // it should throw again, there's no usable socket anymore
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt6),
+ Unexpected
+ );
+
+ // repeat for pkt4
+ Pkt4 pkt4(DHCPDISCOVER, 1);
+
+ // pkt4 does not have interface set yet.
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ BadValue
+ );
+
+ // Try to send over non-existing interface.
+ pkt4.setIface("nosuchinterface45");
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ BadValue
+ );
+
+ // Socket info is set, packet has well defined interface. It should work.
+ pkt4.setIface(LOOPBACK);
+ EXPECT_EQ(7, ifacemgr->getSocket(pkt4));
+
+ EXPECT_NO_THROW(
+ ifacemgr->getIface(LOOPBACK)->delSocket(7);
+ );
+
+ // It should throw again, there's no usable socket anymore.
+ EXPECT_THROW(
+ ifacemgr->getSocket(pkt4),
+ Unexpected
+ );
delete ifacemgr;
}
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 1f68527..bea93fc 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -47,7 +47,6 @@ Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
yiaddr_(DEFAULT_ADDRESS),
siaddr_(DEFAULT_ADDRESS),
giaddr_(DEFAULT_ADDRESS),
- bufferIn_(NULL, 0), // not used, this is TX packet
bufferOut_(DHCPV4_PKT_HDR_LEN),
msg_type_(msg_type)
{
@@ -71,7 +70,6 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
yiaddr_(DEFAULT_ADDRESS),
siaddr_(DEFAULT_ADDRESS),
giaddr_(DEFAULT_ADDRESS),
- bufferIn_(data, len),
bufferOut_(0), // not used, this is RX packet
msg_type_(DHCPDISCOVER)
{
@@ -80,6 +78,9 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
<< " received, at least " << DHCPV4_PKT_HDR_LEN
<< "is expected");
}
+
+ data_.resize(len);
+ memcpy(&data_[0], data, len);
}
size_t
@@ -123,31 +124,35 @@ Pkt4::pack() {
}
bool
Pkt4::unpack() {
- if (bufferIn_.getLength()<DHCPV4_PKT_HDR_LEN) {
+
+ // input buffer (used during message reception)
+ isc::util::InputBuffer bufferIn(&data_[0], data_.size());
+
+ if (bufferIn.getLength()<DHCPV4_PKT_HDR_LEN) {
isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
- << bufferIn_.getLength() << " received, at least "
+ << bufferIn.getLength() << " received, at least "
<< DHCPV4_PKT_HDR_LEN << "is expected");
}
- op_ = bufferIn_.readUint8();
- htype_ = bufferIn_.readUint8();
- hlen_ = bufferIn_.readUint8();
- hops_ = bufferIn_.readUint8();
- transid_ = bufferIn_.readUint32();
- secs_ = bufferIn_.readUint16();
- flags_ = bufferIn_.readUint16();
- ciaddr_ = IOAddress(bufferIn_.readUint32());
- yiaddr_ = IOAddress(bufferIn_.readUint32());
- siaddr_ = IOAddress(bufferIn_.readUint32());
- giaddr_ = IOAddress(bufferIn_.readUint32());
- bufferIn_.readData(chaddr_, MAX_CHADDR_LEN);
- bufferIn_.readData(sname_, MAX_SNAME_LEN);
- bufferIn_.readData(file_, MAX_FILE_LEN);
-
- size_t opts_len = bufferIn_.getLength() - bufferIn_.getPosition();
+ op_ = bufferIn.readUint8();
+ htype_ = bufferIn.readUint8();
+ hlen_ = bufferIn.readUint8();
+ hops_ = bufferIn.readUint8();
+ transid_ = bufferIn.readUint32();
+ secs_ = bufferIn.readUint16();
+ flags_ = bufferIn.readUint16();
+ ciaddr_ = IOAddress(bufferIn.readUint32());
+ yiaddr_ = IOAddress(bufferIn.readUint32());
+ siaddr_ = IOAddress(bufferIn.readUint32());
+ giaddr_ = IOAddress(bufferIn.readUint32());
+ bufferIn.readData(chaddr_, MAX_CHADDR_LEN);
+ bufferIn.readData(sname_, MAX_SNAME_LEN);
+ bufferIn.readData(file_, MAX_FILE_LEN);
+
+ size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
vector<uint8_t> optsBuffer;
// fist use of readVector
- bufferIn_.readVector(optsBuffer, opts_len);
+ bufferIn.readVector(optsBuffer, opts_len);
LibDHCP::unpackOptions4(optsBuffer, options_);
return (true);
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 8517091..189d95d 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -299,10 +299,21 @@ public:
///
/// @return returns option of requested type (or NULL)
/// if no such option is present
-
boost::shared_ptr<Option>
getOption(uint8_t opt_type);
+
+ /// @brief set interface over which packet should be sent
+ ///
+ /// @param interface defines outbound interface
+ void setIface(const std::string& interface){ iface_ = interface; }
+
+ /// @brief gets interface over which packet was received or
+ /// will be transmitted
+ ///
+ /// @return name of the interface
+ std::string getIface() const { return iface_; }
+
protected:
/// converts DHCP message type to BOOTP op type
@@ -385,14 +396,15 @@ protected:
// end of real DHCPv4 fields
- /// input buffer (used during message reception)
- /// Note that it must be modifiable as hooks can modify incoming buffer),
- /// thus OutputBuffer, not InputBuffer
- isc::util::InputBuffer bufferIn_;
-
/// output buffer (used during message
isc::util::OutputBuffer bufferOut_;
+ // that's the data of input buffer used in RX packet. Note that
+ // InputBuffer does not store the data itself, but just expects that
+ // data will be valid for the whole life of InputBuffer. Therefore we
+ // need to keep the data around.
+ std::vector<uint8_t> data_;
+
/// message type (e.g. 1=DHCPDISCOVER)
/// TODO: this will eventually be replaced with DHCP Message Type
/// option (option 53)
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index db3ee3b..66dce8f 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -402,6 +402,8 @@ TEST_F(OptionTest, v6_addgetdel) {
// let's try to delete - should fail
EXPECT_TRUE(false == parent->delOption(2));
+
+ delete parent;
}
}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 0f70442..091bfac 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -561,4 +561,17 @@ TEST(Pkt4Test, unpackOptions) {
EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+22, 3)); // data len=3
}
+// This test verifies methods that are used for manipulating meta fields
+// i.e. fields that are not part of DHCPv4 (e.g. interface name).
+TEST(Pkt4Ttest, metaFields) {
+ Pkt4 pkt(DHCPDISCOVER, 1234);
+
+ pkt.setIface("lo0");
+
+ EXPECT_EQ("lo0", pkt.getIface());
+
+ /// TODO: Expand this test once additonal getters/setters are
+ /// implemented.
+}
+
} // end of anonymous namespace
More information about the bind10-changes
mailing list