]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[295-min-max-lease-time-configuration-options] Checkpoint: added DHCPv6 server tests...
authorFrancis Dupont <fdupont@isc.org>
Mon, 20 May 2019 19:41:51 +0000 (21:41 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sat, 22 Jun 2019 14:05:23 +0000 (10:05 -0400)
src/bin/dhcp6/tests/config_parser_unittest.cc
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/dhcp6_test_utils.cc
src/bin/dhcp6/tests/dhcp6_test_utils.h
src/bin/dhcp6/tests/simple_parser6_unittest.cc

index 5b32232d84c31dd8a4692593d8d9acda4f318725..994efae6b41fb582b7f3bfec911b450ba9d7c1b9 100644 (file)
@@ -450,10 +450,20 @@ public:
     /// @param t2 expected rebind-timer value
     /// @param preferred expected preferred-lifetime value
     /// @param valid expected valid-lifetime value
+    /// @param min_preferred expected min-preferred-lifetime value
+    ///        (0 (default) means same than preferred)
+    /// @param max_preferred expected max-preferred-lifetime value
+    ///        (0 (default) means same than preferred)
+    /// @param min_valid expected min-valid-lifetime value
+    ///        (0 (default) means same than valid)
+    /// @param max_valid expected max-valid-lifetime value
+    ///        (0 (default) means same than valid)
     /// @return the subnet that was examined
     Subnet6Ptr
     checkSubnet(const Subnet6Collection& col, std::string subnet,
-                uint32_t t1, uint32_t t2, uint32_t pref, uint32_t valid) {
+                uint32_t t1, uint32_t t2, uint32_t pref, uint32_t valid,
+                uint32_t min_pref = 0, uint32_t max_pref = 0,
+                uint32_t min_valid = 0, uint32_t max_valid = 0) {
         const auto& index = col.get<SubnetPrefixIndexTag>();
         auto subnet_it = index.find(subnet);
         if (subnet_it == index.cend()) {
@@ -466,6 +476,10 @@ public:
         EXPECT_EQ(t2, s->getT2());
         EXPECT_EQ(pref, s->getPreferred());
         EXPECT_EQ(valid, s->getValid());
+        EXPECT_EQ(min_pref ? min_pref : pref, s->getPreferred().getMin());
+        EXPECT_EQ(max_pref ? max_pref : pref, s->getPreferred().getMax());
+        EXPECT_EQ(min_valid ? min_valid : valid, s->getValid().getMin());
+        EXPECT_EQ(max_valid ? max_valid : valid, s->getValid().getMax());
 
         return (s);
     }
@@ -980,6 +994,64 @@ TEST_F(Dhcp6ParserTest, emptyInterfaceConfig) {
     checkResult(status, 0);
 }
 
+/// Check that valid-lifetime must be between min-valid-lifetime and
+/// max-valid-lifetime when a bound is specified, *AND* a subnet is
+/// specified (boundary check is done when lifetimes are applied).
+TEST_F(Dhcp6ParserTest, outBoundValidLifetime) {
+
+    string too_small =  "{ " + genIfaceConfig() + "," +
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8::/64\" } ],"
+        "    \"subnet\": \"2001:db8::/32\" } ],"
+        "\"valid-lifetime\": 1000, \"min-valid-lifetime\": 1001 }";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP6(too_small));
+
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 1);
+
+    string too_large =  "{ " + genIfaceConfig() + "," +
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8::/64\" } ],"
+        "    \"subnet\": \"2001:db8::/32\" } ],"
+        "\"valid-lifetime\": 4001, \"max-valid-lifetime\": 4000 }";
+
+    ASSERT_NO_THROW(json = parseDHCP6(too_large));
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 1);
+}
+
+/// Check that preferred-lifetime must be between min-preferred-lifetime and
+/// max-preferred-lifetime when a bound is specified, *AND* a subnet is
+/// specified (boundary check is done when lifetimes are applied).
+TEST_F(Dhcp6ParserTest, outBoundPreferredLifetime) {
+
+    string too_small =  "{ " + genIfaceConfig() + "," +
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8::/64\" } ],"
+        "    \"subnet\": \"2001:db8::/32\" } ],"
+        "\"preferred-lifetime\": 1000, \"min-preferred-lifetime\": 1001 }";
+
+    ConstElementPtr json;
+    ASSERT_NO_THROW(json = parseDHCP6(too_small));
+
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 1);
+
+    string too_large =  "{ " + genIfaceConfig() + "," +
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8::/64\" } ],"
+        "    \"subnet\": \"2001:db8::/32\" } ],"
+        "\"preferred-lifetime\": 4001, \"max-preferred-lifetime\": 4000 }";
+
+    ASSERT_NO_THROW(json = parseDHCP6(too_large));
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    checkResult(status, 1);
+}
+
 /// The goal of this test is to verify if configuration without any
 /// subnets defined can be accepted.
 TEST_F(Dhcp6ParserTest, emptySubnet) {
@@ -1009,12 +1081,16 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
     string config = "{ " + genIfaceConfig() + ","
         "\"preferred-lifetime\": 3000,"
+        "\"min-preferred-lifetime\": 2000,"
+        "\"max-preferred-lifetime\": 4000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
         "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
-        "\"valid-lifetime\": 4000 }";
+        "\"valid-lifetime\": 4000,"
+        "\"min-valid-lifetime\": 3000,"
+        "\"max-valid-lifetime\": 5000 }";
 
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
@@ -1034,7 +1110,11 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
     EXPECT_EQ(1000, subnet->getT1());
     EXPECT_EQ(2000, subnet->getT2());
     EXPECT_EQ(3000, subnet->getPreferred());
+    EXPECT_EQ(2000, subnet->getPreferred().getMin());
+    EXPECT_EQ(4000, subnet->getPreferred().getMax());
     EXPECT_EQ(4000, subnet->getValid());
+    EXPECT_EQ(3000, subnet->getValid().getMin());
+    EXPECT_EQ(5000, subnet->getValid().getMax());
 
     // Check that subnet-id is 1
     EXPECT_EQ(1, subnet->getID());
@@ -1336,6 +1416,8 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
 
     string config = "{ " + genIfaceConfig() + ","
         "\"preferred-lifetime\": 3000,"
+        "\"min-preferred-lifetime\": 2000,"
+        "\"max-preferred-lifetime\": 4000,"
         "\"rebind-timer\": 2000, "
         "\"renew-timer\": 1000, "
         "\"subnet6\": [ { "
@@ -1343,9 +1425,15 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
         "    \"renew-timer\": 1, "
         "    \"rebind-timer\": 2, "
         "    \"preferred-lifetime\": 3,"
+        "    \"min-preferred-lifetime\": 2,"
+        "    \"max-preferred-lifetime\": 4,"
         "    \"valid-lifetime\": 4,"
+        "    \"min-valid-lifetime\": 3,"
+        "    \"max-valid-lifetime\": 5,"
         "    \"subnet\": \"2001:db8:1::/64\" } ],"
-        "\"valid-lifetime\": 4000 }";
+        "\"valid-lifetime\": 4000,"
+        "\"min-valid-lifetime\": 3000,"
+        "\"max-valid-lifetime\": 5000 }";
 
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
@@ -1363,7 +1451,11 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
     EXPECT_EQ(1, subnet->getT1());
     EXPECT_EQ(2, subnet->getT2());
     EXPECT_EQ(3, subnet->getPreferred());
+    EXPECT_EQ(2, subnet->getPreferred().getMin());
+    EXPECT_EQ(4, subnet->getPreferred().getMax());
     EXPECT_EQ(4, subnet->getValid());
+    EXPECT_EQ(3, subnet->getValid().getMin());
+    EXPECT_EQ(5, subnet->getValid().getMax());
 }
 
 // This test checks if it is possible to define a subnet with an
