Commit 81c031de authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[1224] Pkt4 class and unittests implementation.

parent 937b5a6f
......@@ -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
......
// 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
}
};
// 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
......@@ -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 {
......
......@@ -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)
......
// 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