Commit 6e68af7d authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

Merge branch 'trac3546'

parents f8ae0911 97ba667a
......@@ -1839,9 +1839,16 @@ Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
uint8_t opt_len = buf[offset++];
if (offset + opt_len > buf.size()) {
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
<< offset + opt_len << " bytes from " << buf.size()
<< "-byte long buffer.");
// We peeked at the option header of the next option, but discovered
// that it would end up beyond buffer end, so the option is
// truncated. Hence we can't parse it. Therefore we revert
// back by two bytes (as if we never parsed them).
return (offset - 2);
// isc_throw(OutOfRange, "Option parse failed. Tried to parse "
// << offset + opt_len << " bytes from " << buf.size()
// << "-byte long buffer.");
}
// Get all definitions with the particular option code. Note that option code
......
......@@ -319,8 +319,11 @@ for the failure is appended as an argument of the log message.
A debug message noting that server has received message with server identifier
option that not matching server identifier that server is using.
% DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet
The IPv6 DHCP server has received a packet that it is unable to interpret.
% DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet: %1
The DHCPv6 server has received a packet that it is unable to interpret.
There may be many causes: truncated header, truncated or malformed option,
trailing padding that contains garbage etc. More information is specified
as a parameter.
% DHCP6_PACKET_PROCESS_FAIL processing of %1 message received from %2 failed: %3
This is a general catch-all message indicating that the processing of the
......
......@@ -311,9 +311,11 @@ bool Dhcpv6Srv::run() {
// Unpack the packet information unless the buffer6_receive callouts
// indicated they did it
if (!skip_unpack) {
if (!query->unpack()) {
try {
query->unpack();
} catch (const std::exception &e) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
DHCP6_PACKET_PARSE_FAIL);
DHCP6_PACKET_PARSE_FAIL).arg(e.what());
continue;
}
}
......@@ -2469,7 +2471,12 @@ Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
if (offset + opt_len > length) {
// @todo: consider throwing exception here.
return (offset);
// We peeked at the option header of the next option, but discovered
// that it would end up beyond buffer end, so the option is
// truncated. Hence we can't parse it. Therefore we revert
// by by those four bytes (as if we never parsed them).
return (offset - 4);
}
if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
......
......@@ -1167,6 +1167,9 @@ TestControl::receivePackets(const TestControlSocket& socket) {
if ((received > 1) && testDiags('i')) {
stats_mgr4_->incrementCounter("multircvd");
}
/// @todo: Add packet exception handling here. Right now any
/// malformed packet will cause perfdhcp to abort.
pkt4->unpack();
processReceivedPacket4(socket, pkt4);
}
......@@ -1185,9 +1188,11 @@ TestControl::receivePackets(const TestControlSocket& socket) {
if ((received > 1) && testDiags('i')) {
stats_mgr6_->incrementCounter("multircvd");
}
if (pkt6->unpack()) {
processReceivedPacket6(socket, pkt6);
}
/// @todo: Add packet exception handling here. Right now any
/// malformed packet will cause perfdhcp to abort.
pkt6->unpack();
processReceivedPacket6(socket, pkt6);
}
}
}
......
......@@ -43,6 +43,7 @@ libkea_dhcp___la_SOURCES += option_definition.cc option_definition.h
libkea_dhcp___la_SOURCES += option_space.cc option_space.h
libkea_dhcp___la_SOURCES += option_string.cc option_string.h
libkea_dhcp___la_SOURCES += protocol_util.cc protocol_util.h
libkea_dhcp___la_SOURCES += pkt.cc pkt.h
libkea_dhcp___la_SOURCES += pkt6.cc pkt6.h
libkea_dhcp___la_SOURCES += pkt4.cc pkt4.h
libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc
......
......@@ -31,14 +31,14 @@ HWAddr::HWAddr()
:htype_(HTYPE_ETHER) {
}
HWAddr::HWAddr(const uint8_t* hwaddr, size_t len, uint8_t htype)
HWAddr::HWAddr(const uint8_t* hwaddr, size_t len, uint16_t htype)
:hwaddr_(hwaddr, hwaddr + len), htype_(htype) {
if (len > MAX_HWADDR_LEN) {
isc_throw(InvalidParameter, "hwaddr length exceeds MAX_HWADDR_LEN");
}
}
HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint8_t htype)
HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint16_t htype)
:hwaddr_(hwaddr), htype_(htype) {
if (hwaddr.size() > MAX_HWADDR_LEN) {
isc_throw(InvalidParameter,
......@@ -49,7 +49,7 @@ HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint8_t htype)
std::string HWAddr::toText(bool include_htype) const {
std::stringstream tmp;
if (include_htype) {
tmp << "hwtype=" << static_cast<int>(htype_) << " ";
tmp << "hwtype=" << static_cast<unsigned int>(htype_) << " ";
}
tmp << std::hex;
bool delim = false;
......@@ -65,7 +65,7 @@ std::string HWAddr::toText(bool include_htype) const {
}
HWAddr
HWAddr::fromText(const std::string& text, const uint8_t htype) {
HWAddr::fromText(const std::string& text, const uint16_t htype) {
/// @todo optimize stream operations here.
std::vector<std::string> split_text;
boost::split(split_text, text, boost::is_any_of(":"),
......
......@@ -41,18 +41,22 @@ public:
/// @param hwaddr pointer to hardware address
/// @param len length of the address pointed by hwaddr
/// @param htype hardware type
HWAddr(const uint8_t* hwaddr, size_t len, uint8_t htype);
HWAddr(const uint8_t* hwaddr, size_t len, uint16_t htype);
/// @brief constructor, based on C++ vector<uint8_t>
/// @param hwaddr const reference to hardware address
/// @param htype hardware type
HWAddr(const std::vector<uint8_t>& hwaddr, uint8_t htype);
HWAddr(const std::vector<uint8_t>& hwaddr, uint16_t htype);
// Vector that keeps the actual hardware address
std::vector<uint8_t> hwaddr_;
// Hardware type
uint8_t htype_;
/// Hardware type
///
/// @note It used to be uint8_t as used in DHCPv4. However, since we're
/// expanding MAC addresses support to DHCPv6 that uses hw_type as
/// 16 bits, we need to be able to store that wider format.
uint16_t htype_;
/// @brief Returns textual representation of a hardware address
/// (e.g. 00:01:02:03:04:05)
......@@ -83,7 +87,7 @@ public:
///
/// @return Instance of the HW address created from text.
static HWAddr fromText(const std::string& text,
const uint8_t htype = HTYPE_ETHER);
const uint16_t htype = HTYPE_ETHER);
/// @brief Compares two hardware addresses for equality
bool operator==(const HWAddr& other) const;
......
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011-2014 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
......@@ -239,7 +239,12 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
if (offset + opt_len > length) {
// @todo: consider throwing exception here.
return (offset);
// We peeked at the option header of the next option, but discovered
// that it would end up beyond buffer end, so the option is
// truncated. Hence we can't parse it. Therefore we revert
// back by those four bytes (as if we never parsed them).
return (offset - 4);
}
if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
......@@ -255,8 +260,9 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
if (opt_type == D6O_VENDOR_OPTS) {
if (offset + 4 > length) {
// Truncated vendor-option. There is expected at least 4 bytes
// long enterprise-id field
return (offset);
// long enterprise-id field. Let's roll back option code + option
// length (4 bytes) and return.
return (offset - 4);
}
// Parse this as vendor option
......@@ -351,9 +357,16 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
uint8_t opt_len = buf[offset++];
if (offset + opt_len > buf.size()) {
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
<< offset + opt_len << " bytes from " << buf.size()
<< "-byte long buffer.");
// We peeked at the option header of the next option, but discovered
// that it would end up beyond buffer end, so the option is
// truncated. Hence we can't parse it. Therefore we revert
// back by two bytes (as if we never parsed them).
return (offset - 2);
// isc_throw(OutOfRange, "Option parse failed. Tried to parse "
// << offset + opt_len << " bytes from " << buf.size()
// << "-byte long buffer.");
}
// Get all definitions with the particular option code. Note that option code
......@@ -421,7 +434,12 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
if (offset + opt_len > length) {
// @todo: consider throwing exception here.
return (offset);
// We peeked at the option header of the next option, but discovered
// that it would end up beyond buffer end, so the option is
// truncated. Hence we can't parse it. Therefore we revert
// back by those four bytes (as if we never parsed them).
return (offset - 4);
}
OptionPtr opt;
......@@ -497,8 +515,13 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
uint8_t data_len = buf[offset++];
if (offset + data_len > buf.size()) {
// Truncated data-option
return (offset);
// The option is truncated.
// We peeked at the data_len, but discovered that it would end up
// beyond buffer end, so the data block is truncated. Hence we can't
// parse it. Therefore we revert back by one byte (as if we never
// parsed it).
return (offset - 1);
}
uint8_t offset_end = offset + data_len;
......@@ -519,7 +542,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
if (offset + 1 >= buf.size()) {
// opt_type must be cast to integer so as it is not treated as
// unsigned char value (a number is presented in error message).
isc_throw(OutOfRange, "Attempt to parse truncated option "
isc_throw(OutOfRange, "Attempt to parse truncated vendor option "
<< static_cast<int>(opt_type));
}
......@@ -539,7 +562,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
// to get one option definition with the particular code. If more are
// returned we report an error.
const OptionDefContainerTypeRange& range = idx->equal_range(opt_type);
// Get the number of returned option definitions for the option code.
// Get the number of returned option definitions for the option code.
size_t num_defs = distance(range.first, range.second);
if (num_defs > 1) {
......
......@@ -123,6 +123,7 @@ public:
/// of to be used to parse options in the packets.
/// @param options Reference to option container. Options will be
/// put here.
/// @return offset to the first byte after the last successfully parsed option
static size_t unpackOptions4(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options);
......@@ -147,7 +148,7 @@ public:
/// offset to beginning of relay_msg option will be stored in it.
/// @param relay_msg_len reference to a size_t structure. If specified,
/// length of the relay_msg option will be stored in it.
/// @return offset to the first byte after last parsed option
/// @return offset to the first byte after the last successfully parsed option
static size_t unpackOptions6(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options,
......@@ -192,6 +193,7 @@ public:
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Options will be
/// put here.
/// @return offset to the first byte after the last successfully parsed option
static size_t unpackVendorOptions6(const uint32_t vendor_id,
const OptionBuffer& buf,
isc::dhcp::OptionCollection& options);
......@@ -206,6 +208,7 @@ public:
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Options will be
/// put here.
/// @return offset to the first byte after the last successfully parsed option
static size_t unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
isc::dhcp::OptionCollection& options);
......
// Copyright (C) 2014 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 <utility>
#include <dhcp/pkt.h>
namespace isc {
namespace dhcp {
Pkt::Pkt(uint32_t transid, const isc::asiolink::IOAddress& local_addr,
const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
uint16_t remote_port)
:transid_(transid),
iface_(""),
ifindex_(-1),
local_addr_(local_addr),
remote_addr_(remote_addr),
local_port_(local_port),
remote_port_(remote_port),
buffer_out_(0)
{
}
Pkt::Pkt(const uint8_t* buf, uint32_t len, const isc::asiolink::IOAddress& local_addr,
const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
uint16_t remote_port)
:transid_(0),
iface_(""),
ifindex_(-1),
local_addr_(local_addr),
remote_addr_(remote_addr),
local_port_(local_port),
remote_port_(remote_port),
buffer_out_(0)
{
data_.resize(len);
if (len) {
memcpy(&data_[0], buf, len);
}
}
void
Pkt::addOption(const OptionPtr& opt) {
options_.insert(std::pair<int, OptionPtr>(opt->getType(), opt));
}
OptionPtr
Pkt::getOption(uint16_t type) const {
OptionCollection::const_iterator x = options_.find(type);
if (x != options_.end()) {
return (*x).second;
}
return (OptionPtr()); // NULL
}
bool
Pkt::delOption(uint16_t type) {
isc::dhcp::OptionCollection::iterator x = options_.find(type);
if (x!=options_.end()) {
options_.erase(x);
return (true); // delete successful
} else {
return (false); // can't find option to be deleted
}
}
bool
Pkt::inClass(const std::string& client_class) {
return (classes_.find(client_class) != classes_.end());
}
void
Pkt::addClass(const std::string& client_class) {
if (classes_.find(client_class) == classes_.end()) {
classes_.insert(client_class);
}
}
void
Pkt::updateTimestamp() {
timestamp_ = boost::posix_time::microsec_clock::universal_time();
}
void Pkt::repack() {
if (!data_.empty()) {
buffer_out_.writeData(&data_[0], data_.size());
}
}
void
Pkt::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
const std::vector<uint8_t>& hw_addr) {
setHWAddrMember(htype, hlen, hw_addr, remote_hwaddr_);
}
void
Pkt::setRemoteHWAddr(const HWAddrPtr& hw_addr) {
if (!hw_addr) {
isc_throw(BadValue, "Setting remote HW address to NULL is"
<< " forbidden.");
}
remote_hwaddr_ = hw_addr;
}
void
Pkt::setHWAddrMember(const uint8_t htype, const uint8_t,
const std::vector<uint8_t>& hw_addr,
HWAddrPtr& storage) {
storage.reset(new HWAddr(hw_addr, htype));
}
HWAddrPtr
Pkt::getMAC(uint32_t hw_addr_src) {
HWAddrPtr mac;
if (hw_addr_src & HWADDR_SOURCE_RAW) {
mac = getRemoteHWAddr();
if (mac) {
return (mac);
} else if (hw_addr_src == HWADDR_SOURCE_RAW) {
// If we're interested only in RAW sockets as source of that info,
// there's no point in trying other options.
return (HWAddrPtr());
}
}
/// @todo: add other MAC acquisition methods here
// Ok, none of the methods were suitable. Return NULL.
return (HWAddrPtr());
}
};
};
This diff is collapsed.
......@@ -27,23 +27,21 @@ using namespace std;
using namespace isc::dhcp;
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
namespace {
/// @brief Default address used in Pkt4 constructor
const IOAddress DEFAULT_ADDRESS("0.0.0.0");
}
namespace isc {
namespace dhcp {
Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
:buffer_out_(DHCPV4_PKT_HDR_LEN),
local_addr_(DEFAULT_ADDRESS),
remote_addr_(DEFAULT_ADDRESS),
iface_(""),
ifindex_(0),
local_port_(DHCP4_SERVER_PORT),
remote_port_(DHCP4_CLIENT_PORT),
:Pkt(transid, DEFAULT_ADDRESS, DEFAULT_ADDRESS, DHCP4_SERVER_PORT,
DHCP4_CLIENT_PORT),
op_(DHCPTypeToBootpType(msg_type)),
hwaddr_(new HWAddr()),
hops_(0),
transid_(transid),
secs_(0),
flags_(0),
ciaddr_(DEFAULT_ADDRESS),
......@@ -58,17 +56,11 @@ Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
}
Pkt4::Pkt4(const uint8_t* data, size_t len)
:buffer_out_(0), // not used, this is RX packet
local_addr_(DEFAULT_ADDRESS),
remote_addr_(DEFAULT_ADDRESS),
iface_(""),
ifindex_(0),
local_port_(DHCP4_SERVER_PORT),
remote_port_(DHCP4_CLIENT_PORT),
:Pkt(data, len, DEFAULT_ADDRESS, DEFAULT_ADDRESS, DHCP4_SERVER_PORT,
DHCP4_CLIENT_PORT),
op_(BOOTREQUEST),
hwaddr_(new HWAddr()),
hops_(0),
transid_(0),
secs_(0),
flags_(0),
ciaddr_(DEFAULT_ADDRESS),
......@@ -76,6 +68,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
siaddr_(DEFAULT_ADDRESS),
giaddr_(DEFAULT_ADDRESS)
{
if (len < DHCPV4_PKT_HDR_LEN) {
isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
<< ") received, at least " << DHCPV4_PKT_HDR_LEN
......@@ -144,7 +137,6 @@ Pkt4::pack() {
// write (len) bytes of padding
vector<uint8_t> zeros(hw_len, 0);
buffer_out_.writeData(&zeros[0], hw_len);
// buffer_out_.writeData(chaddr_, MAX_CHADDR_LEN);
buffer_out_.writeData(sname_, MAX_SNAME_LEN);
buffer_out_.writeData(file_, MAX_FILE_LEN);
......@@ -218,17 +210,34 @@ Pkt4::unpack() {
// Use readVector because a function which parses option requires
// a vector as an input.
size_t offset;
buffer_in.readVector(opts_buffer, opts_len);
if (callback_.empty()) {
LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
offset = LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
} else {
// The last two arguments are set to NULL because they are
// specific to DHCPv6 options parsing. They are unused for
// DHCPv4 case. In DHCPv6 case they hold are the relay message
// offset and length.
callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
offset = callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
}
// If offset is not equal to the size, then something is wrong here. We
// either parsed past input buffer (bug in our code) or we haven't parsed
// everything (received trailing garbage or truncated option).
//
// Invoking Jon Postel's law here: be conservative in what you send, and be
// liberal in what you accept. There's no easy way to log something from
// libdhcp++ library, so we just choose to be silent about remaining
// bytes. We also need to quell compiler warning about unused offset
// variable.
//
// if (offset != size) {
// isc_throw(BadValue, "Received DHCPv6 buffer of size " << size
// << ", were able to parse " << offset << " bytes.");
// }
(void)offset;
// @todo check will need to be called separately, so hooks can be called
// after the packet is parsed, but before its content is verified
check();
......@@ -272,10 +281,6 @@ void Pkt4::setType(uint8_t dhcp_type) {
}
}
void Pkt4::repack() {
buffer_out_.writeData(&data_[0], data_.size());
}
std::string
Pkt4::toText() {
stringstream tmp;
......@@ -341,21 +346,6 @@ Pkt4::setLocalHWAddr(const HWAddrPtr& addr) {
local_hwaddr_ = addr;
}
void
Pkt4::setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
const std::vector<uint8_t>& mac_addr) {
setHWAddrMember(htype, hlen, mac_addr, remote_hwaddr_);
}
void
Pkt4::setRemoteHWAddr(const HWAddrPtr& addr) {
if (!addr) {
isc_throw(BadValue, "Setting remote HW address to NULL is"
<< " forbidden.");
}
remote_hwaddr_ = addr;
}
void
Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
if (snameLen > MAX_SNAME_LEN) {
......@@ -433,37 +423,14 @@ Pkt4::getHlen() const {
}
void
Pkt4::addOption(boost::shared_ptr<Option> opt) {
Pkt4::addOption(const OptionPtr& opt) {
// Check for uniqueness (DHCPv4 options must be unique)
if (getOption(opt->getType())) {
isc_throw(BadValue, "Option " << opt->getType()
<< " already present in this message.");
}
options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
}
boost::shared_ptr<isc::dhcp::Option>
Pkt4::getOption(uint8_t type) const {
OptionCollection::const_iterator x = options_.find(type);
if (x != options_.end()) {
return (*x).second;
}
return boost::shared_ptr<isc::dhcp::Option>(); // NULL
}
bool
Pkt4::delOption(uint8_t type) {
isc::dhcp::OptionCollection::iterator x = options_.find(type);
if (x != options_.end()) {
options_.erase(x);
return (true); // delete successful
}
return (false); // can't find option to be deleted
}
void
Pkt4::updateTimestamp() {
timestamp_ = boost::posix_time::microsec_clock::universal_time();
Pkt::addOption(opt);
}
bool
......@@ -485,17 +452,6 @@ Pkt4::isRelayed() const {
"hops != 0)");