@@ -6289,7 +6381,11 @@ TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) {
         "\"renew-timer\": 1000, \n"
         "\"rebind-timer\": 2000, \n"
         "\"preferred-lifetime\": 3000, \n"
+        "\"min-preferred-lifetime\": 2000, \n"
+        "\"max-preferred-lifetime\": 4000, \n"
         "\"valid-lifetime\": 4000, \n"
+        "\"min-valid-lifetime\": 3000, \n"
+        "\"max-valid-lifetime\": 5000, \n"
         "\"shared-networks\": [ {\n"
         "    \"name\": \"foo\"\n,"
         "    \"subnet6\": [\n"
@@ -6303,7 +6399,11 @@ TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) {
         "        \"renew-timer\": 2,\n"
         "        \"rebind-timer\": 22,\n"
         "        \"preferred-lifetime\": 222,\n"
-        "        \"valid-lifetime\": 2222\n"
+        "        \"min-preferred-lifetime\": 111,\n"
+        "        \"max-preferred-lifetime\": 333,\n"
+        "        \"valid-lifetime\": 2222,\n"
+        "        \"min-valid-lifetime\": 1111,\n"
+        "        \"max-valid-lifetime\": 3333\n"
         "    },\n"
         "    { \n"
         "        \"subnet\": \"2001:db3::/48\",\n"
@@ -6333,9 +6433,15 @@ TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) {
     const Subnet6Collection * subs = net->getAllSubnets();
     ASSERT_TRUE(subs);
     EXPECT_EQ(3, subs->size());
-    checkSubnet(*subs, "2001:db1::/48", 1000, 2000, 3000, 4000);
-    checkSubnet(*subs, "2001:db2::/48", 2, 22, 222, 2222);
-    checkSubnet(*subs, "2001:db3::/48", 1000, 2000, 3000, 4000);
+    checkSubnet(*subs, "2001:db1::/48",
+                1000, 2000, 3000, 4000,
+                2000, 4000, 3000, 5000);
+    checkSubnet(*subs, "2001:db2::/48",
+                2, 22, 222, 2222,
+                111, 333, 1111, 3333);
+    checkSubnet(*subs, "2001:db3::/48",
+                1000, 2000, 3000, 4000,
+                2000, 4000, 3000, 5000);
 
     // Now make sure the subnet was added to global list of subnets.
     CfgSubnets6Ptr subnets6 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
@@ -6343,9 +6449,15 @@ TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) {
 
     subs = subnets6->getAll();
     ASSERT_TRUE(subs);
-    checkSubnet(*subs, "2001:db1::/48", 1000, 2000, 3000, 4000);
-    checkSubnet(*subs, "2001:db2::/48", 2, 22, 222, 2222);
-    checkSubnet(*subs, "2001:db3::/48", 1000, 2000, 3000, 4000);
+    checkSubnet(*subs, "2001:db1::/48",
+                1000, 2000, 3000, 4000,
+                2000, 4000, 3000, 5000);
+    checkSubnet(*subs, "2001:db2::/48",
+                2, 22, 222, 2222,
+                111, 333, 1111, 3333);
+    checkSubnet(*subs, "2001:db3::/48",
+                1000, 2000, 3000, 4000,
+                2000, 4000, 3000, 5000);
 }
 
 // This test checks if parameters are derived properly:
@@ -6372,13 +6484,21 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
         "\"renew-timer\": 1, \n"
         "\"rebind-timer\": 2, \n"
         "\"preferred-lifetime\": 3,\n"
+        "\"min-preferred-lifetime\": 2,\n"
+        "\"max-preferred-lifetime\": 4,\n"
         "\"valid-lifetime\": 4, \n"
+        "\"min-valid-lifetime\": 3, \n"
+        "\"max-valid-lifetime\": 5, \n"
         "\"shared-networks\": [ {\n"
         "    \"name\": \"foo\"\n,"
         "    \"renew-timer\": 10,\n"
         "    \"rebind-timer\": 20, \n"
         "    \"preferred-lifetime\": 30,\n"
+        "    \"min-preferred-lifetime\": 20,\n"
+        "    \"max-preferred-lifetime\": 40,\n"
         "    \"valid-lifetime\": 40, \n"
+        "    \"min-valid-lifetime\": 30, \n"
+        "    \"max-valid-lifetime\": 50, \n"
         "    \"interface-id\": \"oneone\",\n"
         "    \"relay\": {\n"
         "        \"ip-address\": \"1111::1\"\n"
@@ -6396,10 +6516,14 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
         "        \"renew-timer\": 100\n,"
         "        \"rebind-timer\": 200, \n"
         "        \"preferred-lifetime\": 300,\n"
+        "        \"min-preferred-lifetime\": 200,\n"
+        "        \"max-preferred-lifetime\": 400,\n"
         "        \"relay\": {\n"
         "            \"ip-address\": \"2222::2\"\n"
         "        },\n"
         "        \"valid-lifetime\": 400, \n"
