Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Kea
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
445
Issues
445
List
Boards
Labels
Service Desk
Milestones
Merge Requests
71
Merge Requests
71
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
ISC Open Source Projects
Kea
Commits
6a856ed9
Commit
6a856ed9
authored
Jun 13, 2018
by
Francis Dupont
Browse files
Options
Browse Files
Download
Plain Diff
[master] Finished merge of trac5549a (known/unknown)
parents
6de6024c
d9c77f8a
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
592 additions
and
72 deletions
+592
-72
ChangeLog
ChangeLog
+9
-0
doc/examples/kea4/classify2.json
doc/examples/kea4/classify2.json
+27
-1
doc/examples/kea6/classify2.json
doc/examples/kea6/classify2.json
+26
-0
doc/guide/classify.xml
doc/guide/classify.xml
+33
-26
doc/guide/dhcp4-srv.xml
doc/guide/dhcp4-srv.xml
+23
-8
doc/guide/dhcp6-srv.xml
doc/guide/dhcp6-srv.xml
+24
-8
src/bin/dhcp4/dhcp4_srv.cc
src/bin/dhcp4/dhcp4_srv.cc
+26
-2
src/bin/dhcp4/dhcp4_srv.h
src/bin/dhcp4/dhcp4_srv.h
+16
-0
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+112
-0
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/dhcp6_srv.cc
+26
-3
src/bin/dhcp6/dhcp6_srv.h
src/bin/dhcp6/dhcp6_srv.h
+12
-0
src/bin/dhcp6/tests/classify_unittests.cc
src/bin/dhcp6/tests/classify_unittests.cc
+88
-1
src/lib/dhcpsrv/client_class_def.cc
src/lib/dhcpsrv/client_class_def.cc
+27
-4
src/lib/dhcpsrv/client_class_def.h
src/lib/dhcpsrv/client_class_def.h
+29
-2
src/lib/dhcpsrv/parsers/client_class_def_parser.cc
src/lib/dhcpsrv/parsers/client_class_def_parser.cc
+10
-4
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc
+67
-0
src/lib/dhcpsrv/tests/client_class_def_unittest.cc
src/lib/dhcpsrv/tests/client_class_def_unittest.cc
+34
-10
src/lib/dhcpsrv/tests/srv_config_unittest.cc
src/lib/dhcpsrv/tests/srv_config_unittest.cc
+3
-3
No files found.
ChangeLog
View file @
6a856ed9
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
...
...
doc/examples/kea4/classify2.json
View file @
6a856ed9
...
...
@@ -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"
}
]
}
]
},
...
...
doc/examples/kea6/classify2.json
View file @
6a856ed9
...
...
@@ -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"
}
]
}
]
...
...
doc/guide/classify.xml
View file @
6a856ed9
...
...
@@ -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
"
VENDOR_CLASS_
"
,
"
AFTER__
"
(for the to come "after" hook) and
"
EXTERNAL_
"
or equal to
"
ALL
"
,
"
KNOWN
"
,
etc.
"
UNKNOWN
"
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
"
KNOWN
"
class is deferred
after the host reservation lookup (i.e. when the
"
KNOWN
"
belonging
is determined).
or
"
UNKNOWN
"
partition
is determined).
</para></listitem>
<listitem><para>
...
...
doc/guide/dhcp4-srv.xml
View file @
6a856ed9
...
...
@@ -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>
...
...
doc/guide/dhcp6-srv.xml
View file @
6a856ed9
...
...
@@ -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>
...
...
src/bin/dhcp4/dhcp4_srv.cc
View file @
6a856ed9
...
...
@@ -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
{
...
...
src/bin/dhcp4/dhcp4_srv.h
View file @
6a856ed9
...
...
@@ -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
...
...
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
View file @
6a856ed9
...
...
@@ -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
);
...
...
src/bin/dhcp6/dhcp6_srv.cc
View file @
6a856ed9
...
...
@@ -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
())
...
...
src/bin/dhcp6/dhcp6_srv.h
View file @
6a856ed9
...
...
@@ -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.
...
...
src/bin/dhcp6/tests/classify_unittests.cc
View file @
6a856ed9
...
...
@@ -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
);