diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 73f1b00590..349070ec0c 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -15,7 +15,7 @@ CLEANFILES = *.gcno *.gcda lib_LTLIBRARIES = libkea-dhcp++.la libkea_dhcp___la_SOURCES = libkea_dhcp___la_SOURCES += classify.cc classify.h -libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h +libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h dhcp4o6.h libkea_dhcp___la_SOURCES += duid.cc duid.h libkea_dhcp___la_SOURCES += hwaddr.cc hwaddr.h libkea_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h @@ -48,6 +48,7 @@ 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 += pkt4o6.cc pkt4o6.h libkea_dhcp___la_SOURCES += pkt_filter.h pkt_filter.cc libkea_dhcp___la_SOURCES += pkt_filter6.h pkt_filter6.cc libkea_dhcp___la_SOURCES += pkt_filter_inet.cc pkt_filter_inet.h diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h index 6e053f69b1..86df12f99a 100644 --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@ -110,8 +110,8 @@ //#define D6O_ADDRSEL 84 /* RFC7078 */ //#define D6O_ADDRSEL_TABLE 85 /* RFC7078 */ //#define D6O_V6_PCP_SERVER 86 /* RFC7291 */ -//#define D6O_DHCPV4_MSG 87 /* RFC7341 */ -//#define D6O_DHCPV4_O_DHCPV6_SERVER 88 /* RFC7341 */ +#define D6O_DHCPV4_MSG 87 /* RFC7341 */ +#define D6O_DHCPV4_O_DHCPV6_SERVER 88 /* RFC7341 */ //#define D6O_S46_RULE 89 /* RFC7598 */ //#define D6O_S46_BR 90 /* RFC7598 */ //#define D6O_S46_DMR 91 /* RFC7598 */ diff --git a/src/lib/dhcp/pkt4o6.cc b/src/lib/dhcp/pkt4o6.cc new file mode 100644 index 0000000000..2dd3d0ff25 --- /dev/null +++ b/src/lib/dhcp/pkt4o6.cc @@ -0,0 +1,60 @@ +// Copyright (C) 2015 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 + +#include +#include +#include +#include +#include + +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::util; +using namespace std; + +namespace isc { +namespace dhcp { + +Pkt4o6::Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6) + :Pkt4(&pkt4[0], pkt4.size()), pkt6_(pkt6) +{ + static_cast(pkt6->delOption(D6O_DHCPV4_MSG)); + setIface(pkt6->getIface()); + setIndex(pkt6->getIndex()); + setRemoteAddr(pkt6->getRemoteAddr()); +} + +Pkt4o6::Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6) + :Pkt4(*pkt4), pkt6_(pkt6) { +} + +void Pkt4o6::pack() { + // Convert wire-format Pkt4 data in the form of OptionBuffer. + Pkt4::pack(); + OutputBuffer& buf = getBuffer(); + const uint8_t* ptr = static_cast(buf.getData()); + OptionBuffer msg(ptr, ptr + buf.getLength()); + + // Build the DHCPv4 Message option for the DHCPv6 message, and pack the + // entire stuff. + OptionPtr dhcp4_msg(new Option(Option::V6, D6O_DHCPV4_MSG, msg)); + pkt6_->addOption(dhcp4_msg); + pkt6_->pack(); +} + +} // end of namespace isc::dhcp + +} // end of namespace isc diff --git a/src/lib/dhcp/pkt4o6.h b/src/lib/dhcp/pkt4o6.h new file mode 100644 index 0000000000..0e87f8e112 --- /dev/null +++ b/src/lib/dhcp/pkt4o6.h @@ -0,0 +1,83 @@ +// Copyright (C) 2015 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 PKT4O6_H +#define PKT4O6_H + +#include +#include + +#include + +namespace isc { + +namespace dhcp { + +/// @brief Represents DHCPv4-over-DHCPv6 packet +/// +/// This class derives from @c Pkt4 in order to be handled by +/// the DHCPv4 server code. It includes a shared pointer to the +/// DHCPv6 message too. +/// +/// This is an implementation of the DHCPv4-query/response DHCPv6 messages +/// defined in RFC 7341 (http://ietf.org/rfc/rfc7341.txt). +/// See also http://kea.isc.org/wiki/Dhcp4o6Design for design discussions. +class Pkt4o6 : public Pkt4 { +public: + + /// @brief Constructor, used in message reception. + /// + /// @param pkt4 Content of the DHCPv4-message option + /// @param pkt6 encapsulating unpacked DHCPv6 message + /// the DHCPv4 message option will be removed + Pkt4o6(const OptionBuffer& pkt4, const Pkt6Ptr& pkt6); + + /// @brief Constructor, used in replying to a message + /// + /// @param pkt4 DHCPv4 message + /// @param pkt6 DHCPv6 message + Pkt4o6(const Pkt4Ptr& pkt4, const Pkt6Ptr& pkt6); + + /// @brief Returns encapsulating DHCPv6 message + Pkt6Ptr getPkt6() const { return (pkt6_); } + + /// @brief Prepares on-wire format of DHCPv4-over-DHCPv6 packet. + /// + /// Calls pack() on both DHCPv4 and DHCPv6 parts + /// Inserts the DHCPv4-message option + /// @ref Pkt4::pack and @ref Pkt6::pack + virtual void pack(); + + /// @brief Checks if a DHCPv4 message has been transported over DHCPv6 + /// + /// @return Boolean value which indicates whether the message is + /// transported over DHCPv6 (true) or native DHCPv4 (false) + virtual bool isDhcp4o6() const { + return (true); + } + +private: + /// Encapsulating DHCPv6 message + Pkt6Ptr pkt6_; + +}; // Pkt4o6 class + +/// @brief A pointer to Pkt4o6 object. +typedef boost::shared_ptr Pkt4o6Ptr; + +} // isc::dhcp namespace + +} // isc namespace + +#endif diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h index 87dab71a67..e517c35eed 100644 --- a/src/lib/dhcp/pkt6.h +++ b/src/lib/dhcp/pkt6.h @@ -244,7 +244,7 @@ public: /// @param option_code code of the requested option /// @param nesting_level see description above /// - /// @return pointer to the option (or NULL if there is no such option) + /// @return pointer to the option (or NULL if there is no such option) OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level); /// @brief Return first instance of a specified option diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 2e21b53f41..8faf432b14 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -74,6 +74,7 @@ libdhcp___unittests_SOURCES += option_vendor_class_unittest.cc libdhcp___unittests_SOURCES += pkt_captures4.cc pkt_captures6.cc pkt_captures.h libdhcp___unittests_SOURCES += pkt4_unittest.cc libdhcp___unittests_SOURCES += pkt6_unittest.cc +libdhcp___unittests_SOURCES += pkt4o6_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc diff --git a/src/lib/dhcp/tests/pkt4o6_unittest.cc b/src/lib/dhcp/tests/pkt4o6_unittest.cc new file mode 100644 index 0000000000..53cef15b9e --- /dev/null +++ b/src/lib/dhcp/tests/pkt4o6_unittest.cc @@ -0,0 +1,106 @@ +// Copyright (C) 2015 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 + +#include +#include +#include +#include +#include + +#include + +#include + +using namespace isc::dhcp; + +namespace { + +/// @brief A Fixture class dedicated to testing of the Pkt4o6 class that +/// represents a DHCPv4-over-DHCPv6 packet. +class Pkt4o6Test : public ::testing::Test { +protected: + Pkt4o6Test() : + data6_(6, 0), + pkt6_(new Pkt6(&data6_[0], data6_.size())), + pkt4_(new Pkt4(DHCPDISCOVER, 0x12345678)) + { + pkt4_->pack(); + const uint8_t* cp = static_cast( + pkt4_->getBuffer().getData()); + buffer4_.assign(cp, cp + pkt4_->getBuffer().getLength()); + } + +protected: + // commonly used test data + const std::vector data6_; // data for Pkt6 (content unimportant) + Pkt6Ptr pkt6_; // DHCPv6 message for 4o6 + Pkt4Ptr pkt4_; // DHCPv4 message for 4o6 + OptionBuffer buffer4_; // wire-format data buffer of pkt4_ +}; + +// This test verifies that the constructors are working as expected. +TEST_F(Pkt4o6Test, construct) { + // Construct 4o6 packet, unpack the data to examine it + boost::scoped_ptr pkt4o6(new Pkt4o6(buffer4_, pkt6_)); + pkt4o6->unpack(); + // Inspect its internal to confirm it's built as expected. We also test + // isDhcp4o6() here. + EXPECT_TRUE(pkt4o6->isDhcp4o6()); + EXPECT_EQ(pkt6_, pkt4o6->getPkt6()); + EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType()); + + // Same check for the other constructor. It relies on the internal + // behavior of Pkt4's copy constructor, so we need to first unpack pkt4. + pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size())); + pkt4_->unpack(); + pkt4o6.reset(new Pkt4o6(pkt4_, pkt6_)); + EXPECT_TRUE(pkt4o6->isDhcp4o6()); + EXPECT_EQ(pkt6_, pkt4o6->getPkt6()); + EXPECT_EQ(DHCPDISCOVER, pkt4o6->getType()); +} + +// This test verifies that the pack() method handles the building +// process correctly. +TEST_F(Pkt4o6Test, pack) { + // prepare unpacked DHCPv4 packet (see the note in constructor test) + pkt4_.reset(new Pkt4(&buffer4_[0], buffer4_.size())); + pkt4_->unpack(); + + // Construct 4o6 packet to be tested and pack the data. + Pkt4o6 pkt4o6(pkt4_, pkt6_); + pkt4o6.pack(); + + // The packed data should be: + // 4-byte DHCPv6 message header + // 4-byte header part of DHCPv4 message option + // Raw DHCPv4 message (data stored in buffer4_) + EXPECT_EQ(4 + 4 + buffer4_.size(), + pkt4o6.getPkt6()->getBuffer().getLength()); + + // Check the DHCPv4 message option content (Pkt4o6 class is not responsible + // for making it valid, so we won't examine it) + const uint8_t* cp = static_cast( + pkt4o6.getPkt6()->getBuffer().getData()); + EXPECT_EQ(0, cp[4]); + EXPECT_EQ(D6O_DHCPV4_MSG, cp[5]); + EXPECT_EQ((buffer4_.size() >> 8) & 0xff, cp[6]); + EXPECT_EQ(buffer4_.size() & 0xff, cp[7]); + EXPECT_EQ(0, memcmp(&cp[8], &buffer4_[0], buffer4_.size())); +} + +/// @todo: Add a test that handles actual DHCP4o6 traffic capture +/// once we get it. We should add the capture to pkt_captures{4,6}.cc +}