#ifndef __PKT_TRANSFORM_H
#define __PKT_TRANSFORM_H
-#include <boost/shared_ptr.hpp>
#include <dhcp/option.h>
#include "localized_option.h"
///
/// This class provides static functions to read raw
/// data from packet buffer and write raw data to packet
-/// buffer. When reading data with unpack() method, the
-/// corresponding options objects are updated.
-/// When writting to the packet buffer with pack() nethod,
+/// buffer. When reading data with unpack() method, the
+/// corresponding options objects are updated.
+/// When writting to the packet buffer with pack() nethod,
/// options objects carry input data to be written.
-/// This class is used both by \ref PerfPkt4 and
+/// This class is used both by \ref PerfPkt4 and
/// \ref PerfPkt6 classes in case DHCP packets are created
/// from template files. In this case, some of the template
-/// packet's options are replaced before sending it to
+/// packet's options are replaced before sending it to
/// server. Offset of specific options are provided from
/// command line by perfdhcp tool user and passed in
/// options collection.
/// This class also provide mechanism to read some data
-/// from incoming packet buffer and update options
-/// in options collection with these data.
+/// from incoming packet buffer and update options
+/// in options collection with these data.
/// It is assumed either in case of writting or reading
/// that all options have to be added to options collection
/// and their offset have to be specified in buffer
/// to output buffer. Input buffer must contain whole
/// initial packet data. Parts of this data will be
/// overriden by options data specified in options
- /// collection. Such options must have their offsets in
+ /// collection. Such options must have their offsets in
/// a packet specified (\see LocalizedOption to find out how
/// to specify options offset).
///
- /// \note Specified options must fit into size of the
+ /// \note Specified options must fit into size of the
/// initial packet data. Call to this function will fail
/// if option's offset + its size is out of bounds.
///
/// offset
/// \param transid transaction id value
/// \param out_buffer output buffer holding "packed" data
- ///
+ ///
/// \retrun false, if pack operation failed.
static bool pack(const dhcp::Option::Universe universe,
const dhcp::OptionBuffer& in_buffer,
/// \ref isc::dhcp::Pkt6::addOption to specify which options to parse.
/// Each option should be of the \ref isc::perfdhcp::LocalizedOption
/// type with offset value indicated.
- /// Transaction id offset is specified as separate argument and
- /// is used to read transaction id value from buffer.
+ /// Transaction id offset is specified as separate argument and
+ /// is used to read transaction id value from buffer.
///
/// \param universe universe used, V4 or V6
/// \param in_buffer input buffer to be parsed
/// \param transid_offset offset of transaction id in input buffer
/// \param transid transaction id value read from input buffer
/// \return false, if unpack operation failed.
- static bool unpack(const dhcp::Option::Universe universe,
+ static bool unpack(const dhcp::Option::Universe universe,
const dhcp::OptionBuffer& in_buffer,
const dhcp::Option::OptionCollection& options,
const size_t transid_offset,
private:
/// \brief Replaces options contents of options in a buffer
///
- /// The method uses localized options collection added to
- /// replace parts of initial packet data (e.g. read from
- /// template file).
+ /// The method uses localized options collection added to
+ /// replace parts of initial packet data (e.g. read from
+ /// template file).
/// This private method is called from \ref PktTransform::pack
///
/// \param in_buffer input buffer holding initial packet data.
///
/// The method reads options data from the input buffer
/// and stores read data in options objects. Options
- /// must have their offsets in a buffer specified
+ /// must have their offsets in a buffer specified
/// (\see LocalizedOption to find out how to specify
/// option offset).
/// This private method is called by \ref PktTransform::unpack.
///
- /// \note This method iterates through all options in
+ /// \note This method iterates through all options in
/// options collection, checks offset of the option
/// in input buffer and reads data from the buffer to
/// update option's buffer. If provided options collection
/// is empty, call to this function will have no effect.
- ///
+ ///
/// \param universe universe used, V4 or V6
/// \param in_buffer input buffer to be parsed.
- /// \param options oprions collection with their offsets
+ /// \param options oprions collection with their offsets
/// in input buffer specified.
/// \throw isc::Unexpected if options unpack failed.
- static void unpackOptions(const dhcp::Option::Universe universe,
- const dhcp::OptionBuffer& in_buffer,
+ static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
const dhcp::Option::OptionCollection& options);
};
namespace {
-// a sample data
-const uint8_t dummyOp = BOOTREQUEST;
-const uint8_t dummyHtype = 6;
-const uint8_t dummyHlen = 6;
-const uint8_t dummyHops = 13;
-const uint32_t dummyTransid = 0x12345678;
-const uint16_t dummySecs = 42;
-const uint16_t dummyFlags = BOOTP_BROADCAST;
-
-const IOAddress dummyCiaddr("192.0.2.1");
-const IOAddress dummyYiaddr("1.2.3.4");
-const IOAddress dummySiaddr("192.0.2.255");
-const IOAddress dummyGiaddr("255.255.255.255");
-
-// a dummy MAC address
-const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5};
-
// a dummy MAC address, padded with 0s
const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
PerfPkt4Test() {
}
- /// \brief Returns captured SOLICIT packet.
+ /// \brief Returns buffer with sample DHCPDISCOVER message.
///
- /// 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),
- /// but we spent some time to make is less ugly than it used to be.
+ /// This method creates buffer containing on-wire data of
+ /// DHCPDICOSVER message. This buffer is used by tests below
+ /// to create DHCPv4 test packets.
///
- /// \return pointer to Pkt6 that represents received SOLICIT
- std::vector<uint8_t> capture() {
+ /// \return vector containing on-wire data
+ std::vector<uint8_t>& capture() {
// That is only part of the header. It contains all "short" fields,
// larger fields are constructed separately.
12, 3, 0, 1, 2, // Host name option.
13, 3, 10, 11, 12, // Boot file size option
14, 3, 20, 21, 22, // Merit dump file
- 53, 1, 1, // DHCP message type.
- 128, 3, 30, 31, 32,
+ 53, 1, 1, // DHCP message type.
+ 128, 3, 30, 31, 32,
254, 3, 40, 41, 42,
};
// Initialize the vector with the header fields defined above.
- vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
-
- // Append the large header fields.
- std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN, back_inserter(buf));
- std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN, back_inserter(buf));
- std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN, back_inserter(buf));
-
- // Append magic cookie.
- buf.push_back(0x63);
- buf.push_back(0x82);
- buf.push_back(0x53);
- buf.push_back(0x63);
-
- // Append options.
- std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf));
-
+ static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+
+ // If this is a first call to this function. Initialize
+ // remaining data.
+ if (buf.size() == sizeof(hdr))
+ {
+
+ // Append the large header fields.
+ std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN,
+ back_inserter(buf));
+ std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN,
+ back_inserter(buf));
+ std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN,
+ back_inserter(buf));
+
+ // Append magic cookie.
+ buf.push_back(0x63);
+ buf.push_back(0x82);
+ buf.push_back(0x53);
+ buf.push_back(0x63);
+
+ // Append options.
+ std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf));
+ }
return buf;
}
};
// Test constructor to be used for outgoing messages.
// Use non-zero offset and specify transaction id.
- boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data), 10, 0x010203));
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
+ 10, 0x010203));
EXPECT_EQ(0x010203, pkt2->getTransid());
EXPECT_EQ(10, pkt2->getTransIdOffset());
+
+ // Test default constructor. Transaction id offset is expected to be 1.
+ boost::scoped_ptr<PerfPkt4> pkt3(new PerfPkt4(data, sizeof(data)));
+ EXPECT_EQ(1, pkt3->getTransIdOffset());
}
TEST_F(PerfPkt4Test, RawPack) {
// Create new packet.
std::vector<uint8_t> buf = capture();
- boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1));
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
// Initialize options data.
uint8_t buf_hostname[] = { 12, 3, 4, 5, 6 };
uint8_t buf_boot_filesize[] = { 13, 3, 1, 2, 3 };
OptionBuffer vec_hostname(buf_hostname + 2, buf_hostname + 5);
- OptionBuffer vec_boot_filesize(buf_boot_filesize + 2, buf_boot_filesize + 5);
+ OptionBuffer vec_boot_filesize(buf_boot_filesize + 2,
+ buf_boot_filesize + 5);
// Create options objects.
- LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
+ LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
DHO_HOST_NAME,
vec_hostname,
240));
- LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
+ LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
DHO_BOOT_SIZE,
vec_boot_filesize,
245));
ASSERT_NO_THROW(pkt->addOption(pkt_boot_filesize));
ASSERT_NO_THROW(pkt->addOption(pkt_hostname));
- // We have valid options addedwith valid offsets so
+ // We have valid options addedwith valid offsets so
// pack operation should succeed.
ASSERT_TRUE(pkt->rawPack());
// DHO_BOOT_SIZE options.
util::OutputBuffer pkt_output = pkt->getBuffer();
ASSERT_EQ(buf.size(), pkt_output.getLength());
- const uint8_t* out_buf_data = static_cast<const uint8_t*>(pkt_output.getData());
+ const uint8_t* out_buf_data =
+ static_cast<const uint8_t*>(pkt_output.getData());
// Check if options we read from buffer is valid.
EXPECT_EQ(0, memcmp(buf_hostname, out_buf_data + 240, 5));
TEST_F(PerfPkt4Test, RawUnpack) {
// Create new packet.
std::vector<uint8_t> buf = capture();
- boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1));
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
// Create options (existing in the packet) and specify their offsets.
LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
DHO_MERIT_DUMP,
OptionBuffer(),
250));
-
+
LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
DHO_DHCP_MESSAGE_TYPE,
- OptionBuffer(),
+ OptionBuffer(),
255));
// Addition should be successful
ASSERT_NO_THROW(pkt->addOption(opt_merit));
ASSERT_NO_THROW(pkt->addOption(opt_msg_type));
-
+
// Option fit to packet boundaries and offsets are valid,
// so this should unpack successfully.
ASSERT_TRUE(pkt->rawUnpack());
// At this point we should have updated options data (read from buffer).
// Let's try to retrieve them.
- opt_merit = boost::dynamic_pointer_cast<LocalizedOption>(pkt->getOption(DHO_MERIT_DUMP));
- opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>(pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
+ opt_merit = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt->getOption(DHO_MERIT_DUMP));
+ opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>
+ (pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
ASSERT_TRUE(opt_merit);
ASSERT_TRUE(opt_msg_type);
// Get first option payload.
OptionBuffer opt_merit_data = opt_merit->getData();
- // Define reference data.
- uint8_t buf_merit[] = { 20, 21, 22 };
+ // Define reference data.
+ uint8_t buf_merit[] = { 20, 21, 22 };
// Validate first option data.
ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size());
- EXPECT_TRUE(std::equal(opt_merit_data.begin(), opt_merit_data.end(), buf_merit));
-
+ EXPECT_TRUE(std::equal(opt_merit_data.begin(),
+ opt_merit_data.end(),
+ buf_merit));
+
// Get second option payload.
OptionBuffer opt_msg_type_data = opt_msg_type->getData();
EXPECT_EQ(1, opt_msg_type_data[0]);
}
- /*
TEST_F(PerfPkt4Test, InvalidOptions) {
- // Create packet.
- boost::scoped_ptr<PerfPkt4> 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<PerfPkt4> 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());
-}
+ // Create new packet.
+ std::vector<uint8_t> buf = capture();
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size()));
+ // Create option with invalid offset.
+ // This option is at offset 250 (not 251).
+ LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
+ DHO_MERIT_DUMP,
+ OptionBuffer(),
+ 251));
+ ASSERT_NO_THROW(pkt1->addOption(opt_merit));
-TEST_F(PerfPkt4Test, TruncatedPacket) {
- cout << "Testing parsing options from truncated packet."
- << "This may produce spurious errors" << endl;
+ cout << "Testing unpack of invalid options. "
+ << "This may produce spurious errors." << endl;
- // Create truncated (in the middle of duid options)
- boost::scoped_ptr<PerfPkt4> 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());
+ // Unpack is expected to fail because it is supposed to read
+ // option type from buffer and match it with DHO_MERIT_DUMP.
+ // It will not match because option is shifted by on byte.
+ ASSERT_FALSE(pkt1->rawUnpack());
+
+ // Crete another packet.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size()));
+
+ // Create DHO_DHCP_MESSAGE_TYPE option that has wrong offset.
+ // With this offset, option goes beyond packet size (268).
+ LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
+ DHO_DHCP_MESSAGE_TYPE,
+ OptionBuffer(1, 2),
+ 266));
+ // Adding option is expected to be successful because no
+ // offset validation takes place at this point.
+ ASSERT_NO_THROW(pkt2->addOption(opt_msg_type));
+
+ // This is expected to fail because option is out of bounds.
+ ASSERT_FALSE(pkt2->rawPack());
}
-TEST_F(PerfPkt4Test, PackTransactionId) {
- uint8_t data[100] = { 0 };
+TEST_F(PerfPkt4Test, TruncatedPacket) {
+ // Get the whole packet and truncate it to 249 bytes.
+ std::vector<uint8_t> buf = capture();
+ buf.resize(249);
+ boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+ // Option DHO_BOOT_SIZE is now truncated because whole packet
+ // is truncated. This option ends at 249 while last index of
+ // truncated packet is now 248.
+ LocalizedOptionPtr opt_boot_filesize(new LocalizedOption(Option::V4,
+ DHO_BOOT_SIZE,
+ OptionBuffer(3, 1),
+ 245));
+ ASSERT_NO_THROW(pkt->addOption(opt_boot_filesize));
- // Create dummy packet that is simply filled with zeros.
- boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(data,
- sizeof(data),
- 0x010203,
- PerfPkt4::Offset(50)));
+ cout << "Testing pack and unpack of options in truncated "
+ << "packet. This may produce spurious errors." << endl;
- // Reference data are non zero so we can detect them in dummy packet.
- uint8_t ref_data[3] = { 1, 2, 3 };
+ // Both pack and unpack are expected to fail because
+ // added option is out of bounds.
+ EXPECT_FALSE(pkt->rawUnpack());
+ EXPECT_FALSE(pkt->rawPack());
+}
- // This will store given transaction id in the packet data at
- // offset of 50.
+TEST_F(PerfPkt4Test, PackTransactionId) {
+ // Create dummy packet that consists of zeros.
+ std::vector<uint8_t> buf(268, 0);
+ // Initialize transaction id 0x00000102 at offset 10.
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size(),
+ 10, 0x0102));
+
+ // Pack will inject transaction id at offset 10 into the
+ // packet buffer.
ASSERT_TRUE(pkt1->rawPack());
- // Get the output buffer so we can validate it.
+ // Get packet's output buffer and make sure it has valid size.
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<PerfPkt4> pkt2(new PerfPkt4(data,
- sizeof(data),
- 0x010202,
- PerfPkt4::Offset(100)));
- cout << "Testing out of bounds offset. "
- "This may produce spurious errors ..." << endl;
+ ASSERT_EQ(buf.size(), out_buf.getLength());
+ const uint8_t *out_buf_data =
+ static_cast<const uint8_t*>(out_buf.getData());
+
+ // Initialize reference data for transaction id.
+ const uint8_t ref_data[] = { 0, 0, 1, 2 };
+
+ // Expect that reference transaction id matches what we have
+ // read from buffer.
+ EXPECT_EQ(0, memcmp(ref_data, out_buf_data + 10, 4));
+
+ cout << "Testing pack with invalid transaction id offset. "
+ << "This may produce spurious errors" << endl;
+
+ // Create packet with invalid transaction id offset.
+ // Packet length is 268, transaction id is 4 bytes long so last byte of
+ // transaction id is out of bounds.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size(),
+ 265, 0x0102));
EXPECT_FALSE(pkt2->rawPack());
}
TEST_F(PerfPkt4Test, UnpackTransactionId) {
- // Initialize data for dummy packet (zeros only).
- uint8_t data[100] = { 0 };
+ // Initialize packet data, lebgth 268, zeros only.
+ std::vector<uint8_t> in_data(268, 0);
- // Generate transaction id = 0x010203 and inject at offset = 50.
- for (int i = 50; i < 53; ++i) {
- data[i] = i - 49;
+ // Assume that transaction id is at offset 100.
+ // Fill 4 bytes at offset 100 with dummy transaction id.
+ for (int i = 100; i < 104; ++i) {
+ in_data[i] = i - 99;
}
- // Create packet and point out that transaction id is at offset 50.
- boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(data,
- sizeof(data),
- PerfPkt4::Offset(50)));
- // Get transaction id out of buffer and store in class member.
+ // Create packet from initialized buffer.
+ boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
+ in_data.size(),
+ 100));
ASSERT_TRUE(pkt1->rawUnpack());
- // Test value of transaction id.
- EXPECT_EQ(0x010203, pkt1->getTransid());
-
- // Out of bounds transaction id offset.
- boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data,
- sizeof(data),
- PerfPkt4::Offset(300)));
- cout << "Testing out of bounds offset. "
- "This may produce spurious errors ..." << endl;
- EXPECT_FALSE(pkt2->rawUnpack());
- } */
+ // Get unpacked transaction id and compare with reference.
+ EXPECT_EQ(0x01020304, pkt1->getTransid());
+
+ // Create packet with transaction id at invalid offset.
+ boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&in_data[0],
+ in_data.size(),
+ 270));
+
+ cout << "Testing unpack of transaction id at invalid offset."
+ << "This may produce spurious errors." << endl;
+
+ // Unpack is supposed to fail because transaction id is at
+ // out of bounds offset.
+ EXPECT_FALSE(pkt2->rawUnpack());
+}
}