Merge remote-tracking branch 'origin/topic/jsiwek/subnet-literal-const'

* origin/topic/jsiwek/subnet-literal-const:
  Add parsing rules for IPv4/IPv6 subnet literal constants, addresses #888

Closes #888.
This commit is contained in:
Robin Sommer 2012-10-24 15:37:11 -07:00
commit 7ddbca8b35
12 changed files with 107 additions and 36 deletions

View file

@ -1,4 +1,9 @@
2.1-86 | 2012-10-24 15:37:11 -0700
* Add parsing rules for IPv4/IPv6 subnet literal constants.
Addresses #888. (Jon Siwek)
2.1-84 | 2012-10-19 15:12:56 -0700 2.1-84 | 2012-10-19 15:12:56 -0700
* Added a BiF strptime() to wrap the corresponding C function. (Seth * Added a BiF strptime() to wrap the corresponding C function. (Seth

View file

@ -1 +1 @@
2.1-84 2.1-86

View file

@ -248,10 +248,10 @@ IPPrefix::IPPrefix(const in6_addr& in6, uint8_t length)
prefix.Mask(this->length); prefix.Mask(this->length);
} }
IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length) IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length, bool len_is_v6_relative)
: prefix(addr) : prefix(addr)
{ {
if ( prefix.GetFamily() == IPv4 ) if ( prefix.GetFamily() == IPv4 && ! len_is_v6_relative )
{ {
if ( length > 32 ) if ( length > 32 )
reporter->InternalError("Bad IPAddr(v4) IPPrefix length : %d", reporter->InternalError("Bad IPAddr(v4) IPPrefix length : %d",

View file

@ -496,8 +496,15 @@ public:
* @param addr The IP address. * @param addr The IP address.
* *
* @param length The prefix length in the range from 0 to 128 * @param length The prefix length in the range from 0 to 128
*
* @param len_is_v6_relative Whether \a length is relative to the full
* 128 bits of an IPv6 address. If false and \a addr is an IPv4
* address, then \a length is expected to range from 0 to 32. If true
* \a length is expected to range from 0 to 128 even if \a addr is IPv4,
* meaning that the mask is to apply to the IPv4-mapped-IPv6 representation.
*/ */
IPPrefix(const IPAddr& addr, uint8_t length); IPPrefix(const IPAddr& addr, uint8_t length,
bool len_is_v6_relative = false);
/** /**
* Copy constructor. * Copy constructor.

View file

@ -11,16 +11,6 @@
#include "rule-parse.h" #include "rule-parse.h"
int rules_line_number = 0; int rules_line_number = 0;
static string extract_ipv6(string s)
{
if ( s.substr(0, 3) == "[0x" )
s = s.substr(3, s.find("]") - 3);
else
s = s.substr(1, s.find("]") - 1);
return s;
}
%} %}
%x PS %x PS
@ -49,15 +39,14 @@ PID ([0-9a-zA-Z_-]|"::")+
} }
{IP6} { {IP6} {
rules_lval.prefixval = new IPPrefix(IPAddr(extract_ipv6(yytext)), 128); rules_lval.prefixval = new IPPrefix(IPAddr(extract_ip(yytext)), 128, true);
return TOK_IP6; return TOK_IP6;
} }
{IP6}{OWS}"/"{OWS}{D} { {IP6}{OWS}"/"{OWS}{D} {
char* l = strchr(yytext, '/'); int len = 0;
*l++ = '\0'; string ip = extract_ip_and_len(yytext, &len);
int len = atoi(l); rules_lval.prefixval = new IPPrefix(IPAddr(ip), len, true);
rules_lval.prefixval = new IPPrefix(IPAddr(extract_ipv6(yytext)), len);
return TOK_IP6; return TOK_IP6;
} }

View file

@ -148,6 +148,7 @@ D [0-9]+
HEX [0-9a-fA-F]+ HEX [0-9a-fA-F]+
IDCOMPONENT [A-Za-z_][A-Za-z_0-9]* IDCOMPONENT [A-Za-z_][A-Za-z_0-9]*
ID {IDCOMPONENT}(::{IDCOMPONENT})* ID {IDCOMPONENT}(::{IDCOMPONENT})*
IP6 ("["({HEX}:){7}{HEX}"]")|("["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]")
FILE [^ \t\n]+ FILE [^ \t\n]+
PREFIX [^ \t\n]+ PREFIX [^ \t\n]+
FLOAT (({D}*"."?{D})|({D}"."?{D}*))([eE][-+]?{D})? FLOAT (({D}*"."?{D})|({D}"."?{D}*))([eE][-+]?{D})?
@ -229,21 +230,23 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
} }
/* IPv6 literal constant patterns */ /* IPv6 literal constant patterns */
"["({HEX}:){7}{HEX}"]" { {IP6} {
string s(yytext+1); RET_CONST(new AddrVal(extract_ip(yytext)))
RET_CONST(new AddrVal(s.erase(s.size()-1)))
} }
"["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]" {
string s(yytext+3); {IP6}{OWS}"/"{OWS}{D} {
RET_CONST(new AddrVal(s.erase(s.size()-1))) int len = 0;
string ip = extract_ip_and_len(yytext, &len);
RET_CONST(new SubNetVal(IPPrefix(IPAddr(ip), len, true)))
} }
"["({HEX}|:)*"::"({HEX}|:)*"]" {
string s(yytext+1); /* IPv4 literal constant patterns */
RET_CONST(new AddrVal(s.erase(s.size()-1))) ({D}"."){3}{D} RET_CONST(new AddrVal(yytext))
}
"["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]" { ({D}"."){3}{D}{OWS}"/"{OWS}{D} {
string s(yytext+1); int len = 0;
RET_CONST(new AddrVal(s.erase(s.size()-1))) string ip = extract_ip_and_len(yytext, &len);
RET_CONST(new SubNetVal(IPPrefix(IPAddr(ip), len)))
} }
[!%*/+\-,:;<=>?()\[\]{}~$|] return yytext[0]; [!%*/+\-,:;<=>?()\[\]{}~$|] return yytext[0];
@ -484,8 +487,6 @@ F RET_CONST(new Val(false, TYPE_BOOL))
{FLOAT}{OWS}msec(s?) RET_CONST(new IntervalVal(atof(yytext),Milliseconds)) {FLOAT}{OWS}msec(s?) RET_CONST(new IntervalVal(atof(yytext),Milliseconds))
{FLOAT}{OWS}usec(s?) RET_CONST(new IntervalVal(atof(yytext),Microseconds)) {FLOAT}{OWS}usec(s?) RET_CONST(new IntervalVal(atof(yytext),Microseconds))
({D}"."){3}{D} RET_CONST(new AddrVal(yytext))
"0x"{HEX}+ RET_CONST(new Val(static_cast<bro_uint_t>(strtoull(yytext, 0, 16)), TYPE_COUNT)) "0x"{HEX}+ RET_CONST(new Val(static_cast<bro_uint_t>(strtoull(yytext, 0, 16)), TYPE_COUNT))
{H}("."{H})+ RET_CONST(dns_mgr->LookupHost(yytext)) {H}("."{H})+ RET_CONST(dns_mgr->LookupHost(yytext))

View file

@ -43,6 +43,40 @@
#include "Net.h" #include "Net.h"
#include "Reporter.h" #include "Reporter.h"
/**
* Return IP address without enclosing brackets and any leading 0x.
*/
std::string extract_ip(const std::string& i)
{
std::string s(skip_whitespace(i.c_str()));
if ( s.size() > 0 && s[0] == '[' )
s.erase(0, 1);
if ( s.size() > 1 && s.substr(0, 2) == "0x" )
s.erase(0, 2);
size_t pos = 0;
if ( (pos = s.find(']')) != std::string::npos )
s = s.substr(0, pos);
return s;
}
/**
* Given a subnet string, return IP address and subnet length separately.
*/
std::string extract_ip_and_len(const std::string& i, int* len)
{
size_t pos = i.find('/');
if ( pos == std::string::npos )
return i;
if ( len )
*len = atoi(i.substr(pos + 1).c_str());
return extract_ip(i.substr(0, pos));
}
/** /**
* Takes a string, unescapes all characters that are escaped as hex codes * Takes a string, unescapes all characters that are escaped as hex codes
* (\x##) and turns them into the equivalent ascii-codes. Returns a string * (\x##) and turns them into the equivalent ascii-codes. Returns a string

View file

@ -91,6 +91,9 @@ void delete_each(T* t)
delete *it; delete *it;
} }
std::string extract_ip(const std::string& i);
std::string extract_ip_and_len(const std::string& i, int* len);
std::string get_unescaped_string(const std::string& str); std::string get_unescaped_string(const std::string& str);
std::string get_escaped_string(const std::string& str, bool escape_all); std::string get_escaped_string(const std::string& str, bool escape_all);

View file

@ -13,3 +13,5 @@ IPv6 address not case-sensitive (PASS)
size of IPv6 address (PASS) size of IPv6 address (PASS)
IPv6 address type inference (PASS) IPv6 address type inference (PASS)
IPv4 and IPv6 address inequality (PASS) IPv4 and IPv6 address inequality (PASS)
IPv4-mapped-IPv6 equality to IPv4 (PASS)
IPv4-mapped-IPv6 is IPv4 (PASS)

View file

@ -10,3 +10,11 @@ IPv6 subnet !in operator (PASS)
IPv6 subnet type inference (PASS) IPv6 subnet type inference (PASS)
IPv4 and IPv6 subnet inequality (PASS) IPv4 and IPv6 subnet inequality (PASS)
IPv4 address and IPv6 subnet (PASS) IPv4 address and IPv6 subnet (PASS)
IPv4 in IPv4-mapped-IPv6 subnet (PASS)
IPv6 !in IPv4-mapped-IPv6 subnet (PASS)
IPv4-mapped-IPv6 in IPv4-mapped-IPv6 subnet (PASS)
IPv4-mapped-IPv6 subnet equality (PASS)
subnet literal const whitespace (PASS)
subnet literal const whitespace (PASS)
subnet literal const whitespace (PASS)
subnet literal const whitespace (PASS)

View file

@ -1,4 +1,4 @@
# @TEST-EXEC: bro %INPUT >out # @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out # @TEST-EXEC: btest-diff out
function test_case(msg: string, expect: bool) function test_case(msg: string, expect: bool)
@ -43,5 +43,10 @@ event bro_init()
test_case( "IPv4 and IPv6 address inequality", a1 != b1 ); test_case( "IPv4 and IPv6 address inequality", a1 != b1 );
# IPv4-mapped-IPv6 (internally treated as IPv4)
local c1: addr = [::ffff:1.2.3.4];
test_case( "IPv4-mapped-IPv6 equality to IPv4", c1 == 1.2.3.4 );
test_case( "IPv4-mapped-IPv6 is IPv4", is_v4_addr(c1) == T );
} }

View file

@ -1,4 +1,4 @@
# @TEST-EXEC: bro %INPUT >out # @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out # @TEST-EXEC: btest-diff out
function test_case(msg: string, expect: bool) function test_case(msg: string, expect: bool)
@ -43,5 +43,22 @@ event bro_init()
test_case( "IPv4 and IPv6 subnet inequality", s1 != t1 ); test_case( "IPv4 and IPv6 subnet inequality", s1 != t1 );
test_case( "IPv4 address and IPv6 subnet", a1 !in t2 ); test_case( "IPv4 address and IPv6 subnet", a1 !in t2 );
# IPv4-mapped-IPv6 subnets
local u1: subnet = [::ffff:0:0]/96;
test_case( "IPv4 in IPv4-mapped-IPv6 subnet", 1.2.3.4 in u1 );
test_case( "IPv6 !in IPv4-mapped-IPv6 subnet", [fe80::1] !in u1 );
test_case( "IPv4-mapped-IPv6 in IPv4-mapped-IPv6 subnet",
[::ffff:1.2.3.4] in u1 );
test_case( "IPv4-mapped-IPv6 subnet equality",
[::ffff:1.2.3.4]/112 == 1.2.0.0/16 );
test_case( "subnet literal const whitespace",
[::ffff:1.2.3.4] / 112 == 1.2.0.0 / 16 );
test_case( "subnet literal const whitespace",
[::ffff:1.2.3.4]/ 128 == 1.2.3.4/ 32 );
test_case( "subnet literal const whitespace",
[::ffff:1.2.3.4] /96 == 1.2.3.4 /0 );
test_case( "subnet literal const whitespace",
[::ffff:1.2.3.4] / 92 == [::fffe:1.2.3.4] / 92 );
} }