/// @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()) {
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);
}
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) {
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));
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());
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
+ "\"min-preferred-lifetime\": 2000,"
+ "\"max-preferred-lifetime\": 4000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"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));
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
"\"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"
" \"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"
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();
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:
"\"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"
" \"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"
// 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()));
// 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")));
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());
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.
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.
}
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;
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());
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);
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
}
// 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_);
/// @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
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
// 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
/// @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.
///
// 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