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

[master] Merge branch 'trac3145' (IA_PD and IAPREFIX DHCPv6 options)

Conflicts:
	ChangeLog
parents 9273dca3 3a844e85
673. [func] tomek
libdhcp: Added support for IA_PD and IAPREFIX options. New class
for IAPREFIX (Option6_IAPrefix) has been added.
(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
672. [func] tmark
Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
This class provides the common structure and methods to implement the state
......
......@@ -26,6 +26,7 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
libb10_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.h
libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libb10_dhcp___la_SOURCES += option6_iaprefix.cc option6_iaprefix.h
libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
libb10_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h
libb10_dhcp___la_SOURCES += option_int.h
......
......@@ -30,10 +30,24 @@ namespace dhcp {
Option6IA::Option6IA(uint16_t type, uint32_t iaid)
:Option(Option::V6, type), iaid_(iaid), t1_(0), t2_(0) {
// IA_TA has different layout than IA_NA and IA_PD. We can't sue this class
if (type == D6O_IA_TA) {
isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
"a different layout");
}
}
Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
:Option(Option::V6, type) {
// IA_TA has different layout than IA_NA and IA_PD. We can't use this class
if (type == D6O_IA_TA) {
isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
"a different layout");
}
unpack(begin, end);
}
......
......@@ -35,6 +35,9 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
uint32_t pref, uint32_t valid)
:Option(V6, type), addr_(addr), preferred_(pref),
valid_(valid) {
if (!addr.isV6()) {
isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
}
}
Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
......
......@@ -33,7 +33,9 @@ public:
/// length of the fixed part of the IAADDR option
static const size_t OPTION6_IAADDR_LEN = 24;
/// @brief Ctor, used for options constructed (during transmission).
/// @brief Constructor, used for options constructed (during transmission).
///
/// @throw BadValue if specified addr is not IPv6
///
/// @param type option type
/// @param addr reference to an address
......@@ -42,7 +44,9 @@ public:
Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
uint32_t preferred, uint32_t valid);
/// @brief ctor, used for received options.
/// @brief Constructor, used for received options.
///
/// @throw OutOfRange if specified option is truncated
///
/// @param type option type
/// @param begin iterator to first byte of option data
......
// Copyright (C) 2013 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 <asiolink/io_address.h>
#include <dhcp/dhcp6.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option6_iaprefix.h>
#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
#include <sstream>
#include <stdint.h>
#include <arpa/inet.h>
using namespace std;
using namespace isc::asiolink;
using namespace isc::util;
namespace isc {
namespace dhcp {
Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len, uint32_t pref, uint32_t valid)
:Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
// Option6IAAddr will check if prefix is IPv6 and will throw if it is not
if (prefix_len > 128) {
isc_throw(BadValue, prefix_len << " is not a valid prefix length. "
<< "Allowed range is 0..128");
}
}
Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end)
:Option6IAAddr(type, begin, end) {
unpack(begin, end);
}
void Option6IAPrefix::pack(isc::util::OutputBuffer& buf) {
if (!addr_.isV6()) {
isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
}
buf.writeUint16(type_);
// len() returns complete option length. len field contains
// length without 4-byte option header
buf.writeUint16(len() - getHeaderLen());
buf.writeUint32(preferred_);
buf.writeUint32(valid_);
buf.writeUint8(prefix_len_);
buf.writeData(&addr_.toBytes()[0], isc::asiolink::V6ADDRESS_LEN);
// store encapsulated options (the only defined so far is PD_EXCLUDE)
packOptions(buf);
}
void Option6IAPrefix::unpack(OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end) {
if ( distance(begin, end) < OPTION6_IAPREFIX_LEN) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
preferred_ = readUint32( &(*begin) );
begin += sizeof(uint32_t);
valid_ = readUint32( &(*begin) );
begin += sizeof(uint32_t);
prefix_len_ = *begin;
begin += sizeof(uint8_t);
// 16 bytes: IPv6 address
addr_ = IOAddress::fromBytes(AF_INET6, &(*begin));
begin += V6ADDRESS_LEN;
// unpack encapsulated options (the only defined so far is PD_EXCLUDE)
unpackOptions(OptionBuffer(begin, end));
}
std::string Option6IAPrefix::toText(int indent /* =0 */) {
stringstream tmp;
for (int i=0; i<indent; i++)
tmp << " ";
tmp << "type=" << type_ << "(IAPREFIX) prefix=" << addr_.toText() << "/"
<< prefix_len_ << ", preferred-lft=" << preferred_ << ", valid-lft="
<< valid_ << endl;
for (OptionCollection::const_iterator opt=options_.begin();
opt!=options_.end();
++opt) {
tmp << (*opt).second->toText(indent + 2);
}
return tmp.str();
}
uint16_t Option6IAPrefix::len() {
uint16_t length = OPTION6_HDR_LEN + OPTION6_IAPREFIX_LEN;
// length of all suboptions
for (Option::OptionCollection::const_iterator it = options_.begin();
it != options_.end(); ++it) {
length += (*it).second->len();
}
return (length);
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2013 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_IAPREFIX_H
#define OPTION6_IAPREFIX_H
#include <asiolink/io_address.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option.h>
namespace isc {
namespace dhcp {
/// @brief Class that represents IAPREFIX option in DHCPv6
///
/// It is based on a similar class that handles addresses. There are major
/// differences, though. The fields are in different order. There is also
/// additional prefix length field.
///
/// It should be noted that to get a full prefix (2 values: base prefix, and
/// a prefix length) 2 methods are used: getAddress() and getLength(). Although
/// using getAddress() to obtain base prefix is somewhat counter-intuitive at
/// first, it becomes obvious when one realizes that an address is a special
/// case of a prefix with /128. It makes everyone's like much easier, because
/// the base prefix doubles as a regular address in many cases, e.g. when
/// searching for a lease.
class Option6IAPrefix : public Option6IAAddr {
public:
/// length of the fixed part of the IAPREFIX option
static const size_t OPTION6_IAPREFIX_LEN = 25;
/// @brief Constructor, used for options constructed (during transmission).
///
/// @param type option type
/// @param addr reference to an address
/// @param prefix_length length (1-128)
/// @param preferred address preferred lifetime (in seconds)
/// @param valid address valid lifetime (in seconds)
Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& addr,
uint8_t prefix_length, uint32_t preferred, uint32_t valid);
/// @brief Constructor, used for received options.
///
/// @throw OutOfRange if buffer is too short
///
/// @param type option type
/// @param begin iterator to first byte of option data
/// @param end iterator to end of option data (first byte after option end)
Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end);
/// @brief Writes option in wire-format.
///
/// Writes option in wire-format to buf, returns pointer to first unused
/// byte after stored option.
///
/// @throw BadValue if the address is not IPv6
///
/// @param buf pointer to a buffer
void pack(isc::util::OutputBuffer& buf);
/// @brief Parses received buffer.
///
/// @throw OutOfRange when buffer is shorter than 25 bytes
///
/// @param begin iterator to first byte of option data
/// @param end iterator to end of option data (first byte after option end)
virtual void unpack(OptionBufferConstIter begin,
OptionBufferConstIter end);
/// Returns string representation of the option.
///
/// @param indent number of spaces before printing text
///
/// @return string with text representation.
virtual std::string toText(int indent = 0);
/// sets address in this option.
///
/// @param addr address to be sent in this option
void setPrefix(const isc::asiolink::IOAddress& prefix,
uint8_t length) { addr_ = prefix; prefix_len_ = length; }
uint8_t getLength() const { return prefix_len_; }
/// returns data length (data length + DHCPv4/DHCPv6 option header)
virtual uint16_t len();
protected:
uint8_t prefix_len_;
};
} // isc::dhcp namespace
} // isc namespace
#endif // OPTION_IA_H
......@@ -214,7 +214,7 @@ RECORD_DECL(IA_NA_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
RECORD_DECL(IA_PD_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
// ia-prefix
RECORD_DECL(IA_PREFIX_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
OPT_UINT8_TYPE, OPT_BINARY_TYPE);
OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
// lq-query
RECORD_DECL(LQ_QUERY_RECORDS, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
// lq-relay-data
......
......@@ -36,6 +36,7 @@ libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc
libdhcp___unittests_SOURCES += option6_ia_unittest.cc
libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc
libdhcp___unittests_SOURCES += option_int_unittest.cc
libdhcp___unittests_SOURCES += option_int_array_unittest.cc
libdhcp___unittests_SOURCES += option_data_types_unittest.cc
......
......@@ -18,6 +18,7 @@
#include <dhcp/option.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaprefix.h>
#include <util/buffer.h>
#include <boost/scoped_ptr.hpp>
......@@ -43,11 +44,13 @@ public:
buf_[i] = 255 - i;
}
}
OptionBuffer buf_;
OutputBuffer outBuf_;
};
TEST_F(Option6IATest, basic) {
/// @brief performs basic checks on IA option
///
/// Check that an option can be built based on incoming buffer and that
/// the option contains expected values.
/// @param type specifies option type (IA_NA or IA_PD)
void checkIA(uint16_t type) {
buf_[0] = 0xa1; // iaid
buf_[1] = 0xa2;
buf_[2] = 0xa3;
......@@ -65,12 +68,12 @@ TEST_F(Option6IATest, basic) {
// Create an option
// unpack() is called from constructor
scoped_ptr<Option6IA> opt(new Option6IA(D6O_IA_NA,
buf_.begin(),
buf_.begin() + 12));
scoped_ptr<Option6IA> opt;
ASSERT_NO_THROW(opt.reset(new Option6IA(type, buf_.begin(),
buf_.begin() + 12)));
EXPECT_EQ(Option::V6, opt->getUniverse());
EXPECT_EQ(D6O_IA_NA, opt->getType());
EXPECT_EQ(type, opt->getType());
EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
EXPECT_EQ(0x81020304, opt->getT1());
EXPECT_EQ(0x84030201, opt->getT2());
......@@ -79,11 +82,11 @@ TEST_F(Option6IATest, basic) {
// different place
// Test for pack()
opt->pack(outBuf_);
ASSERT_NO_THROW(opt->pack(outBuf_));
// 12 bytes header + 4 bytes content
EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
EXPECT_EQ(D6O_IA_NA, opt->getType());
EXPECT_EQ(type, opt->getType());
EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
......@@ -91,7 +94,7 @@ TEST_F(Option6IATest, basic) {
InputBuffer out(outBuf_.getData(), outBuf_.getLength());
// - if option type is correct
EXPECT_EQ(D6O_IA_NA, out.readUint16());
EXPECT_EQ(type, out.readUint16());
// - if option length is correct
EXPECT_EQ(12, out.readUint16());
......@@ -106,8 +109,31 @@ TEST_F(Option6IATest, basic) {
EXPECT_EQ(0x84030201, out.readUint32() );
EXPECT_NO_THROW(opt.reset());
}
OptionBuffer buf_;
OutputBuffer outBuf_;
};
TEST_F(Option6IATest, basic) {
checkIA(D6O_IA_NA);
}
TEST_F(Option6IATest, pdBasic) {
checkIA(D6O_IA_PD);
}
// Check that this class cannot be used for IA_TA (IA_TA has no T1, T2 fields
// and people tend to think that if it's good for IA_NA and IA_PD, it can
// be used for IA_TA as well and that is not true)
TEST_F(Option6IATest, taForbidden) {
EXPECT_THROW(Option6IA(D6O_IA_TA, buf_.begin(), buf_.begin() + 50),
BadValue);
EXPECT_THROW(Option6IA(D6O_IA_TA, 123), BadValue);
}
// Check that getters/setters are working as expected.
TEST_F(Option6IATest, simple) {
scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 1234));
......@@ -131,12 +157,8 @@ TEST_F(Option6IATest, simple) {
EXPECT_NO_THROW(ia.reset());
}
// test if option can build suboptions
TEST_F(Option6IATest, suboptions_pack) {
buf_[0] = 0xff;
buf_[1] = 0xfe;
buf_[2] = 0xfc;
// test if the option can build suboptions
TEST_F(Option6IATest, suboptionsPack) {
scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 0x13579ace));
ia->setT1(0x2345);
......@@ -154,6 +176,7 @@ TEST_F(Option6IATest, suboptions_pack) {
ASSERT_EQ(4, sub1->len());
ASSERT_EQ(48, ia->len());
// This contains expected on-wire format
uint8_t expected[] = {
D6O_IA_NA/256, D6O_IA_NA%256, // type
0, 44, // length
......@@ -175,18 +198,69 @@ TEST_F(Option6IATest, suboptions_pack) {
};
ia->pack(outBuf_);
ASSERT_EQ(48, outBuf_.getLength());
ASSERT_EQ(48, outBuf_.getLength());
EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 48));
EXPECT_NO_THROW(ia.reset());
}
// test if IA_PD option can build IAPREFIX suboptions
TEST_F(Option6IATest, pdSuboptionsPack) {
// Let's build IA_PD
scoped_ptr<Option6IA> ia;
ASSERT_NO_THROW(ia.reset(new Option6IA(D6O_IA_PD, 0x13579ace)));
ia->setT1(0x2345);
ia->setT2(0x3456);
// Put some dummy option in it
OptionPtr sub1(new Option(Option::V6, 0xcafe));
// Put a valid IAPREFIX option in it
boost::shared_ptr<Option6IAPrefix> addr1(
new Option6IAPrefix(D6O_IAPREFIX, IOAddress("2001:db8:1234:5678::abcd"),
91, 0x5000, 0x7000));
ia->addOption(sub1);
ia->addOption(addr1);
ASSERT_EQ(29, addr1->len());
ASSERT_EQ(4, sub1->len());
ASSERT_EQ(49, ia->len());
uint8_t expected[] = {
D6O_IA_PD/256, D6O_IA_PD%256, // type
0, 45, // length
0x13, 0x57, 0x9a, 0xce, // iaid
0, 0, 0x23, 0x45, // T1
0, 0, 0x34, 0x56, // T2
// iaprefix suboption
D6O_IAPREFIX/256, D6O_IAPREFIX%256, // type
0, 25, // len
0, 0, 0x50, 0, // preferred-lifetime
0, 0, 0x70, 0, // valid-lifetime
91, // prefix length
0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
// suboption
0xca, 0xfe, // type
0, 0 // len
};
ia->pack(outBuf_);
ASSERT_EQ(49, outBuf_.getLength());
EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 49));
EXPECT_NO_THROW(ia.reset());
}
// test if option can parse suboptions
TEST_F(Option6IATest, suboptions_unpack) {
// sizeof (expected) = 48 bytes
uint8_t expected[] = {
const uint8_t expected[] = {
D6O_IA_NA / 256, D6O_IA_NA % 256, // type
0, 28, // length
0x13, 0x57, 0x9a, 0xce, // iaid
......
......@@ -106,4 +106,19 @@ TEST_F(Option6IAAddrTest, basic) {
EXPECT_NO_THROW(opt.reset());
}
/// @todo: Write test for (type, addr, pref, valid) constructor
/// See option6_iaprefix_unittest.cc for similar test
// Tests if broken usage causes exception to be thrown
TEST_F(Option6IAAddrTest, negative) {
// Too short. Minimum length is 24
EXPECT_THROW(Option6IAAddr(D6O_IAADDR, buf_.begin(), buf_.begin() + 23),
OutOfRange);
// This option is for IPv6 addresses only
EXPECT_THROW(Option6IAAddr(D6O_IAADDR, isc::asiolink::IOAddress("192.0.2.1"),
1000, 2000), BadValue);
}
}
// Copyright (C) 2011-2013 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 <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/option6_iaprefix.h>
#include <util/buffer.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <sstream>
#include <arpa/inet.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::util;
using namespace isc::asiolink;
namespace {
class Option6IAPrefixTest : public ::testing::Test {
public:
Option6IAPrefixTest() : buf_(255), outBuf_(255) {
for (int i = 0; i < 255; i++) {
buf_[i] = 255 - i;
}
}
/// @brief creates on-wire representation of IAPREFIX option
///
/// buf_ field is set up to have IAPREFIX with preferred=1000,
/// valid=3000000000 and prefix beign 2001:db8:1::dead:beef/77
void setExampleBuffer() {
for (int i = 0; i < 255; i++) {
buf_[i] = 0;
}
buf_[ 0] = 0x00;
buf_[ 1] = 0x00;
buf_[ 2] = 0x03;
buf_[ 3] = 0xe8; // preferred lifetime = 1000
buf_[ 4] = 0xb2;
buf_[ 5] = 0xd0;
buf_[ 6] = 0x5e;
buf_[ 7] = 0x00; // valid lifetime = 3,000,000,000
buf_[ 8] = 77; // Prefix length = 77
buf_[ 9] = 0x20;
buf_[10] = 0x01;
buf_[11] = 0x0d;
buf_[12] = 0xb8;