Commit 5c430ad5 authored by Marcin Siodelski's avatar Marcin Siodelski

[1955] Added basic unit tests and DHCP v4 packet handler.

parent bff3d478
......@@ -28,6 +28,8 @@ 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
perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
......
// Copyright (C) 2011 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 <iostream>
#include <exceptions/exceptions.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/dhcp6.h>
#include "perf_pkt4.h"
#include "pkt_transform.h"
using namespace std;
using namespace isc;
using namespace dhcp;
namespace isc {
namespace perfdhcp {
PerfPkt4::PerfPkt4(const uint8_t* buf, uint32_t len, size_t transid_offset, uint32_t transid) :
Pkt4(buf, len),
transid_offset_(transid_offset) {
transid_ = transid;
}
PerfPkt4::PerfPkt4(const uint8_t* buf, uint32_t len, size_t transid_offset) :
Pkt4(buf, len),
transid_offset_(transid_offset) {
}
bool
PerfPkt4::rawPack() {
return (PktTransform::pack(dhcp::Option::V4,
data_,
options_,
transid_offset_,
transid_,
bufferOut_));
}
bool
PerfPkt4::rawUnpack() {
return (PktTransform::unpack(dhcp::Option::V4,
data_,
options_,
transid_offset_,
transid_));
}
} // namespace perfdhcp
} // namespace isc
// Copyright (C) 2011 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 __PERF_PKT4_H
#define __PERF_PKT4_H
#include <time.h>
#include <boost/shared_ptr.hpp>
#include <dhcp/pkt4.h>
#include "localized_option.h"
namespace isc {
namespace perfdhcp {
/// \brief PerfPkt4 (DHCPv4 packet)
///
/// This class extends functionality of \ref isc::dhcp::Pkt4 by
/// adding ability to specify options offset in DHCP message
/// and override options' contents with new option.
/// This approach is useful when we create paket object from
/// raw template buffer from file and we want to use it as
/// a base to create test packets to be sent to DHCP server.
///
/// Some of the contents of such a template packets always
/// have to be replaced e.g. transaction id, IA_NA. Other
/// contents (options) may be changed e.g. elapsed time,
/// server id.
///
/// In order to create packet from raw template buffer
/// we have to pass this buffer along with transaction id
/// offset. Class will read transaction id from the buffer.
/// Next, in order to replace contents of selected options
/// in a template packet, we need to add these selected options
/// to packet object using addOption() method. Please note
/// that options must be of the
/// \ref isc::perfdhcp::LocalizedOption type.
///
/// \note: if you don't use template files simply use constructors
/// inherited from parent class and isc::dhcp::Option type instead
///
class PerfPkt4 : public dhcp::Pkt4 {
public:
/// Localized option pointer type.
typedef boost::shared_ptr<LocalizedOption> LocalizedOptionPtr;
/// \brief Constructor, used for outgoing DHCP messages.
///
/// Creates new DHCPv4 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 PerfPkt4::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_offset transaction id offset in outgoing message.
/// \param transid transaction id to be stored in outgoing message.
PerfPkt4(const uint8_t* buf,
uint32_t len,
size_t transid_offset,
uint32_t transid);
/// 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
/// options at custom offsets (e.g. if packet was read from
/// template file) additional information about options'
/// offsets has to be provided - see
/// \ref isc::perfdhcp::LocalizedOption for details.
///
/// 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 PerfPkt4::rawUnpack is
/// called. The transid_ class member will be updated accordingly.
///
/// \note use this constructor only in case you want to create
/// 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 transid_offset transaction id offset in a message.
PerfPkt4(const uint8_t* buf,
uint32_t len,
size_t 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 Prepares on-wire format from raw buffer
///
/// The method copies user buffer to output buffer and
/// extracts transaction id from it based on transaction id
/// offset provided in constructor.
///
/// \note: Use this method to prepare on-wire DHCPv6 message
/// when you use template packets that require replacement
/// of selected options' contents before sending.
///
/// \retrun false, id pack operation failed.
bool rawPack();
/// \brief Handles limited binary packet parsing for packets with
/// custom offsets of options and transaction id
///
/// Function handles reception of packets that have non-default values
/// of options or transaction id offsets. Use
/// \ref isc::dhcp::Pkt4::addOption to specify which options to parse.
/// Each option should be of the: isc::perfdhcp::LocalizedOption
/// type with offset value indicated.
///
/// \return false, if unpack operation failed.
bool rawUnpack();
/// \brief Update packet timestamp with current time
///
/// \throw isc::Unexpected if timestamp update failed
void updateTimestamp();
private:
size_t transid_offset_; ///< transaction id offset
};
} // namespace perfdhcp
} // namespace isc
#endif // __PERF_PKT4_H
// Copyright (C) 2011 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 <iostream>
#include <exceptions/exceptions.h>
#include <dhcp/option.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/dhcp6.h>
#include "pkt_transform.h"
#include "localized_option.h"
using namespace std;
using namespace isc;
using namespace dhcp;
namespace isc {
namespace perfdhcp {
bool
PktTransform::pack(const Option::Universe universe,
const OptionBuffer& in_buffer,
const Option::OptionCollection& options,
const size_t transid_offset,
const uint32_t transid,
util::OutputBuffer& out_buffer) {
// Always override the packet if function is called.
out_buffer.clear();
// Write whole buffer to output buffer.
out_buffer.writeData(&in_buffer[0], in_buffer.size());
uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
(transid_offset == 0)) {
cout << "Failed to build packet: provided transaction id offset: "
<< transid_offset << " is out of bounds (expected 1.."
<< in_buffer.size()-1 << ")." << endl;
return (false);
}
// Seek to the transaction id position in output buffer.
out_buffer.clear();
out_buffer.skip(transid_offset);
try {
if (universe == Option::V6) {
out_buffer.writeUint8(transid >> 24 & 0xFF);
}
out_buffer.writeUint8(transid >> 16 & 0xFF);
out_buffer.writeUint8(transid >> 8 & 0xFF);
out_buffer.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.
out_buffer.skip(in_buffer.size() - out_buffer.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.
PktTransform::packOptions(in_buffer, out_buffer, options);
} catch (const isc::BadValue& e) {
cout << "Building packet failed: " << e.what() << endl;
return (false);
}
return (true);
}
bool
PktTransform::unpack(const Option::Universe universe,
const OptionBuffer& in_buffer,
const Option::OptionCollection& options,
const size_t transid_offset,
uint32_t& transid) {
uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
// Validate transaction id offset.
if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
(transid_offset == 0)) {
cout << "Failed to parse packet: provided transaction id offset: "
<< transid_offset << " is out of bounds (expected 1.."
<< in_buffer.size()-1 << ")." << endl;
return (false);
}
if (universe == Option::V6) {
transid = ((in_buffer[transid_offset] << 16) +
(in_buffer[transid_offset + 1] << 8) +
(in_buffer[transid_offset + 2]))
& 0xFFFFFF;
} else {
transid = ((in_buffer[transid_offset] << 24) +
(in_buffer[transid_offset +1 ] << 16) +
(in_buffer[transid_offset + 2] << 8) +
(in_buffer[transid_offset + 3]));
}
try {
PktTransform::unpackOptions(universe, in_buffer, options);
} catch (const isc::BadValue& e) {
cout << "Packet parsing failed: " << e.what() << endl;
return (false);
}
return (true);
}
void
PktTransform::packOptions(const OptionBuffer& in_buffer,
util::OutputBuffer& out_buffer,
const Option::OptionCollection& options) {
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 (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 offset = option->getOffset();
if ((offset == 0) ||
(offset + option->len() > in_buffer.size())) {
isc_throw(isc::BadValue,
"option offset for option: " << option->getType()
<< " is out of bounds (expected 1.."
<< in_buffer.size() - option->len() << ")");
}
out_buffer.clear();
out_buffer.skip(offset);
// Replace existing option with new value.
option->pack(out_buffer);
}
// Seek to the end of the buffer to make sure its size is correct.
out_buffer.skip(in_buffer.size() - out_buffer.getLength());
}
catch (const Exception&) {
isc_throw(isc::BadValue, "failed to pack options into buffer.");
}
}
void
PktTransform::unpackOptions(const Option::Universe universe,
const OptionBuffer& in_buffer,
const Option::OptionCollection& options) {
for (Option::OptionCollection::const_iterator it = options.begin();
it != options.end(); ++it) {
boost::shared_ptr<LocalizedOption> 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 "
"(Option position not specified)");
} else if (opt_pos + 4 > in_buffer.size()) {
isc_throw(isc::BadValue,
"failed to unpack options from from raw buffer "
"(Option position out of bounds)");
}
size_t offset = opt_pos;
size_t offset_step = 1;
uint16_t opt_type = 0;
if (universe == Option::V6) {
offset_step = 2;
// For DHCPv6 option type is in first two octets.
opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
} else {
// For DHCPv4 option type is in first octet.
opt_type = in_buffer[offset];
}
// Check if we got expected option type.
if (opt_type != option->getType()) {
isc_throw(isc::BadValue,
"failed to unpack option from raw buffer "
"(option type mismatch)");
}
// Get option length which is supposed to be after option type.
offset += offset_step;
uint16_t opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
if (universe == Option::V6) {
opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
} else {
opt_len = in_buffer[offset];
}
// Check if packet is not truncated.
if (offset + opt_len > in_buffer.size()) {
isc_throw(isc::BadValue,
"failed to unpack option from raw buffer "
"(option truncated)");
}
// Seek to actual option data and replace it.
offset += offset_step;
option->setData(in_buffer.begin() + offset,
in_buffer.begin() + offset + opt_len);
}
}
} // namespace perfdhcp
} // namespace isc
// Copyright (C) 2011 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 __PKT_TRANSFORM_H
#define __PKT_TRANSFORM_H
#include <boost/shared_ptr.hpp>
#include <dhcp/option.h>
#include "localized_option.h"
namespace isc {
namespace perfdhcp {
/// \brief Read and write raw data to DHCP packets.
///
/// 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,
/// options objects carry input data to be written.
/// 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
/// 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.
/// 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
/// (\see LocalizedOption).
class PktTransform {
public:
/// \brief Prepares on-wire format from raw buffer
///
/// The method copies input buffer and options contents
/// 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
/// a packet specified (\see LocalizedOption to find out how
/// to specify options offset).
///
/// \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.
///
/// \param universe universe used, V4 or V6
/// \param in_buffer input buffer holiding intial packet
/// data, this can be directly read from template file
/// \param options options collection with offsets
/// \param transid_offset offset of transaction id in a packet,
/// transatcion id will be written to output buffer at this
/// 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,
const dhcp::Option::OptionCollection& options,
const size_t transid_offset,
const uint32_t transid,
util::OutputBuffer& out_buffer);
/// \brief Handles limited binary packet parsing for packets with
/// custom offsets of options and transaction id.
///
/// Function handles parsing of packet that have non-default values
/// of options or transaction id offsets. Use
/// \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.
///
/// \param universe universe used, V4 or V6
/// \param in_buffer input buffer to be parsed
/// \param options options collection with options offsets
/// \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,
const dhcp::OptionBuffer& in_buffer,
const dhcp::Option::OptionCollection& options,
const size_t transid_offset,
uint32_t& transid);
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).
/// This private method is called from \ref PktTransform::pack
///
/// \param in_buffer input buffer holding initial packet data.
/// \param out_buffer output buffer with "packed" options.
/// \param options options collection with actual data and offsets.
/// \throw isc::Unexpected if options update failed.
static void packOptions(const dhcp::OptionBuffer& in_buffer,
util::OutputBuffer& out_buffer,
const dhcp::Option::OptionCollection& options);
/// \brief Reads contents of specified options from buffer
///
/// 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
/// (\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
/// 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
/// in input buffer specified.
/// \throw isc::Unexpected if options unpack failed.
static void unpackOptions(const dhcp::Option::Universe universe,
const dhcp::OptionBuffer& in_buffer,
const dhcp::Option::OptionCollection& options);
};
} // namespace perfdhcp
} // namespace isc
#endif // __PKT_TRANSFORM_H
......@@ -17,9 +17,12 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += command_options_unittest.cc
run_unittests_SOURCES += perf_pkt6_unittest.cc
run_unittests_SOURCES += perf_pkt4_unittest.cc
run_unittests_SOURCES += localized_option_unittest.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......@@ -27,6 +30,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
......
// Copyright (C) 2011-2012 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 <iostream>
#include <sstream>
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <asiolink/io_address.h>
#include <dhcp/option.h>
#include <dhcp/dhcp4.h>
#include "../localized_option.h"
#include "../perf_pkt4.h"
using namespace std;
using namespace isc;
using namespace isc::asiolink;