Commit 3511c6e6 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[2827] Relay structures, message parsing/transmission implemented

parent f99b625b
......@@ -128,7 +128,8 @@ LibDHCP::optionFactory(Option::Universe u,
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options) {
isc::dhcp::Option::OptionCollection& options,
size_t* relay_msg_offset /* = 0 */) {
size_t offset = 0;
size_t length = buf.size();
......@@ -143,6 +144,7 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
while (offset + 4 <= length) {
uint16_t opt_type = isc::util::readUint16(&buf[offset]);
offset += 2;
uint16_t opt_len = isc::util::readUint16(&buf[offset]);
offset += 2;
......@@ -151,6 +153,11 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
return (offset);
}
if (opt_type == D6O_RELAY_MSG && relay_msg_offset) {
// remember offset of the beginning of the relay-msg option
*relay_msg_offset = offset;
}
// Get all definitions with the particular option code. Note that option
// code is non-unique within this container however at this point we
// expect to get one option definition with the particular code. If more
......@@ -193,7 +200,7 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
}
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options) {
isc::dhcp::Option::OptionCollection& options) {
size_t offset = 0;
// Get the list of stdandard option definitions.
......
......@@ -121,8 +121,12 @@ public:
/// @param buf Buffer to be parsed.
/// @param options Reference to option container. Options will be
/// put here.
/// @param relay_msg_offset reference to a size_t structure. If specified,
/// offset to beginning of relay_msg option will be store here.
/// @return offset to the first byte after last parsed option
static size_t unpackOptions6(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options);
isc::dhcp::Option::OptionCollection& options,
size_t* relay_msg_offset = 0);
/// Registers factory method that produces options of specific option types.
///
......
......@@ -21,10 +21,17 @@
#include <sstream>
using namespace std;
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
Pkt6::RelayInfo::RelayInfo()
:msg_type_(0), hop_count_(0), linkaddr_("::"), peeraddr_(""), relay_msg_len_(0) {
// interface_id_, subscriber_id_, remote_id_ initialized to NULL
// echo_options_ initialized to empty collection
}
Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */) :
proto_(proto),
msg_type_(0),
......@@ -54,6 +61,52 @@ Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/) :
}
uint16_t Pkt6::len() {
if (relay_info_.empty()) {
return (directLen());
} else {
calculateRelaySizes();
return (relay_info_[0].relay_msg_len_ + getRelayOverhead(relay_info_[0]));
}
}
uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) {
uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
+ Option::OPTION6_HDR_LEN; // header of the relay-msg option
if (relay.interface_id_) {
len += relay.interface_id_->len();
}
if (relay.subscriber_id_) {
len += relay.subscriber_id_->len();
}
if (relay.remote_id_) {
len += relay.remote_id_->len();
}
for (Option::OptionCollection::const_iterator opt = relay.echo_options_.begin();
opt != relay.echo_options_.end(); ++opt) {
len += (opt->second)->len();
}
return (len);
}
uint16_t Pkt6::calculateRelaySizes() {
uint16_t len = directLen(); // start with length of all options
int relay_index = relay_info_.size();
while (relay_index) {
relay_info_[relay_index - 1].relay_msg_len_ = len;
len += getRelayOverhead(relay_info_[relay_index - 1]);
--relay_index;
}
return (len);
}
uint16_t Pkt6::directLen() {
uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
for (Option::OptionCollection::iterator it = options_.begin();
......@@ -82,6 +135,41 @@ Pkt6::pack() {
bool
Pkt6::packUDP() {
try {
if (!relay_info_.empty()) {
calculateRelaySizes();
for (vector<RelayInfo>::iterator relay = relay_info_.begin();
relay != relay_info_.end(); ++relay) {
bufferOut_.writeUint8(relay->msg_type_);
bufferOut_.writeUint8(relay->hop_count_);
bufferOut_.writeData(&(relay->linkaddr_.toBytes()[0]),
isc::asiolink::V6ADDRESS_LEN);
bufferOut_.writeData(&relay->peeraddr_.toBytes()[0],
isc::asiolink::V6ADDRESS_LEN);
if (relay->interface_id_) {
relay->interface_id_->pack(bufferOut_);
}
if (relay->subscriber_id_) {
relay->subscriber_id_->pack(bufferOut_);
}
if (relay->remote_id_) {
relay->remote_id_->pack(bufferOut_);
}
for (Option::OptionCollection::const_iterator opt =
relay->echo_options_.begin();
opt != relay->echo_options_.end(); ++opt) {
(opt->second)->pack(bufferOut_);
}
bufferOut_.writeUint16(D6O_RELAY_MSG);
bufferOut_.writeUint16(relay->relay_msg_len_);
}
}
// DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
bufferOut_.writeUint8(msg_type_);
// store 3-octet transaction-id
......@@ -127,12 +215,43 @@ Pkt6::unpackUDP() {
return (false);
}
msg_type_ = data_[0];
transid_ = ( (data_[1]) << 16 ) +
((data_[2]) << 8) + (data_[3]);
switch (msg_type_) {
case DHCPV6_SOLICIT:
case DHCPV6_ADVERTISE:
case DHCPV6_REQUEST:
case DHCPV6_CONFIRM:
case DHCPV6_RENEW:
case DHCPV6_REBIND:
case DHCPV6_REPLY:
case DHCPV6_DECLINE:
case DHCPV6_RECONFIGURE:
case DHCPV6_INFORMATION_REQUEST:
default: // assume that uknown messages are not using relay format
{
return (unpackMsg(data_.begin(), data_.end()));
}
case DHCPV6_RELAY_FORW:
case DHCPV6_RELAY_REPL:
return (unpackRelayMsg());
}
}
bool
Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end) {
if (std::distance(begin, end) < 4) {
// truncated message (less than 4 bytes)
return (false);
}
msg_type_ = *begin++;
transid_ = ( (*begin++) << 16 ) +
((*begin++) << 8) + (*begin++);
transid_ = transid_ & 0xffffff;
try {
OptionBuffer opt_buffer(data_.begin() + 4, data_.end());
OptionBuffer opt_buffer(begin, end);
LibDHCP::unpackOptions6(opt_buffer, options_);
} catch (const Exception& e) {
......@@ -142,6 +261,97 @@ Pkt6::unpackUDP() {
return (true);
}
bool
Pkt6::unpackRelayMsg() {
// we use offset + bufsize, because we want to avoid creating unnecessary
// copies. There may be up to 32 relays. While using InputBuffer would
// be probably a bit cleaner, copying data up to 32 times is unacceptable
// price here. Hence a single buffer with offets and lengths.
size_t bufsize = data_.size();
size_t offset = 0;
size_t relay_msg_offset = 0;
while (bufsize >= DHCPV6_RELAY_HDR_LEN) {
RelayInfo relay;
// parse fixed header first (first 34 bytes)
relay.msg_type_ = data_[offset++];
relay.hop_count_ = data_[offset++];
relay.linkaddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
offset += isc::asiolink::V6ADDRESS_LEN;
relay.peeraddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
offset += isc::asiolink::V6ADDRESS_LEN;
bufsize -= DHCPV6_RELAY_HDR_LEN; // 34 bytes (1+1+16+16)
try {
Option::OptionCollection options;
// parse the rest as options
OptionBuffer opt_buffer(&data_[offset], &data_[offset+bufsize]);
LibDHCP::unpackOptions6(opt_buffer, options, &relay_msg_offset);
/// @todo: check that each option appears at most once
//relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
//relay.subscriber_id_ = options->getOption(D6O_SUBSCRIBER_ID);
//relay.remote_id_ = options->getOption(D6O_REMOTE_ID);
Option::OptionCollection::const_iterator relay_iter = options.find(D6O_RELAY_MSG);
if (relay_iter == options.end()) {
// there's nothing to decapsulate. We give up.
isc_throw(InvalidOperation, "Mandatory relay_msg missing");
}
OptionPtr relay_msg = relay_iter->second;
// store relay information parsed so far
addRelayInfo(relay);
/// @todo: implement ERO here
size_t inner_len = relay_msg->len() - relay_msg->getHeaderLen();
if (inner_len >= bufsize) {
// length of the relay_msg option extends beyond end of the message
isc_throw(Unexpected, "Relay-msg option is truncated.");
return false;
}
uint8_t inner_type = relay_msg->getUint8();
offset += relay_msg_offset;
bufsize = inner_len;
if ( (inner_type != DHCPV6_RELAY_FORW) && (inner_type != DHCPV6_RELAY_REPL)) {
// Ok, the inner message is not encapsulated, let's decode it directly
return (unpackMsg(data_.begin() + offset, data_.begin() + offset + inner_len));
}
// Oh well, there's inner relay-forw or relay-repl inside. Let's unpack it as well
} catch (const Exception& e) {
/// @todo: throw exception here once we turn this function to void.
return (false);
}
}
if ( (offset == data_.size()) && (bufsize == 0) ) {
// message has been parsed completely
return (true);
}
/// @todo: log here that there are additional unparsed bytes
return (true);
}
void
Pkt6::addRelayInfo(const RelayInfo& relay) {
if (relay_info_.size() > 32) {
isc_throw(BadValue, "Massage cannot be encapsulated more than 32 times");
}
/// @todo: Implement type checks here (e.g. we could receive relay-forw in relay-repl)
relay_info_.push_back(relay);
}
bool
Pkt6::unpackTCP() {
isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) "
......
......@@ -32,15 +32,35 @@ namespace dhcp {
class Pkt6 {
public:
/// specifes DHCPv6 packet header length
/// specifies non-relayed DHCPv6 packet header length (over UDP)
const static size_t DHCPV6_PKT_HDR_LEN = 4;
/// specifies relay DHCPv6 packet header length (over UDP)
const static size_t DHCPV6_RELAY_HDR_LEN = 34;
/// DHCPv6 transport protocol
enum DHCPv6Proto {
UDP = 0, // most packets are UDP
TCP = 1 // there are TCP DHCPv6 packets (bulk leasequery, failover)
};
struct RelayInfo {
RelayInfo();
uint8_t msg_type_; ///< message type (RELAY-FORW oro RELAY-REPL)
uint8_t hop_count_; ///< number of traversed relays (up to 32)
isc::asiolink::IOAddress linkaddr_; ///< fixed field in relay-forw/relay-reply
isc::asiolink::IOAddress peeraddr_; ///< fixed field in relay-forw/relay-reply
OptionPtr interface_id_; ///< interface-id option (optional)
OptionPtr subscriber_id_; ///< subscriber-id (RFC4580)
OptionPtr remote_id_; ///< remote-id (RFC4649)
uint16_t relay_msg_len_; ///< length of the relay_msg_len
/// used for ERO (Echo Request Option, RFC 4994)
isc::dhcp::Option::OptionCollection echo_options_;
};
/// Constructor, used in replying to a message
///
/// @param msg_type type of message (SOLICIT=1, ADVERTISE=2, ...)
......@@ -246,7 +266,7 @@ public:
/// @brief Returns packet timestamp.
///
/// Returns packet timestamp value updated when
/// packet is received or send.
/// packet is received or sent.
///
/// @return packet timestamp.
const boost::posix_time::ptime& getTimestamp() const { return timestamp_; }
......@@ -259,8 +279,18 @@ public:
/// @return interface name
void setIface(const std::string& iface ) { iface_ = iface; };
/// @brief add information about one traversed relay
///
/// This adds information about one traversed relay, i.e.
/// one relay-forw or relay-repl level of encapsulation.
///
/// @param relay structure with necessary relay information
void addRelayInfo(const RelayInfo& relay);
/// collection of options present in this message
///
/// @todo: Text mentions protected, but this is really public
///
/// @warning This protected member is accessed by derived
/// classes directly. One of such derived classes is
/// @ref perfdhcp::PerfPkt6. The impact on derived clasess'
......@@ -340,6 +370,17 @@ protected:
/// @return true, if build was successful
bool unpackUDP();
bool unpackMsg(OptionBuffer::const_iterator begin,
OptionBuffer::const_iterator end);
bool unpackRelayMsg();
uint16_t getRelayOverhead(const RelayInfo& relay);
uint16_t calculateRelaySizes();
uint16_t directLen();
/// UDP (usually) or TCP (bulk leasequery or failover)
DHCPv6Proto proto_;
......@@ -394,6 +435,9 @@ protected:
/// packet timestamp
boost::posix_time::ptime timestamp_;
/// relay information
std::vector<RelayInfo> relay_info_;
}; // Pkt6 class
typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment