Commit a6a35e99 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

Merge branch 'master' of ssh://git.bind10.isc.org/var/bind10/git/bind10

Conflicts:
	ChangeLog
parents c8dc421c 86a4ce45
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.
......
......@@ -7,3 +7,4 @@ Makefile.in
b10-dhcp6
spec_config.h
spec_config.h.pre
tests/dhcp6_unittests
......@@ -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()) {
......
......@@ -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
///
......
This diff is collapsed.
......@@ -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
......
......@@ -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)
......@@ -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,
......
......@@ -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");