Browse Source

[878] Support for IA and IAADDR options and suboptions added.
- IA support (Option6IA class) added.
- Support for suboptions in IA option.
- IAADDR support (Option6IAAddr class) added.
- Initial tests added for new classes added.

Tomek Mrugalski 13 years ago
parent
commit
af27ec87f0

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

@@ -11,6 +11,8 @@ lib_LTLIBRARIES = libdhcp.la
 libdhcp_la_SOURCES  =
 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 += dhcp6.h
 libdhcp_la_SOURCES += pkt6.cc pkt6.h
 

+ 76 - 36
src/lib/dhcp/libdhcp.cc

@@ -16,10 +16,18 @@
 #include <boost/shared_ptr.hpp>
 #include "dhcp/libdhcp.h"
 #include "config.h"
+#include "dhcp6.h"
+
+#include "option.h"
+#include "option6_ia.h"
+#include "option6_iaaddr.h"
 
 using namespace std;
 using namespace isc::dhcp;
 
+// static array with factory
+std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
+
 std::string
 LibDHCP::version() {
     return PACKAGE_VERSION;
@@ -39,70 +47,102 @@ LibDHCP::version() {
  * @return offset to first byte after last parsed option
  */
 unsigned int
-LibDHCP::unpackOptions6(boost::shared_array<char>& buf,
-                        int buf_len,
-                        unsigned short offset,
+LibDHCP::unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                        unsigned int offset, unsigned int parse_len,
                         isc::dhcp::Option::Option6Lst& options) {
-    int len = buf_len - offset;
-    while (len>4) {
-        int opt_type = buf[offset]*256 + buf[offset+1];
+    if (offset + parse_len > buf_len) {
+        isc_throw(OutOfRange, "Option parse failed. Tried to parse "
+                  << parse_len << " bytes at offset " << offset
+                  << ":  out of buffer");
+    }
+    unsigned int end = offset + parse_len;
+
+    while (offset<end) {
+        unsigned int opt_type = buf[offset]*256 + buf[offset+1];
         offset += 2;
-        len -= 2;
-        int opt_len = buf[offset]*256 + buf[offset+1];
+        unsigned 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;
+        if (offset + opt_len > end ) {
+            cout << "Option " << opt_type << " truncated." << endl;
             return (offset);
         }
-
-        boost::shared_ptr<Option> opt(new Option(Option::V6,
-                                                 opt_type,
-                                                 buf,
-                                                 offset,
-                                                 opt_len));
+        boost::shared_ptr<Option> opt;
+        switch (opt_type) {
+        case D6O_IA_NA:
+        case D6O_IA_PD:
+            // cout << "Creating Option6IA" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IA(Option::V6,
+                                                          opt_type,
+                                                          buf, buf_len,
+                                                          offset,
+                                                          opt_len));
+            break;
+        case D6O_IAADDR:
+            // cout << "Creating Option6IAAddr" << endl;
+            opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
+                                                              buf, buf_len,
+                                                              offset, opt_len));
+            break;
+        default:
+            // cout << "Creating Option" << endl;
+            opt = boost::shared_ptr<Option>(new Option(Option::V6,
+                                                       opt_type,
+                                                       buf,
+                                                       offset,
+                                                       opt_len));
+            break;
+        }
         // 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,
+LibDHCP::packOptions6(boost::shared_array<char> data,
+                      unsigned int data_len,
+                      unsigned int 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" <<
+            if (offset + opt_len > data_len) {
+                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;
+            offset = (*it).second->pack(data, data_len, offset);
         }
     }
     catch (Exception e) {
         cout << "Packet build failed." << endl;
         return (-1);
     }
-    cout << "Packet built" << endl;
     return (offset);
 }
