BIND 10 trac1186, updated. 38ca18436e35147ca7839ee95a4a976b81d7a124 [1186] libdhcp now is able to parse and build packets and options.
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Aug 23 19:28:59 UTC 2011
The branch, trac1186 has been updated
via 38ca18436e35147ca7839ee95a4a976b81d7a124 (commit)
from 146afbdc9134164fa918b5e53aa261d0c0608b0c (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 38ca18436e35147ca7839ee95a4a976b81d7a124
Author: Tomek Mrugalski <tomasz at isc.org>
Date: Tue Aug 23 21:21:11 2011 +0200
[1186] libdhcp now is able to parse and build packets and options.
-----------------------------------------------------------------------
Summary of changes:
src/bin/dhcp6/tests/iface_mgr_unittest.cc | 55 ++++++
src/lib/dhcp/Makefile.am | 1 +
src/lib/dhcp/libdhcp.cc | 91 ++++++++++-
src/lib/dhcp/libdhcp.h | 17 ++-
src/lib/dhcp/option.cc | 192 +++++++++++++++++++++
src/lib/dhcp/option.h | 109 ++++++++++++
src/lib/dhcp/pkt6.cc | 235 ++++++++++++++++++++++++--
src/lib/dhcp/pkt6.h | 60 ++++++-
src/lib/dhcp/tests/Makefile.am | 1 +
src/lib/dhcp/tests/libdhcp_unittest.cc | 1 -
src/lib/dhcp/tests/option_unittest.cc | 81 +++++++++
src/lib/dhcp/tests/pkt6_unittest.cc | 71 ++++++++-
src/lib/{log => dhcp}/tests/run_unittests.cc | 0
13 files changed, 888 insertions(+), 26 deletions(-)
create mode 100644 src/lib/dhcp/option.cc
create mode 100644 src/lib/dhcp/option.h
create mode 100644 src/lib/dhcp/tests/option_unittest.cc
copy src/lib/{log => dhcp}/tests/run_unittests.cc (100%)
-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/tests/iface_mgr_unittest.cc b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
index e390d90..cfd3f2f 100644
--- a/src/bin/dhcp6/tests/iface_mgr_unittest.cc
+++ b/src/bin/dhcp6/tests/iface_mgr_unittest.cc
@@ -52,6 +52,61 @@ public:
}
};
+// uncomment this test to create packet writer. It will
+// write incoming DHCPv6 packets as C arrays. That is useful
+// for generating test sequences based on actual traffic
+//
+// TODO: this potentially should be moved to a separate tool
+//
+
+#if 0
+TEST_F(IfaceMgrTest, dhcp6Sniffer) {
+ // testing socket operation in a portable way is tricky
+ // without interface detection implemented
+
+ unlink("interfaces.txt");
+
+ ofstream interfaces("interfaces.txt", ios::ate);
+ interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
+ interfaces.close();
+
+ NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
+
+ Pkt6 * pkt = 0;
+ int cnt = 0;
+ cout << "---8X-----------------------------------------" << endl;
+ while (true) {
+ pkt = ifacemgr->receive();
+
+ cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
+ cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
+ cout << " Pkt6* pkt;" << endl;
+ cout << " pkt = new Pkt6(" << pkt->data_len_ << ");" << endl;
+ cout << " pkt->remote_port_ = " << pkt-> remote_port_ << ";" << endl;
+ cout << " pkt->remote_addr_ = IOAddress(\"" << pkt->remote_addr_.toText() << "\");" << endl;
+ cout << " pkt->local_port_ = " << pkt-> local_port_ << ";" << endl;
+ cout << " pkt->local_addr_ = IOAddress(\"" << pkt->local_addr_.toText() << "\");" << endl;
+ cout << " pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
+ cout << " pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
+ for (int i=0; i< pkt->data_len_; i++) {
+ cout << " pkt->data_[" << i << "]=" << (int)(unsigned char)pkt->data_[i] << "; ";
+ if (!(i%4))
+ cout << endl;
+ }
+ cout << endl;
+ cout << " return (pkt);" << endl;
+ cout << "}" << endl << endl;
+
+ delete pkt;
+ }
+ cout << "---8X-----------------------------------------" << endl;
+
+ // never happens. Infinite loop is infinite
+ delete pkt;
+ delete ifacemgr;
+}
+#endif
+
TEST_F(IfaceMgrTest, basic) {
// checks that IfaceManager can be instantiated
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index ba3241c..a0ddc56 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -10,6 +10,7 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libdhcp.la
libdhcp_la_SOURCES =
libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
+libdhcp_la_SOURCES += option.cc option.h
libdhcp_la_SOURCES += dhcp6.h
libdhcp_la_SOURCES += pkt6.cc pkt6.h
diff --git a/src/lib/dhcp/libdhcp.cc b/src/lib/dhcp/libdhcp.cc
index c7af264..d03fd3c 100644
--- a/src/lib/dhcp/libdhcp.cc
+++ b/src/lib/dhcp/libdhcp.cc
@@ -12,10 +12,97 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <boost/shared_array.hpp>
+#include <boost/shared_ptr.hpp>
#include "dhcp/libdhcp.h"
+#include "config.h"
+using namespace std;
using namespace isc::dhcp;
-std::string LibDHCP::version() {
- return "0";
+std::string
+LibDHCP::version() {
+ return PACKAGE_VERSION;
+}
+
+/**
+ * Parses provided buffer and creates Option objects.
+ *
+ * Parses provided buf array and stores created Option objects
+ * in options container.
+ *
+ * @param buf Buffer to be parsed.
+ * @param offset Specifies offset for the first option.
+ * @param options Reference to option container. Options will be
+ * put here.
+ *
+ * @return offset to first byte after last parsed option
+ */
+unsigned int
+LibDHCP::unpackOptions6(boost::shared_array<char>& buf,
+ int buf_len,
+ unsigned short offset,
+ isc::dhcp::Option::Option6Lst& options) {
+ int len = buf_len - offset;
+ while (len>4) {
+ int opt_type = buf[offset]*256 + buf[offset+1];
+ offset += 2;
+ len -= 2;
+ int opt_len = buf[offset]*256 + buf[offset+1];
+ offset += 2;
+ len -= 2;
+
+ if (opt_len > len) {
+ cout << "Packet truncated. Unable to parse option " << opt_type
+ << ". " << len << " bytes left in buffer, but option "
+ << "len=" << opt_len << endl;
+ return (offset);
+ }
+
+ boost::shared_ptr<Option> opt(new Option(Option::V6,
+ opt_type,
+ buf,
+ offset,
+ opt_len));
+ // add option to options
+ options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+ offset += opt_len;
+ len -= opt_len;
+ cout << "Parse opt=" << opt_type << ", opt_len=" << opt_len << ", bytes left=" << len << endl;
+ }
+
+ if (len != 0) {
+ cout << "There are " << len << " bytes left to parse." << endl;
+ }
+
+ return (offset);
+}
+
+unsigned int
+LibDHCP::packOptions6(boost::shared_array<char>& data,
+ int data_len,
+ unsigned short offset,
+ isc::dhcp::Option::Option6Lst& options) {
+ char* buf = &data[offset];
+ char* end = &data[data_len-1]; // last byte in shared array
+ try {
+ for (isc::dhcp::Option::Option6Lst::iterator it = options.begin();
+ it != options.end();
+ ++it) {
+ unsigned short opt_len = (*it).second->len();
+ if (buf+opt_len > end) {
+ isc_throw(OutOfRange, "Failed to build option" <<
+ (*it).first << ": out of buffer");
+ }
+ buf = (*it).second->pack(buf, opt_len);
+ offset += opt_len;
+ data_len -= opt_len;
+ }
+ }
+ catch (Exception e) {
+ cout << "Packet build failed." << endl;
+ return (-1);
+ }
+ cout << "Packet built" << endl;
+ return (offset);
}
diff --git a/src/lib/dhcp/libdhcp.h b/src/lib/dhcp/libdhcp.h
index 4b0b35f..5ffa00f 100644
--- a/src/lib/dhcp/libdhcp.h
+++ b/src/lib/dhcp/libdhcp.h
@@ -16,6 +16,7 @@
#define LIBDHCP_H_
#include <iostream>
+#include "dhcp/pkt6.h"
namespace isc {
namespace dhcp {
@@ -23,9 +24,23 @@ namespace dhcp {
class LibDHCP {
public:
+ LibDHCP();
static std::string version();
-
+ bool parsePkt6(Pkt6& pkt);
+ bool builtPkt6(Pkt6& pkt);
+
+
+ static unsigned int
+ packOptions6(boost::shared_array<char>& buf,
+ int buf_len,
+ unsigned short offset,
+ isc::dhcp::Option::Option6Lst& options_);
+ static unsigned int
+ unpackOptions6(boost::shared_array<char>& buf,
+ int buf_len,
+ unsigned short offset,
+ isc::dhcp::Option::Option6Lst& options_);
};
}
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
new file mode 100644
index 0000000..363c89c
--- /dev/null
+++ b/src/lib/dhcp/option.cc
@@ -0,0 +1,192 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <sstream>
+#include <iomanip>
+#include <boost/shared_array.hpp>
+#include "exceptions/exceptions.h"
+
+#include "option.h"
+#include "libdhcp.h"
+
+using namespace std;
+using namespace isc::dhcp;
+
+Option::Option(Universe u, unsigned short type)
+ :universe_(u), type_(type) {
+
+
+}
+
+Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
+ unsigned int offset, unsigned int len)
+ :universe_(u), type_(type), data_(buf),
+ offset_(offset),
+ len_(len) {
+
+ // sanity checks
+ // TODO: universe must be in V4 and V6
+}
+
+char* Option::pack(char* buf, unsigned int len) {
+ switch (universe_) {
+ case V4:
+ return pack4(buf, len);
+ case V6:
+ return pack6(buf, len);
+ default:
+ isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+ }
+
+ return NULL; // should not happen
+}
+
+char*
+Option::pack4(char* buf, unsigned short len) {
+ if (this->len()>len) {
+ isc_throw(OutOfRange, "Failed to pack v4 option=" <<
+ type_ << ",len=" << len_ << ": too small buffer.");
+ }
+ buf[0] = type_;
+ buf[1] = len_;
+ buf += 2;
+ memcpy(buf, &data_[0], len_);
+
+ return buf + len_;
+}
+
+char* Option::pack6(char* buf, unsigned short len) {
+ if (this->len()>len) {
+ isc_throw(OutOfRange, "Failed to pack v6 option=" <<
+ type_ << ",len=" << len_ << ": too small buffer.");
+ }
+ *(uint16_t*)buf = htons(type_);
+ buf += 2;
+ *(uint16_t*)buf = htons(len_);
+ buf += 2;
+ memcpy(buf, &data_[0], len_);
+
+ return buf + len_;
+}
+
+unsigned int
+Option::unpack(boost::shared_array<char> buf,
+ unsigned int buf_len,
+ unsigned int offset,
+ unsigned int parse_len) {
+ switch (universe_) {
+ case V4:
+ return unpack4(buf, buf_len, offset, parse_len);
+ case V6:
+ return unpack6(buf, buf_len, offset, parse_len);
+ default:
+ isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+ }
+
+ return 0; // should not happen
+}
+
+unsigned int
+Option::unpack4(boost::shared_array<char>,
+ unsigned int ,
+ unsigned int ,
+ unsigned int ) {
+ isc_throw(Unexpected, "IPv4 support not implemented yet.");
+ return 0;
+}
+/**
+ * Parses buffer and creates collection of Option objects.
+ *
+ * @param buf pointer to buffer
+ * @param buf_len length of buf
+ * @param offset offset, where start parsing option
+ * @param parse_len how many bytes should be parsed
+ *
+ * @return offset after last parsed option
+ */
+unsigned int
+Option::unpack6(boost::shared_array<char> buf,
+ unsigned int buf_len,
+ unsigned int offset,
+ unsigned int parse_len) {
+
+ if (buf_len < offset+parse_len) {
+ isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
+ << parse_len << " offset=" << offset << " from buffer (length="
+ << buf_len << "): too small buffer.");
+ }
+
+ data_ = buf;
+ offset_ = offset;
+ len_ = buf_len;
+
+ return LibDHCP::unpackOptions6(buf, buf_len, offset,
+ optionLst_);
+}
+
+unsigned short Option::len() {
+ switch (universe_) {
+ case V4:
+ return len_ + 2; // DHCPv4 option header length: 2 bytes
+ case V6:
+ return len_ + 4; // DHCPv6 option header length: 4 bytes
+ default:
+ isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+ }
+
+ return 0; // should not happen
+}
+
+bool Option::valid() {
+ // total length of buffer is not stored. shared_array is not very useful.
+ // we should either add buf_len field or better replace shared_array
+ // with shared_ptr to array
+ if (universe_ != V4 &&
+ universe_ != V6) {
+ return (false);
+ }
+
+ return (true);
+}
+
+/**
+ * Converts generic option to string.
+ *
+ * @return string that represents option.
+ */
+std::string Option::toText() {
+ std::stringstream tmp;
+ tmp << type_ << "(len=" << len_ << "):";
+
+ for (unsigned int i=0; i<len_; i++) {
+ if (i) {
+ tmp << ":";
+ }
+ tmp << setfill('0') << setw(2) << hex << (unsigned short)data_[offset_+i];
+ }
+ return tmp.str();
+}
+
+unsigned short
+Option::getType() {
+ return type_;
+}
+
+Option::~Option() {
+
+}
+
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
new file mode 100644
index 0000000..24ae83e
--- /dev/null
+++ b/src/lib/dhcp/option.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef OPTION_H_
+#define OPTION_H_
+
+#include <string>
+#include <map>
+#include <boost/shared_array.hpp>
+
+namespace isc {
+namespace dhcp {
+
+class Option {
+public:
+ typedef std::map<unsigned int, boost::shared_ptr<Option> > Option4Lst;
+ typedef std::multimap<unsigned int, boost::shared_ptr<Option> > Option6Lst;
+
+ enum Universe { V4, V6 };
+
+ // ctor, used for options constructed, usually during transmission
+ Option(Universe u, unsigned short type);
+
+ // ctor, used for received options
+ // boost::shared_array allows sharing a buffer, but it requires that
+ // different instances share pointer to the whole array, not point
+ // to different elements in shared array. Therefore we need to share
+ // pointer to the whole array and remember offset where data for
+ // this option begins
+ Option(Universe u, unsigned short type, boost::shared_array<char> buf,
+ unsigned int offset,
+ unsigned int len);
+
+ // writes option in wire-format to buf, returns pointer to first unused
+ // byte after stored option
+ virtual char* pack(char* buf, unsigned int len);
+
+ // parses received buffer, returns pointer to first unused byte
+ // after parsed option
+ // TODO: Do we need this overload? Commented out for now
+ // virtual const char* unpack(const char* buf, unsigned int len);
+
+ // parses received buffer, returns offset to the first unused byte after
+ // parsed option
+ virtual unsigned int
+ unpack(boost::shared_array<char> buf,
+ unsigned int buf_len,
+ unsigned int offset,
+ unsigned int parse_len);
+
+ virtual std::string toText();
+
+ unsigned short getType();
+
+ // returns data length (data length + DHCPv4/DHCPv6 option header)
+ virtual unsigned short len();
+
+ // returns if option is valid (e.g. option may be truncated)
+ virtual bool valid();
+
+ // just to force that every option has virtual dtor
+ virtual ~Option();
+
+protected:
+ virtual char* pack4(char* buf, unsigned short len);
+ virtual char* pack6(char* buf, unsigned short len);
+ virtual unsigned int unpack4(boost::shared_array<char> buf,
+ unsigned int buf_len,
+ unsigned int offset,
+ unsigned int parse_len);
+ virtual unsigned int unpack6(boost::shared_array<char> buf,
+ unsigned int buf_len,
+ unsigned int offset,
+ unsigned int parse_len);
+
+ Universe universe_;
+ unsigned short type_;
+
+ boost::shared_array<char> data_;
+ unsigned int data_len_;
+ unsigned int offset_; // data is a shared_pointer that points out to the
+ // whole packet. offset_ specifies where data for
+ // this option begins.
+ unsigned int len_; // length of data only. Use len() if you want to know
+ // proper length with option header overhead
+ char * value_;
+
+ // 2 different containers are used, because v4 options are unique
+ // and v6 allows multiple instances of the same option types
+ // originally 2 separate containers were planned. Let's try if we
+ // can use a single apporach
+ Option6Lst optionLst_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 5f1fa85..ecafce7 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -15,30 +15,243 @@
#include "dhcp/dhcp6.h"
#include "dhcp/pkt6.h"
+#include "dhcp/libdhcp.h"
+#include "exceptions/exceptions.h"
#include <iostream>
+#include <sstream>
+
+using namespace std;
+using namespace isc::dhcp;
namespace isc {
-///
-/// constructor
-///
-/// \param dataLen - length of the data to be allocated
-///
-Pkt6::Pkt6(int dataLen)
+/**
+ * Constructor.
+ *
+ * @param dataLen size of buffer to be allocated for this packet.
+ * @param proto protocol (usually UDP, but TCP will be supported eventually)
+ */
+Pkt6::Pkt6(unsigned int dataLen, DHCPv6Proto_ proto /* = UDP */)
:local_addr_("::"),
- remote_addr_("::") {
+ remote_addr_("::"),
+ proto_(proto)
+{
try {
- data_ = boost::shared_array<char>(new char[dataLen]);
- data_len_ = dataLen;
+ data_ = boost::shared_array<char>(new char[dataLen]);
+ data_len_ = dataLen;
} catch (const std::exception& ex) {
- // TODO move to LOG_FATAL()
- // let's continue with empty pkt for now
+ // TODO move to LOG_FATAL()
+ // let's continue with empty pkt for now
std::cout << "Failed to allocate " << dataLen << " bytes."
<< std::endl;
data_len_ = 0;
}
}
+
+/**
+ * Returns calculated length of the packet.
+ *
+ * This function returns size of required buffer to buld this packet.
+ * To use that function, options_ field must be set.
+ *
+ * @return number of bytes required to build this packet
+ */
+unsigned short Pkt6::len() {
+ unsigned int length = 4; // DHCPv6 header
+
+ for (Option::Option6Lst::iterator it = options_.begin();
+ it != options_.end();
+ ++it) {
+ length += (*it).second->len();
+ }
+
+ return length;
+}
+
+
+/**
+ * Builds on wire packet.
+ *
+ * Prepares on wire packet format.
+ *
+ * @return true if preparation was successful
+ */
+bool
+Pkt6::pack() {
+ switch (proto_) {
+ case UDP:
+ return packUDP();
+ case TCP:
+ return packTCP();
+ default:
+ isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
+ }
+ return false; // never happens
+}
+
+
+/**
+ * Build on wire packet (in UDP format).
+ *
+ * @return true if packet build was successful, false otherwise
+ */
+bool
+Pkt6::packUDP() {
+ unsigned short length = len();
+ if (data_len_ < length) {
+ // we have too small buffer, let's allocate bigger one
+ data_ = boost::shared_array<char>(new char[length]);
+ data_len_ = length;
+ }
+
+ // DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
+ data_[0] = msg_type_;
+ data_[1] = (transid_ >> 16) & 0xff;
+ data_[2] = (transid_ >> 8) & 0xff;
+ data_[3] = (transid_) & 0xff;
+
+ try {
+ // the rest are options
+ unsigned short offset = LibDHCP::packOptions6(data_, length, 4/*offset*/, options_);
+
+ // sanity check
+ if (offset != length) {
+ isc_throw(OutOfRange, "Packet build failed: expected size=" << length
+ << ", actual len=" << offset);
+ }
+ }
+ catch (Exception e) {
+ cout << "Packet build failed." << endl;
+ return false;
+ }
+ cout << "Packet built, len=" << len() << endl;
+ return true;
+}
+
+
+/**
+ * Builds on wire packet for TCP transmission.
+ *
+ * @note This function is not implemented yet.
+ *
+ * @return
+ */
+bool
+Pkt6::packTCP() {
+ /// TODO Implement this function.
+ isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) not implemented yet.");
+}
+
+/**
+ * Dispatch method that handles binary packet parsing.
+ *
+ * This method calls appropriate dispatch function (unpackUDP or unpackTCP)
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpack() {
+ switch (proto_) {
+ case UDP:
+ return unpackUDP();
+ case TCP:
+ return unpackTCP();
+ default:
+ isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
+ }
+ return false; // never happens
+}
+
+/**
+ * This method unpacks UDP packet.
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpackUDP() {
+ if (data_len_ < 4) {
+ std::cout << "DHCPv6 packet truncated. Only " << data_len_
+ << " bytes. Need at least 4." << std::endl;
+ return false;
+ }
+ msg_type_ = data_[0];
+ transid_ = (data_[1] << 16) + (data_[2] << 8) + data_[3];
+
+ unsigned int offset = LibDHCP::unpackOptions6(data_,
+ data_len_,
+ 4, //offset
+ options_);
+ if (offset != data_len_) {
+ cout << "DHCPv6 packet contains trailing garbage. Parsed "
+ << offset << " bytes, packet is " << data_len_ << " bytes."
+ << endl;
+ // just a warning. Ignore trailing garbage and continue
+ }
+ return true;
+}
+
+/**
+ * This method unpacks TDP packet.
+ *
+ * @return true, if parsing was successful, false otherwise
+ */
+bool
+Pkt6::unpackTCP() {
+ isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) not implemented yet.");
+}
+
+
+/**
+ * Returns text representation of the packet.
+ *
+ * This function is useful mainly for debugging.
+ *
+ * @return string with text representation
+ */
+std::string
+Pkt6::toText() {
+ stringstream tmp;
+ tmp << "msgtype=" << msg_type_ << ", transid=0x" << hex << transid_
+ << dec << endl;
+ for (isc::dhcp::Option::Option6Lst::iterator opt=options_.begin();
+ opt != options_.end();
+ ++opt) {
+ tmp << opt->second->toText() << std::endl;
+ }
+ return tmp.str();
+}
+
+/**
+ * Returns the first option of specified type.
+ *
+ * Returns the first option of specified type. Note that in DHCPv6 several
+ * instances of the same option are allowed (and frequently used).
+ * See getOptions().
+ *
+ * @param opt_type option type we are looking for
+ *
+ * @return pointer to found option (or NULL)
+ */
+boost::shared_ptr<isc::dhcp::Option>
+Pkt6::getOption(unsigned short opt_type) {
+ isc::dhcp::Option::Option6Lst::const_iterator x = options_.find(opt_type);
+ if (x!=options_.end()) {
+ return (*x).second;
+ }
+ return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+}
+
+/**
+ * Returns message type.
+ *
+ * @return message type.
+ */
+unsigned char
+Pkt6::getType() {
+ return msg_type_;
+}
+
Pkt6::~Pkt6() {
// no need to delete anything shared_ptr will take care of data_
}
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 9a14d92..6548f9e 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -16,29 +16,58 @@
#define PKT6_H
#include <iostream>
+#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include "io_address.h"
+#include "option.h"
namespace isc {
+
class Pkt6 {
public:
- Pkt6(int len);
+ enum DHCPv6Proto_ {
+ UDP = 0, // most packets are UDP
+ TCP = 1 // there are TCP DHCPv6 packet (bulk leasequery, failover)
+ };
+
+ Pkt6(unsigned char msg_type,
+ unsigned int transid,
+ DHCPv6Proto_ proto = UDP);
+ Pkt6(unsigned int len, DHCPv6Proto_ proto = UDP);
~Pkt6();
- // XXX: probably need getter/setter wrappers
- // and hide fields as protected
- // buffer that holds memory. It is shared_array as options may
- // share pointer to this buffer
+ bool pack();
+ bool unpack();
+
+ DHCPv6Proto_ getProto();
+ void setProto(DHCPv6Proto_ proto = Pkt6::UDP);
+
+ // returns text representation, useful for debugging
+ std::string toText();
+
+ unsigned short len();
+
+ unsigned char getType();
+ unsigned int getTransid();
+
+ boost::shared_ptr<isc::dhcp::Option> getOption(unsigned short opt_type);
+
+ /// TODO Need to implement getOptions() as well
+
+ /// TODO need getter/setter wrappers
+ /// and hide following fields as protected
+ /// buffer that holds memory. It is shared_array as options may
+ /// share pointer to this buffer
boost::shared_array<char> data_;
// length of the data
- int data_len_;
+ unsigned int data_len_;
- // local address (destination if receiving packet, source if sending packet)
+ // local address (dst if receiving packet, src if sending packet)
isc::asiolink::IOAddress local_addr_;
- // remote address (source if receiving packet, destination if sending packet)
+ // remote address (src if receiving packet, dst if sending packet)
isc::asiolink::IOAddress remote_addr_;
// name of the network interface the packet was received/to be sent over
@@ -46,7 +75,8 @@ namespace isc {
// interface index (each network interface has assigned unique ifindex
// it is functional equvalent of name, but sometimes more useful, e.g.
- // when using crazy systems that allow spaces in interface names (Windows)
+ // when using crazy systems that allow spaces in interface names
+ // e.g. windows
int ifindex_;
// local TDP or UDP port
@@ -56,6 +86,18 @@ namespace isc {
int remote_port_;
// XXX: add *a lot* here
+ isc::dhcp::Option::Option6Lst options_;
+
+ protected:
+ bool packTCP();
+ bool packUDP();
+ bool unpackTCP();
+ bool unpackUDP();
+
+ DHCPv6Proto_ proto_; // UDP (most) or TCP (bulk leasequery or failover)
+ int msg_type_; // DHCPv6 message type
+ int transid_; // DHCPv6 transaction-id
+
};
}
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 65a6694..8de7f3c 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -17,6 +17,7 @@ if HAVE_GTEST
TESTS += libdhcp_unittests
libdhcp_unittests_SOURCES = run_unittests.cc
libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc
+libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp/tests/libdhcp_unittest.cc b/src/lib/dhcp/tests/libdhcp_unittest.cc
index 9a2151c..a35f0e4 100644
--- a/src/lib/dhcp/tests/libdhcp_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp_unittest.cc
@@ -35,7 +35,6 @@ public:
TEST_F(LibDhcpTest, basic) {
// dummy test
- // an attempt to bind this socket will fail.
EXPECT_EQ(LibDHCP::version(), "0");
}
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
new file mode 100644
index 0000000..6087609
--- /dev/null
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include "dhcp/dhcp6.h"
+#include "dhcp/option.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class OptionTest : public ::testing::Test {
+public:
+ OptionTest() {
+ }
+};
+
+TEST_F(OptionTest, basic) {
+
+ boost::shared_array<char> simple_buf(new char[128]);
+ for (int i=0; i<128; i++)
+ simple_buf[i] = 0;
+ simple_buf[0]=0xa1;
+ simple_buf[1]=0xa2;
+ simple_buf[2]=0xa3;
+ simple_buf[3]=0xa4;
+
+ // create an option (unpack content)
+ Option* opt = new Option(Option::V6,
+ D6O_CLIENTID,
+ simple_buf,
+ 0,
+ 4);
+
+ // pack this option again in the same buffer, but in
+ // different place
+ char* offset18 = opt->pack(&simple_buf[10], 8);
+
+ // 4 bytes header + 4 bytes content
+ EXPECT_EQ(8, opt->len());
+ EXPECT_EQ(D6O_CLIENTID, opt->getType());
+
+ EXPECT_EQ(offset18, &simple_buf[0]+18);
+
+ // check if pack worked properly:
+ // if option type is correct
+ EXPECT_EQ(D6O_CLIENTID, simple_buf[10]*256 + simple_buf[11]);
+
+ // if option length is correct
+ EXPECT_EQ(4, simple_buf[12]*256 + simple_buf[13]);
+
+ // if option content is correct
+ EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[14],4));
+
+ for (int i=0; i<20; i++) {
+ std::cout << i << ":" << (unsigned short) (unsigned char)simple_buf[i] << " ";
+ }
+ std::cout << std::endl;
+
+ delete opt;
+}
+
+}
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 9bf31f7..c68c414 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -18,12 +18,14 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-
+#include "io_address.h"
#include "dhcp/pkt6.h"
+#include "dhcp/dhcp6.h"
using namespace std;
using namespace isc;
+using namespace isc::asiolink;
namespace {
// empty class for now, but may be extended once Addr6 becomes bigger
@@ -35,10 +37,75 @@ public:
TEST_F(Pkt6Test, constructor) {
Pkt6 * pkt1 = new Pkt6(17);
-
+
ASSERT_EQ(pkt1->data_len_, 17);
delete pkt1;
}
+// captured actual SOLICIT packet: transid=0x3d79fb
+// options: client-id, in_na, dns-server, elapsed-time, option-request
+// this code is autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c)
+Pkt6 *capture1() {
+ Pkt6* pkt;
+ pkt = new Pkt6(98);
+ pkt->remote_port_ = 546;
+ pkt->remote_addr_ = IOAddress("fe80::21e:8cff:fe9b:7349");
+ pkt->local_port_ = 0;
+ pkt->local_addr_ = IOAddress("ff02::1:2");
+ pkt->ifindex_ = 2;
+ pkt->iface_ = "eth0";
+ pkt->data_[0]=1;
+ pkt->data_[1]=192; pkt->data_[2]=129; pkt->data_[3]=6; pkt->data_[4]=0;
+ pkt->data_[5]=1; pkt->data_[6]=0; pkt->data_[7]=14; pkt->data_[8]=0;
+ pkt->data_[9]=1; pkt->data_[10]=0; pkt->data_[11]=1; pkt->data_[12]=21;
+ pkt->data_[13]=158; pkt->data_[14]=60; pkt->data_[15]=22; pkt->data_[16]=0;
+ pkt->data_[17]=30; pkt->data_[18]=140; pkt->data_[19]=155; pkt->data_[20]=115;
+ pkt->data_[21]=73; pkt->data_[22]=0; pkt->data_[23]=3; pkt->data_[24]=0;
+ pkt->data_[25]=40; pkt->data_[26]=0; pkt->data_[27]=0; pkt->data_[28]=0;
+ pkt->data_[29]=1; pkt->data_[30]=255; pkt->data_[31]=255; pkt->data_[32]=255;
+ pkt->data_[33]=255; pkt->data_[34]=255; pkt->data_[35]=255; pkt->data_[36]=255;
+ pkt->data_[37]=255; pkt->data_[38]=0; pkt->data_[39]=5; pkt->data_[40]=0;
+ pkt->data_[41]=24; pkt->data_[42]=32; pkt->data_[43]=1; pkt->data_[44]=13;
+ pkt->data_[45]=184; pkt->data_[46]=0; pkt->data_[47]=1; pkt->data_[48]=0;
+ pkt->data_[49]=0; pkt->data_[50]=0; pkt->data_[51]=0; pkt->data_[52]=0;
+ pkt->data_[53]=0; pkt->data_[54]=0; pkt->data_[55]=0; pkt->data_[56]=18;
+ pkt->data_[57]=52; pkt->data_[58]=255; pkt->data_[59]=255; pkt->data_[60]=255;
+ pkt->data_[61]=255; pkt->data_[62]=255; pkt->data_[63]=255; pkt->data_[64]=255;
+ pkt->data_[65]=255; pkt->data_[66]=0; pkt->data_[67]=23; pkt->data_[68]=0;
+ pkt->data_[69]=16; pkt->data_[70]=32; pkt->data_[71]=1; pkt->data_[72]=13;
+ pkt->data_[73]=184; pkt->data_[74]=0; pkt->data_[75]=1; pkt->data_[76]=0;
+ pkt->data_[77]=0; pkt->data_[78]=0; pkt->data_[79]=0; pkt->data_[80]=0;
+ pkt->data_[81]=0; pkt->data_[82]=0; pkt->data_[83]=0; pkt->data_[84]=221;
+ pkt->data_[85]=221; pkt->data_[86]=0; pkt->data_[87]=8; pkt->data_[88]=0;
+ pkt->data_[89]=2; pkt->data_[90]=0; pkt->data_[91]=100; pkt->data_[92]=0;
+ pkt->data_[93]=6; pkt->data_[94]=0; pkt->data_[95]=2; pkt->data_[96]=0;
+ pkt->data_[97]=23;
+ return (pkt);
+}
+
+TEST_F(Pkt6Test, parse_solicit1) {
+ Pkt6 * sol = capture1();
+
+ ASSERT_EQ(true, sol->unpack());
+
+ boost::shared_ptr<isc::dhcp::Option> null;
+
+ // check that all present options are returned
+ EXPECT_NE(null, sol->getOption(D6O_CLIENTID)); // client-id is present
+ EXPECT_NE(null, sol->getOption(D6O_IA_NA)); // IA_NA is present
+ EXPECT_NE(null, sol->getOption(D6O_ELAPSED_TIME)); // elapsed is present
+ EXPECT_NE(null, sol->getOption(D6O_NAME_SERVERS));
+ EXPECT_NE(null, sol->getOption(D6O_ORO));
+
+ // let's check that non-present options are not returned
+ EXPECT_EQ(null, sol->getOption(D6O_SERVERID)); // server-id is missing
+ EXPECT_EQ(null, sol->getOption(D6O_IA_TA));
+ EXPECT_EQ(null, sol->getOption(D6O_IAADDR));
+
+ std::cout << sol->toText();
+
+ delete sol;
+}
+
}
diff --git a/src/lib/dhcp/tests/run_unittests.cc b/src/lib/dhcp/tests/run_unittests.cc
new file mode 100644
index 0000000..8a9d1e5
--- /dev/null
+++ b/src/lib/dhcp/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
+}
More information about the bind10-changes
mailing list