Browse Source

[1224] Pkt4 class and unittests implementation.

Tomek Mrugalski 13 years ago
parent
commit
81c031de6a

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -16,6 +16,7 @@ libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
 libdhcp_la_SOURCES += option6_addrlst.cc option6_addrlst.h
 libdhcp_la_SOURCES += dhcp6.h
 libdhcp_la_SOURCES += pkt6.cc pkt6.h
+libdhcp_la_SOURCES += pkt4.cc pkt4.h
 
 EXTRA_DIST  = README
 #EXTRA_DIST += log_messages.mes

+ 153 - 0
src/lib/dhcp/pkt4.cc

@@ -0,0 +1,153 @@
+// 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 <dhcp/pkt4.h>
+#include <dhcp/libdhcp.h>
+#include <dhcp/dhcp4.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace isc {
+
+Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
+     :local_addr_(IOAddress("0.0.0.0")),
+      remote_addr_(IOAddress("0.0.0.0")),
+      iface_(""),
+      ifindex_(0),
+      local_port_(DHCP4_SERVER_PORT),
+      remote_port_(DHCP4_CLIENT_PORT),
+      op_(BOOTREPLY),
+      htype_(HTYPE_ETHER),
+      hlen_(0),
+      hops_(0),
+      transid_(transid),
+      secs_(0),
+      flags_(0),
+      ciaddr_(IOAddress("0.0.0.0")),
+      yiaddr_(IOAddress("0.0.0.0")),
+      siaddr_(IOAddress("0.0.0.0")),
+      giaddr_(IOAddress("0.0.0.0")),
+      bufferIn_(0), // not used, this is TX packet
+      bufferOut_(DHCPV4_PKT_HDR_LEN),
+      msg_type_(msg_type)
+{
+    /// TODO: fixed fields, uncomment in ticket #1224
+    memset(chaddr_, 0, MAX_CHADDR_LEN);
+    memset(sname_, 0, MAX_SNAME_LEN);
+    memset(file_, 0, MAX_FILE_LEN);
+}
+
+Pkt4::Pkt4(const uint8_t* data, size_t len)
+     :local_addr_(IOAddress("0.0.0.0")),
+      remote_addr_(IOAddress("0.0.0.0")),
+      iface_(""),
+      ifindex_(-1),
+      local_port_(DHCP4_SERVER_PORT),
+      remote_port_(DHCP4_CLIENT_PORT),
+      /// TODO Fixed fields, uncomment in ticket #1224
+      op_(BOOTREQUEST),
+      transid_(transid_),
+      secs_(0),
+      flags_(0),
+      ciaddr_(IOAddress("0.0.0.0")),
+      yiaddr_(IOAddress("0.0.0.0")),
+      siaddr_(IOAddress("0.0.0.0")),
+      giaddr_(IOAddress("0.0.0.0")),
+      bufferIn_(0), // not used, this is TX packet
+      bufferOut_(DHCPV4_PKT_HDR_LEN),
+      msg_type_(DHCPDISCOVER)
+{
+    bufferIn_.writeData(data, len);
+}
+
+size_t
+Pkt4::len() {
+    size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header
+
+    /// TODO: Include options here (ticket #1228)
+    return (length);
+}
+
+bool
+Pkt4::pack() {
+    /// TODO: Implement this (ticket #1227)
+
+    return (false);
+}
+bool
+Pkt4::unpack() {
+    /// TODO: Implement this (ticket #1226)
+
+    return (false);
+}
+
+std::string
+Pkt4::toText() {
+    stringstream tmp;
+    tmp << "localAddr=[" << local_addr_.toText() << "]:" << local_port_
+        << " remoteAddr=[" << remote_addr_.toText()
+        << "]:" << remote_port_ << endl;
+    tmp << "msgtype=" << msg_type_ 
+        << ", transid=0x" << hex << transid_ << dec 
+        << endl;
+
+    return tmp.str();
+}
+
+void 
+Pkt4::setHWAddr(uint8_t hType, uint8_t hlen, const uint8_t* macAddr) {
+    /// TODO Rewrite this once support for client-identifier option
+    /// is implemented (ticket 1228?)
+    if (hlen>MAX_CHADDR_LEN) {
+        isc_throw(OutOfRange, "Hardware address (len=" << hlen 
+                  << " too long. Max " << MAX_CHADDR_LEN << " supported.");
+    }
+    htype_ = hType;
+    hlen_ = hlen;
+    memset(chaddr_, 0, MAX_CHADDR_LEN);
+    memcpy(chaddr_, macAddr, hlen);
+}
+
+void
+Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
+    if (snameLen > MAX_SNAME_LEN) {
+        isc_throw(OutOfRange, "sname field (len=" << snameLen
+                  << ") too long, Max " << MAX_SNAME_LEN << " supported.");
+    }
+    memset(sname_, 0, MAX_SNAME_LEN);
+    memcpy(sname_, sname, snameLen);
+
+    // no need to store snameLen as any empty space is filled with 0s
+}
+
+void
+Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
+    if (fileLen > MAX_FILE_LEN) {
+        isc_throw(OutOfRange, "file field (len=" << fileLen
+                  << ") too long, Max " << MAX_FILE_LEN << " supported.");
+    }
+    memset(file_, 0, MAX_FILE_LEN);
+    memcpy(file_, file, fileLen);
+
+    // no need to store snameLen as any empty space is filled with 0s
+}
+
+
+};

+ 363 - 0
src/lib/dhcp/pkt4.h

@@ -0,0 +1,363 @@
+// 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 PKT4_H
+#define PKT4_H
+
+#include <iostream>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+#include "asiolink/io_address.h"
+#include "util/buffer.h"
+#include "dhcp/option.h"
+
+namespace isc {
+
+namespace dhcp {
+
+class Pkt4 {
+public:
+
+    // length of the CHADDR field in DHCPv4 message
+    const static size_t MAX_CHADDR_LEN = 16;
+
+    // length of the SNAME field in DHCPv4 message
+    const static size_t MAX_SNAME_LEN = 64;
+
+    // length of the FILE field in DHCPv4 message
+    const static size_t MAX_FILE_LEN = 128;
+
+    /// specifes DHCPv4 packet header length (fixed part)
+    const static size_t DHCPV4_PKT_HDR_LEN = 236;
+
+    /// Constructor, used in replying to a message
+    ///
+    /// @param msg_type type of message (e.g. DHCPDISOVER=1)
+    /// @param transid transaction-id
+    Pkt4(uint8_t msg_type, uint32_t transid);
+
+    /// Constructor, used in message transmission
+    ///
+    /// Creates new message. Transaction-id will randomized.
+    ///
+    /// @param data pointer to received data
+    /// @param len size of buffer to be allocated for this packet.
+    Pkt4(const uint8_t* data, size_t len);
+
+    /// @brief Prepares on-wire format.
+    ///
+    /// Prepares on-wire format of message and all its options.
+    /// Options must be stored in options_ field.
+    /// Output buffer will be stored in data_. Length
+    /// will be set in data_len_.
+    ///
+    /// @return true if packing procedure was successful
+    bool
+    pack();
+
+    /// @brief Parses on-wire form of UDP DHCPv6 packet.
+    ///
+    /// Parses received packet, stored in on-wire format in data_.
+    /// data_len_ must be set to indicate data length.
+    /// Will create a collection of option objects that will
+    /// be stored in options_ container.
+    ///
+    /// @return true, if build was successful
+    bool 
+    unpack();
+
+    /// @brief Returns text representation of the packet.
+    ///
+    /// This function is useful mainly for debugging.
+    ///
+    /// @return string with text representation
+    std::string
+    toText();
+
+    /// @brief Returns calculated length of the packet.
+    ///
+    /// This function returns size of required buffer to buld this packet.
+    /// To use that function, options_ field must be set.
+    ///
+    /// @return number of bytes required to build this packet
+    size_t
+    len();
+
+    /// Sets hops field
+    ///
+    /// @param hops value to be set
+    void
+    setHops(uint8_t hops) { hops_ = hops; };
+
+    /// Returns hops field
+    ///
+    /// @return hops field
+    uint8_t
+    getHops() { return (hops_); };
+
+    // Note: There's no need to manipulate OP field directly,
+    // thus no setOp() method. See op_ comment.
+
+    /// Returns op field
+    ///
+    /// @return op field
+    uint8_t
+    getOp() { return (op_); };
+
+    /// Sets secs field
+    ///
+    /// @param secs value to be set
+    void
+    setSecs(uint16_t secs) { secs_ = secs; };
+
+    /// Returns secs field
+    ///
+    /// @return secs field
+    uint16_t
+    getSecs() { return (secs_); };
+
+    /// Sets flags field
+    ///
+    /// @param flags value to be set
+    void
+    setFlags(uint16_t flags) { flags_ = flags; };
+
+    /// Returns flags field
+    ///
+    /// @return flags field
+    uint16_t
+    getFlags() { return (flags_); };
+
+
+    /// Returns ciaddr field
+    ///
+    /// @return ciaddr field
+    isc::asiolink::IOAddress&
+    getCiaddr() { return (ciaddr_); };
+
+    /// Sets ciaddr field
+    ///
+    /// @param ciaddr value to be set
+    void
+    setCiaddr(const isc::asiolink::IOAddress& ciaddr) { ciaddr_ = ciaddr; };
+
+
+    /// Returns siaddr field
+    ///
+    /// @return siaddr field
+    isc::asiolink::IOAddress&
+    getSiaddr() { return (siaddr_); };
+
+    /// Sets siaddr field
+    ///
+    /// @param siaddr value to be set
+    void
+    setSiaddr(const isc::asiolink::IOAddress& siaddr) { siaddr_ = siaddr; };
+
+
+    /// Returns yiaddr field
+    ///
+    /// @return yiaddr field
+    isc::asiolink::IOAddress&
+    getYiaddr() { return (yiaddr_); };
+
+    /// Sets yiaddr field
+    ///
+    /// @param yiaddr value to be set
+    void
+    setYiaddr(const isc::asiolink::IOAddress& yiaddr) { yiaddr_ = yiaddr; };
+
+
+    /// Returns giaddr field
+    ///
+    /// @return giaddr field
+    isc::asiolink::IOAddress&
+    getGiaddr() { return (giaddr_); };
+
+    /// Sets giaddr field
+    ///
+    /// @param giaddr value to be set
+    void
+    setGiaddr(const isc::asiolink::IOAddress& giaddr) { giaddr_ = giaddr; };
+
+    /// Returns value of transaction-id field
+    ///
+    /// @return transaction-id
+    uint32_t getTransid() { return (transid_); };
+
+    /// Returns message type (e.g. 1 = DHCPDISCOVER)
+    ///
+    /// @return message type
+    uint8_t
+    getType() { return (msg_type_); }
+
+    /// Sets message type (e.g. 1 = DHCPDISCOVER)
+    ///
+    /// @param type message type to be set
+    void setType(uint8_t type) { msg_type_=type; };
+
+    /// @brief Returns sname field
+    ///
+    /// Note: This is 64 bytes long field. It doesn't have to be
+    /// null-terminated. Do no use strlen() or similar on it.
+    ///
+    /// @return sname field
+    const uint8_t*
+    getSname() { return (sname_); };
+
+    /// Sets sname field
+    ///
+    /// @param sname value to be set
+    void
+    setSname(const uint8_t* sname, size_t snameLen = MAX_SNAME_LEN);
+
+    /// @brief Returns file field
+    ///
+    /// Note: This is 128 bytes long field. It doesn't have to be
+    /// null-terminated. Do no use strlen() or similar on it.
+    ///
+    /// @return pointer to file field
+    const uint8_t*
+    getFile() { return (file_); };
+
+    /// Sets file field
+    ///
+    /// @param file value to be set
+    void
+    setFile(const uint8_t* file, size_t fileLen = MAX_FILE_LEN);
+
+    /// Sets hardware address
+    ///
+    /// @param hwType hardware type (will be sent in htype field)
+    /// @param hlen hardware length (will be sent in hlen field)
+    /// @param macAddr pointer to hardware address
+    void setHWAddr(uint8_t hType, uint8_t hlen,
+                   const uint8_t* macAddr);
+
+    /// Returns htype field
+    ///
+    /// @return hardware type
+    uint8_t
+    getHtype() { return (htype_); };
+
+    /// Returns hlen field
+    ///
+    /// @return hardware address length
+    uint8_t
+    getHlen() { return (hlen_); };
+
+    /// @brief Returns chaddr field
+    ///
+    /// Note: This is 16 bytes long field. It doesn't have to be
+    /// null-terminated. Do no use strlen() or similar on it.
+    ///
+    /// @return pointer to hardware address
+    const uint8_t*
+    getChaddr() { return (chaddr_); };
+
+
+protected:
+
+    /// local address (dst if receiving packet, src if sending packet)
+    isc::asiolink::IOAddress local_addr_;
+
+    /// remote address (src if receiving packet, dst if sending packet)
+    isc::asiolink::IOAddress remote_addr_;
+
+    /// name of the network interface the packet was received/to be sent over
+    std::string iface_;
+
+    /// @brief interface index
+    ///
+    /// interface index (each network interface has assigned unique ifindex
+    /// it is functional equvalent of name, but sometimes more useful, e.g.
+    /// when using crazy systems that allow spaces in interface names
+    /// e.g. windows
+    int ifindex_;
+
+    /// local UDP port
+    int local_port_;
+
+    /// remote UDP port
+    int remote_port_;
+
+    /// message operation code (kept due to BOOTP format, this is NOT DHCPv4 type)
+    ///
+    /// Note: This is legacy BOOTP field. There's no need to manipulate it 
+    /// directly. Its value is set based on DHCP message type.
+    uint8_t op_;
+
+    /// link-layer address type
+    uint8_t htype_;
+
+    /// link-layer address length
+    uint8_t hlen_;
+
+    /// Number of relay agents traversed
+    uint8_t hops_;
+
+    /// DHCPv4 transaction-id (32 bits, not 24 bits as in DHCPv6)
+    uint32_t transid_;
+
+    /// elapsed (number of seconds since beginning of transmission)
+    uint16_t secs_;
+
+    /// flags
+    uint16_t flags_;
+
+    // ciaddr field (32 bits): Client's IP address
+    isc::asiolink::IOAddress ciaddr_;
+
+    // yiaddr field (32 bits): Client's IP address ("your"), set by server
+    isc::asiolink::IOAddress yiaddr_;
+
+    // siaddr field (32 bits): next server IP address in boot process(e.g.TFTP)
+    isc::asiolink::IOAddress siaddr_;
+
+    // giaddr field (32 bits): Gateway IP address
+    isc::asiolink::IOAddress giaddr_;
+
+    // ciaddr field (32 bits): Client's IP address
+    uint8_t chaddr_[16]; 
+    
+    // sname 64 bytes
+    uint8_t sname_[64];
+
+    // file
+    uint8_t file_[128];
+
+    // end of real DHCPv4 fields
+
+    /// input buffer (used during message reception)
+    /// Note that it must be modifiable as hooks can modify incoming buffer),
+    /// thus OutputBuffer, not InputBuffer
+    isc::util::OutputBuffer bufferIn_;
+
+    /// output buffer (used during message 
+    isc::util::OutputBuffer bufferOut_;
+
+    /// message type (e.g. 1=DHCPDISCOVER)
+    /// TODO: this will eventually be replaced with DHCP Message Type 
+    /// option (option 53)
+    uint8_t msg_type_;
+
+    /// collection of options present in this message
+    isc::dhcp::Option::Option4Collection options_;
+}; // Pkt4 class
+
+} // isc::dhcp namespace
+
+} // isc namespace
+
+#endif

