From f8436c44e9271f2474a44b069f116e358070174f Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 20 May 2019 21:41:51 +0200 Subject: [PATCH] [295-min-max-lease-time-configuration-options] Checkpoint: added DHCPv6 server tests - reuseExpiredLease missing --- src/bin/dhcp6/tests/config_parser_unittest.cc | 152 +++++++++- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 265 ++++++++++++++++++ src/bin/dhcp6/tests/dhcp6_test_utils.cc | 66 ++++- src/bin/dhcp6/tests/dhcp6_test_utils.h | 42 ++- .../dhcp6/tests/simple_parser6_unittest.cc | 22 +- 5 files changed, 513 insertions(+), 34 deletions(-) diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 5b32232d84..994efae6b4 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -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(); 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()); diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 9bed7df50b..4eb3fcd858 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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 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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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 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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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 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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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 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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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 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. diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index 040aa823f5..fc754cb953 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -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(2000, 3000, 4000)); + subnet_->setValid(Triplet(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(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(l->cltt_); diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index d0a6bab121..7eb960a715 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -527,8 +527,8 @@ public: /// @param expected_t2 expected T2 value /// @return IAADDR option for easy chaining with checkIAAddr method boost::shared_ptr - 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& 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. /// diff --git a/src/bin/dhcp6/tests/simple_parser6_unittest.cc b/src/bin/dhcp6/tests/simple_parser6_unittest.cc index a2fe240dc7..16f3cea273 100644 --- a/src/bin/dhcp6/tests/simple_parser6_unittest.cc +++ b/src/bin/dhcp6/tests/simple_parser6_unittest.cc @@ -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 -- 2.47.2