Commit 526f8713 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[1540] Changes after review:

- port is now specified using uint16_t in IfaceMgr
- support for proper DUID generation implemented
- implemented new tests
- new HWTYPEs defines
- unpack methods now return size_t type
- comments updated in option*.h headers
parent ec387224
......@@ -59,10 +59,10 @@ Dhcpv4Srv::~Dhcpv4Srv() {
bool
Dhcpv4Srv::run() {
while (!shutdown_) {
Pkt4Ptr query; // client's message
Pkt4Ptr rsp; // server's response
query = IfaceMgr::instance().receive4();
// client's message
Pkt4Ptr query = IfaceMgr::instance().receive4();
Pkt4Ptr rsp; // server's response
if (query) {
try {
......
......@@ -12,6 +12,7 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <time.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcp/iface_mgr.h>
......@@ -21,11 +22,13 @@
#include <dhcp/option6_addrlst.h>
#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
using namespace isc::util;
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
const uint32_t HARDCODED_T1 = 1500; // in seconds
......@@ -67,13 +70,11 @@ Dhcpv6Srv::~Dhcpv6Srv() {
IfaceMgr::instance().closeSockets();
}
bool
Dhcpv6Srv::run() {
bool Dhcpv6Srv::run() {
while (!shutdown) {
Pkt6Ptr query; // client's message
Pkt6Ptr rsp; // server's response
query = IfaceMgr::instance().receive6();
Pkt6Ptr query = IfaceMgr::instance().receive6(); // client's message
Pkt6Ptr rsp; // server's response
if (query) {
if (!query->unpack()) {
......@@ -138,24 +139,91 @@ Dhcpv6Srv::run() {
return (true);
}
void
Dhcpv6Srv::setServerID() {
/// TODO implement this for real once interface detection is done.
/// Use hardcoded server-id for now
OptionBuffer srvid(14);
srvid[0] = 0;
srvid[1] = 1; // DUID type 1 = DUID-LLT (see section 9.2 of RFC3315)
srvid[2] = 0;
srvid[3] = 6; // HW type = ethernet (I think. I'm typing this from my head
// in hotel, without Internet connection)
for (int i=4; i<14; i++) {
srvid[i]=i-4;
void Dhcpv6Srv::setServerID() {
/// @todo: DUID should be generated once and then stored, rather
/// than generated each time
/// @todo: This code implements support for DUID-LLT (the recommended one).
/// We should eventually add support for other DUID types: DUID-LL, DUID-EN
/// and DUID-UUID
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
// let's find suitable interface
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
iface != ifaces.end(); ++iface) {
// all those conditions could be merged into one multi-condition
// statement, but let's keep them separated as perhaps one day
// we will grow knobs to selectively turn them on or off. Also,
// this code is used only *once* during first start on a new machine
// and then server-id is stored. (or at least it will be once
// DUID storage is implemente
// I wish there was a this_is_a_real_physical_interface flag...
// mac at least 6 bytes. All decent physical interfaces (Ethernet,
// WiFi, Infiniband, etc.) have 6 bytes long MAC address
if (iface->mac_len_ < 6) {
continue;
}
// let's don't use loopback
if (iface->flag_loopback_) {
continue;
}
// let's skip downed interfaces. It is better to use working ones.
if (!iface->flag_up_) {
continue;
}
uint8_t zeros[IfaceMgr::MAX_MAC_LEN];
memset(zeros, 0, IfaceMgr::MAX_MAC_LEN);
// some interfaces (like lo on Linux) report 6-bytes long
// MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
// to generate DUID.
if (!memcmp(iface->mac_, zeros, iface->mac_len_)) {
continue;
}
// Ok, we have useful MAC. Let's generate DUID-LLT based on
// it. See RFC3315, Section 9.2 for details.
// DUID uses seconds since midnight of 01-01-2000, time() returns
// seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
time_t seconds = time(NULL);
seconds -= DUID_TIME_EPOCH;
OptionBuffer srvid(8 + iface->mac_len_);
writeUint16(DUID_LLT, &srvid[0]);
writeUint16(HWTYPE_ETHERNET, &srvid[2]);
writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
memcpy(&srvid[0]+8, iface->mac_, iface->mac_len_);
serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
srvid.begin(), srvid.end()));
return;
}
serverid_ = boost::shared_ptr<Option>(new Option(Option::V6,
D6O_SERVERID,
srvid.begin(),
srvid.end()));
// if we reached here, there are no suitable interfaces found.
// Either interface detection is not supported on this platform or
// this is really weird box. Let's use DUID-EN instead.
// See Section 9.3 of RFC3315 for details.
OptionBuffer srvid(12);
srand(time(NULL));
writeUint16(DUID_EN, &srvid[0]);
writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
// length of the identifier is company specific. I hereby declare
// ISC standard of 6 bytes long random numbers.
for (int i = 6; i < 12; i++) {
srvid[i] = static_cast<uint8_t>(rand());
}
serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
srvid.begin(), srvid.end()));
}
void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
......@@ -214,7 +282,6 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
}
Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
copyDefaultOptions(solicit, advertise);
......@@ -227,7 +294,6 @@ Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
}
Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
copyDefaultOptions(request, reply);
......@@ -240,33 +306,37 @@ Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
}
Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
return reply;
}
Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY,
rebind->getTransid(),
Pkt6::UDP));
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
return reply;
}
Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
return reply;
}
Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
return reply;
}
Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
return reply;
}
Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
/// @todo: Implement this
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
return reply;
}
......@@ -20,13 +20,15 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include "dhcp/dhcp6.h"
#include "dhcp6/dhcp6_srv.h"
#include "dhcp/option6_ia.h"
#include <dhcp/dhcp6.h>
#include <dhcp/option6_ia.h>
#include <dhcp6/dhcp6_srv.h>
#include <util/buffer.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::util;
// namespace has to be named, because friends are defined in Dhcpv6Srv class
// Maybe it should be isc::test?
......@@ -79,16 +81,84 @@ TEST_F(Dhcpv6SrvTest, basic) {
delete srv;
}
TEST_F(Dhcpv6SrvTest, DUID) {
// tests that DUID is generated properly
Dhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( {
srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
});
OptionPtr srvid = srv->getServerID();
ASSERT_TRUE(srvid);
EXPECT_EQ(D6O_SERVERID, srvid->getType());
OutputBuffer buf(32);
srvid->pack(buf);
// length of the actual DUID
size_t len = buf.getLength() - srvid->getHeaderLen();
InputBuffer data(buf.getData(), buf.getLength());
// ignore first four bytes (standard DHCPv6 header)
data.readUint32();
uint16_t duid_type = data.readUint16();
cout << "Duid-type=" << duid_type << endl;
switch(duid_type) {
case DUID_LLT: {
// DUID must contain at least 6 bytes long MAC
// + 8 bytes of fixed header
EXPECT_GE(14, len);
uint16_t hw_type = data.readUint16();
// there's no real way to find out "correct"
// hardware type
EXPECT_GT(hw_type, 0);
// check that timer is counted since 1.1.2000,
// not from 1.1.1970.
uint32_t seconds = data.readUint32();
EXPECT_LE(seconds, DUID_TIME_EPOCH);
// this test will start failing after 2030.
// Hopefully we will be at BIND12 by then.
// MAC must not be zeros
vector<uint8_t> mac(len-8);
vector<uint8_t> zeros(len-8, 0);
data.readVector(mac, len-8);
EXPECT_NE(mac, zeros);
break;
}
case DUID_EN: {
// there's not much we can check. Just simple
// check if it is not all zeros
vector<uint8_t> content(len-2);
vector<uint8_t> zeros(0, len-2);
data.readVector(content, len-2);
EXPECT_NE(content, zeros);
}
case DUID_LL: // not supported yet
case DUID_UUID: // not supported yet
default:
cout << "Not supported duid type=" << duid_type << endl;
FAIL();
}
}
TEST_F(Dhcpv6SrvTest, Solicit_basic) {
NakedDhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
OptionBuffer clntDuid(32);
for (int i = 0; i < 32; i++)
for (int i = 0; i < 32; i++) {
clntDuid[i] = 100 + i;
}
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234, Pkt6::UDP));
Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
boost::shared_ptr<Option6IA> ia =
boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
......@@ -113,7 +183,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
clntDuid.begin(),
clntDuid.begin()+16));
clntDuid.begin() + 16));
sol->addOption(clientid);
boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
......
......@@ -108,6 +108,15 @@ extern const int dhcpv6_type_name_max;
#define DUID_LLT 1
#define DUID_EN 2
#define DUID_LL 3
#define DUID_UUID 4
// Define hardware types
// Taken from http://www.iana.org/assignments/arp-parameters/
#define HWTYPE_ETHERNET 0x0001
#define HWTYPE_INIFINIBAND 0x0020
// Taken from http://www.iana.org/assignments/enterprise-numbers
#define ENTERPRISE_ID_ISC 2495
/* Offsets into IA_*'s where Option spaces commence. */
#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
......
......@@ -178,6 +178,12 @@ IfaceMgr::stubDetectIfaces() {
cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
iface.flag_up_ = true;
iface.flag_running_ = true;
iface.flag_loopback_ = false;
iface.flag_multicast_ = true;
iface.flag_broadcast_ = true;
iface.hardware_type_ = HWTYPE_ETHERNET;
IOAddress addr(linkLocal);
iface.addAddress(addr);
addInterface(iface);
......@@ -347,9 +353,7 @@ IfaceMgr::getIface(const std::string& ifname) {
return (NULL); // not found
}
int IfaceMgr::openSocket(const std::string& ifname,
const IOAddress& addr,
int port) {
int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr, uint16_t port) {
Iface* iface = getIface(ifname);
if (!iface) {
isc_throw(BadValue, "There is no " << ifname << " interface present.");
......@@ -365,7 +369,7 @@ int IfaceMgr::openSocket(const std::string& ifname,
}
}
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
cout << "Creating UDP4 socket on " << iface.getFullName()
<< " " << addr.toText() << "/port=" << port << endl;
......@@ -409,7 +413,7 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
return (sock);
}
int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
cout << "Creating UDP6 socket on " << iface.getFullName()
<< " " << addr.toText() << "/port=" << port << endl;
......@@ -515,7 +519,7 @@ const std::string & mcast) {
}
bool
IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
IfaceMgr::send(const Pkt6Ptr& pkt) {
struct msghdr m;
struct iovec v;
int result;
......@@ -549,7 +553,7 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
// 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->getBuffer().getData();
v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
v.iov_len = pkt->getBuffer().getLength();
m.msg_iov = &v;
m.msg_iovlen = 1;
......@@ -585,11 +589,10 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
}
bool
IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
IfaceMgr::send(const Pkt4Ptr& pkt)
{
struct msghdr m;
struct iovec v;
int result;
Iface* iface = getIface(pkt->getIface());
if (!iface) {
......@@ -620,7 +623,6 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
m.msg_iov = &v;
m.msg_iovlen = 1;
// OS_LINUX defines are part of ticket #1237
#if defined(OS_LINUX)
// Setting the interface is a bit more involved.
//
......@@ -628,16 +630,14 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
// define the IPv4 packet information. We could set the
// source address if we wanted, but we can safely let the
// kernel decide what that should be.
struct in_pktinfo *pktinfo;
struct cmsghdr *cmsg;
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
cmsg = CMSG_FIRSTHDR(&m);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(*pktinfo));
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(struct in_pktinfo));
pktinfo->ipi_ifindex = pkt->getIndex();
m.msg_controllen = cmsg->cmsg_len;
#endif
......@@ -647,7 +647,7 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
<< " over socket " << getSocket(*pkt) << " on interface "
<< getIface(pkt->getIface())->getFullName() << endl;
result = sendmsg(getSocket(*pkt), &m, 0);
int result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
isc_throw(Unexpected, "Pkt4 send failed.");
}
......@@ -669,22 +669,12 @@ IfaceMgr::receive4() {
IfaceCollection::const_iterator iface;
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
#if 0
// uncomment this once #1237 is merged
// Let's skip loopback and downed interfaces.
if (iface->flag_loopback_ ||
!iface->flag_up_ ||
!iface->flag_running_) {
continue;
}
#endif
SocketCollection::const_iterator s = iface->sockets_.begin();
while (s != iface->sockets_.end()) {
/// @todo: rewrite this as part of #1555
for (SocketCollection::const_iterator s = iface->sockets_.begin();
s != iface->sockets_.end(); ++s) {
// We don't want IPv6 addresses here.
if (s->addr_.getFamily() != AF_INET) {
++s;
continue;
}
......@@ -693,8 +683,6 @@ IfaceMgr::receive4() {
candidate = &(*s);
break;
}
++s;
}
if (candidate) {
......@@ -711,13 +699,8 @@ IfaceMgr::receive4() {
<< iface->getFullName() << endl;
// Now we have a socket, let's get some data from it!
struct msghdr m;
struct iovec v;
int result;
struct sockaddr_in from_addr;
struct in_addr to_addr;
boost::shared_ptr<Pkt4> pkt;
const uint32_t RCVBUFSIZE = 1500;
static uint8_t buf[RCVBUFSIZE];
......@@ -726,13 +709,15 @@ IfaceMgr::receive4() {
memset(&to_addr, 0, sizeof(to_addr));
// Initialize our message header structure.
struct msghdr m;
memset(&m, 0, sizeof(m));
// Point so we can get the from address.
m.msg_name = &from_addr;
m.msg_namelen = sizeof(from_addr);
v.iov_base = (void*)buf;
struct iovec v;
v.iov_base = static_cast<void*>(buf);
v.iov_len = RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
......@@ -746,16 +731,14 @@ IfaceMgr::receive4() {
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
result = recvmsg(candidate->sockfd_, &m, 0);
int result = recvmsg(candidate->sockfd_, &m, 0);
if (result < 0) {
cout << "Failed to receive UDP4 data." << endl;
return (boost::shared_ptr<Pkt4>()); // NULL
return (Pkt4Ptr()); // NULL
}
unsigned int ifindex = iface->getIndex();
// OS_LINUX defines are part of ticket #1237
#if defined(OS_LINUX)
struct cmsghdr* cmsg;
struct in_pktinfo* pktinfo;
......@@ -800,7 +783,7 @@ IfaceMgr::receive4() {
<< iface->getFullName() << endl;
// we have all data let's create Pkt4 object
pkt = boost::shared_ptr<Pkt4>(new Pkt4(buf, result));
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
pkt->setIface(iface->getName());
pkt->setIndex(ifindex);
......@@ -820,7 +803,7 @@ Pkt6Ptr IfaceMgr::receive6() {
struct in6_pktinfo* pktinfo;
struct sockaddr_in6 from;
struct in6_addr to_addr;
int ifindex;
int ifindex = -1;
Pkt6Ptr pkt;
// RFC3315 states that server responses may be
......@@ -866,10 +849,9 @@ Pkt6Ptr IfaceMgr::receive6() {
IfaceCollection::const_iterator iface = ifaces_.begin();
const SocketInfo* candidate = 0;
while (iface != ifaces_.end()) {
SocketCollection::const_iterator s = iface->sockets_.begin();
while (s != iface->sockets_.end()) {
for (SocketCollection::const_iterator s = iface->sockets_.begin();
s != iface->sockets_.end(); ++s) {
if (s->addr_.getFamily() != AF_INET6) {
++s;
continue;
}
if (s->addr_.getAddress().to_v6().is_multicast()) {
......@@ -879,7 +861,6 @@ Pkt6Ptr IfaceMgr::receive6() {
if (!candidate) {
candidate = &(*s); // it's not multicast, but it's better than nothing
}
++s;
}
if (candidate) {
break;
......@@ -887,7 +868,7 @@ Pkt6Ptr IfaceMgr::receive6() {
++iface;
}
if (iface == ifaces_.end()) {
isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
isc_throw(Unexpected, "No suitable IPv6 interfaces detected. Can't receive anything.");
}
if (!candidate) {
......@@ -921,11 +902,11 @@ Pkt6Ptr IfaceMgr::receive6() {
}
if (!found_pktinfo) {
cout << "Unable to find pktinfo" << endl;
return (boost::shared_ptr<Pkt6>()); // NULL
return (Pkt6Ptr()); // NULL
}
} else {
cout << "Failed to receive data." << endl;
return (boost::shared_ptr<Pkt6>()); // NULL
return (Pkt6Ptr()); // NULL
}
......@@ -933,11 +914,11 @@ Pkt6Ptr IfaceMgr::receive6() {
pkt = Pkt6Ptr(new Pkt6(buf, result));
} catch (const std::exception& ex) {
cout << "Failed to create new packet." << endl;
return (boost::shared_ptr<Pkt6>()); // NULL
return (Pkt6Ptr()); // NULL
}
pkt->setLocalAddr(IOAddress::from_bytes(AF_INET6, (const uint8_t*)&to_addr));
pkt->setRemoteAddr(IOAddress::from_bytes(AF_INET6, (const uint8_t*)&from.sin6_addr));
pkt->setLocalAddr(IOAddress::from_bytes(AF_INET6, reinterpret_cast<const uint8_t*>(&to_addr)));
pkt->setRemoteAddr(IOAddress::from_bytes(AF_INET6, reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
pkt->setRemotePort(ntohs(from.sin6_port));
......@@ -962,7 +943,7 @@ Pkt6Ptr IfaceMgr::receive6() {
return (pkt);
}
uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
Iface* iface = getIface(pkt.getIface());
if (!iface) {