Improved error checks, comments, additional unit tests added.
pkglibexec_PROGRAMS = perfdhcp
perfdhcp_SOURCES = perfdhcp.cc
+perfdhcp_SOURCES += localized_option.h
perfdhcp_SOURCES += command_options.cc command_options.h
perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
/// \brief DHCP option at specific offset
///
-/// This class represents DHCP option at specified
+/// This class represents DHCP option with data placed at specified
/// offset in DHCP message.
/// Objects of this type are intended to be used when DHCP packets
/// are created from templates (e.g. read from template file).
-/// Such packets have number of that have to be replaced before
-/// sending: e.g. DUID can be randomized. If option of this type
-/// is added to \ref PerfPkt6 options collection,
+/// Such packets have number of options with contents that have to be
+/// replaced before sending: e.g. DUID can be randomized.
+/// If option of this type is added to \ref PerfPkt6 options collection,
/// \ref perfdhcp::PerfPkt6 will call \ref getOffset on this object
/// to retrieve user-defined option position and replace contents of
/// the output buffer at this offset before packet is sent to the server.
/// \param u specifies universe (V4 or V6)
/// \param type option type (0-255 for V4 and 0-65535 for V6)
/// \param data content of the option
- LocalizedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data) :
+ LocalizedOption(dhcp::Option::Universe u,
+ uint16_t type,
+ const dhcp::OptionBuffer& data) :
dhcp::Option(u, type, data),
offset_(0) {
}
/// \param type option type (0-255 for V4 and 0-65535 for V6)
/// \param data content of the option
/// \param offset location of option in a packet (zero is default)
- LocalizedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data,
+ LocalizedOption(dhcp::Option::Universe u,
+ uint16_t type,
+ const dhcp::OptionBuffer& data,
const size_t offset) :
dhcp::Option(u, type, data),
offset_(offset) {
/// \param first iterator to the first element that should be copied
/// \param last iterator to the next element after the last one
/// to be copied.
- LocalizedOption(dhcp::Option::Universe u, uint16_t type, dhcp::OptionBufferConstIter first,
+ LocalizedOption(dhcp::Option::Universe u,
+ uint16_t type,
+ dhcp::OptionBufferConstIter first,
dhcp::OptionBufferConstIter last) :
dhcp::Option(u, type, first, last),
offset_(0) {
}
- /// \brief Constructor, used to create positioned option from buffer iterators
+ /// \brief Constructor, used to create option from buffer iterators
///
/// This contructor is similar to the previous one, but it does not take
/// the whole vector<uint8_t>, but rather subset of it.
/// \param last iterator to the next element after the last one
/// to be copied.
/// \param offset offset of option in a packet (zero is default)
- LocalizedOption(dhcp::Option::Universe u, uint16_t type, dhcp::OptionBufferConstIter first,
+ LocalizedOption(dhcp::Option::Universe u,
+ uint16_t type,
+ dhcp::OptionBufferConstIter first,
dhcp::OptionBufferConstIter last, const size_t offset) :
dhcp::Option(u, type, first, last),
offset_(offset) {
using namespace std;
using namespace isc;
+using namespace dhcp;
namespace isc {
namespace perfdhcp {
-PerfPkt6::PerfPkt6(const uint8_t* buf, uint32_t len, size_t transid_offset) :
- Pkt6(buf, len, dhcp::Pkt6::UDP),
- transid_offset_(transid_offset) {
+PerfPkt6::PerfPkt6(const uint8_t* buf, uint32_t len,
+ uint32_t transid, const Offset& transid_offset) :
+ Pkt6(buf, len, Pkt6::UDP),
+ transid_offset_(transid_offset.get()) {
+ transid_ = transid;
+}
- // Let's reset timestamp of the packet
- memset(static_cast<void*>(&time_stamp_), 0, sizeof(time_stamp_));
+PerfPkt6::PerfPkt6(const uint8_t* buf,
+ uint32_t len,
+ const Offset&
+ transid_offset) :
+ Pkt6(buf, len, Pkt6::UDP),
+ transid_offset_(transid_offset.get()) {
}
bool
PerfPkt6::rawPack() {
+ // Always override the packet if function is called.
+ bufferOut_.clear();
+ // Write whole buffer to output buffer.
+ bufferOut_.writeData(&data_[0], data_.size());
+
+ if ((transid_offset_ + 4 > data_.size()) ||
+ (transid_offset_ == 0)) {
+ cout << "Failed to build packet: provided transaction id offset: "
+ << transid_offset_ << " is out of bounds (expected 1.."
+ << data_.size()-1 << ")." << endl;
+ return (false);
+ }
+
+ // Seek to the transaction id position in output buffer.
+ bufferOut_.clear();
+ bufferOut_.skip(transid_offset_);
+
try {
- // Always override the packet if function is called.
- bufferOut_.clear();
- // Write whole buffer to output buffer.
- bufferOut_.writeData(&data_[0], data_.size());
- // We already have packet template stored in out buffer
+ // Write 3 octets of transaction id
+ bufferOut_.writeUint8(transid_ >> 16 & 0xFF);
+ bufferOut_.writeUint8(transid_ >> 8 & 0xFF);
+ bufferOut_.writeUint8(transid_ & 0xFF);
+
+ // Buffer pointer is at the end of transaction id.
+ // We have to seek to the end of buffer so as data don't
+ // get truncated.
+ bufferOut_.skip(data_.size() - bufferOut_.getLength());
+
+ // We already have packet template stored in output buffer
// but still some options have to be updated if client
// specified them along with their offsets in the buffer.
rawPackOptions();
- } catch (isc::Unexpected& e) {
- cout << "Failed to build packet: " << e.what() << endl;
+ } catch (isc::BadValue& e) {
+ cout << "Building packet failed: " << e.what() << endl;
return (false);
}
return (true);
bool
PerfPkt6::rawUnpack() {
+ // Validate transaction id offset.
+ if ((transid_offset_ + 4 > data_.size()) ||
+ (transid_offset_ == 0)) {
+ cout << "Failed to parse packet: provided transaction id offset: "
+ << transid_offset_ << " is out of bounds (expected 1.."
+ << data_.size()-1 << ")." << endl;
+ return (false);
+ }
+
// Read transaction id from the packet at the given offset.
transid_ = ( (data_[transid_offset_]) << 16 ) +
((data_[transid_offset_ + 1]) << 8) + (data_[transid_offset_ + 2]);
msg_type_ = data_[0];
try {
rawUnpackOptions();
- } catch (isc::Unexpected& e) {
+ } catch (isc::BadValue& e) {
cout << "Packet parsing failed: " << e.what() << endl;
return (false);
}
void
PerfPkt6::rawPackOptions() {
try {
- // If there are any options on the list we will use provided options offsets
- // to override them in the output buffer with new contents.
- for (dhcp::Option::OptionCollection::const_iterator it = options_.begin();
+ // If there are any options on the list, we will use provided
+ // options offsets to override them in the output buffer
+ // with new contents.
+ for (Option::OptionCollection::const_iterator it = options_.begin();
it != options_.end(); ++it) {
// Get options with their position (offset).
- boost::shared_ptr<LocalizedOption> option = boost::dynamic_pointer_cast<LocalizedOption>(it->second);
- uint32_t position = option->getOffset();
- if (position > 0) {
- bufferOut_.clear();
- bufferOut_.skip(position);
+ boost::shared_ptr<LocalizedOption> option =
+ boost::dynamic_pointer_cast<LocalizedOption>(it->second);
+ uint32_t offset = option->getOffset();
+ if ((offset == 0) ||
+ (offset + option->len() > data_.size())) {
+ isc_throw(isc::BadValue,
+ "option offset for option: " << option->getType()
+ << " is out of bounds (expected 1.."
+ << data_.size() - option->len() << ")");
}
+ bufferOut_.clear();
+ bufferOut_.skip(offset);
+
// Replace existing option with new value.
option->pack(bufferOut_);
}
- // Seek to the end of the buffer to make sure size is correct.
+ // Seek to the end of the buffer to make sure its size is correct.
bufferOut_.skip(data_.size() - bufferOut_.getLength());
}
catch (const Exception&) {
- isc_throw(isc::Unexpected, "Failed to build packet (Option build failed");
+ isc_throw(isc::BadValue, "failed to pack options into buffer.");
}
}
void
PerfPkt6::rawUnpackOptions() {
- for (dhcp::Option::OptionCollection::const_iterator it = options_.begin();
+ for (Option::OptionCollection::const_iterator it = options_.begin();
it != options_.end(); ++it) {
- LocalizedOptionPtr option = boost::dynamic_pointer_cast<LocalizedOption>(it->second);
+ LocalizedOptionPtr option =
+ boost::dynamic_pointer_cast<LocalizedOption>(it->second);
size_t opt_pos = option->getOffset();
-
if (opt_pos == 0) {
- isc_throw(isc::BadValue, "Failed to unpack packet from raw buffer "
+ isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
"(Option position not specified)");
} else if (opt_pos + 4 > data_.size()) {
- isc_throw(isc::BadValue, "Failed to unpack options from from raw buffer "
+ isc_throw(isc::BadValue,
+ "failed to unpack options from from raw buffer "
"(Option position out of bounds)");
}
size_t offset = opt_pos;
+
+ // Get option type from first two octets.
uint16_t opt_type = data_[offset] * 256 + data_[offset + 1];
if (opt_type != option->getType()) {
isc_throw(isc::BadValue,
- "Failed to unpack option from raw buffer (Option type mismatch)");
+ "failed to unpack option from raw buffer "
+ "(option type mismatch)");
}
+
+ // Get length from next two octets.
offset += 2;
uint16_t opt_len = data_[offset] * 256 + data_[offset + 1];
- offset += 2;
if (offset + opt_len > data_.size()) {
isc_throw(isc::BadValue,
- "Failed to unpack option from raw buffer (Option truncated)");
+ "failed to unpack option from raw buffer "
+ "(option truncated)");
}
- option->setData(data_.begin() + offset, data_.begin() + offset + opt_len);
- }
-}
-void
-PerfPkt6::updateTimestamp() {
- if (clock_gettime(CLOCK_REALTIME, &time_stamp_) < 0) {
- isc_throw(isc::Unexpected, "Failed to get timestamp for packet");
+ // Seek to actual option data and replace it.
+ offset += 2;
+ option->setData(data_.begin() + offset,
+ data_.begin() + offset + opt_len);
}
}
///
class PerfPkt6 : public dhcp::Pkt6 {
public:
+
+ /// \brief Represents offset value.
+ ///
+ /// This class represent offsets for DHCP message fields
+ /// like transaction id. Constructors of PerfPkt6 take
+ /// number of arguments of integer type so it is easy to
+ /// mess up arguments of constructors and for example
+ /// swap transaction id with its offset.
+ /// Use of this class implies that client class has to
+ /// explicitely use constructor of this class to pass
+ /// offset value. This should prevent mistakes and save some
+ /// time on debugging.
+ class Offset {
+ public:
+ /// \brief Default constructor
+ explicit Offset() :
+ offset_(1) { };
+
+ /// \brief Constructor
+ ///
+ /// \param offset offset value
+ explicit Offset(size_t offset) :
+ offset_(offset) { };
+
+ /// \brief Returns offset value.
+ ///
+ /// \return offset value.
+ size_t get() const { return offset_; };
+ private:
+ size_t offset_; ///< offset value
+ };
+
/// Localized option pointer type.
typedef boost::shared_ptr<LocalizedOption> LocalizedOptionPtr;
- /// Constructor, used in message transmission
+ /// \brief Constructor, used for outgoing DHCP messages.
+ ///
+ /// Creates new DHCPv6 message using provided buffer.
+ /// Transaction id and its offset are specified through this
+ /// constructor so as they are stored in outgoing message
+ /// when client class calls \ref PerfPkt6::rawPack.
+ ///
+ /// \note This constructor should be used only for outgoing
+ /// messages that are created from raw buffer (e.g. read from
+ /// template files).
+ ///
+ /// \param buf buffer holiding contents of the message (this can
+ /// be directly read from template file).
+ /// \param len length of the data in the buffer.
+ /// \param transid transaction id to be stored in outgoing message.
+ /// \param transid_offset transaction id offset in outgoing message.
+ PerfPkt6(const uint8_t* buf,
+ uint32_t len,
+ uint32_t transid,
+ const Offset& transid_offset);
+
+ /// Constructor, used for incoming DHCP messages.
///
/// Creates new DHCPv6 message using provided buffer. New object
/// will keep copy of contents of provided buffer. If buffer contains
/// offsets has to be provided - see
/// \ref isc::perfdhcp::LocalizedOption for details.
///
- /// Transaction id is not considered DHCP option so
- /// we pass it to constructor as extra argument. This is
- /// required if transaction id offset differs from the
- /// default one for DHCPv6 messages (ocets 2-4).
+ /// Transaction id offset point to location of raw data where
+ /// transaction id field is stored. The transaction id will
+ /// be read from this location when PerfPkt6::rawUnpack is
+ /// called. The transid_ class member will be updated accordingly.
///
/// \note use this constructor only in case you want to create
- /// DHCPv6 message (incoming or outgoing) from the raw buffer
+ /// incoming DHCPv6 object from the raw buffer
/// and you know options offsets. Options offsets are
/// specified from perfdhcp command line by the user.
///
- /// \param buf pointer to a buffer of received packet content
- /// \param len size of buffer of packet content
- /// \param xid_off transaction id offset in a packet
+ /// \param buf pointer to a buffer of received packet content.
+ /// \param len size of buffer of packet content.
+ /// \param transid_offset transaction id offset in a message.
PerfPkt6(const uint8_t* buf,
uint32_t len,
- size_t transid_offset_);
+ const Offset& transid_offset);
/// \brief Returns transaction id offset in packet buffer
///
/// return transaction id offset in packet buffer
size_t getTransIdOffset() const { return transid_offset_; };
- /// \brief Returns current packet timestamp
- ///
- /// \return current packet timestamp
- timespec getTimestamp() const { return time_stamp_; }
-
/// \brief Prepares on-wire format from raw buffer
///
/// The method copies user buffer to output buffer and
void rawUnpackOptions();
size_t transid_offset_; ///< transaction id offset
- timespec time_stamp_; ///< time stamp of last pack or unpack
};
typedef PerfPkt6::LocalizedOptionPtr LocalizedOptionPtr;
namespace {
+
class PerfPkt6Test : public ::testing::Test {
public:
PerfPkt6Test() {
}
- /// @brief returns captured actual SOLICIT packet
+ /// \brief Returns captured SOLICIT packet.
///
/// Captured SOLICIT packet with transid=0x3d79fb and options: client-id,
/// in_na, dns-server, elapsed-time, option-request
- /// This code was autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
+ /// This code was autogenerated
+ /// (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
/// but we spent some time to make is less ugly than it used to be.
///
- /// @return pointer to Pkt6 that represents received SOLICIT
- PerfPkt6* capture1() {
+ /// \return pointer to Pkt6 that represents received SOLICIT
+ PerfPkt6* capture() {
uint8_t data[98];
data[0] = 1;
data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0;
data[93] = 6; data[94] = 0; data[95] = 2; data[96] = 0;
data[97] = 23;
- PerfPkt6* pkt = new PerfPkt6(data, sizeof(data), 0);
+ PerfPkt6* pkt = new PerfPkt6(data, sizeof(data),
+ 0x1, PerfPkt6::Offset());
+
+ return (pkt);
+ }
+
+ /// \brief Returns truncated SOLICIT packet.
+ ///
+ /// Returns truncated SOLICIT packet which will be used for
+ /// negative tests: e.g. pack options out of packet.
+ ///
+ /// \return pointer to Pkt6 that represents truncated SOLICIT
+ PerfPkt6* captureTruncated() {
+ uint8_t data[17];
+ data[0] = 1;
+ data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0;
+ data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0;
+ data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21;
+ data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0;
+
+ PerfPkt6* pkt = new PerfPkt6(data, sizeof(data),
+ 0x1, PerfPkt6::Offset());
return (pkt);
}
+
};
TEST_F(PerfPkt6Test, Constructor) {
+ // Data to be used to create packet.
uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
- timespec zero_t_spec;
- memset(&zero_t_spec, 0, sizeof(zero_t_spec));
- boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data, sizeof(data), 1));
- timespec packet_t_spec = pkt1->getTimestamp();
+ // Test constructors of Offset class.
+ PerfPkt6::Offset of1(0);
+ EXPECT_EQ(0, of1.get());
+
+ PerfPkt6::Offset of2;
+ EXPECT_EQ(1, of2.get());
+ PerfPkt6::Offset of3(10);
+ EXPECT_EQ(10, of3.get());
+
+ // Test constructor to be used for incoming messages.
+ // Use default (1) offset value and don't specify transaction id.
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data, sizeof(data),
+ PerfPkt6::Offset()));
EXPECT_EQ(6, pkt1->getData().size());
- EXPECT_EQ(0, memcmp( &pkt1->getData()[0], data, sizeof(data)));
+ EXPECT_EQ(0, memcmp(&pkt1->getData()[0], data, sizeof(data)));
EXPECT_EQ(1, pkt1->getTransIdOffset());
- EXPECT_EQ(0, memcmp(&packet_t_spec, &zero_t_spec, sizeof(zero_t_spec)));
+
+ // Test constructor to be used for outgoing messages.
+ // Use non-zero offset and specify transaction id.
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data, sizeof(data), 0x010203,
+ PerfPkt6::Offset(10)));
+ EXPECT_EQ(6, pkt2->getData().size());
+ EXPECT_EQ(0, memcmp(&pkt2->getData()[0], data, sizeof(data)));
+ EXPECT_EQ(0x010203, pkt2->getTransid());
+ EXPECT_EQ(10, pkt2->getTransIdOffset());
}
TEST_F(PerfPkt6Test, RawPackUnpack) {
// Create first packet.
- boost::scoped_ptr<PerfPkt6> pkt1(capture1());
+ boost::scoped_ptr<PerfPkt6> pkt1(capture());
// Create some input buffers to initialize options.
uint8_t buf_elapsed_time[] = { 1, 1 };
uint8_t buf_duid[14] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
// Create options.
- dhcp::OptionBuffer vec_elapsed_time(buf_elapsed_time, buf_elapsed_time + 2);
- dhcp::OptionBuffer vec_duid(buf_duid, buf_duid + 14);
- LocalizedOptionPtr pkt1_elapsed_time(new LocalizedOption(Option::V6, 8, vec_elapsed_time, 86));
- LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, 4));
+ OptionBuffer vec_elapsed_time(buf_elapsed_time, buf_elapsed_time + 2);
+ OptionBuffer vec_duid(buf_duid, buf_duid + 14);
+ LocalizedOptionPtr pkt1_elapsed_time(new LocalizedOption(Option::V6,
+ D6O_ELAPSED_TIME,
+ vec_elapsed_time,
+ 86));
+ LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ 4));
// Add option to packet and create on-wire format from added options.
// Contents of options will override contents of packet buffer.
vec_elapsed_time.clear();
vec_duid.clear();
- // Get output buffer from packet 1 to create new packet
+ // Get output buffer from packet 1 to create new packet
// that will be later validated.
- isc::util::OutputBuffer pkt1_output = pkt1->getBuffer();
+ util::OutputBuffer pkt1_output = pkt1->getBuffer();
ASSERT_EQ(pkt1_output.getLength(), pkt1->getData().size());
- boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(static_cast<const uint8_t*>(pkt1_output.getData()),
- pkt1_output.getLength(), 1));
+ const uint8_t* pkt1_output_data = static_cast<const uint8_t*>
+ (pkt1_output.getData());
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(pkt1_output_data,
+ pkt1_output.getLength(),
+ PerfPkt6::Offset()));
- // Create objects specifying options offset in a packet.
+ // Create objects specifying options offset in a packet.
// Offsets will inform pkt2 object where to read data from.
- LocalizedOptionPtr pkt2_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME, vec_elapsed_time, 86));
- LocalizedOptionPtr pkt2_duid(new LocalizedOption(Option::V6, D6O_CLIENTID, vec_duid, 4));
+ LocalizedOptionPtr pkt2_elapsed_time(new LocalizedOption(Option::V6,
+ D6O_ELAPSED_TIME,
+ vec_elapsed_time,
+ 86));
+ LocalizedOptionPtr pkt2_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ 4));
// Add options to packet to pass their offsets.
pkt2->addOption(pkt2_elapsed_time);
pkt2->addOption(pkt2_duid);
ASSERT_TRUE(pkt2->rawUnpack());
// Once option data is stored in options objects we pull it out.
- pkt2_elapsed_time = boost::dynamic_pointer_cast<LocalizedOption>(pkt2->getOption(D6O_ELAPSED_TIME));
- pkt2_duid = boost::dynamic_pointer_cast<LocalizedOption>(pkt2->getOption(D6O_CLIENTID));
+ pkt2_elapsed_time = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt2->getOption(D6O_ELAPSED_TIME));
+ pkt2_duid = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt2->getOption(D6O_CLIENTID));
// Check if options are present. They have to be there since
// we have added them ourselfs.
OptionBuffer pkt2_elapsed_time_data = pkt2_elapsed_time->getData();
OptionBuffer pkt2_duid_data = pkt2_duid->getData();
EXPECT_EQ(0x0101, pkt2_elapsed_time->getUint16());
- EXPECT_TRUE(std::equal(pkt2_duid_data.begin(), pkt2_duid_data.end(), buf_duid));
+ EXPECT_TRUE(std::equal(pkt2_duid_data.begin(),
+ pkt2_duid_data.end(),
+ buf_duid));
+}
+
+TEST_F(PerfPkt6Test, InvalidOptions) {
+ // Create packet.
+ boost::scoped_ptr<PerfPkt6> pkt1(capture());
+ OptionBuffer vec_server_id;
+ vec_server_id.resize(10);
+ // Testing invalid offset of the option (greater than packet size)
+ LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6,
+ D6O_SERVERID,
+ vec_server_id,
+ 150));
+ pkt1->addOption(pkt1_serverid);
+ // Pack has to fail due to invalid offset.
+ EXPECT_FALSE(pkt1->rawPack());
+
+ // Create packet.
+ boost::scoped_ptr<PerfPkt6> pkt2(capture());
+ // Testing offset of the option (lower than pakcet size but
+ // tail of the option out of bounds).
+ LocalizedOptionPtr pkt2_serverid(new LocalizedOption(Option::V6,
+ D6O_SERVERID,
+ vec_server_id,
+ 85));
+ pkt2->addOption(pkt2_serverid);
+ // Pack must fail due to invalid offset.
+ EXPECT_FALSE(pkt2->rawPack());
+}
+
+
+TEST_F(PerfPkt6Test, TruncatedPacket) {
+ cout << "Testing parsing options from truncated packet."
+ << "This may produce spurious errors" << endl;
+
+ // Create truncated (in the middle of duid options)
+ boost::scoped_ptr<PerfPkt6> pkt1(captureTruncated());
+ OptionBuffer vec_duid;
+ vec_duid.resize(30);
+ LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+ D6O_CLIENTID,
+ vec_duid,
+ 4));
+ pkt1->addOption(pkt1_duid);
+ // Pack/unpack must fail because length of the option read from buffer
+ // will extend over the actual packet length.
+ EXPECT_FALSE(pkt1->rawUnpack());
+ EXPECT_FALSE(pkt1->rawPack());
+}
+
+TEST_F(PerfPkt6Test, PackTransactionId) {
+ uint8_t data[100] = { 0 };
+
+ // Create dummy packet that is simply filled with zeros.
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+ sizeof(data),
+ 0x010203,
+ PerfPkt6::Offset(50)));
+
+ // Reference data are non zero so we can detect them in dummy packet.
+ uint8_t ref_data[3] = { 1, 2, 3 };
+
+ // This will store given transaction id in the packet data at
+ // offset of 50.
+ ASSERT_TRUE(pkt1->rawPack());
+
+ // Get the output buffer so we can validate it.
+ util::OutputBuffer out_buf = pkt1->getBuffer();
+ ASSERT_EQ(sizeof(data), out_buf.getLength());
+ const uint8_t *out_buf_data = static_cast<const uint8_t*>
+ (out_buf.getData());
+
+ // Validate transaction id.
+ EXPECT_EQ(0, memcmp(out_buf_data + 50, ref_data, 3));
+
+ // Out of bounds transaction id offset.
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+ sizeof(data),
+ 0x010202,
+ PerfPkt6::Offset(100)));
+ cout << "Testing out of bounds offset. "
+ "This may produce spurious errors ..." << endl;
+ EXPECT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt6Test, UnpackTransactionId) {
+ // Initialize data for dummy packet (zeros only).
+ uint8_t data[100] = { 0 };
+
+ // Generate transaction id = 0x010203 and inject at offset = 50.
+ for (int i = 50; i < 53; ++i) {
+ data[i] = i - 49;
+ }
+ // Create packet and point out that transaction id is at offset 50.
+ boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+ sizeof(data),
+ PerfPkt6::Offset(50)));
+
+ // Get transaction id out of buffer and store in class member.
+ ASSERT_TRUE(pkt1->rawUnpack());
+ // Test value of transaction id.
+ EXPECT_EQ(0x010203, pkt1->getTransid());
+
+ // Out of bounds transaction id offset.
+ boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+ sizeof(data),
+ PerfPkt6::Offset(300)));
+ cout << "Testing out of bounds offset. "
+ "This may produce spurious errors ..." << endl;
+ EXPECT_FALSE(pkt2->rawUnpack());
+
}
}