+        "        \"min-valid-lifetime\": 300, \n"
+        "        \"max-valid-lifetime\": 500, \n"
         "        \"interface-id\": \"twotwo\",\n"
         "        \"rapid-commit\": true,\n"
         "        \"reservation-mode\": \"out-of-pool\"\n"
@@ -6441,7 +6565,8 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
     // derived from shared-network level. Other parameters a derived
     // from global scope to shared-network level and later again to
     // subnet6 level.
-    Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48", 10, 20, 30, 40);
+    Subnet6Ptr s = checkSubnet(*subs, "2001:db1::/48",
+                               10, 20, 30, 40, 20, 40, 30, 50);
     ASSERT_TRUE(s);
     ASSERT_TRUE(s->getInterfaceId());
     EXPECT_TRUE(iface_id1.equals(s->getInterfaceId()));
@@ -6453,7 +6578,8 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
     // was specified explicitly. Other parameters a derived
     // from global scope to shared-network level and later again to
     // subnet6 level.
-    s = checkSubnet(*subs, "2001:db2::/48", 100, 200, 300, 400);
+    s = checkSubnet(*subs, "2001:db2::/48",
+                    100, 200, 300, 400, 200, 400, 300, 500);
     ASSERT_TRUE(s->getInterfaceId());
     EXPECT_TRUE(iface_id2.equals(s->getInterfaceId()));
     EXPECT_TRUE(s->hasRelayAddress(IOAddress("2222::2")));
