Parcourir la source

[1350] Added DHCPv4 option for storing single or list of IPv4 addresses.

Added Option4_AddrLst class that represents DHCPv4 option that stores
a single IPv4 address or a list of IPv4 addresses.
Added tests for this new option.
Added check in Option6_AddrLst, so it accepts only IPv6 addresses.
Removed unnecessary pack4() method in Option class.
Tomek Mrugalski il y a 13 ans
Parent
commit
c5117dc4d2

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -14,6 +14,7 @@ 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 += option4_addrlst.cc option4_addrlst.h
 libdhcp_la_SOURCES += dhcp6.h dhcp4.h
 libdhcp_la_SOURCES += pkt6.cc pkt6.h
 libdhcp_la_SOURCES += pkt4.cc pkt4.h

+ 0 - 17
src/lib/dhcp/option.cc

@@ -128,23 +128,6 @@ Option::pack4(isc::util::OutputBuffer& buf) {
 }
 
 unsigned int
-Option::pack4(boost::shared_array<uint8_t>& 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.");
-    }
-    uint8_t *ptr = &buf[offset];
-    ptr[0] = type_;
-    ptr[1] = len() - getHeaderLen();
-    ptr += 2;
-    memcpy(ptr, &data_[0], data_.size());
-
-    return offset + len();
-}
-
-unsigned int
 Option::pack6(boost::shared_array<uint8_t>& buf,
              unsigned int buf_len,
              unsigned int offset) {

+ 0 - 14
src/lib/dhcp/option.h

@@ -244,20 +244,6 @@ public:
     ~Option();
 
 protected:
-
-    /// Builds raw (over-wire) buffer of this option, including all
-    /// defined suboptions. Version for building DHCPv4 options.
-    ///
-    /// @param buf output buffer (built options will be stored here)
-    /// @param buf_len buffer length (used for buffer overflow checks)
-    /// @param offset offset from start of the buf buffer
-    ///
-    /// @return offset to the next byte after last used byte
-    virtual unsigned int
-    pack4(boost::shared_array<uint8_t>& buf,
-          unsigned int buf_len,
-          unsigned int offset);
-
     /// Builds raw (over-wire) buffer of this option, including all
     /// defined suboptions. Version for building DHCPv4 options.
     ///

+ 136 - 0
src/lib/dhcp/option4_addrlst.cc

@@ -0,0 +1,136 @@
+// 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 <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+#include <util/io_utilities.h>
+#include <dhcp/option4_addrlst.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc::asiolink;
+
+Option4AddrLst::Option4AddrLst(uint8_t type)
+    :Option(V4, type) {
+}
+
+Option4AddrLst::Option4AddrLst(uint8_t type, const AddressContainer& addrs)
+    :Option(V4, type) {
+    setAddresses(addrs);
+    // don't set addrs_ directly. setAddresses() will do additional checks.
+}
+
+
+Option4AddrLst::Option4AddrLst(uint8_t type,
+                               vector<uint8_t>::const_iterator first,
+                               vector<uint8_t>::const_iterator last)
+    :Option(V4, type) {
+    vector<uint8_t> buf = std::vector<uint8_t>(first, last);
+    if ( (buf.size() % V4ADDRESS_LEN) ) {
+        isc_throw(OutOfRange, "DHCPv4 Option4AddrLst " << type_
+                  << " has invalid length=" << buf.size()
+                  << ", must be divisible by 4.");
+    }
+
+    while (first != last) {
+        const uint8_t* ptr = &(*first);
+        addAddress(IOAddress(readUint32(ptr)));
+        first += V4ADDRESS_LEN;
+    }
+}
+
+Option4AddrLst::Option4AddrLst(uint8_t type, const IOAddress& addr)
+    :Option(V4, type) {
+    setAddress(addr);
+}
+
+void
+Option4AddrLst::pack4(isc::util::OutputBuffer& buf) {
+
+    if (addrs_.size() * V4ADDRESS_LEN > 255) {
+        isc_throw(OutOfRange, "DHCPv4 Option4AddrLst " << type_ << " is too big."
+                  << "At most 255 bytes are supported.");
+        /// TODO Larger options can be stored as separate instances
+        /// of DHCPv4 options. Clients MUST concatenate them.
+        /// Fortunately, there are no such large options used today.
+    }
+
+    buf.writeUint8(type_);
+    buf.writeUint8(len() - getHeaderLen());
+
+    AddressContainer::const_iterator addr = addrs_.begin();
+
+    while (addr != addrs_.end()) {
+        buf.writeUint32(*addr);
+        addr++;
+    }
+}
+
+void Option4AddrLst::setAddress(const isc::asiolink::IOAddress& addr) {
+    if (addr.getFamily() != AF_INET) {
+        isc_throw(BadValue, "Can't store non-IPv4 address in "
+                  << "Option4AddrLst option");
+    }
+    addrs_.clear();
+    addAddress(addr);
+}
+
+void Option4AddrLst::setAddresses(const AddressContainer& addrs) {
+
+    // Do not copy it as a whole. addAddress() does sanity checks.
+    // i.e. throw if someone tries to set IPv6 address.
+    addrs_.clear();
+    for (AddressContainer::const_iterator addr = addrs.begin();
+         addr != addrs.end(); ++addr) {
+        addAddress(*addr);
+    }
+}
+
+
+void Option4AddrLst::addAddress(const isc::asiolink::IOAddress& addr) {
+    if (addr.getFamily() != AF_INET) {
+        isc_throw(BadValue, "Can't store non-IPv4 address in "
+                  << "Option4AddrLst option");
+    }
+    addrs_.push_back(addr);
+}
+
+unsigned short
+Option4AddrLst::len() {
+
+    // Returns length of the complete option (option header + data length)
+    return (getHeaderLen() + addrs_.size() * V4ADDRESS_LEN);
+}
+
+std::string Option4AddrLst::toText(int indent /* =0 */ ) {
+    std::stringstream tmp;
+
+    for (int i = 0; i < indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << ", len=" << len()-getHeaderLen() << ": ";
+
+    for (AddressContainer::const_iterator addr = addrs_.begin();
+         addr != addrs_.end(); ++addr) {
+        tmp << (*addr) << " ";
+    }
+
+    return tmp.str();
+}

+ 171 - 0
src/lib/dhcp/option4_addrlst.h

@@ -0,0 +1,171 @@
+// 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 OPTION4_ADDRLST_H_
+#define OPTION4_ADDRLST_H_
+
+#include <string>
+#include <map>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+#include <util/buffer.h>
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief DHCPv4 Option class for handling list of IPv4 addresses.
+///
+/// This class handles a list of IPv4 addresses. An example of such option
+/// is dns-servers option. It can also be used to handle a single address.
+class Option4AddrLst : public isc::dhcp::Option {
+public:
+
+    /// Defines a collection of IPv4 addresses.
+    typedef std::vector<isc::asiolink::IOAddress> AddressContainer;
+
+    /// @brief Constructor, creates an option with empty list of addresses.
+    ///
+    /// Creates empty option that can hold addresses. Addresses can be added
+    /// with addAddress(), setAddress() or setAddresses().
+    ///
+    /// @param type option type
+    Option4AddrLst(uint8_t type);
+
+    /// @brief Constructor, creates an option with a list of addresses.
+    ///
+    /// Creates an option that contains specified list of IPv4 addresses.
+    ///
+    /// @param type option type
+    /// @param addrs container with a list of addresses
+    Option4AddrLst(uint8_t type, const AddressContainer& addrs);
+
+    /// @brief Constrcutor, creates an option with a single address.
+    ///
+    /// Creates an option that contains a single address.
+    ///
+    /// @param type option type
+    /// @param addr a single address that will be stored as 1-elem. address list
+    Option4AddrLst(uint8_t type, const isc::asiolink::IOAddress& addr);
+
+    /// @brief Constructor, used for received options.
+    ///
+    /// This contructor is similar to the previous one, but it does not take
+    /// the whole vector<uint8_t>, but rather subset of it.
+    ///
+    /// TODO: This can be templated to use different containers, not just
+    /// vector. Prototype should look like this:
+    /// template<typename InputIterator> Option(Universe u, uint16_t type,
+    /// InputIterator first, InputIterator last);
+    ///
+    /// vector<int8_t> myData;
+    /// Example usage: new Option(V4, 123, myData.begin()+1, myData.end()-1)
+    /// This will create DHCPv4 option of type 123 that contains data from
+    /// trimmed (first and last byte removed) myData vector.
+    ///
+    /// @param type option type (0-255 for V4 and 0-65535 for V6)
+    /// @param first iterator to the first element that should be copied
+    /// @param last iterator to the next element after the last one
+    ///        to be copied.
+    Option4AddrLst(uint8_t type, std::vector<uint8_t>::const_iterator first,
+           std::vector<uint8_t>::const_iterator last);
+
+    /// @brief Writes option in a wire-format to a buffer.
+    ///
+    /// Method will throw if option storing fails for some reason.
+    ///
+    /// TODO Once old (DHCPv6) implementation is rewritten,
+    /// unify pack4() and pack6() and rename them to just pack().
+    ///
+    /// @param buf output buffer (option will be stored there)
+    virtual void
+    pack4(isc::util::OutputBuffer& buf);
+
+    /// Returns string representation of the option.
+    ///
+    /// @param indent number of spaces before printing text
+    ///
+    /// @return string with text representation.
+    virtual std::string
+    toText(int indent = 0);
+
+    /// Returns length of the complete option (data length + DHCPv4/DHCPv6
+    /// option header)
+    ///
+    /// @return length of the option
+    virtual unsigned short
+    len();
+
+    /// @brief Returns vector with addresses.
+    ///
+    /// We return a copy of our list. Although this includes overhead,
+    /// it also makes this list safe to use after this option object
+    /// is no longer available. As options are expected to hold only
+    /// a couple (1-3) addresses, the overhead is not that big.
+    ///
+    /// @return address container with addresses
+    AddressContainer
+    getAddresses() { return addrs_; };
+
+    /// @brief Sets addresses list.
+    ///
+    /// Clears existing list of addresses and adds a single address to that
+    /// list. This is very convenient method for options that are supposed to
+    /// only a single option. See addAddress() if you want to add
+    /// address to existing list or setAddresses() if you want to
+    /// set the whole list at once.
+    ///
+    /// Passed address must be IPv4 address. Otherwire BadValue exception
+    /// will be thrown.
+    ///
+    /// @param addrs address collection to be set
+    void setAddresses(const AddressContainer& addrs);
+
+    /// @brief Clears address list and sets a single address.
+    ///
+    /// Clears existing list of addresses and adds a single address to that
+    /// list. This is very convenient method for options that are supposed to
+    /// only a single option. See addAddress() if you want to add
+    /// address to existing list or setAddresses() if you want to
+    /// set the whole list at once.
+    ///
+    /// Passed address must be IPv4 address. Otherwire BadValue exception
+    /// will be thrown.
+    ///
+    /// @param addr an address that is going to be set as 1-element address list
+    void setAddress(const isc::asiolink::IOAddress& addr);
+
+    /// @brief Adds address to existing list of addresses.
+    ///
+    /// Adds a single address to that list. See setAddress() if you want to
+    /// define only a single address or setAddresses() if you want to
+    /// set the whole list at once.
+    ///
+    /// Passed address must be IPv4 address. Otherwire BadValue exception
+    /// will be thrown.
+    ///
+    /// @param addr an address thait is going to be added to existing list
+    void addAddress(const isc::asiolink::IOAddress& addr);
+
+protected:
+    /// contains list of addresses
+    AddressContainer addrs_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif

+ 4 - 0
src/lib/dhcp/option6_addrlst.cc

@@ -50,6 +50,10 @@ Option6AddrLst::Option6AddrLst(unsigned short type,
 
 void
 Option6AddrLst::setAddress(const isc::asiolink::IOAddress& addr) {
+    if (addr.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Can't store non-IPv6 address in Option6AddrLst option");
+    }
+
     addrs_.clear();
     addrs_.push_back(addr);
 }

+ 8 - 9
src/lib/dhcp/option6_addrlst.h

@@ -16,17 +16,16 @@
 #define OPTION6_ADDRLST_H_
 
 #include <vector>
-#include "asiolink/io_address.h"
-#include "dhcp/option.h"
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
 
 namespace isc {
 namespace dhcp {
 
-/// @brief Option class for handling list of IPv6 addresses.
+/// @brief DHCPv6 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:
@@ -105,12 +104,12 @@ public:
 
     /// @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
+    /// We return a copy of our list. Although this includes overhead,
+    /// it also makes this list safe to use after this option object
+    /// is no longer available. As options are expected to hold only
+    /// a couple (1-3) addresses, the overhead is not that big.
     ///
+    /// @return address container with addresses
     AddressContainer
     getAddresses() { return addrs_; };
 

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -18,6 +18,7 @@ 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 += ../option4_addrlst.cc ../option4_addrlst.h option4_addrlst_unittest.cc
 libdhcp_unittests_SOURCES += ../option.h ../option.cc option_unittest.cc
 libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
 libdhcp_unittests_SOURCES += ../pkt4.h ../pkt4.cc pkt4_unittest.cc

+ 273 - 0
src/lib/dhcp/tests/option4_addrlst_unittest.cc

@@ -0,0 +1,273 @@
+// 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 <asiolink/io_address.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/option.h>
+#include <dhcp/option4_addrlst.h>
+#include <util/buffer.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace {
+
+// a sample data (list of 4 addresses)
+const uint8_t sampledata[] = {
+    192, 0, 2, 3,     // 192.0.2.3
+    255, 255, 255, 0, // 255.255.255.0 - popular netmask
+    0, 0, 0 , 0,      // used for default routes or (any address)
+    127, 0, 0, 1      // loopback
+};
+
+// expected on-wire format for an option with 1 address
+const uint8_t expected1[] = { // 1 address
+    DHO_DOMAIN_NAME_SERVERS, 4, // type, length
+    192, 0, 2, 3,     // 192.0.2.3
+};
+
+// expected on-wire format for an option with 4 addresses
+const uint8_t expected4[] = { // 4 addresses
+    254, 16,            // type = 254, len = 16
+    192, 0, 2, 3,       // 192.0.2.3
+    255, 255, 255, 0,   // 255.255.255.0 - popular netmask
+    0, 0, 0 ,0,         // used for default routes or (any address)
+    127, 0, 0, 1        // loopback
+};
+
+class Option4AddrLstTest : public ::testing::Test {
+protected:
+
+    Option4AddrLstTest():
+        vec_(vector<uint8_t>(300,0)) // 300 bytes long filled with 0s
+    {
+        sampleAddrs_.push_back(IOAddress("192.0.2.3"));
+        sampleAddrs_.push_back(IOAddress("255.255.255.0"));
+        sampleAddrs_.push_back(IOAddress("0.0.0.0"));
+        sampleAddrs_.push_back(IOAddress("127.0.0.1"));
+    }
+
+    vector<uint8_t> vec_;
+    Option4AddrLst::AddressContainer sampleAddrs_;
+
+};
+
+TEST_F(Option4AddrLstTest, parse1) {
+
+    memcpy(&vec_[0], sampledata, sizeof(sampledata));
+
+    // just one address
+    Option4AddrLst* opt1 = 0;
+    EXPECT_NO_THROW(
+        opt1 = new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS,
+                                  vec_.begin(),
+                                  vec_.begin()+4);
+        // use just first address (4 bytes), not the whole
+        // sampledata
+    );
+
+    EXPECT_EQ(Option::V4, opt1->getUniverse());
+
+    EXPECT_EQ(DHO_DOMAIN_NAME_SERVERS, opt1->getType());
+    EXPECT_EQ(6, opt1->len()); // 2 (header) + 4 (1x IPv4 addr)
+
+    Option4AddrLst::AddressContainer addrs = opt1->getAddresses();
+    ASSERT_EQ(1, addrs.size());
+
+    EXPECT_EQ("192.0.2.3", addrs[0].toText());
+
+    EXPECT_NO_THROW(
+        delete opt1;
+        opt1 = 0;
+    );
+
+    // 1 address
+}
+
+TEST_F(Option4AddrLstTest, parse4) {
+
+    vector<uint8_t> buffer(300,0); // 300 bytes long filled with 0s
+
+    memcpy(&buffer[0], sampledata, sizeof(sampledata));
+
+    // 4 addresses
+    Option4AddrLst* opt4 = 0;
+    EXPECT_NO_THROW(
+        opt4 = new Option4AddrLst(254,
+                                  buffer.begin(),
+                                  buffer.begin()+sizeof(sampledata));
+    );
+
+    EXPECT_EQ(Option::V4, opt4->getUniverse());
+
+    EXPECT_EQ(254, opt4->getType());
+    EXPECT_EQ(18, opt4->len()); // 2 (header) + 16 (4x IPv4 addrs)
+
+    Option4AddrLst::AddressContainer addrs = opt4->getAddresses();
+    ASSERT_EQ(4, addrs.size());
+
+    EXPECT_EQ("192.0.2.3", addrs[0].toText());
+    EXPECT_EQ("255.255.255.0", addrs[1].toText());
+    EXPECT_EQ("0.0.0.0", addrs[2].toText());
+    EXPECT_EQ("127.0.0.1", addrs[3].toText());
+
+    EXPECT_NO_THROW(
+        delete opt4;
+        opt4 = 0;
+    );
+}
+
+TEST_F(Option4AddrLstTest, assembly1) {
+
+    Option4AddrLst* opt = 0;
+    EXPECT_NO_THROW(
+        opt = new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress("192.0.2.3"));
+    );
+    EXPECT_EQ(Option::V4, opt->getUniverse());
+    EXPECT_EQ(DHO_DOMAIN_NAME_SERVERS, opt->getType());
+
+    Option4AddrLst::AddressContainer addrs = opt->getAddresses();
+    ASSERT_EQ(1, addrs.size() );
+    EXPECT_EQ("192.0.2.3", addrs[0].toText());
+
+    OutputBuffer buf(100);
+    EXPECT_NO_THROW(
+        opt->pack4(buf);
+    );
+
+    ASSERT_EQ(6, opt->len());
+    ASSERT_EQ(6, buf.getLength());
+
+    EXPECT_EQ(0, memcmp(expected1, buf.getData(), 6));
+
+    EXPECT_NO_THROW(
+        delete opt;
+        opt = 0;
+    );
+
+    // This is old-fashioned option. We don't serve IPv6 types here!
+    EXPECT_THROW(
+        opt = new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress("2001:db8::1")),
+        BadValue
+    );
+    if (opt) {
+        // test failed. Execption was not thrown, but option was created instead.
+        delete opt;
+    }
+}
+
+TEST_F(Option4AddrLstTest, assembly4) {
+
+
+    Option4AddrLst* opt = 0;
+    EXPECT_NO_THROW(
+        opt = new Option4AddrLst(254, sampleAddrs_);
+    );
+    EXPECT_EQ(Option::V4, opt->getUniverse());
+    EXPECT_EQ(254, opt->getType());
+
+    Option4AddrLst::AddressContainer addrs = opt->getAddresses();
+    ASSERT_EQ(4, addrs.size() );
+    EXPECT_EQ("192.0.2.3", addrs[0].toText());
+    EXPECT_EQ("255.255.255.0", addrs[1].toText());
+    EXPECT_EQ("0.0.0.0", addrs[2].toText());
+    EXPECT_EQ("127.0.0.1", addrs[3].toText());
+
+    OutputBuffer buf(100);
+    EXPECT_NO_THROW(
+        opt->pack4(buf);
+    );
+
+    ASSERT_EQ(18, opt->len()); // 2(header) + 4xsizeof(IPv4addr)
+    ASSERT_EQ(18, buf.getLength());
+
+    ASSERT_EQ(0, memcmp(expected4, buf.getData(), 18));
+
+    EXPECT_NO_THROW(
+        delete opt;
+        opt = 0;
+    );
+
+    // This is old-fashioned option. We don't serve IPv6 types here!
+    sampleAddrs_.push_back(IOAddress("2001:db8::1"));
+    EXPECT_THROW(
+        opt = new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, sampleAddrs_),
+        BadValue
+    );
+    if (opt) {
+        // test failed. Execption was not thrown, but option was created instead.
+        delete opt;
+    }
+}
+
+TEST_F(Option4AddrLstTest, setAddress) {
+    Option4AddrLst* opt = 0;
+    EXPECT_NO_THROW(
+        opt = new Option4AddrLst(123, IOAddress("1.2.3.4"));
+    );
+    opt->setAddress(IOAddress("192.0.255.255"));
+
+    Option4AddrLst::AddressContainer addrs = opt->getAddresses();
+    ASSERT_EQ(1, addrs.size() );
+    EXPECT_EQ("192.0.255.255", addrs[0].toText());
+
+    // We should accept IPv4-only addresses.
+    EXPECT_THROW(
+        opt->setAddress(IOAddress("2001:db8::1")),
+        BadValue
+    );
+
+    EXPECT_NO_THROW(
+        delete opt;
+    );
+}
+
+TEST_F(Option4AddrLstTest, setAddresses) {
+
+    Option4AddrLst* opt = 0;
+
+    EXPECT_NO_THROW(
+        opt = new Option4AddrLst(123); // empty list
+    );
+
+    opt->setAddresses(sampleAddrs_);
+
+    Option4AddrLst::AddressContainer addrs = opt->getAddresses();
+    ASSERT_EQ(4, addrs.size() );
+    EXPECT_EQ("192.0.2.3", addrs[0].toText());
+    EXPECT_EQ("255.255.255.0", addrs[1].toText());
+    EXPECT_EQ("0.0.0.0", addrs[2].toText());
+    EXPECT_EQ("127.0.0.1", addrs[3].toText());
+
+    // We should accept IPv4-only addresses.
+    sampleAddrs_.push_back(IOAddress("2001:db8::1"));
+    EXPECT_THROW(
+        opt->setAddresses(sampleAddrs_),
+        BadValue
+    );
+
+    EXPECT_NO_THROW(
+        delete opt;
+    );
+}
+
+} // namespace