D6O_RELAY_ID = 53, /* RFC5460 */
// D6O_IPV6_ADDRESS_MOS = 54, /* RFC5678 */
// D6O_IPV6_FQDN_MOS = 55, /* RFC5678 */
-// D6O_NTP_SERVER = 56, /* RFC5908 */
+ D6O_NTP_SERVER = 56, /* RFC5908 */
D6O_V6_ACCESS_DOMAIN = 57, /* RFC5986 */
D6O_SIP_UA_CS_LIST = 58, /* RFC6011 */
D6O_BOOTFILE_URL = 59, /* RFC5970 */
// D6O_F_START_TIME_OF_STATE = 133, /* RFC8156 */
// D6O_F_STATE_EXPIRATION_TIME = 134, /* RFC8156 */
D6O_RELAY_SOURCE_PORT = 135, /* RFC8357 */
- D60_V6_SZTP_REDIRECT = 136, /* RFC8572 */
+ D6O_V6_SZTP_REDIRECT = 136, /* RFC8572 */
// Option codes 137-142 are unassigned.
D6O_IPV6_ADDRESS_ANDSF = 143, /* RFC6153 */
D6O_V6_DNR = 144 /* RFC9463 */
static const uint16_t ISC_V6_4O6_SRC_ADDRESS = 60001;
static const uint16_t ISC_V6_4O6_SRC_PORT = 60002;
+/* NTP server (RFC 5908) suboptions. */
+static const uint16_t NTP_SUBOPTION_SRV_ADDR = 1;
+static const uint16_t NTP_SUBOPTION_MC_ADDR = 2;
+static const uint16_t NTP_SUBOPTION_SRV_FQDN = 3;
+
/* Offsets into IA_*'s where Option spaces commence. */
static const uint16_t IA_NA_OFFSET = 12; /* IAID, T1, T2, all 4 octets each */
static const uint16_t IA_TA_OFFSET = 4; /* IAID only, 4 octets */
#define LW_V6_OPTION_SPACE "s46-cont-lw-options"
#define V4V6_RULE_OPTION_SPACE "s46-rule-options"
#define V4V6_BIND_OPTION_SPACE "s46-v4v6bind-options"
+#define V6_NTP_SERVER_SPACE "v6-ntp-server-suboptions"
#define LAST_RESORT_V4_OPTION_SPACE "last-resort-v4"
/// @brief encapsulated option spaces
OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
{ "relay-id", D6O_RELAY_ID, DHCP6_OPTION_SPACE, OPT_BINARY_TYPE, false,
NO_RECORD_DEF, "" },
+ { "ntp-server", D6O_NTP_SERVER, DHCP6_OPTION_SPACE, OPT_EMPTY_TYPE,
+ false, NO_RECORD_DEF, V6_NTP_SERVER_SPACE },
{ "v6-access-domain", D6O_V6_ACCESS_DOMAIN, DHCP6_OPTION_SPACE,
OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" },
{ "sip-ua-cs-list", D6O_SIP_UA_CS_LIST, DHCP6_OPTION_SPACE,
OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
{ "relay-source-port", D6O_RELAY_SOURCE_PORT, DHCP6_OPTION_SPACE,
OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
- { "v6-sztp-redirect", D60_V6_SZTP_REDIRECT, DHCP6_OPTION_SPACE,
+ { "v6-sztp-redirect", D6O_V6_SZTP_REDIRECT, DHCP6_OPTION_SPACE,
OPT_TUPLE_TYPE, true, NO_RECORD_DEF, "" },
{ "ipv6-address-andsf", D6O_IPV6_ADDRESS_ANDSF, DHCP6_OPTION_SPACE,
OPT_IPV6_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
sizeof(V4V6_BIND_OPTION_DEFINITIONS) /
sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]);
+/// @brief NTP server suboption definitions
+const OptionDefParams V6_NTP_SERVER_DEFINITIONS[] = {
+ { "ntp-server-address", NTP_SUBOPTION_SRV_ADDR, V6_NTP_SERVER_SPACE,
+ OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
+ { "ntp-server-multicast", NTP_SUBOPTION_MC_ADDR, V6_NTP_SERVER_SPACE,
+ OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
+ { "ntp-server-fqdn", NTP_SUBOPTION_SRV_FQDN, V6_NTP_SERVER_SPACE,
+ OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" }
+};
+
+const int V6_NTP_SERVER_DEFINITIONS_SIZE =
+ sizeof(V6_NTP_SERVER_DEFINITIONS) /
+ sizeof(V6_NTP_SERVER_DEFINITIONS[0]);
+
} // namespace
} // namespace dhcp
std::vector<uint8_t> opaque_tuple_buf(opaque_tuple_data,
opaque_tuple_data + sizeof(opaque_tuple_data));
- LibDhcpTest::testStdOptionDefs6(D60_V6_SZTP_REDIRECT,
+ LibDhcpTest::testStdOptionDefs6(D6O_V6_SZTP_REDIRECT,
opaque_tuple_buf.begin(),
opaque_tuple_buf.end(),
typeid(OptionOpaqueDataTuples));
EXPECT_EQ(77, elapsed->getValue());
}
+// RFC 5908 v6 NTP server option and suboptions.
+TEST_F(LibDhcpTest, v6NtpServer) {
+ // A NTP server option with the 3 suboptions.
+ std::vector<uint8_t> bin {
+ 0, 56, 0, 53, // ntp-server
+ 0, 1, 0, 16, // ntp-server-address
+ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, // 2001:db8::abcd
+ 0, 0, 0, 0, 0, 0, 0xab, 0xcd,
+ 0, 2, 0, 16, // ntp-server-multicast
+ 0xff, 0x02, 0, 0, 0, 0, 0, 0, // ff02::101
+ 0, 0, 0, 0, 0, 0, 0x01, 0x01,
+ 0, 3, 0, 9, // ntp-server-fqdn
+ 3, 0x66, 0x6f, 0x6f, // foo.bar.
+ 3, 0x62, 0x61, 0x72, 0
+ };
+
+ // List of parsed options will be stored here.
+ isc::dhcp::OptionCollection options;
+
+ OptionBuffer buf(bin);
+
+ size_t parsed = 0;
+
+ EXPECT_NO_THROW(parsed = LibDHCP::unpackOptions6(buf, DHCP6_OPTION_SPACE,
+ options));
+ EXPECT_EQ(bin.size(), parsed);
+
+ // We expect to have exactly one option with 3 suboptions.
+ EXPECT_EQ(1, options.size());
+ auto opt = options.find(D6O_NTP_SERVER);
+ ASSERT_FALSE(opt == options.end());
+
+ // Get the option.
+ OptionCustomPtr option =
+ boost::dynamic_pointer_cast<OptionCustom>(opt->second);
+ ASSERT_TRUE(option);
+ EXPECT_EQ(D6O_NTP_SERVER, option->getType());
+ EXPECT_EQ(57, option->len());
+ EXPECT_EQ(53, option->getData().size());
+
+ // Check the address suboption.
+ ASSERT_TRUE(option->getOption(NTP_SUBOPTION_SRV_ADDR));
+ OptionCustomPtr addr =
+ boost::dynamic_pointer_cast<OptionCustom>(option->getOption(NTP_SUBOPTION_SRV_ADDR));
+ ASSERT_TRUE(addr);
+ EXPECT_EQ("type=00001, len=00016: 2001:db8::abcd (ipv6-address)",
+ addr->toText());
+
+ // Check the multicast suboption.
+ ASSERT_TRUE(option->getOption(NTP_SUBOPTION_MC_ADDR));
+ OptionCustomPtr multicast =
+ boost::dynamic_pointer_cast<OptionCustom>(option->getOption(NTP_SUBOPTION_MC_ADDR));
+ ASSERT_TRUE(multicast);
+ EXPECT_EQ("type=00002, len=00016: ff02::101 (ipv6-address)",
+ multicast->toText());
+
+ // Check the fqdn suboption.
+ ASSERT_TRUE(option->getOption(NTP_SUBOPTION_SRV_FQDN));
+ OptionCustomPtr fqdn =
+ boost::dynamic_pointer_cast<OptionCustom>(option->getOption(NTP_SUBOPTION_SRV_FQDN));
+ ASSERT_TRUE(fqdn);
+ EXPECT_EQ("type=00003, len=00009: \"foo.bar.\" (fqdn)",
+ fqdn->toText());
+
+ // Build back the NTP server option.
+ OptionDefinitionPtr opt_def =
+ LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_NTP_SERVER);
+ ASSERT_TRUE(opt_def);
+ ASSERT_NO_THROW(option.reset(new OptionCustom(*opt_def, Option::V6)));
+ ASSERT_TRUE(option);
+
+ // Add address.
+ OptionDefinitionPtr addr_def =
+ LibDHCP::getOptionDef(V6_NTP_SERVER_SPACE, NTP_SUBOPTION_SRV_ADDR);
+ ASSERT_TRUE(addr_def);
+ OptionBuffer addr_buf = {
+ 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0xab, 0xcd
+ };
+ ASSERT_NO_THROW(addr.reset(new OptionCustom(*addr_def, Option::V6,
+ addr_buf)));
+ ASSERT_TRUE(addr);
+ option->addOption(addr);
+
+ // Add multicast.
+ OptionDefinitionPtr multicast_def =
+ LibDHCP::getOptionDef(V6_NTP_SERVER_SPACE, NTP_SUBOPTION_MC_ADDR);
+ ASSERT_TRUE(multicast_def);
+ OptionBuffer multicast_buf = {
+ 0xff, 0x02, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0x01, 0x01
+ };
+ ASSERT_NO_THROW(multicast.reset(new OptionCustom(*multicast_def,
+ Option::V6,
+ multicast_buf)));
+ ASSERT_TRUE(multicast);
+ option->addOption(multicast);
+
+ // Add fqdn.
+ OptionDefinitionPtr fqdn_def =
+ LibDHCP::getOptionDef(V6_NTP_SERVER_SPACE, NTP_SUBOPTION_SRV_FQDN);
+ ASSERT_TRUE(fqdn_def);
+ OptionBuffer fqdn_buf = {
+ 3, 0x66, 0x6f, 0x6f, 3, 0x62, 0x61, 0x72, 0
+ };
+ ASSERT_NO_THROW(fqdn.reset(new OptionCustom(*fqdn_def, Option::V6,
+ fqdn_buf)));
+ ASSERT_TRUE(fqdn);
+ option->addOption(fqdn);
+
+ // Pack output.
+ isc::util::OutputBuffer outbuf(0);
+ ASSERT_NO_THROW(option->pack(outbuf, true));
+ ASSERT_EQ(bin.size(), outbuf.getLength());
+ EXPECT_TRUE(memcmp(&bin[0], outbuf.getData(), bin.size()) == 0);
+}
+
} // namespace