@@ -6469,7 +6595,7 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) {
     EXPECT_EQ(1, subs->size());
 
     // This subnet should derive its renew-timer from global scope.
-    s = checkSubnet(*subs, "2001:db3::/48", 1, 2, 3, 4);
+    s = checkSubnet(*subs, "2001:db3::/48", 1, 2, 3, 4, 2, 4, 3, 5);
     EXPECT_FALSE(s->getInterfaceId());
     EXPECT_FALSE(s->hasRelays());
     EXPECT_FALSE(s->getRapidCommit());
index 9bed7df50baabe76e6f796b42f241fdc6ad8a97e..4eb3fcd85846721351980c0b3fc67f98d34d64af 100644 (file)
@@ -550,6 +550,236 @@ TEST_F(Dhcpv6SrvTest, pdSolicitBasic) {
     checkClientId(reply, clientid);
 }
 
+// This test verifies that ADVERTISE returns default lifetimes when
+// the client does not add an IAADDR sub option.
+  TEST_F(Dhcpv6SrvTest, defaultLifetimeSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+    subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface("eth0");
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Add no IAADDR sub option.
+
+    // Pass it to the server and get an advertise
+    AllocEngine::ClientContext6 ctx;
+    bool drop = false;
+    srv.initContext(sol, ctx, drop);
+    ASSERT_FALSE(drop);
+    Pkt6Ptr reply = srv.processSolicit(ctx);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                subnet_->getT2());
+    ASSERT_TRUE(addr);
+
+    // Check that the assigned address is indeed from the configured pool
+    checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA,
+                subnet_->getPreferred(), subnet_->getValid());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that ADVERTISE returns default lifetimes when
+// the client adds an IAPREFIX sub option with zero lifetime hints.
+TEST_F(Dhcpv6SrvTest, hintZeroLifetimeSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+    subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface("eth0");
+    OptionPtr iapd = generateIA(D6O_IA_PD, 234, 1500, 3000);
+    sol->addOption(iapd);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Add an IAPREFIX sub option with zero preferred and valid lifetimes.
+    OptionPtr subopt(new Option6IAPrefix(D6O_IAPREFIX,
+                                         IOAddress("::"),
+                                         0, 0, 0));
+    iapd->addOption(subopt);
+
+    // Pass it to the server and get an advertise
+    AllocEngine::ClientContext6 ctx;
+    bool drop = false;
+    srv.initContext(sol, ctx, drop);
+    ASSERT_FALSE(drop);
+    Pkt6Ptr reply = srv.processSolicit(ctx);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_PD was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prefix = checkIA_PD(reply, 234,
+                                                           subnet_->getT1(),
+                                                           subnet_->getT2());
+    ASSERT_TRUE(prefix);
+
+    // Check that the assigned prefix is indeed from the configured pool
+    checkIAAddr(prefix, prefix->getAddress(), Lease::TYPE_PD,
+                subnet_->getPreferred(), subnet_->getValid());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that ADVERTISE returns specified lifetimes when
+// the client adds an IAADDR sub option with in-bound lifetime hints.
+TEST_F(Dhcpv6SrvTest, hintLifetimeSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+    subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface("eth0");
+    OptionPtr iana = generateIA(D6O_IA_NA, 234, 1500, 3000);
+    sol->addOption(iana);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Add an IAADDR sub option.
+    uint32_t hint_pref = 3001;
+    uint32_t hint_valid = 3999;
+    OptionPtr subopt(new Option6IAAddr(D6O_IAADDR, IOAddress("::"),
+                                       hint_pref, hint_valid));
+    iana->addOption(subopt);
+
+    // Pass it to the server and get an advertise
+    AllocEngine::ClientContext6 ctx;
+    bool drop = false;
+    srv.initContext(sol, ctx, drop);
+    ASSERT_FALSE(drop);
+    Pkt6Ptr reply = srv.processSolicit(ctx);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                subnet_->getT2());
+    ASSERT_TRUE(addr);
+
+    // Check that the assigned address is indeed from the configured pool
+    checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA,
+                hint_pref, hint_valid);
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that ADVERTISE returns min lifetimes when
+// the client adds an IAPREFIX sub option with too small lifetime hints.
+TEST_F(Dhcpv6SrvTest, minLifetimeSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+    subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface("eth0");
+    OptionPtr iapd = generateIA(D6O_IA_PD, 234, 1500, 3000);
+    sol->addOption(iapd);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Add an IAPREFIX sub option with too small hints so min values will
+    // be returned in the ADVERTISE.
+    OptionPtr subopt(new Option6IAPrefix(D6O_IAPREFIX,  IOAddress("::"), 0,
+                                         1000, 2000));
+    iapd->addOption(subopt);
+
+    // Pass it to the server and get an advertise
+    AllocEngine::ClientContext6 ctx;
+    bool drop = false;
+    srv.initContext(sol, ctx, drop);
+    ASSERT_FALSE(drop);
+    Pkt6Ptr reply = srv.processSolicit(ctx);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_PD was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prefix = checkIA_PD(reply, 234,
+                                                           subnet_->getT1(),
+                                                           subnet_->getT2());
+    ASSERT_TRUE(prefix);
+
+    // Check that the assigned prefix is indeed from the configured pool
+    checkIAAddr(prefix, prefix->getAddress(), Lease::TYPE_PD,
+                subnet_->getPreferred().getMin(),
+                subnet_->getValid().getMin());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that ADVERTISE returns max lifetimes when
+// the client adds an IAADDR sub option with too large lifetime hints.
+TEST_F(Dhcpv6SrvTest, maxLifetimeSolicit) {
+    NakedDhcpv6Srv srv(0);
+
+    subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+    subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->setIface("eth0");
+    OptionPtr iana = generateIA(D6O_IA_NA, 234, 1500, 3000);
+    sol->addOption(iana);
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Add an IAADDR sub option with too large hints so max values will
+    // be returned in the ADVERTISE.
+    OptionPtr subopt(new Option6IAAddr(D6O_IAADDR, IOAddress("::"),
+                                       5000, 6000));
+    iana->addOption(subopt);
+
+    // Pass it to the server and get an advertise
+    AllocEngine::ClientContext6 ctx;
+    bool drop = false;
+    srv.initContext(sol, ctx, drop);
+    ASSERT_FALSE(drop);
+    Pkt6Ptr reply = srv.processSolicit(ctx);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAAddr> addr = checkIA_NA(reply, 234, subnet_->getT1(),
+                                                subnet_->getT2());
+    ASSERT_TRUE(addr);
+
+    // Check that the assigned address is indeed from the configured pool
+    checkIAAddr(addr, addr->getAddress(), Lease::TYPE_NA,
+                subnet_->getPreferred().getMax(),
+                subnet_->getValid().getMax());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
 // This test verifies that incoming SOLICIT can be handled properly, that an
 // ADVERTISE is generated, that the response has an address and that address
 // really belongs to the configured pool.
@@ -1034,6 +1264,41 @@ TEST_F(Dhcpv6SrvTest, RenewSomeoneElesesLease) {
     testRenewSomeoneElsesLease(Lease::TYPE_NA, IOAddress("2001:db8::1"));
 }
 
+// This test verifies that a renewal returns default lifetimes when
+// the client adds an IAPREFIX sub option with zero lifetime hints.
+TEST_F(Dhcpv6SrvTest, defaultLifetimeRenew) {
+    // Defaults are 3000 and 4000.
+    testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe",
+                   "2001:db8:1:1::cafe:babe", 128, true,
+                   0, 0, 3000, 4000);
+}
+
+// This test verifies that a renewal returns specified lifetimes when
+// the client adds an IAADDR sub option with in-bound lifetime hints.
+TEST_F(Dhcpv6SrvTest, hintLifetimeRenew) {
+    testRenewBasic(Lease::TYPE_PD, "2001:db8:1:2::",
+                   "2001:db8:1:2::", pd_pool_->getLength(), true,
+                   2999, 4001, 2999, 4001);
+}
+
+// This test verifies that a renewal returns min lifetimes when
+// the client adds an IAADDR sub option with too small lifetime hints.
+TEST_F(Dhcpv6SrvTest, minLifetimeRenew) {
+    // Min values are 2000 and 3000.
+    testRenewBasic(Lease::TYPE_NA, "2001:db8:1:1::cafe:babe",
+                   "2001:db8:1:1::cafe:babe", 128, true,
+                   1000, 2000, 2000, 3000);
+}
+
+// This test verifies that a renewal returns max ifetimes when
+// the client adds an IAADDR sub option with too large lifetime hints.
+TEST_F(Dhcpv6SrvTest, maxLifetimeRenew) {
+    // Max  values are 4000 and 5000.
+    testRenewBasic(Lease::TYPE_PD, "2001:db8:1:2::",
+                   "2001:db8:1:2::", pd_pool_->getLength(), true,
+                   5000, 6000, 4000, 5000);
+}
+
 // This test verifies that incoming (positive) RELEASE with address can be
 // handled properly, that a REPLY is generated, that the response has status
 // code and that the lease is indeed removed from the database.
index 040aa823f564ff173b1f5d453f8876f65fe75903..fc754cb95330898aff74abc8bd9f37ba9170e431 100644 (file)
@@ -242,11 +242,23 @@ Dhcpv6SrvTest::createIA(isc::dhcp::Lease::Type lease_type,
 }
 
 void
-Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr,
+Dhcpv6SrvTest::testRenewBasic(Lease::Type type,
+                              const std::string& existing_addr,
                               const std::string& renew_addr,
-                              const uint8_t prefix_len, bool insert_before_renew) {
+                              const uint8_t prefix_len,
+                              bool insert_before_renew,
+                              uint32_t hint_pref,
+                              uint32_t hint_valid,
+                              uint32_t expected_pref,
+                              uint32_t expected_valid) {
     NakedDhcpv6Srv srv(0);
 
+    // Use intervals for lifetimes for lifetime tests.
+    if (hint_pref != 300 || hint_valid != 500) {
+        subnet_->setPreferred(Triplet<uint32_t>(2000, 3000, 4000));
+        subnet_->setValid(Triplet<uint32_t>(3000, 4000, 5000));
+    }
+
     const IOAddress existing(existing_addr);
     const IOAddress renew(renew_addr);
     const uint32_t iaid = 234;
@@ -277,8 +289,42 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr
         EXPECT_NE(l->cltt_, time(NULL));
     }
 
-    Pkt6Ptr req = createMessage(DHCPV6_RENEW, type, IOAddress(renew_addr),
-                                prefix_len, iaid);
+    Pkt6Ptr req;
+
+    if (hint_pref == 300 && hint_valid == 500) {
+        req = createMessage(DHCPV6_RENEW, type, IOAddress(renew_addr),
+                            prefix_len, iaid);
+    } else {
+        // from createMessage
+        req.reset(new Pkt6(DHCPV6_RENEW, 1234));
+        req->setRemoteAddr(IOAddress("fe80::abcd"));
+        req->setIface("eth0");
+
+        // from createIA
+        uint16_t code;
+        OptionPtr subopt;
+        switch (type) {
+        case Lease::TYPE_NA:
+            code = D6O_IA_NA;
+            subopt.reset(new Option6IAAddr(D6O_IAADDR,
+                                           IOAddress(renew_addr),
+                                           hint_pref, hint_valid));
+            break;
+        case Lease::TYPE_PD:
+            code = D6O_IA_PD;
+            subopt.reset(new Option6IAPrefix(D6O_IAPREFIX,
+                                             IOAddress(renew_addr), prefix_len,
+                                             hint_pref, hint_valid));
+            break;
+        default:
+            isc_throw(BadValue, "Invalid lease type specified "
+                      << static_cast<int>(type));
+        }
+
+        Option6IAPtr ia = generateIA(code, iaid, 1500, 3000);
+        ia->addOption(subopt);
+        req->addOption(ia);
+    };
     req->addOption(clientid);
     req->addOption(srv.getServerID());
 
@@ -301,7 +347,8 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr
         ASSERT_TRUE(addr_opt);
 
         // Check that we've got the address we requested
-        checkIAAddr(addr_opt, renew, Lease::TYPE_NA);
+        checkIAAddr(addr_opt, renew, Lease::TYPE_NA,
+                    expected_pref, expected_valid);
 
         // Check that the lease is really in the database
         l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr_opt);