+
+bool
+LibDHCP::OptionFactorRegister(Option::Universe u,
+                              unsigned short opt_type,
+                              Option::Factory * factory) {
+    switch (u) {
+    case Option::V6: {
+        if (v6factories_.find(opt_type)!=v6factories_.end()) {
+            isc_throw(BadValue, "There is already DHCPv6 factory registered "
+                     << "for option type "  << opt_type);
+        }
+        v6factories_[opt_type]=factory;
+        return true;
+    }
+    case Option::V4:
+    default:{
+        isc_throw(BadValue, "This universe type is not supported yet.");
+        return false; // never happens
+    }
+    }
+
+}

+ 12 - 8
src/lib/dhcp/libdhcp.h

@@ -27,20 +27,24 @@ public:
     LibDHCP();
     static std::string version();
 
-    bool parsePkt6(Pkt6& pkt); 
+    bool parsePkt6(Pkt6& pkt);
     bool builtPkt6(Pkt6& pkt);
 
-    
     static unsigned int
-    packOptions6(boost::shared_array<char>& buf,
-                 int buf_len,
-                 unsigned short offset,
+    packOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                 unsigned int offset,
                  isc::dhcp::Option::Option6Lst& options_);
     static unsigned int
-    unpackOptions6(boost::shared_array<char>& buf,
-                   int buf_len,
-                   unsigned short offset,
+    unpackOptions6(boost::shared_array<char> buf, unsigned int buf_len,
+                   unsigned int offset, unsigned int parse_len,
                    isc::dhcp::Option::Option6Lst& options_);
+
+    bool OptionFactorRegister(Option::Universe u,
+                              unsigned short type,
+                              Option::Factory * factory);
+protected:
+    // pointers to factories that produce DHCPv6 options
+    static std::map<unsigned short, Option::Factory*> v6factories_;
 };
 
 }

+ 56 - 48
src/lib/dhcp/option.cc

@@ -32,9 +32,9 @@ Option::Option(Universe u, unsigned short type)
 
 }
 
-Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf, 
+Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
                unsigned int offset, unsigned int len)
-    :universe_(u), type_(type), data_(buf), 
+    :universe_(u), type_(type), data_(buf),
      offset_(offset),
      len_(len) {
 
@@ -42,51 +42,60 @@ Option::Option(Universe u, unsigned short type, boost::shared_array<char> buf,
     // TODO: universe must be in V4 and V6
 }
 
-char* Option::pack(char* buf, unsigned int len) {
+unsigned int
+Option::pack(boost::shared_array<char> buf,
+             unsigned int buf_len,
+             unsigned int offset) {
     switch (universe_) {
     case V4:
-        return pack4(buf, len);
+        return pack4(buf, buf_len, offset);
     case V6:
-        return pack6(buf, len);
+        return pack6(buf, buf_len, offset);
     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=" << 
+
+unsigned int
+Option::pack4(boost::shared_array<char> 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.");
     }
-    buf[0] = type_;
-    buf[1] = len_;
-    buf += 2;
-    memcpy(buf, &data_[0], len_);
+    char *ptr = &buf[offset];
+    ptr[0] = type_;
+    ptr[1] = len_;
+    ptr += 2;
+    memcpy(ptr, &data_[0], len_);
 
-    return buf + len_;
+    return offset + len();
 }
 
-char* Option::pack6(char* buf, unsigned short len) {
-    if (this->len()>len) {
-        isc_throw(OutOfRange, "Failed to pack v6 option=" << 
+unsigned int
+Option::pack6(boost::shared_array<char> buf,
+             unsigned int buf_len,
+             unsigned int offset) {
+    if ( offset+len() > buf_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_;
+    char * ptr = &buf[offset];
+    *(uint16_t*)ptr = htons(type_);
+    ptr += 2;
+    *(uint16_t*)ptr = htons(len_);
+    ptr += 2;
+    memcpy(ptr, &data_[0], len_);
+
+    return offset + len();
 }
 
-unsigned int 
-Option::unpack(boost::shared_array<char> buf, 
+unsigned int
+Option::unpack(boost::shared_array<char> buf,
                unsigned int buf_len,
-               unsigned int offset, 
+               unsigned int offset,
                unsigned int parse_len) {
     switch (universe_) {
     case V4:
@@ -100,41 +109,41 @@ Option::unpack(boost::shared_array<char> buf,
     return 0; // should not happen
 }
 
-unsigned int 
-Option::unpack4(boost::shared_array<char>, 
+unsigned int
+Option::unpack4(boost::shared_array<char>,
+                unsigned int ,
                 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
+Option::unpack6(boost::shared_array<char> buf,
                 unsigned int buf_len,
-                unsigned int offset, 
+                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=" 
+        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,
+    return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
                                    optionLst_);
 }
 
@@ -155,7 +164,7 @@ 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 && 
+    if (universe_ != V4 &&
         universe_ != V6) {
         return (false);
     }
@@ -163,9 +172,9 @@ bool Option::valid() {
     return (true);
 }
 
-/** 
+/**
  * Converts generic option to string.
- * 
+ *
  * @return string that represents option.
  */
 std::string Option::toText() {
@@ -176,12 +185,12 @@ std::string Option::toText() {
         if (i) {
             tmp << ":";
         }
-        tmp << setfill('0') << setw(2) << hex << (unsigned short)data_[offset_+i];
+        tmp << setfill('0') << setw(2) << hex << (unsigned short)(unsigned char)data_[offset_+i];
     }
     return tmp.str();
 }
 
-unsigned short 
+unsigned short
 Option::getType() {
     return type_;
 }
@@ -189,4 +198,3 @@ Option::getType() {
 Option::~Option() {
 
 }
-

+ 35 - 22
src/lib/dhcp/option.h

@@ -24,39 +24,46 @@ namespace dhcp {
 
 class Option {
 public:
+    enum Universe { V4, V6 };
     typedef std::map<unsigned int, boost::shared_ptr<Option> > Option4Lst;
     typedef std::multimap<unsigned int, boost::shared_ptr<Option> > Option6Lst;
-
-    enum Universe { V4, V6 };
+    typedef boost::shared_ptr<Option> Factory(Option::Universe u,
+                                              unsigned short type,
+                                              boost::shared_array<char> buf,
+                                              unsigned int offset,
+                                              unsigned int len);
 
     // ctor, used for options constructed, usually during transmission
-    Option(Universe u, unsigned short type); 
+    Option(Universe u, unsigned short type);
 
     // ctor, used for received options
-    // boost::shared_array allows sharing a buffer, but it requires that 
+    // 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, 
+    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 
+    // writes option in wire-format to buf, returns pointer to first unused
     // byte after stored option
-    virtual char* pack(char* buf, unsigned int len);
+    virtual unsigned int
+    pack(boost::shared_array<char> buf,
+         unsigned int buf_len,
+         unsigned int offset);
 
-    // parses received buffer, returns pointer to first unused byte 
+    // 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, 
+    virtual unsigned int
+        unpack(boost::shared_array<char> buf,
                unsigned int buf_len,
-               unsigned int offset, 
+               unsigned int offset,
                unsigned int parse_len);
 
     virtual std::string toText();
@@ -65,23 +72,29 @@ public:
 
     // 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(); 
+    virtual bool valid();
 
     // just to force that every option has virtual dtor
-    virtual ~Option(); 
+    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, 
+    virtual unsigned int
+    pack4(boost::shared_array<char> buf,
+          unsigned int buf_len,
+          unsigned int offset);
+    virtual unsigned int
+    pack6(boost::shared_array<char> buf,
+          unsigned int buf_len,
+          unsigned int offset);
+    virtual unsigned int unpack4(boost::shared_array<char> buf,
                                  unsigned int buf_len,
-                                 unsigned int offset, 
+                                 unsigned int offset,
                                  unsigned int parse_len);
-    virtual unsigned int unpack6(boost::shared_array<char> buf, 
+    virtual unsigned int unpack6(boost::shared_array<char> buf,
                                  unsigned int buf_len,
-                                 unsigned int offset, 
+                                 unsigned int offset,
                                  unsigned int parse_len);
 
     Universe universe_;
@@ -92,7 +105,7 @@ protected:
     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 
+    unsigned int len_; // length of data only. Use len() if you want to know
                        // proper length with option header overhead
     char * value_;
 

+ 129 - 0
src/lib/dhcp/option6_ia.cc

@@ -0,0 +1,129 @@
+// 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_ia.h"
+#include "dhcp6.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+Option6IA::Option6IA(Universe u, unsigned short type, unsigned int iaid)
+    :Option(u, type), iaid_(iaid) {
+
+}
+
+
+Option6IA::Option6IA(Universe u, unsigned short type, 
+                   boost::shared_array<char> buf, 
+                   unsigned int buf_len,
+                   unsigned int offset, 
+                   unsigned int option_len)
+    :Option(u, type) {
+    unpack(buf, buf_len, offset, option_len);
+}
+
+unsigned int
+Option6IA::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.");
+    }
+    
+    char* ptr = &buf[offset];
+    *(uint16_t*)ptr = htons(type_);
+    ptr += 2;
+    buf_len -= 2;
+    *(uint16_t*)ptr = htons(len());
+    ptr += 2;
+    buf_len -= 2;
+    
+    *(uint32_t*)ptr = htonl(iaid_);
+    ptr += 4;
+    buf_len -= 4;
+
+    *(uint32_t*)ptr = htonl(t1_);
+    ptr += 4;
+    buf_len -= 4;
+
+    *(uint32_t*)ptr = htonl(t2_);
+    ptr += 4;
+    buf_len -= 4;
+
+    offset = LibDHCP::packOptions6(buf, buf_len, offset+16, optionLst_);
+    return offset;
+}
+
+unsigned int 
+Option6IA::unpack(boost::shared_array<char> buf,
+                  unsigned int buf_len,
+                  unsigned int offset, 
+                  unsigned int parse_len) {
+    if (parse_len<12 || offset+12>buf_len) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+    iaid_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    t1_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    t2_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    offset = LibDHCP::unpackOptions6(buf, buf_len, offset, 
+                                     parse_len - 12, optionLst_);
+
+    return (offset);
+}
+
+std::string Option6IA::toText() {
+    stringstream tmp;
+    switch (type_) {
+    case D6O_IA_NA:
+        tmp << "IA_NA";
+        break;
+    case D6O_IA_PD:
+        tmp << "IA_PD";
+        break;
+    }
+    tmp << " iaid=" << iaid_ << " t1=" << t1_ << " t2=" << t2_
+        << " " << optionLst_.size() << " sub-options:" << endl;
+
+    for (Option6Lst::const_iterator opt=optionLst_.begin();
+         opt!=optionLst_.end();
+         ++opt) {
+        tmp << "  " << (*opt).second->toText();
+    }
+    return tmp.str();
+}
+
+unsigned short Option6IA::len() {
+    
+    unsigned short length = 12; // header
+
+    // length of all suboptions
+    for (Option::Option6Lst::iterator it = optionLst_.begin();
+         it != optionLst_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
+

+ 75 - 0
src/lib/dhcp/option6_ia.h

@@ -0,0 +1,75 @@
+// 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_IA_H_
+#define OPTION_IA_H_
+
+#include "option.h"
+
+namespace isc {
+namespace dhcp {
+
+class Option6IA: public Option {
+        
+public:
+    // ctor, used for options constructed, usually during transmission
+    Option6IA(Universe u, unsigned short type, unsigned int iaid); 
+
+    // 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
+    Option6IA(Universe u, unsigned short type, boost::shared_array<char> buf, 
+              unsigned int buf_len,
+              unsigned int offset, 
+              unsigned int len);
+    
+    // writes option in wire-format to buf, returns pointer to first unused 
+    // byte after stored option
+    unsigned int
+    pack(boost::shared_array<char> buf, unsigned int buf_len, 
+         unsigned int offset);
+
+    // 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();
+
+    void setT1(unsigned int t1) { t1_=t1; }
+    void setT2(unsigned int t2) { t2_=t2; }
+
+    unsigned int getIAID() { return iaid_; }
+    unsigned int getT1()   { return t1_; }
+    unsigned int getT2()   { return t2_; }
+
+    // returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual unsigned short len();
+
+protected:
+    unsigned int iaid_;
+    unsigned int t1_;
+    unsigned int t2_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+    
+#endif /* OPTION_IA_H_ */

+ 129 - 0
src/lib/dhcp/option6_iaaddr.cc

@@ -0,0 +1,129 @@
+// 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_iaaddr.h"
+#include "dhcp6.h"
+#include "io_address.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+Option6IAAddr::Option6IAAddr(unsigned short type, 
+                             isc::asiolink::IOAddress addr,
+                             unsigned int pref,
+                             unsigned int valid)
+    :Option(V6, type), addr_(addr), preferred_(pref),
+     valid_(valid) {
+}
+
+Option6IAAddr::Option6IAAddr(unsigned short type, 
+                             boost::shared_array<char> buf, 
+                             unsigned int buf_len,
+                             unsigned int offset, 
+                             unsigned int option_len)
+    :Option(V6, type), addr_(IOAddress("::")) {
+    unpack(buf, buf_len, offset, option_len);
+}
+
+unsigned int
+Option6IAAddr::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());
+    offset += 2;
+
+    memcpy(&buf[offset], addr_.getAddress().to_v6().to_bytes().data(), 16);
+    offset += 16;
+
+    *(uint32_t*)&buf[offset] = htonl(preferred_);
+    offset += 4;
+    *(uint32_t*)&buf[offset] = htonl(valid_);
+    offset += 4;
+
+    // parse suboption (there shouldn't be any)
+    offset = LibDHCP::packOptions6(buf, buf_len, offset, optionLst_);
+    return offset;
+}
+
+unsigned int 
+Option6IAAddr::unpack(boost::shared_array<char> buf,
+                  unsigned int buf_len,
+                  unsigned int offset, 
+                  unsigned int parse_len) {
+    if (parse_len<24 || offset+24>buf_len) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+    
+    // 16 bytes: IPv6 address
+    /// TODO Implement fromBytes() method in IOAddress
+    char addr_str[INET6_ADDRSTRLEN];
+    inet_ntop(AF_INET6, &buf[offset], addr_str,INET6_ADDRSTRLEN);
+    addr_ = IOAddress(string(addr_str));
+    offset += 16;
+
+    preferred_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+
+    valid_ = ntohl(*(uint32_t*)&buf[offset]);
+    offset +=4;
+    offset = LibDHCP::unpackOptions6(buf, buf_len, offset, 
+                                     parse_len - 24, optionLst_);
+
+    return offset;
+}
+
+std::string Option6IAAddr::toText() {
+    stringstream tmp;
+    tmp << "addr: " << addr_.toText() << ", preferred-lft=" << preferred_ 
+        << ", valid-lft=" << valid_ << endl;
+
+    for (Option6Lst::const_iterator opt=optionLst_.begin();
+         opt!=optionLst_.end();
+         ++opt) {
+        tmp << "    " << (*opt).second->toText() << endl;
+    }
+    return tmp.str();
+}
+
+unsigned short Option6IAAddr::len() {
+    
+    unsigned short length = 24; // header
+
+    // length of all suboptions
+    // TODO implement:
+    // protected: unsigned short Option::lenHelper(int header_size);
+   
+    for (Option::Option6Lst::iterator it = optionLst_.begin();
+         it != optionLst_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
+

+ 80 - 0
src/lib/dhcp/option6_iaaddr.h

@@ -0,0 +1,80 @@
+// 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_IAADDR_H_
+#define OPTION6_IAADDR_H_
+
+#include "io_address.h"
+#include "option.h"
+
+namespace isc {
+namespace dhcp {
+
+class Option6IAAddr: public Option {
+        
+public:
+    // ctor, used for options constructed, usually during transmission
+    Option6IAAddr(unsigned short type, 
+                  isc::asiolink::IOAddress addr,
+                  unsigned int prefered,
+                  unsigned int valid); 
+
+    // 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
+    Option6IAAddr(unsigned short type, boost::shared_array<char> buf, 
+                  unsigned int buf_len,
+                  unsigned int offset, 
+                  unsigned int len);
+    
+    // writes option in wire-format to buf, returns pointer to first unused 
+    // byte after stored option
+    unsigned int
+    pack(boost::shared_array<char> buf, unsigned int buf_len, 
+         unsigned int offset);
+
+    // 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();
+
+    void setAddress(isc::asiolink::IOAddress addr) { addr_ = addr; }
+    void setPreferred(unsigned int pref) { preferred_=pref; }
+    void setValid(unsigned int valid) { valid_=valid; }
+
+    isc::asiolink::IOAddress getAddress() { return addr_; }
+    unsigned int getPreferred()   { return preferred_; }
+    unsigned int getValid()   { return valid_; }
+
+    // returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual unsigned short len();
+
+protected:
+    isc::asiolink::IOAddress addr_;
+    unsigned int preferred_;
+    unsigned int valid_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+    
+#endif /* OPTION_IA_H_ */

+ 9 - 8
src/lib/dhcp/pkt6.cc

@@ -66,7 +66,7 @@ unsigned short Pkt6::len() {
         length += (*it).second->len();
     }
 
-    return length;
+    return (length);
 }
 
 
@@ -87,7 +87,7 @@ Pkt6::pack() {
     default:
         isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
     }
-    return false; // never happens
+    return (false); // never happens
 }
 
 
@@ -123,10 +123,10 @@ Pkt6::packUDP() {
     }
     catch (Exception e) {
         cout << "Packet build failed." << endl;
-        return false;
+        return (false);
     }
     cout << "Packet built, len=" << len() << endl;
-    return true;
+    return (true);
 }
 
 
@@ -160,7 +160,7 @@ Pkt6::unpack() {
     default:
         isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
     }
-    return false; // never happens
+    return (false); // never happens
 }
 
 /**
@@ -173,7 +173,7 @@ Pkt6::unpackUDP() {
     if (data_len_ < 4) {
         std::cout << "DHCPv6 packet truncated. Only " << data_len_
                   << " bytes. Need at least 4." << std::endl;
-        return false;
+        return (false);
     }
     msg_type_ = data_[0];
     transid_ = (data_[1] << 16) + (data_[2] << 8) + data_[3];
@@ -181,6 +181,7 @@ Pkt6::unpackUDP() {
     unsigned int offset = LibDHCP::unpackOptions6(data_,
                                                   data_len_,
                                                   4, //offset
+                                                  data_len_ - 4,
                                                   options_);
     if (offset != data_len_) {
         cout << "DHCPv6 packet contains trailing garbage. Parsed "
@@ -188,7 +189,7 @@ Pkt6::unpackUDP() {
              << endl;
         // just a warning. Ignore trailing garbage and continue
     }
-    return true;
+    return (true);
 }
 
 /**
@@ -249,7 +250,7 @@ Pkt6::getOption(unsigned short opt_type) {
  */
 unsigned char
 Pkt6::getType() {
-    return msg_type_;
+    return (msg_type_);
 }
 
 Pkt6::~Pkt6() {

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

@@ -17,6 +17,8 @@ if HAVE_GTEST
 TESTS += libdhcp_unittests
 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 += ../option.h ../option.cc option_unittest.cc
 libdhcp_unittests_SOURCES += ../pkt6.h ../pkt6.cc pkt6_unittest.cc
 

+ 2 - 1
src/lib/dhcp/tests/libdhcp_unittest.cc

@@ -20,6 +20,7 @@
 #include <gtest/gtest.h>
 
 #include "dhcp/libdhcp.h"
+#include "config.h"
 
 using namespace std;
 using namespace isc;
@@ -35,7 +36,7 @@ public:
 TEST_F(LibDhcpTest, basic) {
     // dummy test
 
-    EXPECT_EQ(LibDHCP::version(), "0");
+    EXPECT_EQ(LibDHCP::version(), PACKAGE_VERSION);
 }
 
 }

+ 106 - 0
src/lib/dhcp/tests/option6_ia_unittest.cc

@@ -0,0 +1,106 @@
+// 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 "dhcp/dhcp6.h"
+#include "dhcp/option.h"
+#include "dhcp/option6_ia.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class Option6IATest : public ::testing::Test {
+public:
+    Option6IATest() {
+    }
+};
+
+TEST_F(Option6IATest, basic) {
+
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+    simple_buf[0]=0xa1; // iaid
+    simple_buf[1]=0xa2;
+    simple_buf[2]=0xa3;
+    simple_buf[3]=0xa4;
+
+    simple_buf[4]=0x81; // T1
+    simple_buf[5]=0x02;
+    simple_buf[6]=0x03;
+    simple_buf[7]=0x04;
+
+    simple_buf[8]=0x84; // T2
+    simple_buf[9]=0x03;
+    simple_buf[10]=0x02;
+    simple_buf[11]=0x01;
+
+    // create an option (unpack content)
+    Option6IA* opt = new Option6IA(Option::V6, 
+                                   D6O_IA_NA,
+                                   simple_buf,
+                                   128,
+                                   0, 
+                                   12);
+
+    EXPECT_EQ(D6O_IA_NA, opt->getType());
+    EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
+    EXPECT_EQ(0x81020304, opt->getT1());
+    EXPECT_EQ(0x84030201, opt->getT2());
+    
+    // pack this option again in the same buffer, but in
+    // different place
+    int offset = opt->pack(simple_buf, 128, 60);
+
+    // 4 bytes header + 4 bytes content
+    EXPECT_EQ(12, opt->len());
+    EXPECT_EQ(D6O_IA_NA, opt->getType());
+
+    EXPECT_EQ(offset, 76); // 60 + lenght(IA_NA) = 76
+
+    // check if pack worked properly:
+    // if option type is correct
+    EXPECT_EQ(D6O_IA_NA, simple_buf[60]*256 + simple_buf[61]);
+
+    // if option length is correct
+    EXPECT_EQ(12, simple_buf[62]*256 + simple_buf[63]);
+    
+    // if iaid is correct
+    unsigned int iaid = htonl(*(unsigned int*)&simple_buf[64]);
+    EXPECT_EQ(0xa1a2a3a4, iaid );
+ 
+   // if T1 is correct
+    EXPECT_EQ(0x81020304, (simple_buf[68] << 24) +
+                          (simple_buf[69] << 16) +
+                          (simple_buf[70] << 8) + 
+                          (simple_buf[71]) );
+
+    // if T1 is correct
+    EXPECT_EQ(0x84030201, (simple_buf[72] << 24) +
+                          (simple_buf[73] << 16) +
+                          (simple_buf[74] << 8) + 
+                          (simple_buf[75]) );
+
+    delete opt;
+}
+
+}

+ 96 - 0
src/lib/dhcp/tests/option6_iaaddr_unittest.cc

@@ -0,0 +1,96 @@
+// 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 "dhcp/dhcp6.h"
+#include "dhcp/option.h"
+#include "dhcp/option6_iaaddr.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+
+namespace {
+class Option6IAAddrTest : public ::testing::Test {
+public:
+    Option6IAAddrTest() {
+    }
+};
+
+TEST_F(Option6IAAddrTest, basic) {
+
+    boost::shared_array<char> simple_buf(new char[128]);
+    for (int i=0; i<128; i++)
+        simple_buf[i] = 0;
+    simple_buf[0]=0x20;
+    simple_buf[1]=0x01;
+    simple_buf[2]=0x0d;
+    simple_buf[3]=0xb8;
+    simple_buf[4]=0x00;
+    simple_buf[5]=0x01;
+    simple_buf[12]=0xde;
+    simple_buf[13]=0xad;
+    simple_buf[14]=0xbe;
+    simple_buf[15]=0xef; // 2001:db8:1::dead:beef
+
+    simple_buf[16]=0x00;
+    simple_buf[17]=0x00;
+    simple_buf[18]=0x03;
+    simple_buf[19]=0xe8; // 1000
+
+    simple_buf[20]=0xb2;
+    simple_buf[21]=0xd0;
+    simple_buf[22]=0x5e;
+    simple_buf[23]=0x00; // 3.000.000.000 
+
+    // create an option (unpack content)
+    Option6IAAddr* opt = new Option6IAAddr(D6O_IAADDR,
+                                           simple_buf,
+                                           128,
+                                           0, 
+                                           24);
+    
+    // pack this option again in the same buffer, but in
+    // different place
+    int offset = opt->pack(simple_buf, 128, 50);
+
+    EXPECT_EQ(78, offset);
+
+    // 4 bytes header + 4 bytes content
+    EXPECT_EQ("2001:db8:1::dead:beef", opt->getAddress().toText());
+    EXPECT_EQ(1000, opt->getPreferred());
+    EXPECT_EQ(3000000000, opt->getValid());
+
+    EXPECT_EQ(D6O_IAADDR, opt->getType());
+
+    // check if pack worked properly:
+    // if option type is correct
+    EXPECT_EQ(D6O_IAADDR, simple_buf[50]*256 + simple_buf[51]);
+
+    // if option length is correct
+    EXPECT_EQ(24, simple_buf[52]*256 + simple_buf[53]);
+    
+    // if option content is correct
+    EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[54],24));
+
+    delete opt;
+}
+
+}

+ 2 - 2
src/lib/dhcp/tests/option_unittest.cc

@@ -52,13 +52,13 @@ TEST_F(OptionTest, basic) {
     
     // pack this option again in the same buffer, but in
     // different place
-    char* offset18 = opt->pack(&simple_buf[10], 8);
+    int offset18 = opt->pack(simple_buf, 128, 10);
 
     // 4 bytes header + 4 bytes content
     EXPECT_EQ(8, opt->len());
     EXPECT_EQ(D6O_CLIENTID, opt->getType());
 
-    EXPECT_EQ(offset18, &simple_buf[0]+18);
+    EXPECT_EQ(offset18, 18);
 
     // check if pack worked properly:
     // if option type is correct

+ 1 - 1
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -56,7 +56,7 @@ Pkt6 *capture1() {
     pkt->ifindex_ = 2;
     pkt->iface_ = "eth0";
     pkt->data_[0]=1;
-    pkt->data_[1]=192;     pkt->data_[2]=129;     pkt->data_[3]=6;     pkt->data_[4]=0;
+    pkt->data_[1]=01;     pkt->data_[2]=02;     pkt->data_[3]=03;     pkt->data_[4]=0;
     pkt->data_[5]=1;     pkt->data_[6]=0;     pkt->data_[7]=14;     pkt->data_[8]=0;
     pkt->data_[9]=1;     pkt->data_[10]=0;     pkt->data_[11]=1;     pkt->data_[12]=21;
     pkt->data_[13]=158;     pkt->data_[14]=60;     pkt->data_[15]=22;     pkt->data_[16]=0;