Commit e90cf0ee authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac4483' (integer literal in classification)

parents 73d7b173 355c20fb
......@@ -9,6 +9,7 @@ EXTRA_DIST += devel/qa.dox
nobase_dist_doc_DATA = examples/ddns/sample1.json
nobase_dist_doc_DATA += examples/ddns/template.json
nobase_dist_doc_DATA += examples/kea4/backends.json
nobase_dist_doc_DATA += examples/kea4/classify.json
nobase_dist_doc_DATA += examples/kea4/hooks.json
nobase_dist_doc_DATA += examples/kea4/leases-expiration.json
nobase_dist_doc_DATA += examples/kea4/multiple-options.json
......@@ -17,6 +18,7 @@ nobase_dist_doc_DATA += examples/kea4/several-subnets.json
nobase_dist_doc_DATA += examples/kea4/single-subnet.json
nobase_dist_doc_DATA += examples/kea6/advanced.json
nobase_dist_doc_DATA += examples/kea6/backends.json
nobase_dist_doc_DATA += examples/kea6/classify.json
nobase_dist_doc_DATA += examples/kea6/hooks.json
nobase_dist_doc_DATA += examples/kea6/leases-expiration.json
nobase_dist_doc_DATA += examples/kea6/multiple-options.json
......
# This is an example configuration file for the DHCPv4 server in Kea.
# The purpose of this example is to showcase how clients can be classified.
{ "Dhcp4": {
# Kea is told to listen on ethX interface only.
"interfaces-config": {
"interfaces": [ "ethX" ]
},
# Let's use the simplest backend: memfile and use some reasonable values
# for timers. They are of no concern for the classification demonstration.
"lease-database": { "type": "memfile" },
"renew-timer": 1000,
"rebind-timer": 2000,
"valid-lifetime": 4000,
# This list defines several classes that incoming packets can be assigned to.
# One packet can belong to zero or more classes.
"client-classes": [
# The first class attempts to match the whole hardware address to a specific
# value. All incoming packets with that MAC address will get a special
# value of the option. If there are many hosts that require special
# treatment, it is much better to use host reservations. However, doing
# tricks with MAC addresses may prove useful in some cases, e.g.
# by matching OUI to known values we can detect certain vendors.
{
"name": "special_snowflake",
"test": "pkt4.mac == 0x010203040506",
"option-data": [{
"name": "domain-name-servers",
"data": "127.0.0.1"
}]
},
# Let's classify all incoming DISCOVER (message type 1) to a separate
# class.
{
"name": "discovers",
"test": "pkt4.msgtype == 1"
},
# Clients are supposed to set the transaction-id field to a random value.
# Clients that send it with 0 are most likely broken. Let's mark them
# as such.
{
"name": "broken",
"test": "pkt4.transid == 0"
},
# Let's pick VoIP phones. Those that send their class identifiers
# as Aastra, should belong to VoIP class. For a list of all options,
# see www.iana.org/assignments/bootp-dhcp-parameters/
{
"name": "VoIP",
"test": "substring(option[60].hex,0,6) == 'Aastra'"
},
],
# The following list defines subnets. For some subnets we defined
# a class that is allowed in that subnet. If not specified,
# everyone is allowed. When a class is specified, only packets belonging
# to that class are allowed for that subnet.
"subnet4": [
{
# This one is for VoIP devices only.
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24",
"client-class": "VoIP",
"interface": "ethX"
},
# This one doesn't have any client-class specified, so everyone
# is allowed in. The normal subnet selection rules still apply,
# though.
{
"pools": [ { "pool": "192.0.3.1 - 192.0.3.200" } ],
"subnet": "192.0.3.0/24",
"interface": "ethX"
}
]
},
# The following configures logging. It assumes that messages with at least
# informational level (info, warn, error) will will be logged to stdout.
"Logging": {
"loggers": [
{
"name": "kea-dhcp4",
"output_options": [
{
"output": "stdout"
}
],
"severity": "INFO"
}
]
}
}
# This is an example configuration file for the DHCPv4 server in Kea.
# The purpose of this example is to showcase how clients can be classified.
{ "Dhcp6":
{
# Kea is told to listen on ethX interface only.
"interfaces-config": {
"interfaces": [ "ethX" ]
},
# Let's use the simplest backend: memfile and use some reasonable values
# for timers. They are of no concern for the classification demonstration.
"lease-database": { "type": "memfile" },
"renew-timer": 1000,
"rebind-timer": 2000,
"preferred-lifetime": 3000,
"valid-lifetime": 4000,
# This list defines several classes that incoming packets can be assigned to.
# One packet can belong to zero or more classes.
"client-classes": [
# The first class attempts to match all packets coming in on ethX interface.
{
"name": "lab",
"test": "pkt.iface == 'ethX'",
"option-data": [{
"name": "dns-servers",
"data": "2001:db8::1"
}]
},
# Let's classify all incoming RENEW (message type 5) to a separate
# class.
{
"name": "renews",
"test": "pkt6.msgtype == 5"
},
# Let's pick cable modems. In this simple example we'll assume the device
# is a cable modem if it sends a vendor option with enterprise-id equal
# to 4491.
{
"name": "cable-modems",
"test": "vendor.enterprise == 4491"
},
],
# The following list defines subnets. Each subnet consists of at
# least subnet and pool entries.
"subnet6": [
{
"pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64",
"client-class": "cable-modems",
"interface": "ethX"
},
{
"pools": [ { "pool": "2001:db8:2::/80" } ],
"subnet": "2001:db8:2::/64",
"interface": "ethX"
}
]
},
# The following configures logging. Kea will log all debug messages
# to /var/log/kea-debug.log file.
"Logging": {
"loggers": [
{
"name": "kea-dhcp6",
"output_options": [
{
"output": "/var/log/kea-debug.log"
}
],
"debuglevel": 99,
"severity": "DEBUG"
}
]
}
}
......@@ -181,7 +181,7 @@
<entry>Integer literal</entry>
<entry>123</entry>
<entry>'123'</entry>
<entry>An integer value</entry>
<entry>A 32 bit unsigned integer value</entry>
</row>
<row></row>
<row>
......@@ -256,8 +256,9 @@
<row>
<entry>Length of packet</entry>
<entry>pkt.len</entry>
<entry>0x00000100</entry>
<entry>The length of a DHCP packet (UDP header field) padded to 4 bytes.</entry>
<entry>513</entry>
<entry>The length of a DHCP packet (UDP header field), expressed
as a 32 bit unsigned integer.</entry>
</row>
<row>
<entry>Hardware address in DHCPv4 packet</entry>
......@@ -268,13 +269,13 @@
<row>
<entry>Hardware length in DHCPv4 packet</entry>
<entry>pkt4.hlen</entry>
<entry>0x00000006</entry>
<entry>6</entry>
<entry>The value of the hlen field of the DHCPv4 packet padded to 4 bytes</entry>
</row>
<row>
<entry>Hardware type in DHCPv4 packet</entry>
<entry>pkt4.htype</entry>
<entry>0x0000007b</entry>
<entry>6</entry>
<entry>The value of the htype field of the DHCPv4 packet padded to 4 bytes</entry>
</row>
<row>
......@@ -301,19 +302,33 @@
<entry>192.0.2.1</entry>
<entry>The value of the siaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
</row>
<row>
<entry>Message Type in DHCPv4 packet</entry>
<entry>pkt4.msgtype</entry>
<entry>1</entry>
<entry>The value of the message type field in the DHCPv4
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Transaction ID (xid) in DHCPv4 packet</entry>
<entry>pkt4.transid</entry>
<entry>12345</entry>
<entry>The value of the transaction id in the DHCPv4
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Message Type in DHCPv6 packet</entry>
<entry>pkt6.msgtype</entry>
<entry>1</entry>
<entry>The value of the message type field in the DHCPv6
packet.</entry>
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Transaction ID in DHCPv6 packet</entry>
<entry>pkt6.transid</entry>
<entry>12345</entry>
<entry>The value of the transaction id in the DHCPv6
packet.</entry>
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
......@@ -334,7 +349,7 @@
<row>
<entry>Enterprise-id from vendor option</entry>
<entry>vendor.enterprise</entry>
<entry>0x0000118b</entry>
<entry>4491</entry>
<entry>If the vendor option is present, it returns the
value of the enterprise-id field padded to 4
bytes. Returns '' otherwise.</entry>
......@@ -375,7 +390,7 @@
<row>
<entry>Enterprise-id from vendor class option</entry>
<entry>vendor-class.enterprise</entry>
<entry>0x0000118b</entry>
<entry>4491</entry>
<entry>If the vendor option is present, it returns the
value of the enterprise-id field padded to 4
bytes. Returns '' otherwise.</entry>
......@@ -411,8 +426,16 @@
</para>
<para>
Integers in the expression are converted to strings
when the expression is read into Kea.
Integers in an expression are converted to 32 bit unsigned integers and
are represented as four byte strings. For example 123 is represented as
0x0000007b. All expressions that return numeric values use 32 bit
unsigned integers, even if the field in the packet is smaller. In general
it is easier to use decimal notation to represent integers, but it is also
possible to use hex notation. When using hex notation to represent an
integer care should be taken to make sure the value is represented as 32
bits, e.g. use 0x00000001 instead of 0x1 or 0x01. Also, make
sure the value is specified in network order, e.g. 1 is
represented as 0x00000001.
</para>
<para>
......
......@@ -444,6 +444,9 @@ Pkt4::setHWAddrMember(const uint8_t htype, const uint8_t hlen,
isc_throw(OutOfRange, "Invalid HW Address specified");
}
/// @todo: what if mac_addr.size() doesn't match hlen?
/// We would happily copy over hardware address that is possibly
/// too long or doesn't match hlen value.
hw_addr.reset(new HWAddr(mac_addr, htype));
}
......
......@@ -139,7 +139,7 @@ EvalContext::convertUint32(const std::string& number,
} catch (const boost::bad_lexical_cast &) {
error(loc, "Invalid value in " + number);
}
if (n >= std::numeric_limits<uint32_t>::max()) {
if (n > std::numeric_limits<uint32_t>::max()) {
error(loc, "Invalid value in "
+ number + ". Allowed range: 0..4294967295");
}
......@@ -147,6 +147,17 @@ EvalContext::convertUint32(const std::string& number,
return (static_cast<uint32_t>(n));
}
std::string
EvalContext::fromUint32(const uint32_t integer) {
std::string tmp(4, 0);
tmp[0] = (integer >> 24) & 0xff;
tmp[1] = (integer >> 16) & 0xff;
tmp[2] = (integer >> 8) & 0xff;
tmp[3] = integer & 0xff;
return (tmp);
}
void
EvalContext::fatal (const std::string& what)
{
......
......@@ -69,13 +69,13 @@ public:
///
/// @param loc location within the parsed file when experienced a problem.
/// @param what string explaining the nature of the error.
void error(const isc::eval::location& loc, const std::string& what);
static void error(const isc::eval::location& loc, const std::string& what);
/// @brief Error handler
///
/// This is a simplified error reporting tool for possible future
/// cases when the EvalParser is not able to handle the packet.
void error(const std::string& what);
static void error(const std::string& what);
/// @brief Fatal error handler
///
......@@ -103,12 +103,14 @@ public:
/// @brief Attempts to convert string to unsigned 32bit integer
///
/// For reverse conversion, see @ref fromUint32
///
/// @param number string to be converted
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
uint32_t convertUint32(const std::string& number,
const isc::eval::location& loc);
static uint32_t convertUint32(const std::string& number,
const isc::eval::location& loc);
/// @brief Attempts to convert string to unsigned 8bit integer
///
......@@ -116,8 +118,8 @@ public:
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
uint8_t convertUint8(const std::string& number,
const isc::eval::location& loc);
static uint8_t convertUint8(const std::string& number,
const isc::eval::location& loc);
/// @brief Nest level conversion
///
......@@ -127,7 +129,17 @@ public:
/// @throw calls the syntax error function if the value is not in
/// the range 0..31
uint8_t convertNestLevelNumber(const std::string& nest_level,
const isc::eval::location& loc);
const isc::eval::location& loc);
/// @brief Converts integer to string representation
///
/// The integer is coded as a 4 byte long string in network order, e.g.
/// 6 is represented as 00000006. For reverse conversion, see
/// @ref convertUint32.
///
/// @param integer value to be converted
/// @return 4 byte long string that encodes the value.
static std::string fromUint32(const uint32_t integer);
/// @brief Returns the universe (v4 or v6)
///
......
......@@ -745,11 +745,11 @@ int yy_flex_debug = 1;
static yyconst flex_int16_t yy_rule_linenum[51] =
{ 0,
82, 86, 92, 102, 108, 122, 129, 143, 144, 145,
146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
176, 177, 178, 179, 180, 181, 182, 183, 184, 185
82, 86, 92, 102, 108, 126, 133, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189
} ;
static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
......@@ -1286,7 +1286,11 @@ YY_RULE_SETUP
std::string tmp(yytext);
try {
static_cast<void>(boost::lexical_cast<int>(tmp));
// In substring we want to use negative values (e.g. -1).
// In enterprise-id we need to use values up to 0xffffffff.
// To cover both of those use cases, we need at least
// int64_t.
static_cast<void>(boost::lexical_cast<int64_t>(tmp));
} catch (const boost::bad_lexical_cast &) {
driver.error(loc, "Failed to convert " + tmp + " to an integer.");
}
......@@ -1298,7 +1302,7 @@ YY_RULE_SETUP
case 6:
/* rule 6 can match eol */
YY_RULE_SETUP
#line 122 "lexer.ll"
#line 126 "lexer.ll"
{
// This string specifies option name starting with a letter
// and further containing letters, digits, hyphens and
......@@ -1308,7 +1312,7 @@ YY_RULE_SETUP
YY_BREAK
case 7:
YY_RULE_SETUP
#line 129 "lexer.ll"
#line 133 "lexer.ll"
{
// IPv4 or IPv6 address
std::string tmp(yytext);
......@@ -1325,229 +1329,229 @@ YY_RULE_SETUP
YY_BREAK
case 8:
YY_RULE_SETUP
#line 143 "lexer.ll"
#line 147 "lexer.ll"
return isc::eval::EvalParser::make_EQUAL(loc);
YY_BREAK
case 9:
YY_RULE_SETUP
#line 144 "lexer.ll"
#line 148 "lexer.ll"
return isc::eval::EvalParser::make_OPTION(loc);
YY_BREAK
case 10:
YY_RULE_SETUP
#line 145 "lexer.ll"
#line 149 "lexer.ll"
return isc::eval::EvalParser::make_RELAY4(loc);
YY_BREAK
case 11:
YY_RULE_SETUP
#line 146 "lexer.ll"
#line 150 "lexer.ll"
return isc::eval::EvalParser::make_RELAY6(loc);
YY_BREAK
case 12:
YY_RULE_SETUP
#line 147 "lexer.ll"
#line 151 "lexer.ll"
return isc::eval::EvalParser::make_PEERADDR(loc);
YY_BREAK
case 13:
YY_RULE_SETUP
#line 148 "lexer.ll"
#line 152 "lexer.ll"
return isc::eval::EvalParser::make_LINKADDR(loc);
YY_BREAK
case 14:
YY_RULE_SETUP
#line 149 "lexer.ll"
#line 153 "lexer.ll"
return isc::eval::EvalParser::make_TEXT(loc);
YY_BREAK
case 15:
YY_RULE_SETUP
#line 150 "lexer.ll"
#line 154 "lexer.ll"
return isc::eval::EvalParser::make_HEX(loc);
YY_BREAK
case 16:
YY_RULE_SETUP
#line 151 "lexer.ll"
#line 155 "lexer.ll"
return isc::eval::EvalParser::make_EXISTS(loc);
YY_BREAK
case 17:
YY_RULE_SETUP
#line 152 "lexer.ll"
#line 156 "lexer.ll"
return isc::eval::EvalParser::make_PKT(loc);
YY_BREAK
case 18:
YY_RULE_SETUP
#line 153 "lexer.ll"
#line 157 "lexer.ll"
return isc::eval::EvalParser::make_IFACE(loc);
YY_BREAK
case 19:
YY_RULE_SETUP
#line 154 "lexer.ll"
#line 158 "lexer.ll"
return isc::eval::EvalParser::make_SRC(loc);
YY_BREAK
case 20:
YY_RULE_SETUP
#line 155 "lexer.ll"
#line 159 "lexer.ll"
return isc::eval::EvalParser::make_DST(loc);
YY_BREAK
case 21:
YY_RULE_SETUP
#line 156 "lexer.ll"
#line 160 "lexer.ll"
return isc::eval::EvalParser::make_LEN(loc);
YY_BREAK
case 22:
YY_RULE_SETUP
#line 157 "lexer.ll"
#line 161 "lexer.ll"
return isc::eval::EvalParser::make_PKT4(loc);
YY_BREAK
case 23:
YY_RULE_SETUP
#line 158 "lexer.ll"
#line 162 "lexer.ll"
return isc::eval::EvalParser::make_CHADDR(loc);
YY_BREAK
case 24:
YY_RULE_SETUP
#line 159 "lexer.ll"
#line 163 "lexer.ll"
return isc::eval::EvalParser::make_HLEN(loc);
YY_BREAK
case 25:
YY_RULE_SETUP
#line 160 "lexer.ll"
#line 164 "lexer.ll"
return isc::eval::EvalParser::make_HTYPE(loc);
YY_BREAK
case 26:
YY_RULE_SETUP
#line 161 "lexer.ll"
#line 165 "lexer.ll"
return isc::eval::EvalParser::make_CIADDR(loc);
YY_BREAK
case 27:
YY_RULE_SETUP
#line 162 "lexer.ll"
#line 166 "lexer.ll"
return isc::eval::EvalParser::make_GIADDR(loc);
YY_BREAK
case 28:
YY_RULE_SETUP
#line 163 "lexer.ll"
#line 167 "lexer.ll"
return isc::eval::EvalParser::make_YIADDR(loc);
YY_BREAK
case 29:
YY_RULE_SETUP
#line 164 "lexer.ll"
#line 168 "lexer.ll"
return isc::eval::EvalParser::make_SIADDR(loc);
YY_BREAK
case 30:
YY_RULE_SETUP
#line 165 "lexer.ll"
#line 169 "lexer.ll"
return isc::eval::EvalParser::make_PKT6(loc);
YY_BREAK
case 31:
YY_RULE_SETUP
#line 166 "lexer.ll"
#line 170 "lexer.ll"
return isc::eval::EvalParser::make_MSGTYPE(loc);
YY_BREAK
case 32:
YY_RULE_SETUP