Commit 2f39435c authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[1186] Added suboptions support in Option class, tests implemented.

parent 22767526
......@@ -27,7 +27,7 @@ using namespace std;
using namespace isc::dhcp;
Option::Option(Universe u, unsigned short type)
:universe_(u), type_(type) {
:universe_(u), type_(type), data_len_(0) {
}
......@@ -82,14 +82,20 @@ Option::pack6(boost::shared_array<char> buf,
isc_throw(OutOfRange, "Failed to pack v6 option=" <<
type_ << ",len=" << len() << ": too small buffer.");
}
int length = len() - getHeaderLen();
char * ptr = &buf[offset];
*(uint16_t*)ptr = htons(type_);
ptr += 2;
*(uint16_t*)ptr = htons(data_len_);
*(uint16_t*)ptr = htons(length);
ptr += 2;
memcpy(ptr, &data_[offset_], data_len_);
if (data_len_)
memcpy(ptr, &data_[offset_], data_len_);
return offset + len();
offset += 4 + data_len_; // end of this option
return LibDHCP::packOptions6(buf, buf_len, offset, optionLst_);
}
unsigned int
......@@ -126,8 +132,9 @@ Option::unpack6(boost::shared_array<char> buf,
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.");
<< parse_len << " offset=" << offset
<< " from buffer (length=" << buf_len
<< "): too small buffer.");
}
data_ = buf;
......@@ -139,16 +146,15 @@ Option::unpack6(boost::shared_array<char> buf,
}
unsigned short Option::len() {
switch (universe_) {
case V4:
return data_len_ + 2; // DHCPv4 option header length: 2 bytes
case V6:
return data_len_ + 4; // DHCPv6 option header length: 4 bytes
default:
isc_throw(BadValue, "Unknown universe defined for Option " << type_);
int length = getHeaderLen() + data_len_;
for (Option::Option6Lst::iterator it = optionLst_.begin();
it != optionLst_.end();
++it) {
length += (*it).second->len();
}
return 0; // should not happen
return (length);
}
bool Option::valid() {
......@@ -165,19 +171,25 @@ bool Option::valid() {
void
isc::dhcp::Option::addOption(boost::shared_ptr<isc::dhcp::Option> opt) {
optionLst_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
optionLst_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(),
opt));
}
std::string Option::toText() {
std::string Option::toText(int indent /* =0 */ ) {
std::stringstream tmp;
tmp << type_ << "(len=" << data_len_ << "):";
for (int i=0; i<indent; i++)
tmp << " ";
tmp << "type=" << type_ << ", len=" << data_len_ << ":";
for (unsigned int i=0; i<data_len_; i++) {
if (i) {
tmp << ":";
}
tmp << setfill('0') << setw(2) << hex << (unsigned short)(unsigned char)data_[offset_+i];
tmp << setfill('0') << setw(2) << hex
<< (unsigned short)(unsigned char)data_[offset_+i];
}
return tmp.str();
}
......@@ -196,6 +208,17 @@ Option::getData() {
}
}
unsigned short
Option::getHeaderLen() {
switch (universe_) {
case V4:
return 2; // header length for v4
case V6:
return 4; // header length for v6
}
return 0; // should not happen
}
Option::~Option() {
......
......@@ -80,10 +80,12 @@ public:
///
/// Returns string representation of the option.
///
/// @param indent number of spaces before printing text
///
/// @return string with text representation.
///
virtual std::string
toText();
toText(int indent = 0);
///
/// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
......@@ -101,11 +103,21 @@ public:
virtual unsigned short
len();
// returns if option is valid (e.g. option may be truncated)
/// @brief Returns length of header (2 for v4, 4 for v6)
///
/// @return length of option header
///
virtual unsigned short
getHeaderLen();
/// returns if option is valid (e.g. option may be truncated)
virtual bool
valid();
// returns pointer to actual data
/// Returns pointer to actual data.
///
/// @return pointer to actual data (or NULL if there is no data)
///
virtual char*
getData();
......
......@@ -89,8 +89,12 @@ Option6IA::unpack(boost::shared_array<char> buf,
return (offset);
}
std::string Option6IA::toText() {
std::string Option6IA::toText(int indent /* = 0*/) {
stringstream tmp;
for (int i=0; i<indent; i++)
tmp << " ";
switch (type_) {
case D6O_IA_NA:
tmp << "IA_NA";
......
......@@ -51,7 +51,7 @@ public:
unsigned int offset,
unsigned int parse_len);
virtual std::string toText();
virtual std::string toText(int indent = 0);
void setT1(unsigned int t1) { t1_=t1; }
void setT2(unsigned int t2) { t2_=t2; }
......
......@@ -99,8 +99,11 @@ Option6IAAddr::unpack(boost::shared_array<char> buf,
return offset;
}
std::string Option6IAAddr::toText() {
std::string Option6IAAddr::toText(int indent /* =0 */) {
stringstream tmp;
for (int i=0; i<indent; i++)
tmp << " ";
tmp << "addr: " << addr_.toText() << ", preferred-lft=" << preferred_
<< ", valid-lft=" << valid_ << endl;
......
......@@ -55,7 +55,7 @@ public:
unsigned int offset,
unsigned int parse_len);
virtual std::string toText();
virtual std::string toText(int indent = 0);
void setAddress(isc::asiolink::IOAddress addr) { addr_ = addr; }
void setPreferred(unsigned int pref) { preferred_=pref; }
......
......@@ -87,8 +87,8 @@ TEST_F(LibDhcpTest, unpackOptions6) {
1, 1, 0, 1, 114 // opt5 (5 bytes)
};
// Option is used as a simple option implementation
// More advanced classes are used in tests dedicated for
// specific options.
// More advanced uses are validated in tests dedicated for
// specific derived classes.
isc::dhcp::Option::Option6Lst options; // list of options
......@@ -109,36 +109,36 @@ TEST_F(LibDhcpTest, unpackOptions6) {
ASSERT_NE(x, options.end()); // option 1 should exist
EXPECT_EQ(12, x->second->getType()); // this should be option 12
ASSERT_EQ(9, x->second->len()); // it should be of length 9
EXPECT_EQ(0, memcmp(x->second->getData(), packed+4, 5)); // data len = 5
EXPECT_EQ(0, memcmp(x->second->getData(), packed+4, 5)); // data len=5
x = options.find(13);
ASSERT_NE(x, options.end()); // option 13 should exist
EXPECT_EQ(13, x->second->getType()); // this should be option 13
ASSERT_EQ(7, x->second->len()); // it should be of length 7
EXPECT_EQ(0, memcmp(x->second->getData(), packed+13, 3)); // data length = 3
EXPECT_EQ(0, memcmp(x->second->getData(), packed+13, 3)); // data len=3
x = options.find(14);
ASSERT_NE(x, options.end()); // option 3 should exist
EXPECT_EQ(14, x->second->getType()); // this should be option 14
ASSERT_EQ(6, x->second->len()); // it should be of length 6
EXPECT_EQ(0, memcmp(x->second->getData(), packed+20, 2)); // data length = 2
EXPECT_EQ(0, memcmp(x->second->getData(), packed+20, 2)); // data len=2
x = options.find(256);
ASSERT_NE(x, options.end()); // option 256 should exist
EXPECT_EQ(256, x->second->getType()); // this should be option 256
ASSERT_EQ(8, x->second->len()); // it should be of length 7
EXPECT_EQ(0, memcmp(x->second->getData(), packed+26, 4)); // data length = 4
EXPECT_EQ(0, memcmp(x->second->getData(), packed+26, 4)); // data len=4
x = options.find(257);
ASSERT_NE(x, options.end()); // option 257 should exist
EXPECT_EQ(257, x->second->getType()); // this should be option 257
ASSERT_EQ(5, x->second->len()); // it should be of length 5
EXPECT_EQ(0, memcmp(x->second->getData(), packed+34, 1)); // data length = 2
EXPECT_EQ(0, memcmp(x->second->getData(), packed+34, 1)); // data len=1
x = options.find(0);
EXPECT_EQ(x, options.end()); // option 0 not found
x = options.find(1); // 1 is htons(256). Worth checking
x = options.find(1); // 1 is htons(256) on little endians. Worth checking
EXPECT_EQ(x, options.end()); // option 1 not found
x = options.find(2);
......
......@@ -18,6 +18,7 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
#include "dhcp/dhcp6.h"
#include "dhcp/option.h"
......@@ -33,7 +34,63 @@ public:
}
};
TEST_F(OptionTest, basic) {
// v4 is not really implemented yet. A simple test will do for now
TEST_F(OptionTest, basic4) {
Option* opt = new Option(Option::V4, 17);
EXPECT_EQ(17, opt->getType());
EXPECT_EQ(NULL, opt->getData());
EXPECT_EQ(2, opt->len()); // just v4 header
delete opt;
}
// tests simple constructor
TEST_F(OptionTest, basic6) {
Option* opt = new Option(Option::V6, 1);
EXPECT_EQ(1, opt->getType());
EXPECT_EQ(NULL, opt->getData());
EXPECT_EQ(4, opt->len()); // just v6 header
delete opt;
}
// tests contructor used in pkt reception
// option contains actual data
TEST_F(OptionTest, data1) {
boost::shared_array<char> buf(new char[32]);
for (int i=0; i<32; i++)
buf[i] = 100+i;
Option* opt = new Option(Option::V6, 333, //type
buf,
3, // offset
7); // 7 bytes of data
EXPECT_EQ(333, opt->getType());
ASSERT_EQ(&buf[3], opt->getData());
ASSERT_EQ(11, opt->len());
EXPECT_EQ(0, memcmp(&buf[3], opt->getData(), 7) );
int offset = opt->pack(buf, 32, 20);
EXPECT_EQ(31, offset);
EXPECT_EQ(buf[20], 333/256); // type
EXPECT_EQ(buf[21], 333%256);
EXPECT_EQ(buf[22], 0); // len
EXPECT_EQ(buf[23], 7);
// payload
EXPECT_EQ(0, memcmp(&buf[3], &buf[24], 7) );
delete opt;
}
// another text that tests the same thing, just
// with different input parameters
TEST_F(OptionTest, data2) {
boost::shared_array<char> simple_buf(new char[128]);
for (int i=0; i<128; i++)
......@@ -70,12 +127,91 @@ TEST_F(OptionTest, basic) {
// 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;
}
// check that an option can contain 2 suboptions:
// opt1
// +----opt2
// |
// +----opt3
//
TEST_F(OptionTest, suboptions1) {
boost::shared_array<char> buf(new char[128]);
for (int i=0; i<128; i++)
buf[i] = 100+i;
Option* opt1 = new Option(Option::V6, 65535, //type
buf,
0, // offset
3); // 3 bytes of data
boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
buf,
3, // offset
5)); // 5 bytes of data
opt1->addOption(opt2);
opt1->addOption(opt3);
// opt2 len = 4 (just header)
// opt3 len = 9 4(header)+5(data)
// opt1 len = 7 + suboptions() = 7 + 4 + 9 = 20
EXPECT_EQ(4, opt2->len());
EXPECT_EQ(9, opt3->len());
EXPECT_EQ(20, opt1->len());
char expected[] = {
0xff, 0xff, 0, 16, 100, 101, 102,
0, 7, 0, 5, 103, 104, 105, 106, 107,
0, 13, 0, 0 // no data at all
};
int offset = opt1->pack(buf, 128, 20);
EXPECT_EQ(40, offset);
// payload
EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
delete opt1;
}
// check that an option can contain 2 suboptions:
// opt1
// +----opt2
// |
// +----opt3
//
TEST_F(OptionTest, suboptions2) {
boost::shared_array<char> buf(new char[128]);
for (int i=0; i<128; i++)
buf[i] = 100+i;
Option* opt1 = new Option(Option::V6, 65535, //type
buf,
0, // offset
3); // 3 bytes of data
boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
buf,
3, // offset
5)); // 5 bytes of data
opt1->addOption(opt2);
opt2->addOption(opt3);
// opt3 len = 9 4(header)+5(data)
// opt2 len = 4 (just header) + len(opt3)
// opt1 len = 7 + len(opt2)
char expected[] = {
0xff, 0xff, 0, 16, 100, 101, 102,
0, 13, 0, 9,
0, 7, 0, 5, 103, 104, 105, 106, 107,
};
int offset = opt1->pack(buf, 128, 20);
EXPECT_EQ(40, offset);
// payload
EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
delete opt1;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment