Browse Source

[2490] Support for creating options using a collection of string values.

Marcin Siodelski 12 years ago
parent
commit
35c3fb1625

+ 7 - 0
src/lib/dhcp/option_data_types.h

@@ -30,6 +30,13 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief Exception to be thrown when cast to the data type was unsuccessful.
+class BadDataTypeCast : public Exception {
+public:
+    BadDataTypeCast(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 
 /// @brief Data types of DHCP option fields.
 enum OptionDataType {

+ 156 - 8
src/lib/dhcp/option_definition.cc

@@ -20,6 +20,7 @@
 #include <dhcp/option6_int.h>
 #include <dhcp/option6_int_array.h>
 #include <dhcp/option_definition.h>
+#include <util/encode/hex.h>
 
 using namespace std;
 using namespace isc::util;
@@ -44,11 +45,6 @@ OptionDefinition::DataTypeUtil::DataTypeUtil() {
     data_types_["record"] = OPT_RECORD_TYPE;
 }
 
-template<typename T>
-T OptionDefinition::DataTypeUtil::dataTypeCast(const std::string& value_str) const {
-    return (T());
-}
-
 OptionDataType
 OptionDefinition::DataTypeUtil::getOptionDataType(const std::string& data_type) {
     std::map<std::string, OptionDataType>::const_iterator data_type_it =
@@ -59,6 +55,135 @@ OptionDefinition::DataTypeUtil::getOptionDataType(const std::string& data_type)
     return (OPT_UNKNOWN_TYPE);
 }
 
+template<typename T>
+T OptionDefinition::DataTypeUtil::lexicalCastWithRangeCheck(const std::string& value_str) const {
+    if (!OptionDataTypeTraits<T>::integer_type &&
+        OptionDataTypeTraits<T>::type != OPT_BOOLEAN_TYPE) {
+        isc_throw(BadDataTypeCast, "unable to do lexical cast to non-integer and"
+                  << " non-boolean data type");
+    }
+    int64_t result = 0;
+    try {
+        result = boost::lexical_cast<int64_t>(value_str);
+    } catch (const boost::bad_lexical_cast& ex) {
+        std::string data_type_str = "boolean";
+        if (OptionDataTypeTraits<T>::integer_type) {
+            data_type_str = "integer";
+        }
+        isc_throw(BadDataTypeCast, "unable to do lexical cast to " << data_type_str
+                  << " data type for value " << value_str << ": " << ex.what());
+    }
+    if (OptionDataTypeTraits<T>::integer_type) {
+        if (result > numeric_limits<T>::max() ||
+            result < numeric_limits<T>::min()) {
+            isc_throw(BadDataTypeCast, "unable to do lexical cast for value "
+                      << value_str << ". This value is expected to be in the range of "
+                      << numeric_limits<T>::min() << ".." << numeric_limits<T>::max());
+        }
+    }
+    return (static_cast<T>(result));
+}
+
+void
+OptionDefinition::DataTypeUtil::writeToBuffer(const std::string& value, uint16_t type,
+                                              OptionBuffer& buf) {
+    switch (type) {
+    case OPT_BINARY_TYPE:
+        {
+            OptionBuffer binary;
+            try {
+                util::encode::decodeHex(value, binary);
+            } catch (const Exception& ex) {
+                isc_throw(BadDataTypeCast, "unable to cast " << value
+                          << " to binary data type: " << ex.what());
+            }
+            buf.insert(buf.end(), binary.begin(), binary.end());
+            return;
+        }
+    case OPT_BOOLEAN_TYPE:
+        {
+            bool bool_value = lexicalCastWithRangeCheck<bool>(value);
+            if (bool_value) {
+                buf.push_back(static_cast<uint8_t>(1));
+            } else {
+                buf.push_back(static_cast<uint8_t>(0));
+            }
+            return;
+        }
+    case OPT_INT8_TYPE:
+        {
+            buf.push_back(static_cast<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value)));
+            return;
+        }
+    case OPT_INT16_TYPE:
+        {
+            int16_t int_value = lexicalCastWithRangeCheck<int16_t>(value);
+            buf.resize(buf.size() + 2);
+            writeUint16(static_cast<uint16_t>(int_value), &buf[buf.size() - 2]);
+            return;
+        }
+    case OPT_INT32_TYPE:
+        {
+            int32_t int_value = lexicalCastWithRangeCheck<int32_t>(value);
+            buf.resize(buf.size() + 4);
+            writeUint32(static_cast<uint32_t>(int_value), &buf[buf.size() - 4]);
+            return;
+        }
+    case OPT_UINT8_TYPE:
+        {
+            buf.push_back(lexicalCastWithRangeCheck<int8_t>(value));
+            return;
+        }
+    case OPT_UINT16_TYPE:
+        {
+            uint16_t uint_value = lexicalCastWithRangeCheck<uint16_t>(value);
+            buf.resize(buf.size() + 2);
+            writeUint16(uint_value, &buf[buf.size() - 2]);
+            return;
+        }
+    case OPT_UINT32_TYPE:
+        {
+            uint32_t uint_value = lexicalCastWithRangeCheck<uint32_t>(value);
+            buf.resize(buf.size() + 4);
+            writeUint32(uint_value, &buf[buf.size() - 4]);
+            return;
+        }
+    case OPT_IPV4_ADDRESS_TYPE:
+        {
+            asiolink::IOAddress address(value);
+            if (!address.getAddress().is_v4()) {
+                isc_throw(BadDataTypeCast, "provided address " << address.toText()
+                          << " is not a valid IPV4 address");
+            }
+            asio::ip::address_v4::bytes_type addr_bytes =
+                address.getAddress().to_v4().to_bytes();
+            buf.resize(buf.size() + addr_bytes.size());
+            std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+                               buf.end());
+            return;
+        }
+    case OPT_IPV6_ADDRESS_TYPE:
+        {
+            asiolink::IOAddress address(value);
+            if (!address.getAddress().is_v6()) {
+                isc_throw(BadDataTypeCast, "provided address " << address.toText()
+                          << " is not a valid IPV6 address");
+            }
+            asio::ip::address_v6::bytes_type addr_bytes =
+                address.getAddress().to_v6().to_bytes();
+            buf.resize(buf.size() + addr_bytes.size());
+            std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+                               buf.end());
+            return;
+        }
+    default:
+        ;
+    }
+    isc_throw(isc::NotImplemented, "write of string, FQDN record into option buffer"
+              " is not supported yet");
+
+}
+
 OptionDefinition::OptionDefinition(const std::string& name,
                                  const uint16_t code,
                                  const std::string& type,
@@ -148,9 +273,32 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 }
 
 OptionPtr
-OptionDefinition::optionFactory(Option::Universe, uint16_t,
-                                const std::vector<std::string>&) const {
-    return (OptionPtr());
+OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
+                                const std::vector<std::string>& values) const {
+    validate();
+
+    OptionBuffer buf;
+    if (!array_type_ && type_ != OPT_RECORD_TYPE) {
+        if (values.size() == 0) {
+            isc_throw(InvalidOptionValue, "no option value specified");
+        }
+        DataTypeUtil::instance().writeToBuffer(values[0], type_, buf);
+    } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
+        for (size_t i = 0; i < values.size(); ++i) {
+            DataTypeUtil::instance().writeToBuffer(values[i], type_, buf);
+        }
+    } else if (type_ == OPT_RECORD_TYPE) {
+        const RecordFieldsCollection& records = getRecordFields();
+        if (records.size() > values.size()) {
+            isc_throw(InvalidOptionValue, "number of data fields for the option"
+                      << " type " << type_ << " is greater than number of values"
+                      << " provided.");
+        }
+        for (size_t i = 0; i < records.size(); ++i) {
+            DataTypeUtil::instance().writeToBuffer(values[i], records[i], buf);
+        }
+    }
+    return (optionFactory(u, type, buf.begin(), buf.end()));
 }
 
 void

+ 14 - 3
src/lib/dhcp/option_definition.h

@@ -27,6 +27,14 @@
 namespace isc {
 namespace dhcp {
 
+/// @brief Exception to be thrown when invalid option value has been
+/// specified for a particular option definition.
+class InvalidOptionValue : public Exception {
+public:
+    InvalidOptionValue(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief Forward declaration to OptionDefinition.
 class OptionDefinition;
 
@@ -137,9 +145,6 @@ private:
             return (instance);
         }
 
-        template<typename T>
-        T dataTypeCast(const std::string& value_str) const;
-
         /// @brief Convert type given as string value to option data type.
         ///
         /// @param data_type_name data type string.
@@ -147,6 +152,12 @@ private:
         /// @return option data type.
         OptionDataType getOptionDataType(const std::string& data_type_name);
 
+        template<typename T>
+        T lexicalCastWithRangeCheck(const std::string& value_str) const;
+
+        void writeToBuffer(const std::string& value, uint16_t type,
+                           OptionBuffer& buf);
+
     private:
         /// @brief Private constructor.
         ///

+ 242 - 7
src/lib/dhcp/tests/option_definition_unittest.cc

@@ -202,7 +202,7 @@ TEST_F(OptionDefinitionTest, factoryAddrList6) {
 // can be used to create option instance with the optionFactory function.
 TEST_F(OptionDefinitionTest, factoryTokenizedAddrList6) {
     OptionDefinition opt_def("OPTION_NIS_SERVERS", D6O_NIS_SERVERS,
-                             "ipv6_address", true);
+                             "ipv6-address", true);
 
     // Create a vector of some V6 addresses.
     std::vector<asiolink::IOAddress> addrs;
@@ -225,11 +225,7 @@ TEST_F(OptionDefinitionTest, factoryTokenizedAddrList6) {
         option_v6 = opt_def.optionFactory(Option::V6, D6O_NIS_SERVERS,
                                           addrs_str);
     );
-    // This is temporary check to make this test pass until factory function is
-    // implemented and returns the pointer rather than NULL.
-    ASSERT_FALSE(option_v6);
-
-    /*    // Non-null pointer option is supposed to be returned and it
+    // Non-null pointer option is supposed to be returned and it
     // should have Option6AddrLst type.
     ASSERT_TRUE(option_v6);
     ASSERT_TRUE(typeid(*option_v6) == typeid(Option6AddrLst));
@@ -243,7 +239,7 @@ TEST_F(OptionDefinitionTest, factoryTokenizedAddrList6) {
         option_cast_v6->getAddresses();
     // Returned addresses must match the addresses that have been used to create
     // the option instance.
-    EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin())); */
+    EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
 }
 
 TEST_F(OptionDefinitionTest, factoryAddrList4) {
@@ -292,6 +288,50 @@ TEST_F(OptionDefinitionTest, factoryAddrList4) {
                  isc::OutOfRange);
 }
 
+// This test checks that a vector of strings, holding IPv4 addresses,
+// can be used to create option instance with the optionFactory function.
+TEST_F(OptionDefinitionTest, factoryTokenizedAddrList4) {
+    OptionDefinition opt_def("OPTION_NIS_SERVERS", DHO_NIS_SERVERS,
+                             "ipv4-address", true);
+
+    // Create a vector of some V6 addresses.
+    std::vector<asiolink::IOAddress> addrs;
+    addrs.push_back(asiolink::IOAddress("192.168.0.1"));
+    addrs.push_back(asiolink::IOAddress("172.16.1.1"));
+    addrs.push_back(asiolink::IOAddress("127.0.0.1"));
+    addrs.push_back(asiolink::IOAddress("213.41.23.12"));
+
+    // Create a vector of strings representing addresses given above.
+    std::vector<std::string> addrs_str;
+    for (std::vector<asiolink::IOAddress>::const_iterator it = addrs.begin();
+         it != addrs.end(); ++it) {
+        addrs_str.push_back(it->toText());
+    }
+
+    // Create DHCPv4 option using the list of IPv4 addresses given in the
+    // string form.
+    OptionPtr option_v4;
+    ASSERT_NO_THROW(
+        option_v4 = opt_def.optionFactory(Option::V4, DHO_NIS_SERVERS,
+                                          addrs_str);
+    );
+    // Non-null pointer option is supposed to be returned and it
+    // should have Option6AddrLst type.
+    ASSERT_TRUE(option_v4);
+    ASSERT_TRUE(typeid(*option_v4) == typeid(Option4AddrLst));
+    // Cast to the actual option type to get IPv4 addresses from it.
+    boost::shared_ptr<Option4AddrLst> option_cast_v4 =
+        boost::static_pointer_cast<Option4AddrLst>(option_v4);
+    // Check that cast was successful.
+    ASSERT_TRUE(option_cast_v4);
+    // Get the list of parsed addresses from the option object.
+    std::vector<asiolink::IOAddress> addrs_returned =
+        option_cast_v4->getAddresses();
+    // Returned addresses must match the addresses that have been used to create
+    // the option instance.
+    EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
+}
+
 TEST_F(OptionDefinitionTest, factoryEmpty) {
     OptionDefinition opt_def("OPTION_RAPID_COMMIT", D6O_RAPID_COMMIT, "empty");
 
@@ -362,6 +402,53 @@ TEST_F(OptionDefinitionTest, factoryBinary) {
                            buf.begin()));
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedBinary) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "binary", true);
+
+    // Prepare some dummy data (serverid): 0, 1, 2 etc.
+    OptionBuffer buf(16);
+    for (int i = 0; i < buf.size(); ++i) {
+        buf[i] = i;
+    }
+    std::vector<std::string> hex_data;
+    hex_data.push_back("00010203");
+    hex_data.push_back("04050607");
+    hex_data.push_back("08090A0B0C0D0E0F");
+
+    // Create option instance with the factory function.
+    // If the OptionDefinition code works properly than
+    // object of the type Option should be returned.
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, 1000, hex_data);
+    );
+    // Expect base option type returned.
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+    // Sanity check on universe, length and size. These are
+    // the basic parameters identifying any option.
+    EXPECT_EQ(Option::V6, option_v6->getUniverse());
+    EXPECT_EQ(4, option_v6->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v6->getData().size());
+
+    // Get data from the option and compare against reference buffer.
+    // They are expected to match.
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+
+    // Repeat the same test scenario for DHCPv4 option.
+    OptionPtr option_v4;
+    ASSERT_NO_THROW(option_v4 = opt_def.optionFactory(Option::V4, 214, hex_data));
+    EXPECT_EQ(Option::V4, option_v4->getUniverse());
+    EXPECT_EQ(2, option_v4->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v4->getData().size());
+
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+}
+
+
 TEST_F(OptionDefinitionTest, factoryIA6) {
     // This option consists of IAID, T1 and T2 fields (each 4 bytes long).
     const int option6_ia_len = 12;
@@ -444,6 +531,37 @@ TEST_F(OptionDefinitionTest, factoryIAAddr6) {
      );
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedIAAddr6) {
+    // This option consists of IPV6 Address (16 bytes) and preferred-lifetime and
+    // valid-lifetime fields (each 4 bytes long).
+    OptionDefinition opt_def("OPTION_IAADDR", D6O_IAADDR, "record");
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+    ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+
+    // Check the positive scenario.
+    std::vector<std::string> data_field_values;
+    data_field_values.push_back("2001:0db8::ff00:0042:8329");
+    data_field_values.push_back("1234");
+    data_field_values.push_back("5678");
+
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(option_v6 = opt_def.optionFactory(Option::V6, D6O_IAADDR,
+                                                      data_field_values));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IAAddr));
+    boost::shared_ptr<Option6IAAddr> option_cast_v6 =
+        boost::static_pointer_cast<Option6IAAddr>(option_v6);
+    EXPECT_EQ("2001:db8::ff00:42:8329", option_cast_v6->getAddress().toText());
+    EXPECT_EQ(1234, option_cast_v6->getPreferred());
+    EXPECT_EQ(5678, option_cast_v6->getValid());
+
+    // This should work for DHCPv6 only, try passing in\valid universe value.
+    EXPECT_THROW(
+        opt_def.optionFactory(Option::V4, D6O_IAADDR, data_field_values),
+        isc::BadValue
+    );
+}
+
 TEST_F(OptionDefinitionTest, factoryIntegerInvalidType) {
     // The template function factoryInteger<> accepts integer values only
     // as template typename. Here we try passing different type and
@@ -479,6 +597,31 @@ TEST_F(OptionDefinitionTest, factoryUint8) {
     // @todo Add more cases for DHCPv4
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedUint8) {
+    OptionDefinition opt_def("OPTION_PREFERENCE", D6O_PREFERENCE, "uint8");
+
+    OptionPtr option_v6;
+    std::vector<std::string> values;
+    values.push_back("123");
+    values.push_back("456");
+    try {
+        option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
+    } catch (std::exception& ex) {
+        std::cout << ex.what() << std::endl;
+    }
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
+    );
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint8_t>));
+    // Validate the value.
+    boost::shared_ptr<Option6Int<uint8_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6Int<uint8_t> >(option_v6);
+    EXPECT_EQ(123, option_cast_v6->getValue());
+
+    // @todo Add more cases for DHCPv4
+}
+
+
 TEST_F(OptionDefinitionTest, factoryUint16) {
     OptionDefinition opt_def("OPTION_ELAPSED_TIME", D6O_ELAPSED_TIME, "uint16");
 
@@ -505,6 +648,27 @@ TEST_F(OptionDefinitionTest, factoryUint16) {
     // @todo Add more cases for DHCPv4
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedUint16) {
+    OptionDefinition opt_def("OPTION_ELAPSED_TIME", D6O_ELAPSED_TIME, "uint16");
+
+    OptionPtr option_v6;
+
+    std::vector<std::string> values;
+    values.push_back("1234");
+    values.push_back("5678");
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, values);
+    );
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint16_t>));
+    // Validate the value.
+    boost::shared_ptr<Option6Int<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6Int<uint16_t> >(option_v6);
+    EXPECT_EQ(1234, option_cast_v6->getValue());
+
+    // @todo Add more cases for DHCPv4
+
+}
+
 TEST_F(OptionDefinitionTest, factoryUint32) {
     OptionDefinition opt_def("OPTION_CLT_TIME", D6O_CLT_TIME, "uint32");
 
@@ -532,6 +696,26 @@ TEST_F(OptionDefinitionTest, factoryUint32) {
     // @todo Add more cases for DHCPv4
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedUint32) {
+    OptionDefinition opt_def("OPTION_CLT_TIME", D6O_CLT_TIME, "uint32");
+
+    OptionPtr option_v6;
+    std::vector<std::string> values;
+    values.push_back("123456");
+    values.push_back("789");
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, values);
+    );
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint32_t>));
+    // Validate the value.
+    boost::shared_ptr<Option6Int<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6Int<uint32_t> >(option_v6);
+    EXPECT_EQ(123456, option_cast_v6->getValue());
+
+    // @todo Add more cases for DHCPv4
+}
+
+
 TEST_F(OptionDefinitionTest, factoryUint16Array) {
     // Let's define some dummy option.
     const uint16_t opt_code = 79;
@@ -575,6 +759,30 @@ TEST_F(OptionDefinitionTest, factoryUint16Array) {
     );
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedUint16Array) {
+    // Let's define some dummy option.
+    const uint16_t opt_code = 79;
+    OptionDefinition opt_def("OPTION_UINT16_ARRAY", opt_code, "uint16", true);
+
+    OptionPtr option_v6;
+    std::vector<std::string> str_values;
+    str_values.push_back("12345");
+    str_values.push_back("5679");
+    str_values.push_back("12");
+    EXPECT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
+    );
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint16_t>));
+    boost::shared_ptr<Option6IntArray<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6IntArray<uint16_t> >(option_v6);
+    // Get the values from the initiated options and validate.
+    std::vector<uint16_t> values = option_cast_v6->getValues();
+    EXPECT_EQ(12345, values[0]);
+    EXPECT_EQ(5679, values[1]);
+    EXPECT_EQ(12, values[2]);
+}
+
+
 TEST_F(OptionDefinitionTest, factoryUint32Array) {
     // Let's define some dummy option.
     const uint16_t opt_code = 80;
@@ -619,6 +827,33 @@ TEST_F(OptionDefinitionTest, factoryUint32Array) {
     );
 }
 
+TEST_F(OptionDefinitionTest, factoryTokenizedUint32Array) {
+    // Let's define some dummy option.
+    const uint16_t opt_code = 80;
+
+    OptionDefinition opt_def("OPTION_UINT32_ARRAY", opt_code, "uint32", true);
+
+    OptionPtr option_v6;
+    std::vector<std::string> str_values;
+    str_values.push_back("123456");
+    str_values.push_back("7");
+    str_values.push_back("256");
+    str_values.push_back("1111");
+
+    EXPECT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
+    );
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint32_t>));
+    boost::shared_ptr<Option6IntArray<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6IntArray<uint32_t> >(option_v6);
+    // Get the values from the initiated options and validate.
+    std::vector<uint32_t> values = option_cast_v6->getValues();
+    EXPECT_EQ(123456, values[0]);
+    EXPECT_EQ(7, values[1]);
+    EXPECT_EQ(256, values[2]);
+    EXPECT_EQ(1111, values[3]);
+}
+
 TEST_F(OptionDefinitionTest, recognizeFormat) {
     // IA_NA option format.
     OptionDefinition opt_def1("OPTION_IA_NA", D6O_IA_NA, "record");