@@ -317,7 +364,8 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr
         ASSERT_TRUE(prefix_opt);
 
         // Check that we've got the address we requested
-        checkIAAddr(prefix_opt, renew, Lease::TYPE_PD);
+        checkIAAddr(prefix_opt, renew, Lease::TYPE_PD,
+                    expected_pref, expected_valid);
         EXPECT_EQ(pd_pool_->getLength(), prefix_opt->getLength());
 
         // Check that the lease is really in the database
@@ -330,8 +378,10 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr
     }
 
     // Check that preferred, valid and cltt were really updated
-    EXPECT_EQ(subnet_->getPreferred(), l->preferred_lft_);
-    EXPECT_EQ(subnet_->getValid(), l->valid_lft_);
+    EXPECT_EQ(expected_pref ? expected_pref : subnet_->getPreferred().get(),
+              l->preferred_lft_);
+    EXPECT_EQ(expected_valid ? expected_valid : subnet_->getValid().get(),
+              l->valid_lft_);
 
     // Checking for CLTT is a bit tricky if we want to avoid off by 1 errors
     int32_t cltt = static_cast<int32_t>(l->cltt_);
index d0a6bab121054ba95b1e87860e0cbbe748f79093..7eb960a715147657d670377933f82dc1e193e64f 100644 (file)
@@ -527,8 +527,8 @@ public:
     /// @param expected_t2 expected T2 value
     /// @return IAADDR option for easy chaining with checkIAAddr method
     boost::shared_ptr<isc::dhcp::Option6IAAddr>
