diff options
author | Tomek Mrugalski <tomasz@isc.org> | 2011-08-24 20:20:34 +0200 |
---|---|---|
committer | Tomek Mrugalski <tomasz@isc.org> | 2011-10-14 16:24:03 +0200 |
commit | af27ec87f09d82918b96c9dd6d236b4e39989f7f (patch) | |
tree | 85a424f8a7ba13254221437875003253e9390463 /src | |
parent | [1186] libdhcp now is able to parse and build packets and options. (diff) | |
download | kea-af27ec87f09d82918b96c9dd6d236b4e39989f7f.tar.xz kea-af27ec87f09d82918b96c9dd6d236b4e39989f7f.zip |
[878] Support for IA and IAADDR options and suboptions added.
- IA support (Option6IA class) added.
- Support for suboptions in IA option.
- IAADDR support (Option6IAAddr class) added.
- Initial tests added for new classes added.
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/dhcp/Makefile.am | 2 | ||||
-rw-r--r-- | src/lib/dhcp/libdhcp.cc | 112 | ||||
-rw-r--r-- | src/lib/dhcp/libdhcp.h | 20 | ||||
-rw-r--r-- | src/lib/dhcp/option.cc | 104 | ||||
-rw-r--r-- | src/lib/dhcp/option.h | 57 | ||||
-rw-r--r-- | src/lib/dhcp/option6_ia.cc | 129 | ||||
-rw-r--r-- | src/lib/dhcp/option6_ia.h | 75 | ||||
-rw-r--r-- | src/lib/dhcp/option6_iaaddr.cc | 129 | ||||
-rw-r--r-- | src/lib/dhcp/option6_iaaddr.h | 80 | ||||
-rw-r--r-- | src/lib/dhcp/pkt6.cc | 17 | ||||
-rw-r--r-- | src/lib/dhcp/tests/Makefile.am | 2 | ||||
-rw-r--r-- | src/lib/dhcp/tests/libdhcp_unittest.cc | 3 | ||||
-rw-r--r-- | src/lib/dhcp/tests/option6_ia_unittest.cc | 106 | ||||
-rw-r--r-- | src/lib/dhcp/tests/option6_iaaddr_unittest.cc | 96 | ||||
-rw-r--r-- | src/lib/dhcp/tests/option_unittest.cc | 4 | ||||
-rw-r--r-- | src/lib/dhcp/tests/pkt6_unittest.cc | 2 |
16 files changed, 812 insertions, 126 deletions
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index a0ddc56f13..973453cb39 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -11,6 +11,8 @@ lib_LTLIBRARIES = libdhcp.la libdhcp_la_SOURCES = libdhcp_la_SOURCES += libdhcp.cc libdhcp.h libdhcp_la_SOURCES += option.cc option.h +libdhcp_la_SOURCES += option6_ia.cc option6_ia.h +libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.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 d03fd3cf66..4e816ee1a7 100644 --- a/src/lib/dhcp/libdhcp.cc +++ b/src/lib/dhcp/libdhcp.cc @@ -16,10 +16,18 @@ #include <boost/shared_ptr.hpp> #include "dhcp/libdhcp.h" #include "config.h" +#include "dhcp6.h" + +#include "option.h" +#include "option6_ia.h" +#include "option6_iaaddr.h" using namespace std; using namespace isc::dhcp; +// static array with factory +std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_; + std::string LibDHCP::version() { return PACKAGE_VERSION; @@ -39,70 +47,102 @@ LibDHCP::version() { * @return offset to first byte after last parsed option */ unsigned int -LibDHCP::unpackOptions6(boost::shared_array<char>& buf, - int buf_len, - unsigned short offset, +LibDHCP::unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len, + unsigned int offset, unsigned int parse_len, isc::dhcp::Option::Option6Lst& options) { - int len = buf_len - offset; - while (len>4) { - int opt_type = buf[offset]*256 + buf[offset+1]; + if (offset + parse_len > buf_len) { + isc_throw(OutOfRange, "Option parse failed. Tried to parse " + << parse_len << " bytes at offset " << offset + << ": out of buffer"); + } + unsigned int end = offset + parse_len; + + while (offset<end) { + unsigned int opt_type = buf[offset]*256 + buf[offset+1]; offset += 2; - len -= 2; - int opt_len = buf[offset]*256 + buf[offset+1]; + unsigned 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; + if (offset + opt_len > end ) { + cout << "Option " << opt_type << " truncated." << endl; return (offset); } - - boost::shared_ptr<Option> opt(new Option(Option::V6, - opt_type, - buf, - offset, - opt_len)); + boost::shared_ptr<Option> opt; + switch (opt_type) { + case D6O_IA_NA: + case D6O_IA_PD: + // cout << "Creating Option6IA" << endl; + opt = boost::shared_ptr<Option>(new Option6IA(Option::V6, + opt_type, + buf, buf_len, + offset, + opt_len)); + break; + case D6O_IAADDR: + // cout << "Creating Option6IAAddr" << endl; + opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type, + buf, buf_len, + offset, opt_len)); + break; + default: + // cout << "Creating Option" << endl; + opt = boost::shared_ptr<Option>(new Option(Option::V6, + opt_type, + buf, + offset, + opt_len)); + break; + } // 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, +LibDHCP::packOptions6(boost::shared_array<char> data, + unsigned int data_len, + unsigned int 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" << + if (offset + opt_len > data_len) { + 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; + offset = (*it).second->pack(data, data_len, offset); } } catch (Exception e) { cout << "Packet build failed." << endl; return (-1); } - cout << "Packet built" << endl; return (offset); } + +bool +LibDHCP::OptionFactorRegister(Option::Universe u, + unsigned short opt_type, + Option::Factory * factory) { + switch (u) { + case Option::V6: { + if (v6factories_.find(opt_type)!=v6factories_.end()) { + isc_throw(BadValue, "There is already DHCPv6 factory registered " + << "for option type " << opt_type); + } + v6factories_[opt_type]=factory; + return true; + } + case Option::V4: + default:{ + isc_throw(BadValue, "This universe type is not supported yet."); + return false; // never happens + } + } + +} diff --git a/src/lib/dhcp/libdhcp.h b/src/lib/dhcp/libdhcp.h index 5ffa00fc9e..32a6ef1e1c 100644 --- a/src/lib/dhcp/libdhcp.h +++ b/src/lib/dhcp/libdhcp.h @@ -27,20 +27,24 @@ public: LibDHCP(); static std::string version(); - bool parsePkt6(Pkt6& pkt); + bool parsePkt6(Pkt6& pkt); bool builtPkt6(Pkt6& pkt); - static unsigned int - packOptions6(boost::shared_array<char>& buf, - int buf_len, - unsigned short offset, + packOptions6(boost::shared_array<char> buf, unsigned int buf_len, + unsigned int offset, isc::dhcp::Option::Option6Lst& options_); static unsigned int - unpackOptions6(boost::shared_array<char>& buf, - int buf_len, - unsigned short offset, + unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len, + unsigned int offset, unsigned int parse_len, isc::dhcp::Option::Option6Lst& options_); + + bool OptionFactorRegister(Option::Universe u, + unsigned short type, + Option::Factory * factory); +protected: + // pointers to factories that produce DHCPv6 options + static std::map<unsigned short, Option::Factory*> v6factories_; }; } diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc index 363c89c4bf..0391487368 100644 --- a/src/lib/dhcp/option.cc +++ b/src/lib/dhcp/option.cc @@ -32,9 +32,9 @@ Option::Option(Universe u, unsigned short type) } -Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf, +Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf, unsigned int offset, unsigned int len) - :universe_(u), type_(type), data_(buf), + :universe_(u), type_(type), data_(buf), offset_(offset), len_(len) { @@ -42,51 +42,60 @@ Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf, // TODO: universe must be in V4 and V6 } -char* Option::pack(char* buf, unsigned int len) { +unsigned int +Option::pack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset) { switch (universe_) { case V4: - return pack4(buf, len); + return pack4(buf, buf_len, offset); case V6: - return pack6(buf, len); + return pack6(buf, buf_len, offset); 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=" << + +unsigned int +Option::pack4(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset) { + if ( offset+len() > buf_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_); + char *ptr = &buf[offset]; + ptr[0] = type_; + ptr[1] = len_; + ptr += 2; + memcpy(ptr, &data_[0], len_); - return buf + len_; + return offset + len(); } -char* Option::pack6(char* buf, unsigned short len) { - if (this->len()>len) { - isc_throw(OutOfRange, "Failed to pack v6 option=" << +unsigned int +Option::pack6(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset) { + if ( offset+len() > buf_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_; + char * ptr = &buf[offset]; + *(uint16_t*)ptr = htons(type_); + ptr += 2; + *(uint16_t*)ptr = htons(len_); + ptr += 2; + memcpy(ptr, &data_[0], len_); + + return offset + len(); } -unsigned int -Option::unpack(boost::shared_array<char> buf, +unsigned int +Option::unpack(boost::shared_array<char> buf, unsigned int buf_len, - unsigned int offset, + unsigned int offset, unsigned int parse_len) { switch (universe_) { case V4: @@ -100,41 +109,41 @@ Option::unpack(boost::shared_array<char> buf, return 0; // should not happen } -unsigned int -Option::unpack4(boost::shared_array<char>, +unsigned int +Option::unpack4(boost::shared_array<char>, + unsigned int , 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 +Option::unpack6(boost::shared_array<char> buf, unsigned int buf_len, - unsigned int offset, + 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=" + 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, + return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len, optionLst_); } @@ -155,7 +164,7 @@ 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 && + if (universe_ != V4 && universe_ != V6) { return (false); } @@ -163,9 +172,9 @@ bool Option::valid() { return (true); } -/** +/** * Converts generic option to string. - * + * * @return string that represents option. */ std::string Option::toText() { @@ -176,12 +185,12 @@ std::string Option::toText() { if (i) { tmp << ":"; } - tmp << setfill('0') << setw(2) << hex << (unsigned short)data_[offset_+i]; + tmp << setfill('0') << setw(2) << hex << (unsigned short)(unsigned char)data_[offset_+i]; } return tmp.str(); } -unsigned short +unsigned short Option::getType() { return type_; } @@ -189,4 +198,3 @@ Option::getType() { Option::~Option() { } - diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 24ae83e29e..de1e46de0e 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -24,39 +24,46 @@ namespace dhcp { class Option { public: + enum Universe { V4, V6 }; typedef std::map<unsigned int, boost::shared_ptr<Option> > Option4Lst; typedef std::multimap<unsigned int, boost::shared_ptr<Option> > Option6Lst; - - enum Universe { V4, V6 }; + typedef boost::shared_ptr<Option> Factory(Option::Universe u, + unsigned short type, + boost::shared_array<char> buf, + unsigned int offset, + unsigned int len); // ctor, used for options constructed, usually during transmission - Option(Universe u, unsigned short type); + Option(Universe u, unsigned short type); // ctor, used for received options - // boost::shared_array allows sharing a buffer, but it requires that + // 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, + 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 + // writes option in wire-format to buf, returns pointer to first unused // byte after stored option - virtual char* pack(char* buf, unsigned int len); + virtual unsigned int + pack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset); - // parses received buffer, returns pointer to first unused byte + // 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, + virtual unsigned int + unpack(boost::shared_array<char> buf, unsigned int buf_len, - unsigned int offset, + unsigned int offset, unsigned int parse_len); virtual std::string toText(); @@ -65,23 +72,29 @@ public: // 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(); + virtual bool valid(); // just to force that every option has virtual dtor - virtual ~Option(); + 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, + virtual unsigned int + pack4(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset); + virtual unsigned int + pack6(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset); + virtual unsigned int unpack4(boost::shared_array<char> buf, unsigned int buf_len, - unsigned int offset, + unsigned int offset, unsigned int parse_len); - virtual unsigned int unpack6(boost::shared_array<char> buf, + virtual unsigned int unpack6(boost::shared_array<char> buf, unsigned int buf_len, - unsigned int offset, + unsigned int offset, unsigned int parse_len); Universe universe_; @@ -92,7 +105,7 @@ protected: 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 + unsigned int len_; // length of data only. Use len() if you want to know // proper length with option header overhead char * value_; diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc new file mode 100644 index 0000000000..c4edf3f63f --- /dev/null +++ b/src/lib/dhcp/option6_ia.cc @@ -0,0 +1,129 @@ +// 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 <stdint.h> +#include <arpa/inet.h> +#include <sstream> +#include "exceptions/exceptions.h" + +#include "libdhcp.h" +#include "option6_ia.h" +#include "dhcp6.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; + +Option6IA::Option6IA(Universe u, unsigned short type, unsigned int iaid) + :Option(u, type), iaid_(iaid) { + +} + + +Option6IA::Option6IA(Universe u, unsigned short type, + boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int option_len) + :Option(u, type) { + unpack(buf, buf_len, offset, option_len); +} + +unsigned int +Option6IA::pack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset) { + if (len() > buf_len) { + isc_throw(OutOfRange, "Failed to pack IA option: len=" << len() + << ", buffer=" << buf_len << ": too small buffer."); + } + + char* ptr = &buf[offset]; + *(uint16_t*)ptr = htons(type_); + ptr += 2; + buf_len -= 2; + *(uint16_t*)ptr = htons(len()); + ptr += 2; + buf_len -= 2; + + *(uint32_t*)ptr = htonl(iaid_); + ptr += 4; + buf_len -= 4; + + *(uint32_t*)ptr = htonl(t1_); + ptr += 4; + buf_len -= 4; + + *(uint32_t*)ptr = htonl(t2_); + ptr += 4; + buf_len -= 4; + + offset = LibDHCP::packOptions6(buf, buf_len, offset+16, optionLst_); + return offset; +} + +unsigned int +Option6IA::unpack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int parse_len) { + if (parse_len<12 || offset+12>buf_len) { + isc_throw(OutOfRange, "Option " << type_ << " truncated"); + } + iaid_ = ntohl(*(uint32_t*)&buf[offset]); + offset +=4; + t1_ = ntohl(*(uint32_t*)&buf[offset]); + offset +=4; + t2_ = ntohl(*(uint32_t*)&buf[offset]); + offset +=4; + offset = LibDHCP::unpackOptions6(buf, buf_len, offset, + parse_len - 12, optionLst_); + + return (offset); +} + +std::string Option6IA::toText() { + stringstream tmp; + switch (type_) { + case D6O_IA_NA: + tmp << "IA_NA"; + break; + case D6O_IA_PD: + tmp << "IA_PD"; + break; + } + tmp << " iaid=" << iaid_ << " t1=" << t1_ << " t2=" << t2_ + << " " << optionLst_.size() << " sub-options:" << endl; + + for (Option6Lst::const_iterator opt=optionLst_.begin(); + opt!=optionLst_.end(); + ++opt) { + tmp << " " << (*opt).second->toText(); + } + return tmp.str(); +} + +unsigned short Option6IA::len() { + + unsigned short length = 12; // header + + // length of all suboptions + for (Option::Option6Lst::iterator it = optionLst_.begin(); + it != optionLst_.end(); + ++it) { + length += (*it).second->len(); + } + return (length); +} + diff --git a/src/lib/dhcp/option6_ia.h b/src/lib/dhcp/option6_ia.h new file mode 100644 index 0000000000..20667e1b09 --- /dev/null +++ b/src/lib/dhcp/option6_ia.h @@ -0,0 +1,75 @@ +// 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_IA_H_ +#define OPTION_IA_H_ + +#include "option.h" + +namespace isc { +namespace dhcp { + +class Option6IA: public Option { + +public: + // ctor, used for options constructed, usually during transmission + Option6IA(Universe u, unsigned short type, unsigned int iaid); + + // 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 + Option6IA(Universe u, unsigned short type, boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int len); + + // writes option in wire-format to buf, returns pointer to first unused + // byte after stored option + unsigned int + pack(boost::shared_array<char> buf, unsigned int buf_len, + unsigned int offset); + + // 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(); + + void setT1(unsigned int t1) { t1_=t1; } + void setT2(unsigned int t2) { t2_=t2; } + + unsigned int getIAID() { return iaid_; } + unsigned int getT1() { return t1_; } + unsigned int getT2() { return t2_; } + + // returns data length (data length + DHCPv4/DHCPv6 option header) + virtual unsigned short len(); + +protected: + unsigned int iaid_; + unsigned int t1_; + unsigned int t2_; +}; + +} // isc::dhcp namespace +} // isc namespace + +#endif /* OPTION_IA_H_ */ diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc new file mode 100644 index 0000000000..47a1a64b93 --- /dev/null +++ b/src/lib/dhcp/option6_iaaddr.cc @@ -0,0 +1,129 @@ +// 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 <stdint.h> +#include <arpa/inet.h> +#include <sstream> +#include "exceptions/exceptions.h" + +#include "libdhcp.h" +#include "option6_iaaddr.h" +#include "dhcp6.h" +#include "io_address.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; + +Option6IAAddr::Option6IAAddr(unsigned short type, + isc::asiolink::IOAddress addr, + unsigned int pref, + unsigned int valid) + :Option(V6, type), addr_(addr), preferred_(pref), + valid_(valid) { +} + +Option6IAAddr::Option6IAAddr(unsigned short type, + boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int option_len) + :Option(V6, type), addr_(IOAddress("::")) { + unpack(buf, buf_len, offset, option_len); +} + +unsigned int +Option6IAAddr::pack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset) { + if (len() > buf_len) { + isc_throw(OutOfRange, "Failed to pack IA option: len=" << len() + << ", buffer=" << buf_len << ": too small buffer."); + } + + *(uint16_t*)&buf[offset] = htons(type_); + offset += 2; + *(uint16_t*)&buf[offset] = htons(len()); + offset += 2; + + memcpy(&buf[offset], addr_.getAddress().to_v6().to_bytes().data(), 16); + offset += 16; + + *(uint32_t*)&buf[offset] = htonl(preferred_); + offset += 4; + *(uint32_t*)&buf[offset] = htonl(valid_); + offset += 4; + + // parse suboption (there shouldn't be any) + offset = LibDHCP::packOptions6(buf, buf_len, offset, optionLst_); + return offset; +} + +unsigned int +Option6IAAddr::unpack(boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int parse_len) { + if (parse_len<24 || offset+24>buf_len) { + isc_throw(OutOfRange, "Option " << type_ << " truncated"); + } + + // 16 bytes: IPv6 address + /// TODO Implement fromBytes() method in IOAddress + char addr_str[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &buf[offset], addr_str,INET6_ADDRSTRLEN); + addr_ = IOAddress(string(addr_str)); + offset += 16; + + preferred_ = ntohl(*(uint32_t*)&buf[offset]); + offset +=4; + + valid_ = ntohl(*(uint32_t*)&buf[offset]); + offset +=4; + offset = LibDHCP::unpackOptions6(buf, buf_len, offset, + parse_len - 24, optionLst_); + + return offset; +} + +std::string Option6IAAddr::toText() { + stringstream tmp; + tmp << "addr: " << addr_.toText() << ", preferred-lft=" << preferred_ + << ", valid-lft=" << valid_ << endl; + + for (Option6Lst::const_iterator opt=optionLst_.begin(); + opt!=optionLst_.end(); + ++opt) { + tmp << " " << (*opt).second->toText() << endl; + } + return tmp.str(); +} + +unsigned short Option6IAAddr::len() { + + unsigned short length = 24; // header + + // length of all suboptions + // TODO implement: + // protected: unsigned short Option::lenHelper(int header_size); + + for (Option::Option6Lst::iterator it = optionLst_.begin(); + it != optionLst_.end(); + ++it) { + length += (*it).second->len(); + } + return (length); +} + diff --git a/src/lib/dhcp/option6_iaaddr.h b/src/lib/dhcp/option6_iaaddr.h new file mode 100644 index 0000000000..89412ab211 --- /dev/null +++ b/src/lib/dhcp/option6_iaaddr.h @@ -0,0 +1,80 @@ +// 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 OPTION6_IAADDR_H_ +#define OPTION6_IAADDR_H_ + +#include "io_address.h" +#include "option.h" + +namespace isc { +namespace dhcp { + +class Option6IAAddr: public Option { + +public: + // ctor, used for options constructed, usually during transmission + Option6IAAddr(unsigned short type, + isc::asiolink::IOAddress addr, + unsigned int prefered, + unsigned int valid); + + // 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 + Option6IAAddr(unsigned short type, boost::shared_array<char> buf, + unsigned int buf_len, + unsigned int offset, + unsigned int len); + + // writes option in wire-format to buf, returns pointer to first unused + // byte after stored option + unsigned int + pack(boost::shared_array<char> buf, unsigned int buf_len, + unsigned int offset); + + // 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(); + + void setAddress(isc::asiolink::IOAddress addr) { addr_ = addr; } + void setPreferred(unsigned int pref) { preferred_=pref; } + void setValid(unsigned int valid) { valid_=valid; } + + isc::asiolink::IOAddress getAddress() { return addr_; } + unsigned int getPreferred() { return preferred_; } + unsigned int getValid() { return valid_; } + + // returns data length (data length + DHCPv4/DHCPv6 option header) + virtual unsigned short len(); + +protected: + isc::asiolink::IOAddress addr_; + unsigned int preferred_; + unsigned int valid_; +}; + +} // isc::dhcp namespace +} // isc namespace + +#endif /* OPTION_IA_H_ */ diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc index ecafce756c..c6e890127e 100644 --- a/src/lib/dhcp/pkt6.cc +++ b/src/lib/dhcp/pkt6.cc @@ -66,7 +66,7 @@ unsigned short Pkt6::len() { length += (*it).second->len(); } - return length; + return (length); } @@ -87,7 +87,7 @@ Pkt6::pack() { default: isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)"); } - return false; // never happens + return (false); // never happens } @@ -123,10 +123,10 @@ Pkt6::packUDP() { } catch (Exception e) { cout << "Packet build failed." << endl; - return false; + return (false); } cout << "Packet built, len=" << len() << endl; - return true; + return (true); } @@ -160,7 +160,7 @@ Pkt6::unpack() { default: isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)"); } - return false; // never happens + return (false); // never happens } /** @@ -173,7 +173,7 @@ Pkt6::unpackUDP() { if (data_len_ < 4) { std::cout << "DHCPv6 packet truncated. Only " << data_len_ << " bytes. Need at least 4." << std::endl; - return false; + return (false); } msg_type_ = data_[0]; transid_ = (data_[1] << 16) + (data_[2] << 8) + data_[3]; @@ -181,6 +181,7 @@ Pkt6::unpackUDP() { unsigned int offset = LibDHCP::unpackOptions6(data_, data_len_, 4, //offset + data_len_ - 4, options_); if (offset != data_len_) { cout << "DHCPv6 packet contains trailing garbage. Parsed " @@ -188,7 +189,7 @@ Pkt6::unpackUDP() { << endl; // just a warning. Ignore trailing garbage and continue } - return true; + return (true); } /** @@ -249,7 +250,7 @@ Pkt6::getOption(unsigned short opt_type) { */ unsigned char Pkt6::getType() { - return msg_type_; + return (msg_type_); } Pkt6::~Pkt6() { diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 8de7f3c4aa..f2b88bf2f1 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -17,6 +17,8 @@ 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 += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc +libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc diff --git a/src/lib/dhcp/tests/libdhcp_unittest.cc b/src/lib/dhcp/tests/libdhcp_unittest.cc index a35f0e4879..116f91be25 100644 --- a/src/lib/dhcp/tests/libdhcp_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp_unittest.cc @@ -20,6 +20,7 @@ #include <gtest/gtest.h> #include "dhcp/libdhcp.h" +#include "config.h" using namespace std; using namespace isc; @@ -35,7 +36,7 @@ public: TEST_F(LibDhcpTest, basic) { // dummy test - EXPECT_EQ(LibDHCP::version(), "0"); + EXPECT_EQ(LibDHCP::version(), PACKAGE_VERSION); } } diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc new file mode 100644 index 0000000000..29dfcbf827 --- /dev/null +++ b/src/lib/dhcp/tests/option6_ia_unittest.cc @@ -0,0 +1,106 @@ +// 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" +#include "dhcp/option6_ia.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; + +namespace { +class Option6IATest : public ::testing::Test { +public: + Option6IATest() { + } +}; + +TEST_F(Option6IATest, 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; // iaid + simple_buf[1]=0xa2; + simple_buf[2]=0xa3; + simple_buf[3]=0xa4; + + simple_buf[4]=0x81; // T1 + simple_buf[5]=0x02; + simple_buf[6]=0x03; + simple_buf[7]=0x04; + + simple_buf[8]=0x84; // T2 + simple_buf[9]=0x03; + simple_buf[10]=0x02; + simple_buf[11]=0x01; + + // create an option (unpack content) + Option6IA* opt = new Option6IA(Option::V6, + D6O_IA_NA, + simple_buf, + 128, + 0, + 12); + + EXPECT_EQ(D6O_IA_NA, opt->getType()); + EXPECT_EQ(0xa1a2a3a4, opt->getIAID()); + EXPECT_EQ(0x81020304, opt->getT1()); + EXPECT_EQ(0x84030201, opt->getT2()); + + // pack this option again in the same buffer, but in + // different place + int offset = opt->pack(simple_buf, 128, 60); + + // 4 bytes header + 4 bytes content + EXPECT_EQ(12, opt->len()); + EXPECT_EQ(D6O_IA_NA, opt->getType()); + + EXPECT_EQ(offset, 76); // 60 + lenght(IA_NA) = 76 + + // check if pack worked properly: + // if option type is correct + EXPECT_EQ(D6O_IA_NA, simple_buf[60]*256 + simple_buf[61]); + + // if option length is correct + EXPECT_EQ(12, simple_buf[62]*256 + simple_buf[63]); + + // if iaid is correct + unsigned int iaid = htonl(*(unsigned int*)&simple_buf[64]); + EXPECT_EQ(0xa1a2a3a4, iaid ); + + // if T1 is correct + EXPECT_EQ(0x81020304, (simple_buf[68] << 24) + + (simple_buf[69] << 16) + + (simple_buf[70] << 8) + + (simple_buf[71]) ); + + // if T1 is correct + EXPECT_EQ(0x84030201, (simple_buf[72] << 24) + + (simple_buf[73] << 16) + + (simple_buf[74] << 8) + + (simple_buf[75]) ); + + delete opt; +} + +} diff --git a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc new file mode 100644 index 0000000000..eae8260f16 --- /dev/null +++ b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc @@ -0,0 +1,96 @@ +// 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" +#include "dhcp/option6_iaaddr.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; + +namespace { +class Option6IAAddrTest : public ::testing::Test { +public: + Option6IAAddrTest() { + } +}; + +TEST_F(Option6IAAddrTest, basic) { + + boost::shared_array<char> simple_buf(new char[128]); + for (int i=0; i<128; i++) + simple_buf[i] = 0; + simple_buf[0]=0x20; + simple_buf[1]=0x01; + simple_buf[2]=0x0d; + simple_buf[3]=0xb8; + simple_buf[4]=0x00; + simple_buf[5]=0x01; + simple_buf[12]=0xde; + simple_buf[13]=0xad; + simple_buf[14]=0xbe; + simple_buf[15]=0xef; // 2001:db8:1::dead:beef + + simple_buf[16]=0x00; + simple_buf[17]=0x00; + simple_buf[18]=0x03; + simple_buf[19]=0xe8; // 1000 + + simple_buf[20]=0xb2; + simple_buf[21]=0xd0; + simple_buf[22]=0x5e; + simple_buf[23]=0x00; // 3.000.000.000 + + // create an option (unpack content) + Option6IAAddr* opt = new Option6IAAddr(D6O_IAADDR, + simple_buf, + 128, + 0, + 24); + + // pack this option again in the same buffer, but in + // different place + int offset = opt->pack(simple_buf, 128, 50); + + EXPECT_EQ(78, offset); + + // 4 bytes header + 4 bytes content + EXPECT_EQ("2001:db8:1::dead:beef", opt->getAddress().toText()); + EXPECT_EQ(1000, opt->getPreferred()); + EXPECT_EQ(3000000000, opt->getValid()); + + EXPECT_EQ(D6O_IAADDR, opt->getType()); + + // check if pack worked properly: + // if option type is correct + EXPECT_EQ(D6O_IAADDR, simple_buf[50]*256 + simple_buf[51]); + + // if option length is correct + EXPECT_EQ(24, simple_buf[52]*256 + simple_buf[53]); + + // if option content is correct + EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[54],24)); + + delete opt; +} + +} diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc index 6087609611..cb9034cc1b 100644 --- a/src/lib/dhcp/tests/option_unittest.cc +++ b/src/lib/dhcp/tests/option_unittest.cc @@ -52,13 +52,13 @@ TEST_F(OptionTest, basic) { // pack this option again in the same buffer, but in // different place - char* offset18 = opt->pack(&simple_buf[10], 8); + int offset18 = opt->pack(simple_buf, 128, 10); // 4 bytes header + 4 bytes content EXPECT_EQ(8, opt->len()); EXPECT_EQ(D6O_CLIENTID, opt->getType()); - EXPECT_EQ(offset18, &simple_buf[0]+18); + EXPECT_EQ(offset18, 18); // check if pack worked properly: // if option type is correct diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc index c68c4143d5..0b3829faf4 100644 --- a/src/lib/dhcp/tests/pkt6_unittest.cc +++ b/src/lib/dhcp/tests/pkt6_unittest.cc @@ -56,7 +56,7 @@ Pkt6 *capture1() { 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_[1]=01; pkt->data_[2]=02; pkt->data_[3]=03; 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; |