Commit af27ec87 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[878] Support for IA and IAADDR options and suboptions added.

 - IA support (Option6IA class) added.
 - Support for suboptions in IA option.
 - IAADDR support (Option6IAAddr class) added.
 - Initial tests added for new classes added.
parent b47533e9
......@@ -11,6 +11,8 @@ lib_LTLIBRARIES = libdhcp.la
libdhcp_la_SOURCES =
libdhcp_la_SOURCES += libdhcp.cc libdhcp.h
libdhcp_la_SOURCES += option.cc option.h
libdhcp_la_SOURCES += option6_ia.cc option6_ia.h
libdhcp_la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libdhcp_la_SOURCES += dhcp6.h
libdhcp_la_SOURCES += pkt6.cc pkt6.h
......
......@@ -16,10 +16,18 @@
#include <boost/shared_ptr.hpp>
#include "dhcp/libdhcp.h"
#include "config.h"
#include "dhcp6.h"
#include "option.h"
#include "option6_ia.h"
#include "option6_iaaddr.h"
using namespace std;
using namespace isc::dhcp;
// static array with factory
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
std::string
LibDHCP::version() {
return PACKAGE_VERSION;
......@@ -39,70 +47,102 @@ LibDHCP::version() {
* @return offset to first byte after last parsed option
*/
unsigned int
LibDHCP::unpackOptions6(boost::shared_array<char>& buf,
int buf_len,
unsigned short offset,
LibDHCP::unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
unsigned int offset, unsigned int parse_len,
isc::dhcp::Option::Option6Lst& options) {
int len = buf_len - offset;
while (len>4) {
int opt_type = buf[offset]*256 + buf[offset+1];
if (offset + parse_len > buf_len) {
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
<< parse_len << " bytes at offset " << offset
<< ": out of buffer");
}
unsigned int end = offset + parse_len;
while (offset<end) {
unsigned int opt_type = buf[offset]*256 + buf[offset+1];
offset += 2;
len -= 2;
int opt_len = buf[offset]*256 + buf[offset+1];
unsigned int opt_len = buf[offset]*256 + buf[offset+1];
offset += 2;
len -= 2;
if (opt_len > len) {
cout << "Packet truncated. Unable to parse option " << opt_type
<< ". " << len << " bytes left in buffer, but option "
<< "len=" << opt_len << endl;
if (offset + opt_len > end ) {
cout << "Option " << opt_type << " truncated." << endl;
return (offset);
}
boost::shared_ptr<Option> opt(new Option(Option::V6,
opt_type,
buf,
offset,
opt_len));
boost::shared_ptr<Option> opt;
switch (opt_type) {
case D6O_IA_NA:
case D6O_IA_PD:
// cout << "Creating Option6IA" << endl;
opt = boost::shared_ptr<Option>(new Option6IA(Option::V6,
opt_type,
buf, buf_len,
offset,
opt_len));
break;
case D6O_IAADDR:
// cout << "Creating Option6IAAddr" << endl;
opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
buf, buf_len,
offset, opt_len));
break;
default:
// cout << "Creating Option" << endl;
opt = boost::shared_ptr<Option>(new Option(Option::V6,
opt_type,
buf,
offset,
opt_len));
break;
}
// add option to options
options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
offset += opt_len;
len -= opt_len;
cout << "Parse opt=" << opt_type << ", opt_len=" << opt_len << ", bytes left=" << len << endl;
}
if (len != 0) {
cout << "There are " << len << " bytes left to parse." << endl;
}
return (offset);
}
unsigned int
LibDHCP::packOptions6(boost::shared_array<char>& data,
int data_len,
unsigned short offset,
LibDHCP::packOptions6(boost::shared_array<char> data,
unsigned int data_len,
unsigned int offset,
isc::dhcp::Option::Option6Lst& options) {
char* buf = &data[offset];
char* end = &data[data_len-1]; // last byte in shared array
try {
for (isc::dhcp::Option::Option6Lst::iterator it = options.begin();
it != options.end();
++it) {
unsigned short opt_len = (*it).second->len();
if (buf+opt_len > end) {
isc_throw(OutOfRange, "Failed to build option" <<
if (offset + opt_len > data_len) {
isc_throw(OutOfRange, "Failed to build option " <<
(*it).first << ": out of buffer");
}
buf = (*it).second->pack(buf, opt_len);
offset += opt_len;
data_len -= opt_len;
offset = (*it).second->pack(data, data_len, offset);
}
}
catch (Exception e) {
cout << "Packet build failed." << endl;
return (-1);
}
cout << "Packet built" << endl;
return (offset);
}
bool
LibDHCP::OptionFactorRegister(Option::Universe u,
unsigned short opt_type,
Option::Factory * factory) {
switch (u) {
case Option::V6: {
if (v6factories_.find(opt_type)!=v6factories_.end()) {
isc_throw(BadValue, "There is already DHCPv6 factory registered "
<< "for option type " << opt_type);
}
v6factories_[opt_type]=factory;
return true;
}
case Option::V4:
default:{
isc_throw(BadValue, "This universe type is not supported yet.");
return false; // never happens
}
}
}
......@@ -27,20 +27,24 @@ public:
LibDHCP();
static std::string version();
bool parsePkt6(Pkt6& pkt);
bool parsePkt6(Pkt6& pkt);
bool builtPkt6(Pkt6& pkt);
static unsigned int
packOptions6(boost::shared_array<char>& buf,
int buf_len,
unsigned short offset,
packOptions6(boost::shared_array<char> buf, unsigned int buf_len,
unsigned int offset,
isc::dhcp::Option::Option6Lst& options_);
static unsigned int
unpackOptions6(boost::shared_array<char>& buf,
int buf_len,
unsigned short offset,
unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
unsigned int offset, unsigned int parse_len,
isc::dhcp::Option::Option6Lst& options_);
bool OptionFactorRegister(Option::Universe u,
unsigned short type,
Option::Factory * factory);
protected:
// pointers to factories that produce DHCPv6 options
static std::map<unsigned short, Option::Factory*> v6factories_;
};
}
......
......@@ -32,9 +32,9 @@ Option::Option(Universe u, unsigned short type)
}
Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
unsigned int offset, unsigned int len)
:universe_(u), type_(type), data_(buf),
:universe_(u), type_(type), data_(buf),
offset_(offset),
len_(len) {
......@@ -42,51 +42,60 @@ Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
// TODO: universe must be in V4 and V6
}
char* Option::pack(char* buf, unsigned int len) {
unsigned int
Option::pack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset) {
switch (universe_) {
case V4:
return pack4(buf, len);
return pack4(buf, buf_len, offset);
case V6:
return pack6(buf, len);
return pack6(buf, buf_len, offset);
default:
isc_throw(BadValue, "Unknown universe defined for Option " << type_);
}
return NULL; // should not happen
}
char*
Option::pack4(char* buf, unsigned short len) {
if (this->len()>len) {
isc_throw(OutOfRange, "Failed to pack v4 option=" <<
unsigned int
Option::pack4(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset) {
if ( offset+len() > buf_len ) {
isc_throw(OutOfRange, "Failed to pack v4 option=" <<
type_ << ",len=" << len_ << ": too small buffer.");
}
buf[0] = type_;
buf[1] = len_;
buf += 2;
memcpy(buf, &data_[0], len_);
char *ptr = &buf[offset];
ptr[0] = type_;
ptr[1] = len_;
ptr += 2;
memcpy(ptr, &data_[0], len_);
return buf + len_;
return offset + len();
}
char* Option::pack6(char* buf, unsigned short len) {
if (this->len()>len) {
isc_throw(OutOfRange, "Failed to pack v6 option=" <<
unsigned int
Option::pack6(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset) {
if ( offset+len() > buf_len ) {
isc_throw(OutOfRange, "Failed to pack v6 option=" <<
type_ << ",len=" << len_ << ": too small buffer.");
}
*(uint16_t*)buf = htons(type_);
buf += 2;
*(uint16_t*)buf = htons(len_);
buf += 2;
memcpy(buf, &data_[0], len_);
return buf + len_;
char * ptr = &buf[offset];
*(uint16_t*)ptr = htons(type_);
ptr += 2;
*(uint16_t*)ptr = htons(len_);
ptr += 2;
memcpy(ptr, &data_[0], len_);
return offset + len();
}
unsigned int
Option::unpack(boost::shared_array<char> buf,
unsigned int
Option::unpack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int offset,
unsigned int parse_len) {
switch (universe_) {
case V4:
......@@ -100,41 +109,41 @@ Option::unpack(boost::shared_array<char> buf,
return 0; // should not happen
}
unsigned int
Option::unpack4(boost::shared_array<char>,
unsigned int
Option::unpack4(boost::shared_array<char>,
unsigned int ,
unsigned int ,
unsigned int ,
unsigned int ) {
isc_throw(Unexpected, "IPv4 support not implemented yet.");
return 0;
}
/**
/**
* Parses buffer and creates collection of Option objects.
*
*
* @param buf pointer to buffer
* @param buf_len length of buf
* @param offset offset, where start parsing option
* @param parse_len how many bytes should be parsed
*
*
* @return offset after last parsed option
*/
unsigned int
Option::unpack6(boost::shared_array<char> buf,
unsigned int
Option::unpack6(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int offset,
unsigned int parse_len) {
if (buf_len < offset+parse_len) {
isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
<< parse_len << " offset=" << offset << " from buffer (length="
isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
<< parse_len << " offset=" << offset << " from buffer (length="
<< buf_len << "): too small buffer.");
}
data_ = buf;
offset_ = offset;
len_ = buf_len;
return LibDHCP::unpackOptions6(buf, buf_len, offset,
return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
optionLst_);
}
......@@ -155,7 +164,7 @@ bool Option::valid() {
// total length of buffer is not stored. shared_array is not very useful.
// we should either add buf_len field or better replace shared_array
// with shared_ptr to array
if (universe_ != V4 &&
if (universe_ != V4 &&
universe_ != V6) {
return (false);
}
......@@ -163,9 +172,9 @@ bool Option::valid() {
return (true);
}
/**
/**
* Converts generic option to string.
*
*
* @return string that represents option.
*/
std::string Option::toText() {
......@@ -176,12 +185,12 @@ std::string Option::toText() {
if (i) {
tmp << ":";
}
tmp << setfill('0') << setw(2) << hex << (unsigned short)data_[offset_+i];
tmp << setfill('0') << setw(2) << hex << (unsigned short)(unsigned char)data_[offset_+i];
}
return tmp.str();
}
unsigned short
unsigned short
Option::getType() {
return type_;
}
......@@ -189,4 +198,3 @@ Option::getType() {
Option::~Option() {
}
......@@ -24,39 +24,46 @@ namespace dhcp {
class Option {
public:
enum Universe { V4, V6 };
typedef std::map<unsigned int, boost::shared_ptr<Option> > Option4Lst;
typedef std::multimap<unsigned int, boost::shared_ptr<Option> > Option6Lst;
enum Universe { V4, V6 };
typedef boost::shared_ptr<Option> Factory(Option::Universe u,
unsigned short type,
boost::shared_array<char> buf,
unsigned int offset,
unsigned int len);
// ctor, used for options constructed, usually during transmission
Option(Universe u, unsigned short type);
Option(Universe u, unsigned short type);
// ctor, used for received options
// boost::shared_array allows sharing a buffer, but it requires that
// boost::shared_array allows sharing a buffer, but it requires that
// different instances share pointer to the whole array, not point
// to different elements in shared array. Therefore we need to share
// pointer to the whole array and remember offset where data for
// this option begins
Option(Universe u, unsigned short type, boost::shared_array<char> buf,
unsigned int offset,
Option(Universe u, unsigned short type, boost::shared_array<char> buf,
unsigned int offset,
unsigned int len);
// writes option in wire-format to buf, returns pointer to first unused
// writes option in wire-format to buf, returns pointer to first unused
// byte after stored option
virtual char* pack(char* buf, unsigned int len);
virtual unsigned int
pack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset);
// parses received buffer, returns pointer to first unused byte
// parses received buffer, returns pointer to first unused byte
// after parsed option
// TODO: Do we need this overload? Commented out for now
// virtual const char* unpack(const char* buf, unsigned int len);
// parses received buffer, returns offset to the first unused byte after
// parsed option
virtual unsigned int
unpack(boost::shared_array<char> buf,
virtual unsigned int
unpack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int offset,
unsigned int parse_len);
virtual std::string toText();
......@@ -65,23 +72,29 @@ public:
// returns data length (data length + DHCPv4/DHCPv6 option header)
virtual unsigned short len();
// returns if option is valid (e.g. option may be truncated)
virtual bool valid();
virtual bool valid();
// just to force that every option has virtual dtor
virtual ~Option();
virtual ~Option();
protected:
virtual char* pack4(char* buf, unsigned short len);
virtual char* pack6(char* buf, unsigned short len);
virtual unsigned int unpack4(boost::shared_array<char> buf,
virtual unsigned int
pack4(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset);
virtual unsigned int
pack6(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset);
virtual unsigned int unpack4(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int offset,
unsigned int parse_len);
virtual unsigned int unpack6(boost::shared_array<char> buf,
virtual unsigned int unpack6(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int offset,
unsigned int parse_len);
Universe universe_;
......@@ -92,7 +105,7 @@ protected:
unsigned int offset_; // data is a shared_pointer that points out to the
// whole packet. offset_ specifies where data for
// this option begins.
unsigned int len_; // length of data only. Use len() if you want to know
unsigned int len_; // length of data only. Use len() if you want to know
// proper length with option header overhead
char * value_;
......
// 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 <stdint.h>
#include <arpa/inet.h>
#include <sstream>
#include "exceptions/exceptions.h"
#include "libdhcp.h"
#include "option6_ia.h"
#include "dhcp6.h"
using namespace std;
using namespace isc;
using namespace isc::dhcp;
Option6IA::Option6IA(Universe u, unsigned short type, unsigned int iaid)
:Option(u, type), iaid_(iaid) {
}
Option6IA::Option6IA(Universe u, unsigned short type,
boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int option_len)
:Option(u, type) {
unpack(buf, buf_len, offset, option_len);
}
unsigned int
Option6IA::pack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset) {
if (len() > buf_len) {
isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
<< ", buffer=" << buf_len << ": too small buffer.");
}
char* ptr = &buf[offset];
*(uint16_t*)ptr = htons(type_);
ptr += 2;
buf_len -= 2;
*(uint16_t*)ptr = htons(len());
ptr += 2;
buf_len -= 2;
*(uint32_t*)ptr = htonl(iaid_);
ptr += 4;
buf_len -= 4;
*(uint32_t*)ptr = htonl(t1_);
ptr += 4;
buf_len -= 4;
*(uint32_t*)ptr = htonl(t2_);
ptr += 4;
buf_len -= 4;
offset = LibDHCP::packOptions6(buf, buf_len, offset+16, optionLst_);
return offset;
}
unsigned int
Option6IA::unpack(boost::shared_array<char> buf,
unsigned int buf_len,
unsigned int offset,
unsigned int parse_len) {
if (parse_len<12 || offset+12>buf_len) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
iaid_ = ntohl(*(uint32_t*)&buf[offset]);
offset +=4;
t1_ = ntohl(*(uint32_t*)&buf[offset]);
offset +=4;
t2_ = ntohl(*(uint32_t*)&buf[offset]);
offset +=4;
offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
parse_len - 12, optionLst_);
return (offset);
}
std::string Option6IA::toText() {
stringstream tmp;
switch (type_) {
case D6O_IA_NA:
tmp << "IA_NA";
break;
case D6O_IA_PD:
tmp << "IA_PD";
break;
}
tmp << " iaid=" << iaid_ << " t1=" << t1_ << " t2=" << t2_
<< " " << optionLst_.size() << " sub-options:" << endl;
for (Option6Lst::const_iterator opt=optionLst_.begin();
opt!=optionLst_.end();
++opt) {
tmp << " " << (*