Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
b47533e9
Commit
b47533e9
authored
Aug 23, 2011
by
Tomek Mrugalski
🛰
Browse files
[1186] libdhcp now is able to parse and build packets and options.
parent
f4c7155d
Changes
13
Hide whitespace changes
Inline
Side-by-side
src/bin/dhcp6/tests/iface_mgr_unittest.cc
View file @
b47533e9
...
...
@@ -54,6 +54,61 @@ public:
}
};
// uncomment this test to create packet writer. It will
// write incoming DHCPv6 packets as C arrays. That is useful
// for generating test sequences based on actual traffic
//
// TODO: this potentially should be moved to a separate tool
//
#if 0
TEST_F(IfaceMgrTest, dhcp6Sniffer) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
unlink("interfaces.txt");
ofstream interfaces("interfaces.txt", ios::ate);
interfaces << "eth0 fe80::21e:8cff:fe9b:7349";
interfaces.close();
NakedIfaceMgr * ifacemgr = new NakedIfaceMgr();
Pkt6 * pkt = 0;
int cnt = 0;
cout << "---8X-----------------------------------------" << endl;
while (true) {
pkt = ifacemgr->receive();
cout << "// Received " << pkt->data_len_ << " bytes packet:" << endl;
cout << "Pkt6 *capture" << cnt++ << "() {" << endl;
cout << " Pkt6* pkt;" << endl;
cout << " pkt = new Pkt6(" << pkt->data_len_ << ");" << endl;
cout << " pkt->remote_port_ = " << pkt-> remote_port_ << ";" << endl;
cout << " pkt->remote_addr_ = IOAddress(\"" << pkt->remote_addr_.toText() << "\");" << endl;
cout << " pkt->local_port_ = " << pkt-> local_port_ << ";" << endl;
cout << " pkt->local_addr_ = IOAddress(\"" << pkt->local_addr_.toText() << "\");" << endl;
cout << " pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
cout << " pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
for (int i=0; i< pkt->data_len_; i++) {
cout << " pkt->data_[" << i << "]=" << (int)(unsigned char)pkt->data_[i] << "; ";
if (!(i%4))
cout << endl;
}
cout << endl;
cout << " return (pkt);" << endl;
cout << "}" << endl << endl;
delete pkt;
}
cout << "---8X-----------------------------------------" << endl;
// never happens. Infinite loop is infinite
delete pkt;
delete ifacemgr;
}
#endif
TEST_F
(
IfaceMgrTest
,
basic
)
{
// checks that IfaceManager can be instantiated
...
...
src/lib/dhcp/Makefile.am
View file @
b47533e9
...
...
@@ -10,6 +10,7 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES
=
libdhcp.la
libdhcp_la_SOURCES
=
libdhcp_la_SOURCES
+=
libdhcp.cc libdhcp.h
libdhcp_la_SOURCES
+=
option.cc option.h
libdhcp_la_SOURCES
+=
dhcp6.h
libdhcp_la_SOURCES
+=
pkt6.cc pkt6.h
...
...
src/lib/dhcp/libdhcp.cc
View file @
b47533e9
...
...
@@ -12,10 +12,97 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include
<boost/shared_array.hpp>
#include
<boost/shared_ptr.hpp>
#include
"dhcp/libdhcp.h"
#include
"config.h"
using
namespace
std
;
using
namespace
isc
::
dhcp
;
std
::
string
LibDHCP
::
version
()
{
return
"0"
;
std
::
string
LibDHCP
::
version
()
{
return
PACKAGE_VERSION
;
}
/**
* Parses provided buffer and creates Option objects.
*
* Parses provided buf array and stores created Option objects
* in options container.
*
* @param buf Buffer to be parsed.
* @param offset Specifies offset for the first option.
* @param options Reference to option container. Options will be
* put here.
*
* @return offset to first byte after last parsed option
*/
unsigned
int
LibDHCP
::
unpackOptions6
(
boost
::
shared_array
<
char
>&
buf
,
int
buf_len
,
unsigned
short
offset
,
isc
::
dhcp
::
Option
::
Option6Lst
&
options
)
{
int
len
=
buf_len
-
offset
;
while
(
len
>
4
)
{
int
opt_type
=
buf
[
offset
]
*
256
+
buf
[
offset
+
1
];
offset
+=
2
;
len
-=
2
;
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
;
return
(
offset
);
}
boost
::
shared_ptr
<
Option
>
opt
(
new
Option
(
Option
::
V6
,
opt_type
,
buf
,
offset
,
opt_len
));
// 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
,
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"
<<
(
*
it
).
first
<<
": out of buffer"
);
}
buf
=
(
*
it
).
second
->
pack
(
buf
,
opt_len
);
offset
+=
opt_len
;
data_len
-=
opt_len
;
}
}
catch
(
Exception
e
)
{
cout
<<
"Packet build failed."
<<
endl
;
return
(
-
1
);
}
cout
<<
"Packet built"
<<
endl
;
return
(
offset
);
}
src/lib/dhcp/libdhcp.h
View file @
b47533e9
...
...
@@ -16,6 +16,7 @@
#define LIBDHCP_H_
#include
<iostream>
#include
"dhcp/pkt6.h"
namespace
isc
{
namespace
dhcp
{
...
...
@@ -23,9 +24,23 @@ namespace dhcp {
class
LibDHCP
{
public:
LibDHCP
();
static
std
::
string
version
();
bool
parsePkt6
(
Pkt6
&
pkt
);
bool
builtPkt6
(
Pkt6
&
pkt
);
static
unsigned
int
packOptions6
(
boost
::
shared_array
<
char
>&
buf
,
int
buf_len
,
unsigned
short
offset
,
isc
::
dhcp
::
Option
::
Option6Lst
&
options_
);
static
unsigned
int
unpackOptions6
(
boost
::
shared_array
<
char
>&
buf
,
int
buf_len
,
unsigned
short
offset
,
isc
::
dhcp
::
Option
::
Option6Lst
&
options_
);
};
}
...
...
src/lib/dhcp/option.cc
0 → 100644
View file @
b47533e9
// 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
<string.h>
#include
<stdint.h>
#include
<arpa/inet.h>
#include
<sstream>
#include
<iomanip>
#include
<boost/shared_array.hpp>
#include
"exceptions/exceptions.h"
#include
"option.h"
#include
"libdhcp.h"
using
namespace
std
;
using
namespace
isc
::
dhcp
;
Option
::
Option
(
Universe
u
,
unsigned
short
type
)
:
universe_
(
u
),
type_
(
type
)
{
}
Option
::
Option
(
Universe
u
,
unsigned
short
type
,
boost
::
shared_array
<
char
>
buf
,
unsigned
int
offset
,
unsigned
int
len
)
:
universe_
(
u
),
type_
(
type
),
data_
(
buf
),
offset_
(
offset
),
len_
(
len
)
{
// sanity checks
// TODO: universe must be in V4 and V6
}
char
*
Option
::
pack
(
char
*
buf
,
unsigned
int
len
)
{
switch
(
universe_
)
{
case
V4
:
return
pack4
(
buf
,
len
);
case
V6
:
return
pack6
(
buf
,
len
);
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="
<<
type_
<<
",len="
<<
len_
<<
": too small buffer."
);
}
buf
[
0
]
=
type_
;
buf
[
1
]
=
len_
;
buf
+=
2
;
memcpy
(
buf
,
&
data_
[
0
],
len_
);
return
buf
+
len_
;
}
char
*
Option
::
pack6
(
char
*
buf
,
unsigned
short
len
)
{
if
(
this
->
len
()
>
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_
;
}
unsigned
int
Option
::
unpack
(
boost
::
shared_array
<
char
>
buf
,
unsigned
int
buf_len
,
unsigned
int
offset
,
unsigned
int
parse_len
)
{
switch
(
universe_
)
{
case
V4
:
return
unpack4
(
buf
,
buf_len
,
offset
,
parse_len
);
case
V6
:
return
unpack6
(
buf
,
buf_len
,
offset
,
parse_len
);
default:
isc_throw
(
BadValue
,
"Unknown universe defined for Option "
<<
type_
);
}
return
0
;
// should not happen
}
unsigned
int
Option
::
unpack4
(
boost
::
shared_array
<
char
>
,
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
buf_len
,
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="
<<
buf_len
<<
"): too small buffer."
);
}
data_
=
buf
;
offset_
=
offset
;
len_
=
buf_len
;
return
LibDHCP
::
unpackOptions6
(
buf
,
buf_len
,
offset
,
optionLst_
);
}
unsigned
short
Option
::
len
()
{
switch
(
universe_
)
{
case
V4
:
return
len_
+
2
;
// DHCPv4 option header length: 2 bytes
case
V6
:
return
len_
+
4
;
// DHCPv6 option header length: 4 bytes
default:
isc_throw
(
BadValue
,
"Unknown universe defined for Option "
<<
type_
);
}
return
0
;
// should not happen
}
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
&&
universe_
!=
V6
)
{
return
(
false
);
}
return
(
true
);
}
/**
* Converts generic option to string.
*
* @return string that represents option.
*/
std
::
string
Option
::
toText
()
{
std
::
stringstream
tmp
;
tmp
<<
type_
<<
"(len="
<<
len_
<<
"):"
;
for
(
unsigned
int
i
=
0
;
i
<
len_
;
i
++
)
{
if
(
i
)
{
tmp
<<
":"
;
}
tmp
<<
setfill
(
'0'
)
<<
setw
(
2
)
<<
hex
<<
(
unsigned
short
)
data_
[
offset_
+
i
];
}
return
tmp
.
str
();
}
unsigned
short
Option
::
getType
()
{
return
type_
;
}
Option
::~
Option
()
{
}
src/lib/dhcp/option.h
0 → 100644
View file @
b47533e9
// 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 OPTION_H_
#define OPTION_H_
#include
<string>
#include
<map>
#include
<boost/shared_array.hpp>
namespace
isc
{
namespace
dhcp
{
class
Option
{
public:
typedef
std
::
map
<
unsigned
int
,
boost
::
shared_ptr
<
Option
>
>
Option4Lst
;
typedef
std
::
multimap
<
unsigned
int
,
boost
::
shared_ptr
<
Option
>
>
Option6Lst
;
enum
Universe
{
V4
,
V6
};
// ctor, used for options constructed, usually during transmission
Option
(
Universe
u
,
unsigned
short
type
);
// ctor, used for received options
// 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
,
unsigned
int
len
);
// writes option in wire-format to buf, returns pointer to first unused
// byte after stored option
virtual
char
*
pack
(
char
*
buf
,
unsigned
int
len
);
// 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
,
unsigned
int
buf_len
,
unsigned
int
offset
,
unsigned
int
parse_len
);
virtual
std
::
string
toText
();
unsigned
short
getType
();
// 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
();
// just to force that every option has virtual dtor
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
,
unsigned
int
buf_len
,
unsigned
int
offset
,
unsigned
int
parse_len
);
virtual
unsigned
int
unpack6
(
boost
::
shared_array
<
char
>
buf
,
unsigned
int
buf_len
,
unsigned
int
offset
,
unsigned
int
parse_len
);
Universe
universe_
;
unsigned
short
type_
;
boost
::
shared_array
<
char
>
data_
;
unsigned
int
data_len_
;
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
// proper length with option header overhead
char
*
value_
;
// 2 different containers are used, because v4 options are unique
// and v6 allows multiple instances of the same option types
// originally 2 separate containers were planned. Let's try if we
// can use a single apporach
Option6Lst
optionLst_
;
};
}
// namespace isc::dhcp
}
// namespace isc
#endif
src/lib/dhcp/pkt6.cc
View file @
b47533e9
...
...
@@ -15,30 +15,243 @@
#include
"dhcp/dhcp6.h"
#include
"dhcp/pkt6.h"
#include
"dhcp/libdhcp.h"
#include
"exceptions/exceptions.h"
#include
<iostream>
#include
<sstream>
using
namespace
std
;
using
namespace
isc
::
dhcp
;
namespace
isc
{
///
/// constructor
///
/// \param dataLen - length of the data to be allocated
///
Pkt6
::
Pkt6
(
int
dataLen
)
/**
* Constructor.
*
* @param dataLen size of buffer to be allocated for this packet.
* @param proto protocol (usually UDP, but TCP will be supported eventually)
*/
Pkt6
::
Pkt6
(
unsigned
int
dataLen
,
DHCPv6Proto_
proto
/* = UDP */
)
:
local_addr_
(
"::"
),
remote_addr_
(
"::"
)
{
remote_addr_
(
"::"
),
proto_
(
proto
)
{
try
{
data_
=
boost
::
shared_array
<
char
>
(
new
char
[
dataLen
]);
data_len_
=
dataLen
;
data_
=
boost
::
shared_array
<
char
>
(
new
char
[
dataLen
]);
data_len_
=
dataLen
;
}
catch
(
const
std
::
exception
&
ex
)
{
// TODO move to LOG_FATAL()
// let's continue with empty pkt for now
// TODO move to LOG_FATAL()
// let's continue with empty pkt for now
std
::
cout
<<
"Failed to allocate "
<<
dataLen
<<
" bytes."
<<
std
::
endl
;
data_len_
=
0
;
}
}
/**
* 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
*/
unsigned
short
Pkt6
::
len
()
{
unsigned
int
length
=
4
;
// DHCPv6 header
for
(
Option
::
Option6Lst
::
iterator
it
=
options_
.
begin
();
it
!=
options_
.
end
();
++
it
)
{
length
+=
(
*
it
).
second
->
len
();
}
return
length
;
}
/**
* Builds on wire packet.
*
* Prepares on wire packet format.
*
* @return true if preparation was successful
*/
bool
Pkt6
::
pack
()
{
switch
(
proto_
)
{
case
UDP
:
return
packUDP
();
case
TCP
:
return
packTCP
();
default:
isc_throw
(
BadValue
,
"Invalid protocol specified (non-TCP, non-UDP)"
);
}
return
false
;
// never happens
}
/**
* Build on wire packet (in UDP format).
*
* @return true if packet build was successful, false otherwise
*/
bool
Pkt6
::
packUDP
()
{
unsigned
short
length
=
len
();
if
(
data_len_
<
length
)
{
// we have too small buffer, let's allocate bigger one
data_
=
boost
::
shared_array
<
char
>
(
new
char
[
length
]);
data_len_
=
length
;
}
// DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
data_
[
0
]
=
msg_type_
;
data_
[
1
]
=
(
transid_
>>
16
)
&
0xff
;
data_
[
2
]
=
(
transid_
>>
8
)
&
0xff
;
data_
[
3
]
=
(
transid_
)
&
0xff
;
try
{
// the rest are options
unsigned
short
offset
=
LibDHCP
::
packOptions6
(
data_
,
length
,
4
/*offset*/
,
options_
);
// sanity check
if
(
offset
!=
length
)
{