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
* 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);
}
IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length)
IPPrefix::IPPrefix(const IPAddr& addr, uint8_t length, bool len_is_v6_relative)
: prefix(addr)
{
if ( prefix.GetFamily() == IPv4 )
if ( prefix.GetFamily() == IPv4 && ! len_is_v6_relative )
{
if ( length > 32 )
reporter->InternalError("Bad IPAddr(v4) IPPrefix length : %d",

View file

@ -496,8 +496,15 @@ public:
* @param addr The IP address.
*
* @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.

View file

@ -11,16 +11,6 @@
#include "rule-parse.h"
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
@ -49,15 +39,14 @@ PID ([0-9a-zA-Z_-]|"::")+
}
{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;
}
{IP6}{OWS}"/"{OWS}{D} {
char* l = strchr(yytext, '/');
*l++ = '\0';
int len = atoi(l);
rules_lval.prefixval = new IPPrefix(IPAddr(extract_ipv6(yytext)), len);
int len = 0;
string ip = extract_ip_and_len(yytext, &len);
rules_lval.prefixval = new IPPrefix(IPAddr(ip), len, true);
return TOK_IP6;
}

View file

@ -148,6 +148,7 @@ D [0-9]+
HEX [0-9a-fA-F]+
IDCOMPONENT [A-Za-z_][A-Za-z_0-9]*
ID {IDCOMPONENT}(::{IDCOMPONENT})*
IP6 ("["({HEX}:){7}{HEX}"]")|("["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]")
FILE [^ \t\n]+
PREFIX [^ \t\n]+
FLOAT (({D}*"."?{D})|({D}"."?{D}*))([eE][-+]?{D})?
@ -229,21 +230,23 @@ ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+))
}
/* IPv6 literal constant patterns */
"["({HEX}:){7}{HEX}"]" {
string s(yytext+1);
RET_CONST(new AddrVal(s.erase(s.size()-1)))
{IP6} {
RET_CONST(new AddrVal(extract_ip(yytext)))
}
"["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]" {
string s(yytext+3);
RET_CONST(new AddrVal(s.erase(s.size()-1)))
{IP6}{OWS}"/"{OWS}{D} {
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);
RET_CONST(new AddrVal(s.erase(s.size()-1)))
}
"["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]" {
string s(yytext+1);
RET_CONST(new AddrVal(s.erase(s.size()-1)))
/* IPv4 literal constant patterns */
({D}"."){3}{D} RET_CONST(new AddrVal(yytext))
({D}"."){3}{D}{OWS}"/"{OWS}{D} {
int len = 0;
string ip = extract_ip_and_len(yytext, &len);
RET_CONST(new SubNetVal(IPPrefix(IPAddr(ip), len)))
}
[!%*/+\-,:;<=>?()\[\]{}~$|] 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}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))
{H}("."{H})+ RET_CONST(dns_mgr->LookupHost(yytext))

View file

@ -43,6 +43,40 @@
#include "Net.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
* (\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;
}
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_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)
IPv6 address type inference (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)
IPv4 and IPv6 subnet inequality (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
function test_case(msg: string, expect: bool)
@ -43,5 +43,10 @@ event bro_init()
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
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 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 );
}