From: Tomek Mrugalski Date: Thu, 8 Sep 2011 14:57:10 +0000 (+0200) Subject: [1186] Option6_AddrLst implemented (useful for storing v6 addresses) X-Git-Tag: perftcpdns_before_epoll~86^2~29 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=522d27a63d1ff318173e7e4aeb6c1265aba93ca5;p=thirdparty%2Fkea.git [1186] Option6_AddrLst implemented (useful for storing v6 addresses) --- diff --git a/src/bin/dhcp6/iface_mgr.cc b/src/bin/dhcp6/iface_mgr.cc index a8da298baf..e108317091 100644 --- a/src/bin/dhcp6/iface_mgr.cc +++ b/src/bin/dhcp6/iface_mgr.cc @@ -546,9 +546,11 @@ IfaceMgr::receive() { // That's ugly. // TODO add IOAddress constructor that will take struct in6_addr* + // TODO: there's from_bytes() method added in IOAddress. Use it! inet_ntop(AF_INET6, &to_addr, addr_str,INET6_ADDRSTRLEN); pkt->local_addr_ = IOAddress(string(addr_str)); + // TODO: there's from_bytes() method added in IOAddress. Use it! inet_ntop(AF_INET6, &from.sin6_addr, addr_str, INET6_ADDRSTRLEN); pkt->remote_addr_ = IOAddress(string(addr_str)); diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index 973453cb39..ce674eebde 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -13,6 +13,7 @@ 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 += option6_addrlst.cc option6_addrlst.h libdhcp_la_SOURCES += dhcp6.h libdhcp_la_SOURCES += pkt6.cc pkt6.h diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc new file mode 100644 index 0000000000..218baaf6b1 --- /dev/null +++ b/src/lib/dhcp/option6_addrlst.cc @@ -0,0 +1,132 @@ +// 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 +#include +#include +#include "exceptions/exceptions.h" + +#include "libdhcp.h" +#include "option6_addrlst.h" +#include "dhcp6.h" +#include "io_address.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; + + +Option6AddrLst::Option6AddrLst(unsigned short type, + std::vector& addrs) + :Option(V6, type) { + addrs_ = addrs; +} + +Option6AddrLst::Option6AddrLst(unsigned short type, + isc::asiolink::IOAddress addr) + :Option(V6, type) { + addrs_.push_back(addr); +} + +Option6AddrLst::Option6AddrLst(unsigned short type, + boost::shared_array buf, + unsigned int buf_len, + unsigned int offset, + unsigned int option_len) + :Option(V6, type) { + unpack(buf, buf_len, offset, option_len); +} + +void +Option6AddrLst::setAddress(isc::asiolink::IOAddress addr) { + addrs_.clear(); + addrs_.push_back(addr); +} + +void +Option6AddrLst::setAddresses(std::vector& addrs) { + addrs_ = addrs; +} + +unsigned int +Option6AddrLst::pack(boost::shared_array 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."); + } + + *(uint16_t*)&buf[offset] = htons(type_); + offset += 2; + *(uint16_t*)&buf[offset] = htons(len()-4); // len() returns complete option + // length. len field contains length without 4-byte option header + offset += 2; + + for (std::vector::const_iterator addr=addrs_.begin(); + addr!=addrs_.end(); + ++addr) { + memcpy(&buf[offset], + addr->getAddress().to_v6().to_bytes().data(), + 16); + offset += 16; + } + + return offset; +} + +unsigned int +Option6AddrLst::unpack(boost::shared_array buf, + unsigned int buf_len, + unsigned int offset, + unsigned int option_len) { + if (offset+option_len > buf_len) { + isc_throw(OutOfRange, "Option " << type_ + << " truncated."); + } + + if (option_len%16) { + isc_throw(OutOfRange, "Option " << type_ + << " malformed: len=" << option_len + << " is not divisible by 16."); + } + while (option_len > 0) { + addrs_.push_back(IOAddress::from_bytes(AF_INET6, &buf[offset])); + offset += 16; + option_len -= 16; + } + + return offset; +} + +std::string Option6AddrLst::toText(int indent /* =0 */) { + stringstream tmp; + for (int i=0; itoText() << " "; + } + return tmp.str(); +} + +unsigned short Option6AddrLst::len() { + + return (4 /* DHCPv6 option header len */ + addrs_.size()*16); +} diff --git a/src/lib/dhcp/option6_addrlst.h b/src/lib/dhcp/option6_addrlst.h new file mode 100644 index 0000000000..2dc0f1d446 --- /dev/null +++ b/src/lib/dhcp/option6_addrlst.h @@ -0,0 +1,127 @@ +// 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 OPTION6_ADDRLST_H_ +#define OPTION6_ADDRLST_H_ + +#include +#include "io_address.h" +#include "option.h" + +namespace isc { +namespace dhcp { + +/// @brief Option class for handling list of IPv6 addresses. +/// +/// This class handles a list of IPv6 addresses. An example of such option +/// is dns-servers option. It can also be used to handle single address. +/// +class Option6AddrLst: public Option { + + +public: + typedef std::vector AddrsContainer; + + /// @brief Constructor used during option generation. + /// + /// @param type option type + /// @param addrs vector of addresses to be stored + /// + Option6AddrLst(unsigned short type, + AddrsContainer& addrs); + + /// @brief Simplified constructor for a single address + /// + /// @param type option type + /// @param addr a single address to be stored + /// + Option6AddrLst(unsigned short type, + isc::asiolink::IOAddress addr); + + /// @brief Constructor used for parsing received option + /// + /// @param type option type + /// @param buf pointer to packet buffer + /// @param buf_len length of packet buffer + /// @param offset offset to beginning of option data + /// @param len length of option data + /// + Option6AddrLst(unsigned short type, boost::shared_array buf, + unsigned int buf_len, + unsigned int offset, + unsigned int len); + + /// @brief Assembles on-wire form of this option + /// + /// @param buf pointer to packet buffer + /// @param buf_len length of packet buffer + /// @param offset offset to place, where option is to be stored + /// + /// @return offset to the next unused char (just after stored option) + /// + unsigned int + pack(boost::shared_array buf, unsigned int buf_len, + unsigned int offset); + + /// @brief Parses received data + /// + /// @param buf pointer to packet buffer + /// @param buf_len length of packet buffer + /// @param offset offset to option data + /// @param parse_len specified option data length + /// + /// @return offset to the next unparsed char (just after parsed option) + /// + virtual unsigned int + unpack(boost::shared_array buf, + unsigned int buf_len, + unsigned int offset, + unsigned int parse_len); + + virtual std::string toText(int indent = 0); + + /// @brief Sets a single address. + /// + /// @param addr a single address to be added + /// + void setAddress(isc::asiolink::IOAddress addr); + + /// @brief Sets list of addresses. + /// + /// @param addrs a vector of addresses to be added + /// + void setAddresses(std::vector& addrs); + + /// @brief Returns vector with addresses. + /// + /// As user may want to use/modify this list, it is better to return + /// a copy rather than const reference to the original. This is + /// usually one or two addresses long, so it is not a big deal. + /// + /// @return vector with addresses + /// + AddrsContainer + getAddresses() { return addrs_; }; + + // returns data length (data length + DHCPv4/DHCPv6 option header) + virtual unsigned short len(); + +protected: + AddrsContainer addrs_; +}; + +} // isc::dhcp namespace +} // isc namespace + +#endif /* OPTION_ADDRLST_H_ */ diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index f2b88bf2f1..c00bef589d 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -19,6 +19,7 @@ libdhcp_unittests_SOURCES = run_unittests.cc libdhcp_unittests_SOURCES += ../libdhcp.h ../libdhcp.cc libdhcp_unittest.cc libdhcp_unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc libdhcp_unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc +libdhcp_unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc diff --git a/src/lib/dhcp/tests/option6_addrlst_unittest.cc b/src/lib/dhcp/tests/option6_addrlst_unittest.cc new file mode 100644 index 0000000000..ce0bc22062 --- /dev/null +++ b/src/lib/dhcp/tests/option6_addrlst_unittest.cc @@ -0,0 +1,208 @@ +// 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 +#include +#include + +#include +#include + +#include "io_address.h" +#include "dhcp/dhcp6.h" +#include "dhcp/option.h" +#include "dhcp/option6_addrlst.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; + +namespace { +class Option6AddrLstTest : public ::testing::Test { +public: + Option6AddrLstTest() { + } +}; + +TEST_F(Option6AddrLstTest, basic) { + + char sampledata[] = { + // 2001:db8:1::dead:beef + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0, + 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef, + + // ff02::face:b00c + 0xff, 02, 0, 0, 0, 0, 0 , 0, + 0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c, + + // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + char expected1[] = { + D6O_NAME_SERVERS/256, D6O_NAME_SERVERS%256,//type + 0, 16, // len = 16 (1 address) + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0, + 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef, + + }; + + char expected2[] = { + D6O_SIP_SERVERS_ADDR/256, D6O_SIP_SERVERS_ADDR%256, + 0, 32, // len = 32 (2 addresses) + // 2001:db8:1::dead:beef + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0, + 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef, + + // ff02::face:b00c + 0xff, 02, 0, 0, 0, 0, 0 , 0, + 0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c, + + // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + char expected3[] = { + D6O_NIS_SERVERS/256, D6O_NIS_SERVERS%256, + 0, 48, + // 2001:db8:1::dead:beef + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0, 0, + 0, 0, 0, 0, 0xde, 0xad, 0xbe, 0xef, + + // ff02::face:b00c + 0xff, 02, 0, 0, 0, 0, 0 , 0, + 0, 0, 0, 0, 0xfa, 0xce, 0xb0, 0x0c, + + // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + boost::shared_array buf(new char[300]); + for (int i=0; i<300; i++) + buf[i] = 0; + + memcpy(&buf[0], sampledata, 48); + + // just a single address + Option6AddrLst* opt1 = new Option6AddrLst(D6O_NAME_SERVERS, + buf, + 128, + 0, + 16); + + + EXPECT_EQ(D6O_NAME_SERVERS, opt1->getType()); + EXPECT_EQ(20, opt1->len()); + Option6AddrLst::AddrsContainer addrs = opt1->getAddresses(); + ASSERT_EQ(1, addrs.size()); + IOAddress addr = addrs[0]; + EXPECT_EQ("2001:db8:1::dead:beef", addr.toText()); + + // pack this option again in the same buffer, but in + // different place + int offset = opt1->pack(buf,300, 100); + + EXPECT_EQ(120, offset); + EXPECT_EQ( 0, memcmp(expected1, &buf[100], 20) ); + + // two addresses + Option6AddrLst* opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR, + buf, + 128, + 0, + 32); + EXPECT_EQ(D6O_SIP_SERVERS_ADDR, opt2->getType()); + EXPECT_EQ(36, opt2->len()); + addrs = opt2->getAddresses(); + ASSERT_EQ(2, addrs.size()); + EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText()); + EXPECT_EQ("ff02::face:b00c", addrs[1].toText()); + + // pack this option again in the same buffer, but in + // different place + offset = opt2->pack(buf,300, 150); + + EXPECT_EQ(150+36, offset); + EXPECT_EQ( 0, memcmp(expected2, &buf[150], 36)); + + + // three addresses + Option6AddrLst* opt3 = new Option6AddrLst(D6O_NIS_SERVERS, + buf, + 128, + 0, + 48); + + EXPECT_EQ(D6O_NIS_SERVERS, opt3->getType()); + EXPECT_EQ(52, opt3->len()); + addrs = opt3->getAddresses(); + ASSERT_EQ(3, addrs.size()); + EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText()); + EXPECT_EQ("ff02::face:b00c", addrs[1].toText()); + EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addrs[2].toText()); + + // pack this option again in the same buffer, but in + // different place + offset = opt3->pack(buf,300, 200); + + EXPECT_EQ(252, offset); + EXPECT_EQ( 0, memcmp(expected3, &buf[200], 52) ); + + delete opt1; + delete opt2; + delete opt3; +} + +TEST_F(Option6AddrLstTest, constructors) { + Option6AddrLst * opt1 = new Option6AddrLst(1234, + IOAddress("::1")); + EXPECT_EQ(1234, opt1->getType()); + + Option6AddrLst::AddrsContainer addrs = opt1->getAddresses(); + ASSERT_EQ(1, addrs.size() ); + EXPECT_EQ("::1", addrs[0].toText()); + + addrs.clear(); + addrs.push_back(IOAddress(string("fe80::1234"))); + addrs.push_back(IOAddress(string("2001:db8:1::baca"))); + + Option6AddrLst * opt2 = new Option6AddrLst(5678, + addrs); + + Option6AddrLst::AddrsContainer check = opt2->getAddresses(); + ASSERT_EQ(2, check.size() ); + EXPECT_EQ("fe80::1234", check[0].toText()); + EXPECT_EQ("2001:db8:1::baca", check[1].toText()); + + delete opt1; + delete opt2; +} + +TEST_F(Option6AddrLstTest, setAddress) { + Option6AddrLst * opt1 = new Option6AddrLst(1234, + IOAddress("::1")); + opt1->setAddress(IOAddress("::2")); + + Option6AddrLst::AddrsContainer addrs = opt1->getAddresses(); + ASSERT_EQ(1, addrs.size() ); + EXPECT_EQ("::2", addrs[0].toText()); + + delete opt1; +} + +} // namespace