Commit 6a856ed9 authored by Francis Dupont's avatar Francis Dupont

[master] Finished merge of trac5549a (known/unknown)

parents 6de6024c d9c77f8a
1426. [func] fdupont
Added KNOWN and UNKNOWN built-in client classes: after host lookup
if a matching host entry is found the incoming packet is added to
the KNOWN class, if none is found to the UNKNOWN class. Then
expressions depending directly or indirectly on these classes are
evaluated. Note these classes may be used to select a pool but
they may not to select a subnet.
(Trac #5549, git xxx)
1425. [bug] marcin
Improved performance of the DHCP server running in High
Availability configuration by optimizing the management of
......
......@@ -126,7 +126,33 @@
} ],
"subnet": "192.0.4.0/23",
"interface": "ethY"
}
},
// This subnet is divided in two pools for unknown and known
// (i.e. which have a reservation) clients. The built-in KNOWN and
// UNKNOWN classes are set or not at host reservation lookup (KNOWN if
// this returns something, UNKNOWN if this finds nothing) and client
//classes depending on it are evaluated.
// This happens after subnet selection and before address allocation
//from pools.
{
"pools": [
{
"pool": "192.0.8.100 - 192.0.8.200",
"client-class": "UNKNOWN"
},
{
"pool": "192.0.9.100 - 192.0.9.200",
"client-class": "KNOWN"
}
],
"subnet": "192.0.8.0/23",
"reservations": [
{ "hw-address": "00:00:00:11:22:33", "hostname": "h1" },
{ "hw-address": "00:00:00:44:55:66", "hostname": "h4" },
{ "hw-address": "00:00:00:77:88:99", "hostname": "h7" },
{ "hw-address": "00:00:00:aa:bb:cc", "hostname": "ha" }
]
}
]
},
......
......@@ -96,6 +96,32 @@
} ],
"subnet": "2001:db8:4::/64",
"interface": "ethY"
},
// This subnet is divided in two pools for unknown and known
// (i.e. which have a reservation) clients. The built-in KNOWN and
// UNKNOWN classes are set or not at host reservation lookup (KNOWN if
// this returns something, UNKNOWN if this finds nothing) and client
//classes depending on it are evaluated.
// This happens after subnet selection and before address allocation
//from pools.
{
"pools": [
{
"pool": "2001:db8:8::/64",
"client-class": "UNKNOWN"
},
{
"pool": "2001:db8:9::/64",
"client-class": "KNOWN"
}
],
"subnet": "2001:db8:8::/46",
"reservations": [
{ "hw-address": "00:00:00:11:22:33", "hostname": "h1" },
{ "hw-address": "00:00:00:44:55:66", "hostname": "h4" },
{ "hw-address": "00:00:00:77:88:99", "hostname": "h7" },
{ "hw-address": "00:00:00:aa:bb:cc", "hostname": "ha" }
]
}
]
......
......@@ -78,10 +78,11 @@
</para></listitem>
<listitem><para>
Classes with matching expressions and not marked for later ("on
request" or depending on the KNOWN builtin class) evaluation are
processed in the order they are defined in the configuration:
the boolean expression is evaluated and when it returns true
("match") the incoming packet is associated to the class.
request" or depending on the KNOWN/UNKNOWN builtin classes)
evaluation are processed in the order they are defined in the
configuration: the boolean expression is evaluated and when it
returns true ("match") the incoming packet is associated to the
class.
</para></listitem>
<listitem><para>
If a private or code 43 DHCPv4 option is received, decoding it
......@@ -98,18 +99,22 @@
that has a class which matches one of the packet's classes.
</para></listitem>
<listitem><para>
Host reservations are looked for. If an identifier from the incoming
packet matches a host reservation in the subnet or shared network,
the packet is associated with the KNOWN builtin class and all classes
of the host reservation.
Host reservations are looked for. If an identifier from the
incoming packet matches a host reservation in the subnet or
shared network, the packet is associated with the KNOWN class
and all classes of the host reservation. If a reservation is not
found, the packet is assigned to UNKNOWN class.
</para></listitem>
<listitem><para>
Classes with matching expressions using directly or indirectly
the KNOWN builtin class and not marked for later ("on request")
evaluation are processed in the order they are defined in the
configuration: the boolean expression is evaluated and when it
returns true ("match") the incoming packet is associated to the
class.
the KNOWN/UNKNOWN builtin classes and not marked for later ("on
request") evaluation are processed in the order they are defined
in the configuration: the boolean expression is evaluated and
when it returns true ("match") the incoming packet is associated
to the class. The determination whether there is a reservation
for a given client is made after a subnet is selected. As such, it
is not possible to use KNOWN/UNKNOWN classes to select a shared
network or a subnet.
</para></listitem>
<listitem><para>
If needed, addresses and prefixes from pools are assigned,
......@@ -198,11 +203,12 @@
begin with all capital letters.
</para>
<para>Currently recognized builtin class names are ALL and KNOWN
and prefixes VENDOR_CLASS_, AFTER_ , EXTERNAL_ and HA_. The AFTER_ prefix
is a provision for a not yet written hook, the EXTERNAL_ prefix
can be freely used: builtin classes are implicitly defined so
never raise warnings if they do not appear in the configuration.
<para>Currently recognized builtin class names are ALL, KNOWN
and UNKNOWN, and prefixes VENDOR_CLASS_, HA_, AFTER_ and
EXTERNAL_. The AFTER_ prefix is a provision for a not yet
written hook, the EXTERNAL_ prefix can be freely used: builtin
classes are implicitly defined so never raise warnings if they
do not appear in the configuration.
</para>
</section>
......@@ -245,11 +251,12 @@
</para>
<para>
Dependencies between classes are checked too: for instance forward
dependencies are rejected when the configuration is parsed:
an expression can only depend on already defined classes (including
builtin classes) and which are evaluated in a previous or the
same evaluation phase. This does not apply to the KNOWN class.
Dependencies between classes are checked too: for instance
forward dependencies are rejected when the configuration is
parsed: an expression can only depend on already defined classes
(including builtin classes) and which are evaluated in a
previous or the same evaluation phase. This does not apply to
the KNOWN or UNKNOWN classes.
</para>
<para>
......@@ -335,7 +342,7 @@
<entry>Unknown client</entry>
<entry>unknown</entry>
<entry>not member('KNOWN')</entry>
<entry>If there is a hostreservation for the client
<entry>If there is a host reservation for the client
"false" else "true"</entry>
</row>
<row>
......@@ -594,13 +601,13 @@
built-in, i.e., beginning by &quot;VENDOR_CLASS_&quot;,
&quot;AFTER__&quot; (for the to come "after" hook) and
&quot;EXTERNAL_&quot; or equal to &quot;ALL&quot;, &quot;KNOWN&quot;,
etc.
&quot;UNKNOWN&quot;etc.
</para>
<para>"known" and "unknown" are short hands for "member('KNOWN')" and
"not member('KNOWN')". Note the evaluation of any expression using
directly or indirectly the &quot;KNOWN&quot; class is deferred
after the host reservation lookup (i.e. when the &quot;KNOWN&quot;
belonging is determined).
or &quot;UNKNOWN&quot; partition is determined).
</para></listitem>
<listitem><para>
......
......@@ -2243,11 +2243,26 @@ It is merely echoed by the server
</para>
<para>
The process of doing classification is conducted in several steps. The first step
is to assess an incoming packet and assign it to zero or more classes. The
second step is to choose a subnet, possibly based on the class information.
The third step is to assign classes from host reservations and
evaluate class expressions depending on the "KNOWN" class.
In a similar way a pool can be constrained to serve only known
clients, i.e. clients which have a reservation, using the
built-in "KNOWN" or "UNKNOWN" classes. One can assign addresses
to registered clients without giving a different address per
reservations, for instance when there is not enough available
addresses. The determination whether there is a reservation
for a given client is made after a subnet is selected. As such, it
is not possible to use KNOWN/UNKNOWN classes to select a shared
network or a subnet.
</para>
<para>
The process of doing classification is conducted in five steps.
The first step is to assess an incoming packet and assign it to
zero or more classes.
The second step is to choose a subnet, possibly based on the
class information.
The next step is to evaluate class expressions depending on the
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool selection and to assign classes from host reservations.
After the list of required classes is built and each class of the list
has its expression evaluated: when it returns true the packet is added
as a member of the class.
......@@ -3616,9 +3631,9 @@ It is merely echoed by the server
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
with classification using expressions. The "KNOWN" builtin class is
added to the packet and any class depending on it directly or indirectly
and not only-if-required is evaluated.
with classification using expressions. The "KNOWN" or "UNKNOWN" builtin
class is added to the packet and any class depending on it directly or
indirectly and not only-if-required is evaluated.
</para>
<note>
......
......@@ -2237,11 +2237,27 @@ should include options from the isc option space:
</para>
<para>
The process of doing classification is conducted in several steps. The first step
is to assess an incoming packet and assign it to zero or more classes. The
second step is to choose a subnet, possibly based on the class information.
The third step is to assign classes from host reservations and
evaluate class expressions depending on the "KNOWN" class.
In a similar way a pool can be constrained to serve only known
clients, i.e. clients which have a reservation, using the
built-in "KNOWN" or "UNKNOWN" classes. One can assign addresses
to registered clients without giving a different address per
reservations, for instance when there is not enough available
addresses. The determination whether there is a reservation
for a given client is made after a subnet is selected. As such, it
is not possible to use KNOWN/UNKNOWN classes to select a shared
network or a subnet.
</para>
<para>
The process of doing classification is conducted in five steps.
The first step is to assess an incoming packet and assign it to
zero or more classes.
The second step is to choose a subnet, possibly based on the
class information.
The next step is to evaluate class expressions depending on the
built-in "KNOWN"/"UNKNOWN" classes after host reservation lookup,
using them for pool/pd-pool selection and to assign classes from host
reservations.
After the list of required classes is built and each class of the list
has its expression evaluated: when it returns true the packet is added
as a member of the class.
......@@ -3271,9 +3287,9 @@ should include options from the isc option space:
</screen>
<para>Static class assignments, as shown above, can be used in conjunction
with classification using expressions. The "KNOWN" builtin class is
added to the packet and any class depending on it directly or indirectly
and not only-if-required is evaluated.
with classification using expressions. The "KNOWN" or "UNKNOWN" builtin
class is added to the packet and any class depending on it directly or
indirectly and not only-if-required is evaluated.
</para>
<note>
......
......@@ -163,6 +163,22 @@ Dhcpv4Exchange::Dhcpv4Exchange(const AllocEnginePtr& alloc_engine,
}
}
// Set KNOWN builtin class if something was found, UNKNOWN if not.
if (!context_->hosts_.empty()) {
query->addClass("KNOWN");
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
.arg(query->getLabel())
.arg("KNOWN");
} else {
query->addClass("UNKNOWN");
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
.arg(query->getLabel())
.arg("UNKNOWN");
}
// Perform second pass of classification.
Dhcpv4Srv::evaluateClasses(query, true);
const ClientClasses& classes = query_->getClasses();
if (!classes.empty()) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_ASSIGNED)
......@@ -3197,10 +3213,14 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
// All packets belongs to ALL.
pkt->addClass("ALL");
// First phase: built-in vendor class processing
// First: built-in vendor class processing.
classifyByVendor(pkt);
// Run match expressions
// Run match expressions on classes not depending on KNOWN/UNKNOWN.
evaluateClasses(pkt, false);
}
void Dhcpv4Srv::evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known) {
// Note getClientClassDictionary() cannot be null
const ClientClassDictionaryPtr& dict =
CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
......@@ -3217,6 +3237,10 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
if ((*it)->getRequired()) {
continue;
}
// Not the right pass.
if ((*it)->getDependOnKnown() != depend_on_known) {
continue;
}
// Evaluate the expression which can return false (no match),
// true (match) or raise an exception (error)
try {
......
......@@ -850,6 +850,22 @@ protected:
/// @param pkt packet to be classified
void classifyPacket(const Pkt4Ptr& pkt);
public:
/// @brief Evaluate classes.
///
/// @note Second part of the classification.
///
/// Evaluate expressions of client classes: if it returns true the class
/// is added to the incoming packet.
///
/// @param pkt packet to be classified.
/// @param depend_on_known if false classes depending on the KNOWN or
/// UNKNOWN classes are skipped, if true only these classes are evaluated.
static void evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known);
protected:
/// @brief Assigns incoming packet to zero or more classes (required pass).
///
/// @note This required classification evaluates all classes which
......
......@@ -2576,6 +2576,118 @@ TEST_F(Dhcpv4SrvTest, clientPoolClassify) {
EXPECT_FALSE(offer->getYiaddr().isV4Zero());
}
// Checks if the KNOWN built-in classes is indeed used for pool selection.
TEST_F(Dhcpv4SrvTest, clientPoolClassifyKnown) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
// This test configures 2 pools.
// The first one requires reservation, the second does the opposite.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
"{ \"pools\": [ { "
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
" \"client-class\": \"KNOWN\" }, "
" { \"pool\": \"192.0.3.1 - 192.0.3.100\", "
" \"client-class\": \"UNKNOWN\" } ], "
" \"subnet\": \"192.0.0.0/16\" } "
"],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config, true));
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
CfgMgr::instance().commit();
// check if returned status is OK
ASSERT_TRUE(status);
comment_ = config::parseAnswer(rcode_, status);
ASSERT_EQ(0, rcode_);
// Create a simple packet that we'll use for classification
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
dis->setCiaddr(IOAddress("192.0.2.1"));
dis->setIface("eth0");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
// First pool requires reservation so the second will be used
Pkt4Ptr offer = srv.processDiscover(dis);
ASSERT_TRUE(offer);
EXPECT_EQ(DHCPOFFER, offer->getType());
EXPECT_EQ("192.0.3.1", offer->getYiaddr().toText());
}
// Checks if the UNKNOWN built-in classes is indeed used for pool selection.
TEST_F(Dhcpv4SrvTest, clientPoolClassifyUnknown) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
// This test configures 2 pools.
// The first one requires no reservation, the second does the opposite.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
"{ \"pools\": [ { "
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
" \"client-class\": \"UNKNOWN\" }, "
" { \"pool\": \"192.0.3.1 - 192.0.3.100\", "
" \"client-class\": \"KNOWN\" } ], "
" \"subnet\": \"192.0.0.0/16\", "
" \"reservations\": [ { "
" \"hw-address\": \"00:00:00:11:22:33\", "
" \"hostname\": \"foo.bar\" } ] } "
"],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config, true));
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
CfgMgr::instance().commit();
// check if returned status is OK
ASSERT_TRUE(status);
comment_ = config::parseAnswer(rcode_, status);
ASSERT_EQ(0, rcode_);
// Create a simple packet that we'll use for classification
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
dis->setCiaddr(IOAddress("192.0.2.1"));
dis->setIface("eth0");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
// Set harware address / identifier
const HWAddr& hw = HWAddr::fromText("00:00:00:11:22:33");
HWAddrPtr hw_addr(new HWAddr(hw));
dis->setHWAddr(hw_addr);
// First pool requires no reservation so the second will be used
Pkt4Ptr offer = srv.processDiscover(dis);
ASSERT_TRUE(offer);
EXPECT_EQ(DHCPOFFER, offer->getType());
EXPECT_EQ("192.0.3.1", offer->getYiaddr().toText());
}
// Verifies last resort option 43 is backward compatible
TEST_F(Dhcpv4SrvTest, option43LastResort) {
IfaceMgrTestConfig test_config(true);
......
......@@ -379,6 +379,22 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
// Find host reservations using specified identifiers.
alloc_engine_->findReservation(ctx);
}
// Set KNOWN builtin class if something was found, UNKNOWN if not.
if (!ctx.hosts_.empty()) {
pkt->addClass("KNOWN");
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
.arg(pkt->getLabel())
.arg("KNOWN");
} else {
pkt->addClass("UNKNOWN");
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLASS_ASSIGNED)
.arg(pkt->getLabel())
.arg("UNKNOWN");
}
// Perform second pass of classification.
evaluateClasses(pkt, true);
}
bool Dhcpv6Srv::run() {
......@@ -3284,10 +3300,14 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
pkt->addClass("ALL");
string classes = "ALL ";
// First phase: built-in vendor class processing
// First: built-in vendor class processing
classifyByVendor(pkt, classes);
// Run match expressions
// Run match expressions on classes not depending on KNOWN/UNKNOWN.
evaluateClasses(pkt, false);
}
void Dhcpv6Srv::evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known) {
// Note getClientClassDictionary() cannot be null
const ClientClassDictionaryPtr& dict =
CfgMgr::instance().getCurrentCfg()->getClientClassDictionary();
......@@ -3304,6 +3324,10 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
if ((*it)->getRequired()) {
continue;
}
// Not the right pass.
if ((*it)->getDependOnKnown() != depend_on_known) {
continue;
}
// Evaluate the expression which can return false (no match),
// true (match) or raise an exception (error)
try {
......@@ -3314,7 +3338,6 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
.arg(status);
// Matching: add the class
pkt->addClass((*it)->getName());
classes += (*it)->getName() + " ";
} else {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, EVAL_RESULT)
.arg((*it)->getName())
......
......@@ -681,6 +681,18 @@ protected:
/// @param pkt packet to be classified
void classifyPacket(const Pkt6Ptr& pkt);
/// @brief Evaluate classes.
///
/// @note Second part of the classification.
///
/// Evaluate expressions of client classes: if it returns true the class
/// is added to the incoming packet.
///
/// @param pkt packet to be classified.
/// @param depend_on_known if false classes depending on the KNOWN or
/// UNKNOWN classes are skipped, if true only these classes are evaluated.
void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known);
/// @brief Assigns classes retrieved from host reservation database.
///
/// @param pkt Pointer to the packet to which classes will be assigned.
......
......@@ -1031,7 +1031,7 @@ TEST_F(ClassifyTest, clientClassifyPool) {
" \"client-class\": \"xyzzy\" "
" } "
" ], "
" \"subnet\": \"2001:db8:2::/40\" "
" \"subnet\": \"2001:db8::/40\" "
" } "
"], "
"\"valid-lifetime\": 4000 }";
......@@ -1085,6 +1085,93 @@ TEST_F(ClassifyTest, clientClassifyPool) {
EXPECT_TRUE(ia_na3->getOption(D6O_IAADDR));
}
// Checks if the [UN]KNOWN built-in classes is indeed used for pool selection.
TEST_F(ClassifyTest, clientClassifyPoolKnown) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// This test configures 2 pools.
// The first one requires reservation, the second does the opposite.
std::string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ "
" { \"pools\": [ "
" { "
" \"pool\": \"2001:db8:1::/64\", "
" \"client-class\": \"KNOWN\" "
" }, "
" { "
" \"pool\": \"2001:db8:2::/64\", "
" \"client-class\": \"UNKNOWN\" "
" } "
" ], "
" \"subnet\": \"2001:db8::/40\", "
" \"reservations\": [ "
" { \"duid\": \"01:02:03:04\", \"hostname\": \"foo\" } ] "
" } "
"], "
"\"valid-lifetime\": 4000 }";
ASSERT_NO_THROW(configure(config));
OptionPtr clientid1 = generateClientId();
Pkt6Ptr query1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
query1->setRemoteAddr(IOAddress("2001:db8:1::3"));
query1->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
query1->addOption(clientid1);
query1->setIface("eth1");
// First pool requires reservation so the second will be used
srv.classifyPacket(query1);
AllocEngine::ClientContext6 ctx1;
bool drop = false;
srv.initContext(query1, ctx1, drop);
ASSERT_FALSE(drop);
Pkt6Ptr response1 = srv.processSolicit(ctx1);
ASSERT_TRUE(response1);
OptionPtr ia_na1 = response1->getOption(D6O_IA_NA);
ASSERT_TRUE(ia_na1);
EXPECT_FALSE(ia_na1->getOption(D6O_STATUS_CODE));
OptionPtr iaaddr1 = ia_na1->getOption(D6O_IAADDR);
ASSERT_TRUE(iaaddr1);
boost::shared_ptr<Option6IAAddr> addr1 =
boost::dynamic_pointer_cast<Option6IAAddr>(iaaddr1);
ASSERT_TRUE(addr1);
EXPECT_EQ("2001:db8:2::", addr1->getAddress().toText());
// Try with DUID 01:02:03:04
uint8_t duid[] = { 0x01, 0x02, 0x03, 0x04 };
OptionBuffer buf(duid, duid + sizeof(duid));
OptionPtr clientid2(new Option(Option::V6, D6O_CLIENTID, buf));
Pkt6Ptr query2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 2345));
query2->setRemoteAddr(IOAddress("2001:db8:1::3"));
query2->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
query2->addOption(clientid2);
query2->setIface("eth1");
// Now the first pool will be used
srv.classifyPacket(query2);
AllocEngine::ClientContext6 ctx2;
srv.initContext(query2, ctx2, drop);
ASSERT_FALSE(drop);
Pkt6Ptr response2 = srv.processSolicit(ctx2);
ASSERT_TRUE(response2);
OptionPtr ia_na2 = response2->getOption(D6O_IA_NA);
ASSERT_TRUE(ia_na2);
EXPECT_FALSE(ia_na2->getOption(D6O_STATUS_CODE));
OptionPtr iaaddr2 = ia_na2->getOption(D6O_IAADDR);
ASSERT_TRUE(iaaddr2);
boost::shared_ptr<Option6IAAddr> addr2 =
boost::dynamic_pointer_cast<Option6IAAddr>(iaaddr2);
ASSERT_TRUE(addr2);
EXPECT_EQ("2001:db8:1::", addr2->getAddress().toText());
}
// Tests whether a packet with custom vendor-class (not erouter or docsis)
// is classified properly.
TEST_F(ClassifyTest, vendorClientClassification2) {
......
......@@ -21,7 +21,7 @@ ClientClassDef::ClientClassDef(const std::string& name,
const ExpressionPtr& match_expr,
const CfgOptionPtr& cfg_option)
: name_(name), match_expr_(match_expr), required_(false),
cfg_option_(cfg_option),
depend_on_known_(false), cfg_option_(cfg_option),
next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()) {
// Name can't be blank
......@@ -39,8 +39,8 @@ ClientClassDef::ClientClassDef(const std::string& name,
}
ClientClassDef::ClientClassDef(const ClientClassDef& rhs)
: name_(rhs.name_), match_expr_(ExpressionPtr()),
required_(false), cfg_option_(new CfgOption()),
: name_(rhs.name_), match_expr_(ExpressionPtr()), required_(false),
depend_on_known_(false), cfg_option_(new CfgOption()),
next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()) {
if (rhs.match_expr_) {
......@@ -57,6 +57,7 @@ ClientClassDef::ClientClassDef(const ClientClassDef& rhs)
}
required_ = rhs.required_;
depend_on_known_ = rhs.depend_on_known_;
next_server_ = rhs.next_server_;
sname_ = rhs.sname_;
filename_ = rhs.filename_;
......@@ -105,6 +106,16 @@ ClientClassDef::setRequired(bool required) {
required_ = required;
}
bool
ClientClassDef::getDependOnKnown() const {
return (depend_on_known_);
}
void
ClientClassDef::setDependOnKnown(bool depend_on_known) {
depend_on_known_ = depend_on_known;
}
const CfgOptionDefPtr&
ClientClassDef::getCfgOptionDef() const {
return (cfg_option_def_);
......@@ -138,6 +149,7 @@ ClientClassDef::equals(const ClientClassDef& other) const {
(cfg_option_def_ && other.cfg_option_def_ &&
(*cfg_option_def_ == *other.cfg_option_def_))) &&
(required_ == other.required_) &&
(depend_on_known_ == other.depend_on_known_) &&
(next_server_ == other.next_server_) &&
(sname_ == other.sname_) &&
(filename_ == other.filename_));
......@@ -205,6 +217,7 @@ ClientClassDictionary::addClass(const std::string& name,
const ExpressionPtr& match_expr,
const std::string& test,
<