Commit 859e0b3e authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[2827] Pkt6 now supports relays properly.

parent 3511c6e6
5XX. [func] tomek
b10-dhcp6: Pkt6 class is now able to parse and build relayed
DHCPv6 messages.
(Trac #2827, git TBD)
582. [func] naokikambe
New statistics items related unixdomain sockets added into Xfrout :
open, openfail, close, bindfail, acceptfail, accept, senderr, and
......
......@@ -129,7 +129,8 @@ LibDHCP::optionFactory(Option::Universe u,
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options,
size_t* relay_msg_offset /* = 0 */) {
size_t* relay_msg_offset /* = 0 */,
size_t* relay_msg_len /* = 0 */) {
size_t offset = 0;
size_t length = buf.size();
......@@ -153,9 +154,14 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
return (offset);
}
if (opt_type == D6O_RELAY_MSG && relay_msg_offset) {
if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
// remember offset of the beginning of the relay-msg option
*relay_msg_offset = offset;
*relay_msg_len = opt_len;
// do not create that relay-msg option
offset += opt_len;
continue;
}
// Get all definitions with the particular option code. Note that option
......
......@@ -115,18 +115,27 @@ public:
/// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
///
/// Parses provided buffer and stores created Option objects
/// in options container.
/// Parses provided buffer and stores created Option objects in options
/// container. The last two parameters are optional and are used in
/// relay parsing. If they are specified, relay-msg option is not created,
/// but rather those two parameters are specified to point out where
/// the relay-msg option resides and what is its length. This is perfromance
/// optimization that avoids unnecessary copying of potentially large
/// relay-msg option. It is not used for anything, except in the next
/// iteration its content will be treated as buffer to be parsed.
///
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Options will be
/// put here.
/// @param relay_msg_offset reference to a size_t structure. If specified,
/// offset to beginning of relay_msg option will be store here.
/// offset to beginning of relay_msg option will be stored in it.
/// @param relay_msg_len reference to a size_t structure. If specified,
/// length of the relay_msg option will be stored in it.
/// @return offset to the first byte after last parsed option
static size_t unpackOptions6(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options,
size_t* relay_msg_offset = 0);
size_t* relay_msg_offset = 0,
size_t* relay_msg_len = 0);
/// Registers factory method that produces options of specific option types.
///
......
......@@ -230,7 +230,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
// 1 byte larger than the size of the string
// representation of this FQDN.
data_size = fqdn.size() + 1;
} else {
} else if ( (*field == OPT_BINARY_TYPE) || (*field == OPT_STRING_TYPE) ) {
// In other case we are dealing with string or binary value
// which size can't be determined. Thus we consume the
// remaining part of the buffer for it. Note that variable
......@@ -238,14 +238,11 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
// that the validate() function in OptionDefinition object
// should have checked wheter it is a case for this option.
data_size = std::distance(data, data_buf.end());
}
if (data_size == 0) {
} else {
// If we reached the end of buffer we assume that this option is
// truncated because there is no remaining data to initialize
// an option field.
if (data_size == 0) {
isc_throw(OutOfRange, "option buffer truncated");
}
isc_throw(OutOfRange, "option buffer truncated");
}
} else {
// Our data field requires that there is a certain chunk of
......
......@@ -27,7 +27,7 @@ namespace isc {
namespace dhcp {
Pkt6::RelayInfo::RelayInfo()
:msg_type_(0), hop_count_(0), linkaddr_("::"), peeraddr_(""), relay_msg_len_(0) {
:msg_type_(0), hop_count_(0), linkaddr_("::"), peeraddr_("::"), relay_msg_len_(0) {
// interface_id_, subscriber_id_, remote_id_ initialized to NULL
// echo_options_ initialized to empty collection
}
......@@ -69,22 +69,28 @@ uint16_t Pkt6::len() {
}
}
OptionPtr Pkt6::getRelayOption(uint16_t opt_type, uint8_t relay_level) {
if (relay_level >= relay_info_.size()) {
isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
<< " There is no info about " << relay_level + 1 << " relay.");
}
for (Option::OptionCollection::iterator it = relay_info_[relay_level].options_.begin();
it != relay_info_[relay_level].options_.end(); ++it) {
if ((*it).second->getType() == opt_type) {
return (it->second);
}
}
return (OptionPtr());
}
uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) {
uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
+ Option::OPTION6_HDR_LEN; // header of the relay-msg option
if (relay.interface_id_) {
len += relay.interface_id_->len();
}
if (relay.subscriber_id_) {
len += relay.subscriber_id_->len();
}
if (relay.remote_id_) {
len += relay.remote_id_->len();
}
for (Option::OptionCollection::const_iterator opt = relay.echo_options_.begin();
opt != relay.echo_options_.end(); ++opt) {
for (Option::OptionCollection::const_iterator opt = relay.options_.begin();
opt != relay.options_.end(); ++opt) {
len += (opt->second)->len();
}
......@@ -148,19 +154,9 @@ Pkt6::packUDP() {
bufferOut_.writeData(&relay->peeraddr_.toBytes()[0],
isc::asiolink::V6ADDRESS_LEN);
if (relay->interface_id_) {
relay->interface_id_->pack(bufferOut_);
}
if (relay->subscriber_id_) {
relay->subscriber_id_->pack(bufferOut_);
}
if (relay->remote_id_) {
relay->remote_id_->pack(bufferOut_);
}
for (Option::OptionCollection::const_iterator opt =
relay->echo_options_.begin();
opt != relay->echo_options_.end(); ++opt) {
relay->options_.begin();
opt != relay->options_.end(); ++opt) {
(opt->second)->pack(bufferOut_);
}
......@@ -271,12 +267,13 @@ Pkt6::unpackRelayMsg() {
size_t bufsize = data_.size();
size_t offset = 0;
size_t relay_msg_offset = 0;
while (bufsize >= DHCPV6_RELAY_HDR_LEN) {
RelayInfo relay;
size_t relay_msg_offset = 0;
size_t relay_msg_len = 0;
// parse fixed header first (first 34 bytes)
relay.msg_type_ = data_[offset++];
relay.hop_count_ = data_[offset++];
......@@ -287,46 +284,45 @@ Pkt6::unpackRelayMsg() {
bufsize -= DHCPV6_RELAY_HDR_LEN; // 34 bytes (1+1+16+16)
try {
Option::OptionCollection options;
// parse the rest as options
OptionBuffer opt_buffer(&data_[offset], &data_[offset+bufsize]);
LibDHCP::unpackOptions6(opt_buffer, options, &relay_msg_offset);
LibDHCP::unpackOptions6(opt_buffer, relay.options_, &relay_msg_offset,
&relay_msg_len);
/// @todo: check that each option appears at most once
//relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
//relay.subscriber_id_ = options->getOption(D6O_SUBSCRIBER_ID);
//relay.remote_id_ = options->getOption(D6O_REMOTE_ID);
Option::OptionCollection::const_iterator relay_iter = options.find(D6O_RELAY_MSG);
if (relay_iter == options.end()) {
// there's nothing to decapsulate. We give up.
isc_throw(InvalidOperation, "Mandatory relay_msg missing");
if (relay_msg_offset == 0 || relay_msg_len == 0) {
isc_throw(BadValue, "Mandatory relay-msg option missing");
}
OptionPtr relay_msg = relay_iter->second;
// store relay information parsed so far
addRelayInfo(relay);
/// @todo: implement ERO here
size_t inner_len = relay_msg->len() - relay_msg->getHeaderLen();
if (inner_len >= bufsize) {
if (relay_msg_len >= bufsize) {
// length of the relay_msg option extends beyond end of the message
isc_throw(Unexpected, "Relay-msg option is truncated.");
return false;
}
uint8_t inner_type = relay_msg->getUint8();
offset += relay_msg_offset;
bufsize = inner_len;
if ( (inner_type != DHCPV6_RELAY_FORW) && (inner_type != DHCPV6_RELAY_REPL)) {
// Ok, the inner message is not encapsulated, let's decode it directly
return (unpackMsg(data_.begin() + offset, data_.begin() + offset + inner_len));
uint8_t inner_type = data_[offset + relay_msg_offset];
offset += relay_msg_offset; // offset is relative
bufsize = relay_msg_len; // length is absolute
if ( (inner_type != DHCPV6_RELAY_FORW) &&
(inner_type != DHCPV6_RELAY_REPL)) {
// Ok, the inner message is not encapsulated, let's decode it
// directly
return (unpackMsg(data_.begin() + offset, data_.begin() + offset
+ relay_msg_len));
}
// Oh well, there's inner relay-forw or relay-repl inside. Let's unpack it as well
// Oh well, there's inner relay-forw or relay-repl inside. Let's
// unpack it as well. The next loop iteration will take care
// of that.
} catch (const Exception& e) {
/// @todo: throw exception here once we turn this function to void.
return (false);
......
......@@ -44,21 +44,26 @@ public:
TCP = 1 // there are TCP DHCPv6 packets (bulk leasequery, failover)
};
/// @brief structure that describes a single relay information
///
/// Client sends messages. Each relay along its way will encapsulate the message.
/// This structure represents all information added by a single relay.
struct RelayInfo {
/// @brief default constructor
RelayInfo();
uint8_t msg_type_; ///< message type (RELAY-FORW oro RELAY-REPL)
uint8_t hop_count_; ///< number of traversed relays (up to 32)
isc::asiolink::IOAddress linkaddr_;///< fixed field in relay-forw/relay-reply
isc::asiolink::IOAddress peeraddr_;///< fixed field in relay-forw/relay-reply
/// @brief length of the relay_msg_len
/// Used when calculating length during pack/unpack
uint16_t relay_msg_len_;
uint8_t msg_type_; ///< message type (RELAY-FORW oro RELAY-REPL)
uint8_t hop_count_; ///< number of traversed relays (up to 32)
isc::asiolink::IOAddress linkaddr_; ///< fixed field in relay-forw/relay-reply
isc::asiolink::IOAddress peeraddr_; ///< fixed field in relay-forw/relay-reply
OptionPtr interface_id_; ///< interface-id option (optional)
OptionPtr subscriber_id_; ///< subscriber-id (RFC4580)
OptionPtr remote_id_; ///< remote-id (RFC4649)
uint16_t relay_msg_len_; ///< length of the relay_msg_len
/// used for ERO (Echo Request Option, RFC 4994)
isc::dhcp::Option::OptionCollection echo_options_;
/// options received from a specified relay, except relay-msg option
isc::dhcp::Option::OptionCollection options_;
};
/// Constructor, used in replying to a message
......@@ -109,7 +114,6 @@ public:
/// @return reference to output buffer
const isc::util::OutputBuffer& getBuffer() const { return (bufferOut_); };
/// @brief Returns reference to input buffer.
///
/// @return reference to input buffer
......@@ -180,6 +184,8 @@ public:
/// @return pointer to found option (or NULL)
OptionPtr getOption(uint16_t type);
OptionPtr getRelayOption(uint16_t type, uint8_t nesting_level);
/// @brief Returns all instances of specified type.
///
/// Returns all instances of options of the specified type. DHCPv6 protocol
......@@ -335,6 +341,15 @@ public:
/// be freed by the caller.
const char* getName() const;
/// relay information
///
/// this is a public field. Otherwise we hit one of the two problems:
/// we return reference to an internal field (and that reference could
/// be potentially used past Pkt6 object lifetime causing badness) or
/// we return a copy (which is inefficient and also causes any updates
/// to be impossible). Therefore public field is considered the best
/// (or least bad) solution.
std::vector<RelayInfo> relay_info_;
protected:
/// Builds on wire packet for TCP transmission.
///
......@@ -370,15 +385,41 @@ protected:
/// @return true, if build was successful
bool unpackUDP();
/// @brief unpacks direct (non-relayed) message
///
/// This method unpacks specified buffer range as a direct
/// (e.g. solicit or request) message. This method is called from
/// unpackUDP() when received message is detected to be direct.
///
/// @param begin start of the buffer
/// @param end end of the buffer
/// @return true if parsing was successful and there are no leftover bytes
bool unpackMsg(OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end);
/// @brief unpacks relayed message (RELAY-FORW or RELAY-REPL)
///
/// This method is called from unpackUDP() when received message
/// is detected to be relay-message. It goes iteratively over
/// all relays (if there are multiple encapsulation levels).
///
/// @return true if parsing was successful
bool unpackRelayMsg();
/// @brief calculates overhead introduced in specified relay
///
/// It is used when calculating message size and packing message
/// @return number of bytes needed to store relay information
uint16_t getRelayOverhead(const RelayInfo& relay);
/// @brief calculates overhead for all relays defined for this message
/// @return number of bytes needed to store all relay information
uint16_t calculateRelaySizes();
/// @brief calculates size of the message as if were sent directly
///
/// This is equal to len() if the message is direct.
/// @return number of bytes required to store the message
uint16_t directLen();
/// UDP (usually) or TCP (bulk leasequery or failover)
......@@ -435,9 +476,6 @@ protected:
/// packet timestamp
boost::posix_time::ptime timestamp_;
/// relay information
std::vector<RelayInfo> relay_info_;
}; // Pkt6 class
typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
......
......@@ -766,9 +766,15 @@ TEST_F(OptionCustomTest, recordDataTruncated) {
// 2 bytes of uint16_t value and IPv6 address. Option definitions specifies
// 3 data fields for this option but the length of the data is insufficient
// to initialize 3 data field.
EXPECT_THROW(
option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 18)),
isc::OutOfRange
// @todo:
// Currently the code was modified to allow empty string or empty binary data
// Potentially change this back to EXPECT_THROW(..., OutOfRange) once we
// decide how to treat zero length strings and binary data (they are typically
// valid or invalid on a per option basis, so there likely won't be a single
// one answer to all)
EXPECT_NO_THROW(
option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 18))
);
// Try to further reduce the length of the buffer to make it insufficient
......
......@@ -17,9 +17,15 @@
#include <asiolink/io_address.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/option_custom.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>
#include <util/encode/hex.h>
#include <gtest/gtest.h>
#include <iostream>
......@@ -31,6 +37,7 @@ using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace boost;
namespace {
// empty class for now, but may be extended once Addr6 becomes bigger
......@@ -99,6 +106,67 @@ Pkt6* capture1() {
return (pkt);
}
/// @brief creates doubly related solicit message
///
/// This is a traffic capture exported from wireshark. It includes a SOLICIT
/// message that passed through two relays. Each relay include interface-id,
/// remote-id and relay-forw encapsulation. It is especially interesting,
/// because of the following properties:
/// - double encapsulation
/// - first relay inserts relay-msg before extra options
/// - second relay inserts relay-msg after extra options
/// - both relays are from different vendors
/// - interface-id are different for each relay
/// - first relay inserts valid remote-id
/// - second relay inserts remote-id with empty vendor data
/// - the solicit message requests for custom options in ORO
/// - there are option types in RELAY-FORW that do not appear in SOLICIT
/// - there are option types in SOLICT that do not appear in RELAY-FORW
///
/// RELAY-FORW
/// - relay message option
/// - RELAY-FORW
/// - interface-id option
/// - remote-id option
/// - RELAY-FORW
/// SOLICIT
/// - client-id option
/// - ia_na option
/// - elapsed time
/// - ORO
/// - interface-id option
/// - remote-id option
///
/// The original capture was posted to dibbler users mailing list.
///
/// @return created double relayed SOLICIT message
Pkt6* capture2() {
// string exported from Wireshark
string hex_string =
"0c01200108880db800010000000000000000fe80000000000000020021fffe5c18a900"
"09007d0c0000000000000000000000000000000000fe80000000000000020021fffe5c"
"18a9001200154953414d3134342065746820312f312f30352f30310025000400000de9"
"00090036016b4fe20001000e0001000118b033410000215c18a90003000c00000001ff"
"ffffffffffffff00080002000000060006001700f200f30012001c4953414d3134347c"
"3239397c697076367c6e743a76703a313a313130002500120000197f0001000118b033"
"410000215c18a9";
std::vector<uint8_t> bin;
// Decode the hex string and store it in bin (which happens
// to be OptionBuffer format)
isc::util::encode::decodeHex(hex_string, bin);
Pkt6* pkt = new Pkt6(&bin[0], bin.size());
pkt->setRemotePort(547);
pkt->setRemoteAddr(IOAddress("fe80::1234"));
pkt->setLocalPort(547);
pkt->setLocalAddr(IOAddress("ff05::1:3"));
pkt->setIndex(2);
pkt->setIface("eth0");
return (pkt);
}
TEST_F(Pkt6Test, unpack_solicit1) {
Pkt6* sol = capture1();
......@@ -234,7 +302,7 @@ TEST_F(Pkt6Test, addGetDelOptions) {
}
TEST_F(Pkt6Test, Timestamp) {
boost::scoped_ptr<Pkt6> pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
scoped_ptr<Pkt6> pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
// Just after construction timestamp is invalid
ASSERT_TRUE(pkt->getTimestamp().is_not_a_date_time());
......@@ -243,17 +311,17 @@ TEST_F(Pkt6Test, Timestamp) {
pkt->updateTimestamp();
// Get updated packet time.
boost::posix_time::ptime ts_packet = pkt->getTimestamp();
posix_time::ptime ts_packet = pkt->getTimestamp();
// After timestamp is updated it should be date-time.
ASSERT_FALSE(ts_packet.is_not_a_date_time());
// Check current time.
boost::posix_time::ptime ts_now =
boost::posix_time::microsec_clock::universal_time();
posix_time::ptime ts_now =
posix_time::microsec_clock::universal_time();
// Calculate period between packet time and now.
boost::posix_time::time_period ts_period(ts_packet, ts_now);
posix_time::time_period ts_period(ts_packet, ts_now);
// Duration should be positive or zero.
EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
......@@ -306,5 +374,180 @@ TEST_F(Pkt6Test, getName) {
}
}
// This test verifies that a fancy solicit that passed through two
// relays can be parsed properly. See capture2() method description
// for details regarding the packet.
TEST_F(Pkt6Test, relayUnpack) {
scoped_ptr<Pkt6> msg(capture2());
EXPECT_NO_THROW(msg->unpack());
EXPECT_EQ(DHCPV6_SOLICIT, msg->getType());
EXPECT_EQ(217, msg->len());
ASSERT_EQ(2, msg->relay_info_.size());
OptionPtr opt;
// part 1: Check options inserted by the first relay
// There should be 2 options in first relay
EXPECT_EQ(2, msg->relay_info_[0].options_.size());
// There should be interface-id option
ASSERT_TRUE(opt = msg->getRelayOption(D6O_INTERFACE_ID, 0));
OptionBuffer data = opt->getData();
EXPECT_EQ(32, opt->len()); // 28 bytes of data + 4 bytes header
EXPECT_EQ(data.size(), 28);
// That's a strange interface-id, but this is a real life example
EXPECT_TRUE(0 == memcmp("ISAM144|299|ipv6|nt:vp:1:110", &data[0], 28));
// get the remote-id option
ASSERT_TRUE(opt = msg->getRelayOption(D6O_REMOTE_ID, 0));
EXPECT_EQ(22, opt->len()); // 18 bytes of data + 4 bytes header
shared_ptr<OptionCustom> custom = dynamic_pointer_cast<OptionCustom>(opt);
uint32_t vendor_id = custom->readInteger<uint32_t>(0);
EXPECT_EQ(6527, vendor_id); // 6527 = Panthera Networks
uint8_t expected_remote_id[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0xb0, 0x33, 0x41, 0x00,
0x00, 0x21, 0x5c, 0x18, 0xa9 };
OptionBuffer remote_id = custom->readBinary(1);
ASSERT_EQ(sizeof(expected_remote_id), remote_id.size());
ASSERT_EQ(0, memcmp(expected_remote_id, &remote_id[0], remote_id.size()));
// part 2: Check options inserted by the second relay
// get the interface-id from the second relay
ASSERT_TRUE(opt = msg->getRelayOption(D6O_INTERFACE_ID, 1));
data = opt->getData();
EXPECT_EQ(25, opt->len()); // 21 bytes + 4 bytes header
EXPECT_EQ(data.size(), 21);
EXPECT_TRUE(0 == memcmp("ISAM144 eth 1/1/05/01", &data[0], 21));
// get the remote-id option
ASSERT_TRUE(opt = msg->getRelayOption(D6O_REMOTE_ID, 1));
EXPECT_EQ(8, opt->len());
custom = dynamic_pointer_cast<OptionCustom>(opt);
vendor_id = custom->readInteger<uint32_t>(0);
EXPECT_EQ(3561, vendor_id); // 3561 = Broadband Forum
// @todo: See if we can validate empty remote-id field
// Let's check if there is no leak between options stored in
// the SOLICIT message and the relay.
EXPECT_FALSE(opt = msg->getRelayOption(D6O_IA_NA, 1));
// Part 3: Let's check options in the message itself
// This is not redundant compared to other direct messages tests,
// as we parsed it differently
EXPECT_EQ(DHCPV6_SOLICIT, msg->getType());
EXPECT_EQ(0x6b4fe2, msg->getTransid());
ASSERT_TRUE(opt = msg->getOption(D6O_CLIENTID));
EXPECT_EQ(18, opt->len()); // 14 bytes of data + 4 bytes of header
uint8_t expected_client_id[] = { 0x00, 0x01, 0x00, 0x01, 0x18, 0xb0, 0x33, 0x41, 0x00,
0x00, 0x21, 0x5c, 0x18, 0xa9 };
data = opt->getData();
ASSERT_EQ(data.size(), sizeof(expected_client_id));
ASSERT_EQ(0, memcmp(&data[0], expected_client_id, data.size()));
ASSERT_TRUE(opt = msg->getOption(D6O_IA_NA));
shared_ptr<Option6IA> ia = dynamic_pointer_cast<Option6IA>(opt);
ASSERT_TRUE(ia);
EXPECT_EQ(1, ia->getIAID());
EXPECT_EQ(0xffffffff, ia->getT1());
EXPECT_EQ(0xffffffff, ia->getT2());
ASSERT_TRUE(opt = msg->getOption(D6O_ELAPSED_TIME));
EXPECT_EQ(6, opt->len()); // 2 bytes of data + 4 bytes of header
shared_ptr<OptionInt<uint16_t> > elapsed = dynamic_pointer_cast<OptionInt<uint16_t> > (opt);
ASSERT_TRUE(elapsed);
EXPECT_EQ(0, elapsed->getValue());
ASSERT_TRUE(opt = msg->getOption(D6O_ORO));
shared_ptr<OptionIntArray<uint16_t> > oro = dynamic_pointer_cast<OptionIntArray<uint16_t> > (opt);
const std::vector<uint16_t> oro_list = oro->getValues();
EXPECT_EQ(3, oro_list.size());
EXPECT_EQ(23, oro_list[0]);
EXPECT_EQ(242, oro_list[1]);
EXPECT_EQ(243, oro_list[2]);
}
// This test verified that message with relay information can be
// packed and then unpacked.
TEST_F(Pkt6Test, relayPack) {
scoped_ptr<Pkt6> parent(new Pkt6(DHCPV6_ADVERTISE, 0x020304));