-        checkIA_NA(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
-                   uint32_t expected_t1, uint32_t expected_t2);
+    checkIA_NA(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
+               uint32_t expected_t1, uint32_t expected_t2);
 
     /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
     ///        IA_PD option
@@ -542,11 +542,19 @@ public:
     checkIA_PD(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
                uint32_t expected_t1, uint32_t expected_t2);
 
+
     // Check that generated IAADDR option contains expected address
     // and lifetime values match the configured subnet
+    /// @param expected_pref check that lease preferedlifetime has the not-zero
+    /// expected value (zero value means that do not check).
+    /// @param expected_valid check that lease valid lifetime has the not-zero
+    /// expected value (zero value means that do not check).
     void checkIAAddr(const boost::shared_ptr<isc::dhcp::Option6IAAddr>& addr,
                      const isc::asiolink::IOAddress& expected_addr,
-                     isc::dhcp::Lease::Type type) {
+                     isc::dhcp::Lease::Type type,
+                     uint32_t expected_pref = 0,
+                     uint32_t expected_valid = 0) {
+
 
         // Check that the assigned address is indeed from the configured pool.
         // Note that when comparing addresses, we compare the textual
@@ -554,8 +562,24 @@ public:
         // an ostream, which means it can't be used in EXPECT_EQ.
         EXPECT_TRUE(subnet_->inPool(type, addr->getAddress()));
         EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText());
