Parcourir la source

[1186] Option6_AddrLst implemented (useful for storing v6 addresses)

Tomek Mrugalski il y a 13 ans
Parent
commit
522d27a63d

+ 2 - 0
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));
 

+ 1 - 0
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
 

+ 132 - 0
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 <stdint.h>
+#include <arpa/inet.h>
+#include <sstream>
+#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<isc::asiolink::IOAddress>& 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<char> 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<isc::asiolink::IOAddress>& addrs) {
+    addrs_ = addrs;
+}
+
+unsigned int
+Option6AddrLst::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.");
+    }
+
+    *(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<IOAddress>::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<char> 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; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << " " << addrs_.size() << "addr(s): ";
+
+    for (AddrsContainer::const_iterator addr=addrs_.begin();
+         addr!=addrs_.end();
+         ++addr) {
+        tmp << addr->toText() << " ";
+    }
+    return tmp.str();
+}
+
+unsigned short Option6AddrLst::len() {
+
+    return (4 /* DHCPv6 option header len */ + addrs_.size()*16);
+}

+ 127 - 0
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 <vector>
+#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<isc::asiolink::IOAddress> 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<char> 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<char> 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<char> 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<isc::asiolink::IOAddress>& 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_ */

+ 1 - 0
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
 

+ 208 - 0
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 <config.h>
+#include <iostream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#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<char> 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