+ 1 - 1
src/lib/dhcp/pkt6.h

@@ -19,7 +19,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_array.hpp>
 #include "asiolink/io_address.h"
-#include "option.h"
+#include "dhcp/option.h"
 
 namespace isc {
 

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -22,6 +22,7 @@ libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittes
 libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
 libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
 libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
+libdhcp_unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc
 
 libdhcp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 libdhcp_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)

+ 381 - 0
src/lib/dhcp/tests/pkt4_unittest.cc

@@ -0,0 +1,381 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <boost/static_assert.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+
+#include "io_address.h"
+#include "dhcp/pkt4.h"
+#include "dhcp/dhcp4.h"
+#include "exceptions/exceptions.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace boost;
+
+// can't compare const to value directly, as it gives strange
+// linker errors in gtest.h
+
+static size_t DHCPV4_PKT_HDR_LEN = Pkt4::DHCPV4_PKT_HDR_LEN;
+
+namespace {
+
+TEST(Pkt4Test, constructor) {
+
+    ASSERT_EQ(236U, DHCPV4_PKT_HDR_LEN);
+    Pkt4 * pkt = 0;
+
+    // minimal 
+    uint8_t testData[250];
+    for (int i=0 ; i < 250; i++)
+        testData[i]=i; 
+
+    // positive case1. Normal received packet
+    EXPECT_NO_THROW(
+        pkt = new Pkt4(testData, 236);
+    );
+
+    EXPECT_EQ(236, pkt->len());
+
+    EXPECT_NO_THROW(
+        delete pkt;
+        pkt = 0;
+    );
+
+    // positive case2. Normal outgoing packet
+    EXPECT_NO_THROW(
+        pkt = new Pkt4(DHCPDISCOVER, 0xffffffff);
+    );
+
+    // DHCPv4 packet must be at least 236 bytes long
+    EXPECT_EQ(DHCPV4_PKT_HDR_LEN, pkt->len());
+    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
+    EXPECT_EQ(0xffffffff, pkt->getTransid());
+    EXPECT_NO_THROW(
+        delete pkt;
+        pkt = 0;
+    );
+
+    // negative case. Should drop truncated messages
+    EXPECT_THROW(
+        pkt = new Pkt4(testData, 235),
+        OutOfRange
+    );
+    if (pkt) {
+        // test failed anyway. Exception should have been thrown
+        delete pkt;
+    }
+}
+
+// a sample transaction-id
+const static uint32_t dummyTransid = 0x12345678;
+
+// 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 };
+
+// let's use some creative test content here (128 chars + \0)
+const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit. Proin mollis placerat metus, at "
+    "lacinia orci ornare vitae. Mauris amet.";
+    
+// yet another type of test content (64 chars + \0)
+const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit posuere.";
+
+BOOST_STATIC_ASSERT(sizeof(dummyFile)  == Pkt4::MAX_FILE_LEN + 1);
+BOOST_STATIC_ASSERT(sizeof(dummySname) == Pkt4::MAX_SNAME_LEN + 1);
+
+/// Generates test packet
+///
+/// Allocates and generates test packet, with all fixed
+/// fields set to non-zero values. Content is not always
+/// reasonable.
+///
+/// See generateTestPacket2() function that returns
+/// exactly the same packet in on-wire format.
+///
+/// @return pointer to allocated Pkt4 object.
+boost::shared_ptr<Pkt4>
+generateTestPacket1() {
+
+    boost::shared_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, dummyTransid));
+    // hwType = 6(ETHERNET), hlen = 6(MAC address len)
+    pkt->setHWAddr(6, 6, dummyMacAddr); 
+    pkt->setHops(13); // 13 relays. Wow!
+    // transaction-id is already set
+    pkt->setSecs(42);
+    pkt->setFlags( 0xffffU ); // all flags set
+    pkt->setCiaddr(IOAddress("192.0.2.1"));
+    pkt->setYiaddr(IOAddress("1.2.3.4"));
+    pkt->setSiaddr(IOAddress("192.0.2.255"));
+    pkt->setGiaddr(IOAddress("255.255.255.255"));
+    // chaddr already set with setHWAddr()
+    pkt->setSname(dummySname, 64); 
+    pkt->setFile(dummyFile, 128);
+
+    return (pkt);
+}
+
+/// Generates test packet
+///
+/// Allocates and generates on-wire buffer that represents
+/// test packet, with all fixed fields set to non-zero values. 
+/// Content is not always reasonable.
+///
+/// See generateTestPacket1() function that returns
+/// exactly the same packet as Pkt4 object.
+///
+/// @return pointer to allocated Pkt4 object
+shared_array<uint8_t>
+generateTestPacket2() {
+
+    shared_array<uint8_t> buf(new uint8_t[Pkt4::DHCPV4_PKT_HDR_LEN]);
+
+    // that is only part of the header. It contains all "short" fields, 
+    // larger fields are constructed separately
+    uint8_t hdr[] = {
+        1, 6, 6, 13, // op, htype, hlen, hops,
+        0x12, 0x34, 0x56, 0x78, // transaction-id
+        0, 42, 0xff, 0xff, // 42 secs, 0xffff flags
+        192, 0, 2, 1, // ciaddr
+        1, 2, 3, 4, // yiaddr
+        192, 0, 2, 255, // siaddr
+        255, 255, 255, 255, // giaddr
+    };
+
+    BOOST_STATIC_ASSERT(28 == sizeof(hdr));
+
+    size_t offset = 0;
+
+    memcpy(&buf[0] + offset, hdr, sizeof(hdr));
+    offset += sizeof(hdr);
+
+    memcpy(&buf[0] + offset, dummyMacAddr, 6); // chaddr is 16 bytes
+    offset += Pkt4::MAX_CHADDR_LEN;
+    memcpy(&buf[0] + offset, dummySname, 64); // sname is 64 bytes
+    offset += Pkt4::MAX_SNAME_LEN;
+    memcpy(&buf[0] + offset, dummyFile, 128);
+    offset += Pkt4::MAX_FILE_LEN;
+
+    return (buf);
+}
+
+TEST(Pkt4Test, fixedFields) {
+
+    shared_ptr<Pkt4> pkt = generateTestPacket1();
+
+    // ok, let's check packet values
+    EXPECT_EQ(1, pkt->getOp());
+    EXPECT_EQ(6, pkt->getHtype());
+    EXPECT_EQ(6, pkt->getHlen());
+    EXPECT_EQ(13, pkt->getHops());
+    EXPECT_EQ(dummyTransid, pkt->getTransid());
+    EXPECT_EQ(42, pkt->getSecs());
+    EXPECT_EQ(0xffff, pkt->getFlags());
+
+    EXPECT_EQ(string("192.0.2.1"), pkt->getCiaddr().toText());
+    EXPECT_EQ(string("1.2.3.4"), pkt->getYiaddr().toText());
+    EXPECT_EQ(string("192.0.2.255"), pkt->getSiaddr().toText());
+    EXPECT_EQ(string("255.255.255.255"), pkt->getGiaddr().toText());
+
+    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
+    EXPECT_FALSE( memcmp(dummyChaddr, pkt->getChaddr(), 16) );
+
+    EXPECT_FALSE( memcmp(dummySname, pkt->getSname(), 64) );
+
+    EXPECT_FALSE( memcmp(dummyFile, pkt->getFile(), 128) );
+
+    EXPECT_EQ(DHCPDISCOVER, pkt->getType());
+}
+
+#if 0
+/// TODO Uncomment when ticket #1227 is implemented
+TEST(Pkt4Test, fixedFieldsPack) {
+    shared_ptr<Pkt4> pkt = generateTestPacket1();
+    shared_array<uint8_t> expectedFormat = generateTestPacket2();
+
+    EXPECT_NO_THROW(
+        pkt->pack();
+    );
+
+    ASSERT_EQ(Pkt4::DHCPV4_PKT_HDR_LEN, pkt->len());
+
+    EXPECT_EQ(0, memcmp(&expectedFormat[0], pkt->getData(), pkt->len()));
+}
+
+/// TODO Uncomment when ticket #1226 is implemented
+TEST(Pkt4Test, fixedFieldsUnpack) {
+    shared_array<uint8_t> expectedFormat = generateTestPkt2();
+
+    shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0], 
+                                  Pkt4::DHCPV4_PKT_HDR_LEN));
+
+    // ok, let's check packet values
+    EXPECT_EQ(1, pkt->getOp());
+    EXPECT_EQ(6, pkt->getHtype());
+    EXPECT_EQ(6, pkt->getHlen());
+    EXPECT_EQ(13, pkt->getHops());
+    EXPECT_EQ(transid, pkt->getTransid());
+    EXPECT_EQ(42, pkt->getSecs());
+    EXPECT_EQ(0xffff, pkt->getFlags());
+
+    EXPECT_EQ(string("192.0.2.1"), pkt->getCiaddr.toText());
+    EXPECT_EQ(string("1.2.3.4"), pkt->getYiaddr.toText());
+    EXPECT_EQ(string("192.0.2.255"), pkt->getSiaddr.toText());
+    EXPECT_EQ(string("255.255.255.255"), pkt->getGiaddr.toText());
+
+    // chaddr is always 16 bytes long and contains link-layer addr (MAC)
+    EXPECT_FALSE( memcmp(expectedChaddr, pkt->getChaddr(), 16) );
+
+    EXPECT_FALSE( memcmp(expectedSname, pkt->getSname(), 64) );
+
+    EXPECT_FALSE( memcmp(expectedFile, pkt->getFile(), 128) );
+
+    EXPECT_EQ(DHCPSOLICIT, pkt->getType());
+}
+#endif
+
+// this test is for hardware addresses (htype, hlen and chaddr fields)
+TEST(Pkt4Test, hwAddr) {
+
+    uint8_t mac[Pkt4::MAX_CHADDR_LEN];
+    uint8_t expectedChaddr[Pkt4::MAX_CHADDR_LEN];
+
+    Pkt4* pkt = 0;
+    // let's test each hlen, from 0 till 16
+    for (int macLen=0; macLen < Pkt4::MAX_CHADDR_LEN; macLen++) {
+        for (int i=0; i < Pkt4::MAX_CHADDR_LEN; i++) {
+            mac[i] = 0;
+            expectedChaddr[i] = 0;
+        }
+        for (int i=0; i < macLen; i++) {
+            mac[i] = 128+i;
+            expectedChaddr[i] = 128+i;
+        }
+        
+        // type and transaction doesn't matter in this test
+        pkt = new Pkt4(DHCPOFFER, 1234);
+        pkt->setHWAddr(255-macLen*10, // just weird htype
+                       macLen,
+                       mac);
+        EXPECT_EQ(0, memcmp(expectedChaddr, pkt->getChaddr(), 
+                            Pkt4::MAX_CHADDR_LEN));
+
+#if 0
+        /// TODO Uncomment when ticket #1227 is implemented)
+        EXPECT_NO_THROW( 
+            pkt->pack();
+        );
+
+        // CHADDR starts at offset 28 in DHCP packet
+        EXPECT_EQ(0, memcmp(pkt->getData()+28, expectedChaddr, 
+                            Pkt4::MAX_CHADDR_LEN));
+#endif
+
+        delete pkt;
+    }
+
+    /// TODO: extend this test once options support is implemented. HW address
+    /// longer than 16 bytes should be stored in client-identifier option
+}
+
+// this test verifies handling of sname field
+TEST(Pkt4Test, sname) {
+
+    uint8_t sname[Pkt4::MAX_SNAME_LEN];
+    uint8_t expectedSname[Pkt4::MAX_SNAME_LEN];
+
+    Pkt4* pkt = 0;
+    // let's test each sname length, from 0 till 64
+    for (int snameLen=0; snameLen < Pkt4::MAX_SNAME_LEN; snameLen++) {
+        for (int i=0; i < Pkt4::MAX_SNAME_LEN; i++) {
+            sname[i] = 0;
+            expectedSname[i] = 0;
+        }
+        for (int i=0; i < snameLen; i++) {
+            sname[i] = i;
+            expectedSname[i] = i;
+        }
+        
+        // type and transaction doesn't matter in this test
+        pkt = new Pkt4(DHCPOFFER, 1234);
+        pkt->setSname(sname, snameLen);
+
+        EXPECT_EQ(0, memcmp(expectedSname, pkt->getSname(), Pkt4::MAX_SNAME_LEN));
+
+#if 0
+        /// TODO Uncomment when ticket #1227 is implemented)
+        EXPECT_NO_THROW( 
+            pkt->pack();
+        );
+
+        // SNAME starts at offset 44 in DHCP packet
+        EXPECT_EQ(0, memcmp(pkt->getData()+44, expectedChaddr, Pkt4::MAX_SNAME_LEN));
+#endif
+
+        delete pkt;
+    }
+}
+
+TEST(Pkt4Test, file) {
+
+    uint8_t file[Pkt4::MAX_FILE_LEN];
+    uint8_t expectedFile[Pkt4::MAX_FILE_LEN];
+
+    Pkt4* pkt = 0;
+    // let's test each file length, from 0 till 64
+    for (int fileLen=0; fileLen < Pkt4::MAX_FILE_LEN; fileLen++) {
+        for (int i=0; i < Pkt4::MAX_FILE_LEN; i++) {
+            file[i] = 0;
+            expectedFile[i] = 0;
+        }
+        for (int i=0; i < fileLen; i++) {
+            file[i] = i;
+            expectedFile[i] = i;
+        }
+        
+        // type and transaction doesn't matter in this test
+        pkt = new Pkt4(DHCPOFFER, 1234);
+        pkt->setFile(file, fileLen);
+
+        EXPECT_EQ(0, memcmp(expectedFile, pkt->getFile(), Pkt4::MAX_FILE_LEN));
+
+#if 0
+        /// TODO Uncomment when ticket #1227 is implemented)
+        EXPECT_NO_THROW( 
+            pkt->pack();
+        );
+
+        // FILE starts at offset 44 in DHCP packet
+        EXPECT_EQ(0, memcmp(pkt->getData()+44, expectedChaddr, Pkt4::MAX_FILE_LEN));
+#endif
+
+        delete pkt;
+    }
+
+}
+
+} // end of anonymous namespace