-        EXPECT_EQ(subnet_->getPreferred(), addr->getPreferred());
-        EXPECT_EQ(subnet_->getValid(), addr->getValid());
+        if (subnet_->getPreferred().getMin() != subnet_->getPreferred().getMax()) {
+            EXPECT_LE(subnet_->getPreferred().getMin(), addr->getPreferred());
+            EXPECT_GE(subnet_->getPreferred().getMax(), addr->getPreferred());
+        } else {
+            EXPECT_EQ(subnet_->getPreferred(), addr->getPreferred());
+        }
+        if (subnet_->getValid().getMin() != subnet_->getValid().getMax()) {
+            EXPECT_LE(subnet_->getValid().getMin(), addr->getValid());
+            EXPECT_GE(subnet_->getValid().getMax(), addr->getValid());
+        } else {
+            EXPECT_EQ(subnet_->getValid(), addr->getValid());
+        }
+        if (expected_pref) {
+            EXPECT_EQ(expected_pref, addr->getPreferred());
+        }
+        if (expected_valid) {
+            EXPECT_EQ(expected_valid, addr->getValid());
+        }
     }
 
     // Checks if the lease sent to client is present in the database
@@ -668,11 +692,17 @@ public:
     /// @param prefix_len length of the prefix (128 for addresses)
     /// @param insert_before_renew should the lease be inserted into the database
     ///        before we try renewal?
+    /// @param hint_pref preferred lifetime hint (default is 300)
+    /// @param hint_valid valid lifetime hint (default is 500)
+    /// @param expected_pref expected preferred lifetime (zero means not check)
+    /// @param expected_valid expected valid lifetime (zero means not check)
     void
     testRenewBasic(isc::dhcp::Lease::Type type,
                    const std::string& existing_addr,
                    const std::string& renew_addr, const uint8_t prefix_len,
-                   bool insert_before_renew = true);
+                   bool insert_before_renew = true,
+                   uint32_t hint_pref = 300, uint32_t hint_valid = 500,
+                   uint32_t expected_pref = 0, uint32_t expected_valid = 0);
 
     /// @brief Checks if RENEW with invalid IAID is processed correctly.
     ///
index a2fe240dc7c6dee1c13d6ae0303cd8d4b2d9f30b..16f3cea2736b75a19ec046a56be6f25f9dd75c84 100644 (file)
@@ -152,29 +152,37 @@ TEST_F(SimpleParser6Test, globalDefaults6) {
 // scope to the subnet scope.
 TEST_F(SimpleParser6Test, inheritGlobalToSubnet6) {
     ElementPtr global = parseJSON("{ \"renew-timer\": 1,"
-                                          "  \"rebind-timer\": 2,"
-                                          "  \"preferred-lifetime\": 3,"
-                                          "  \"valid-lifetime\": 4,"
-                                          "  \"subnet6\": [ { \"renew-timer\": 100 } ] "
-                                          "}");
+                                  "  \"rebind-timer\": 2,"
+                                  "  \"preferred-lifetime\": 3,"
+                                  "  \"min-preferred-lifetime\": 2,"
+                                  "  \"max-preferred-lifetime\": 4,"
+                                  "  \"valid-lifetime\": 4,"
+                                  "  \"min-valid-lifetime\": 3,"
+                                  "  \"max-valid-lifetime\": 5,"
+                                  "  \"subnet6\": [ { \"renew-timer\": 100 } ] "
+                                  "}");
 
     ConstElementPtr subnets = global->find("subnet6");
     ASSERT_TRUE(subnets);
     ConstElementPtr subnet = subnets->get(0);
     ASSERT_TRUE(subnet);
 
-    // we should inherit 3 parameters. Renew-timer should remain intact,
+    // we should inherit 7 parameters. Renew-timer should remain intact,
     // as it was already defined in the subnet scope.
     size_t num;
     EXPECT_NO_THROW(num = SimpleParser6::deriveParameters(global));
-    EXPECT_EQ(3, num);
+    EXPECT_EQ(7, num);
 
     // Check the values. 3 of them are inherited, while the fourth one
     // was already defined in the subnet, so should not be inherited.
     checkIntegerValue(subnet, "renew-timer", 100);
     checkIntegerValue(subnet, "rebind-timer", 2);
     checkIntegerValue(subnet, "preferred-lifetime", 3);
+    checkIntegerValue(subnet, "min-preferred-lifetime", 2);
+    checkIntegerValue(subnet, "max-preferred-lifetime", 4);
     checkIntegerValue(subnet, "valid-lifetime", 4);
+    checkIntegerValue(subnet, "min-valid-lifetime", 3);
+    checkIntegerValue(subnet, "max-valid-lifetime", 5);
 }
 
 // This test checks if the parameters in "subnet6" are assigned default values