From: Tobias Brunner Date: Fri, 23 Feb 2024 15:19:19 +0000 (+0100) Subject: identification: Add support to match subnets/ranges against each other X-Git-Tag: 5.9.14rc1~5^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea6a6344d37db564d925bbbf6f669a5eef5118d0;p=thirdparty%2Fstrongswan.git identification: Add support to match subnets/ranges against each other Previously, it was only possible to match addresses against subnets and ranges, but not the other way around or subnets and ranges against each other. --- diff --git a/src/libstrongswan/tests/suites/test_identification.c b/src/libstrongswan/tests/suites/test_identification.c index eac6b55a5f..db40437692 100644 --- a/src/libstrongswan/tests/suites/test_identification.c +++ b/src/libstrongswan/tests/suites/test_identification.c @@ -786,7 +786,7 @@ START_TEST(test_matches_binary) } END_TEST -START_TEST(test_matches_range) +START_TEST(test_matches_range_addr) { identification_t *a, *b; @@ -869,6 +869,242 @@ START_TEST(test_matches_range) } END_TEST +START_TEST(test_matches_range_subnet) +{ + identification_t *a, *b; + + /* IPv4 subnets */ + a = identification_create_from_string("192.168.1.0/24"); + ck_assert(a->get_type(a) == ID_IPV4_ADDR_SUBNET); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/32", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.0.0/16", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.255", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.0.0-192.168.255.255", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv4 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, + chunk_from_chars(0xc0,0xa8,0x01,0x28,0xc0,0xa8,0x01,0x00)); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + a->destroy(a); + + a = identification_create_from_string("192.168.1.1/32"); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.1/31", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.1", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.2", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.0.0-192.168.1.255", ID_MATCH_ONE_WILDCARD)); + a->destroy(a); + + /* IPv6 subnets */ + a = identification_create_from_string("fec0::0/64"); + ck_assert(a->get_type(a) == ID_IPV6_ADDR_SUBNET); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/64", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::/48", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::-fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::-fec0::f:ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::4001-fec0::4ffe", ID_MATCH_NONE)); + ck_assert(id_matches(a, "feb0::1-fec0::0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv6 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, + chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff, + 0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 )); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + a->destroy(a); + + a = identification_create_from_string("fec0::1/128"); + ck_assert(a->get_type(a) == ID_IPV6_ADDR_SUBNET); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/64", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::0-fec0::0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1-fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::0-fec0::1", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::1-fec0::2", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD)); + a->destroy(a); + + /* Malformed IPv4 subnet encoding */ + a = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "0.0.0.0-255.255.255.255", ID_MATCH_NONE)); + a->destroy(a); + + /* Malformed IPv6 subnet encoding */ + a = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "::/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", ID_MATCH_NONE)); + a->destroy(a); +} +END_TEST + +START_TEST(test_matches_range_range) +{ + identification_t *a, *b; + + /* IPv4 ranges */ + a = identification_create_from_string("192.168.1.0-192.168.1.255"); + ck_assert(a->get_type(a) == ID_IPV4_ADDR_RANGE); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.0.0/16", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.255", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.0.0-192.168.255.255", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.0.240-192.168.1.0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv4 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, + chunk_from_chars(0xc0,0xa8,0x01,0x28,0xc0,0xa8,0x01,0x00)); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + a->destroy(a); + + a = identification_create_from_string("192.168.1.1-192.168.1.1"); + ck_assert(a->get_type(a) == ID_IPV4_ADDR_RANGE); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0/32", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "192.168.1.0-192.168.1.1", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.1.1-192.168.1.2", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "192.168.0.0-192.168.1.255", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + a->destroy(a); + + /* IPv6 ranges */ + a = identification_create_from_string("fec0::-fec0::ffff:ffff:ffff:ffff"); + ck_assert(a->get_type(a) == ID_IPV6_ADDR_RANGE); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/64", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::/48", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::-fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::-fec0::f:ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::4001-fec0::4ffe", ID_MATCH_NONE)); + ck_assert(id_matches(a, "feb0::1-fec0::0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE)); + + /* Malformed IPv6 subnet and range encoding */ + b = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, + chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff, + 0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 )); + ck_assert(a->matches(a, b) == ID_MATCH_NONE); + b->destroy(b); + a->destroy(a); + + a = identification_create_from_string("fec0::1-fec0::1"); + ck_assert(a->get_type(a) == ID_IPV6_ADDR_RANGE); + ck_assert(id_matches(a, "%any", ID_MATCH_ANY)); + ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS)); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::/64", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::0-fec0::0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "fec0::1-fec0::1", ID_MATCH_PERFECT)); + ck_assert(id_matches(a, "fec0::0-fec0::1", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::1-fec0::2", ID_MATCH_ONE_WILDCARD)); + ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD)); + a->destroy(a); + + /* Malformed IPv4 range encoding */ + a = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty); + ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "0.0.0.0-255.255.255.255", ID_MATCH_NONE)); + a->destroy(a); + + /* Malformed IPv6 range encoding */ + a = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty); + ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE)); + ck_assert(id_matches(a, "::/0", ID_MATCH_NONE)); + ck_assert(id_matches(a, "::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", ID_MATCH_NONE)); + a->destroy(a); +} +END_TEST + START_TEST(test_matches_string) { identification_t *a; @@ -1204,7 +1440,9 @@ Suite *identification_suite_create() tcase_add_loop_test(tc, test_matches_two_ou, 0, countof(rdn_matching)); tcase_add_test(tc, test_matches_any); tcase_add_test(tc, test_matches_binary); - tcase_add_test(tc, test_matches_range); + tcase_add_test(tc, test_matches_range_addr); + tcase_add_test(tc, test_matches_range_subnet); + tcase_add_test(tc, test_matches_range_range); tcase_add_test(tc, test_matches_string); tcase_add_loop_test(tc, test_matches_empty, ID_ANY, ID_KEY_ID + 1); tcase_add_loop_test(tc, test_matches_empty_reverse, ID_ANY, ID_KEY_ID + 1); diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index 9a8f3200ef..d31955b380 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -1046,10 +1046,9 @@ METHOD(identification_t, matches_dn_relaxed, id_match_t, /** * Transform netmask to CIDR bits */ -static int netmask_to_cidr(char *netmask, size_t address_size) +static uint8_t netmask_to_cidr(char *netmask, uint8_t address_size) { - uint8_t byte; - int i, netbits = 0; + uint8_t i, byte, netbits = 0; for (i = 0; i < address_size; i++) { @@ -1075,118 +1074,369 @@ static int netmask_to_cidr(char *netmask, size_t address_size) return netbits; } +/** + * Converts the given network/netmask to an address range + */ +static void subnet_to_range(chunk_t encoding, uint8_t address_size, + uint8_t *from, uint8_t *to) +{ + uint8_t *network, *netmask, netbits, mask, i; + + network = encoding.ptr; + netmask = encoding.ptr + address_size; + netbits = netmask_to_cidr(netmask, address_size); + + memcpy(from, network, address_size); + memcpy(to, network, address_size); + + i = netbits / 8; + if (i < address_size) + { + mask = 0xff << (8 - netbits % 8); + from[i] = from[i] & mask; + to[i] = to[i] | ~mask; + memset(&from[i+1], 0, address_size - i - 1); + memset(&to[i+1], 0xff, address_size - i - 1); + } +} + +/** + * Matches one subnet (or address if netmask is NULL) against another + */ +static id_match_t match_subnets(uint8_t *network, uint8_t *netmask, + uint8_t *other_network, uint8_t *other_netmask, + uint8_t address_size) +{ + uint8_t netbits, other_netbits, i; + + other_netbits = other_netmask ? netmask_to_cidr(other_netmask, address_size) + : 8 * address_size; + if (!other_netbits) + { + return ID_MATCH_MAX_WILDCARDS; + } + + netbits = netmask ? netmask_to_cidr(netmask, address_size) : 8 * address_size; + + if (netbits == other_netbits) + { + return memeq(network, other_network, address_size) ? ID_MATCH_PERFECT + : ID_MATCH_NONE; + } + else if (netbits < other_netbits) + { + return ID_MATCH_NONE; + } + + for (i = 0; i < (other_netbits + 7)/8; i++) + { + if ((network[i] ^ other_network[i]) & other_netmask[i]) + { + return ID_MATCH_NONE; + } + } + return ID_MATCH_ONE_WILDCARD; +} + +/** + * Matches two address ranges against each other + */ +static id_match_t match_ranges(uint8_t *from, uint8_t *to, + uint8_t *other_from, uint8_t *other_to, + uint8_t address_size) +{ + const uint8_t zeroes[16] = { 0 }; + const uint8_t ones[16] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }; + int match_from, match_to; + + if (memcmp(to, from, address_size) < 0 || + memcmp(other_to, other_from, address_size) < 0) + { + /* to is smaller than from in one of the ranges */ + return ID_MATCH_NONE; + } + else if (memeq(other_from, zeroes, address_size) && + memeq(other_to, ones, address_size)) + { + return ID_MATCH_MAX_WILDCARDS; + } + + match_from = memcmp(from, other_from, address_size); + match_to = memcmp(to, other_to, address_size); + + if (!match_from && !match_to) + { + return ID_MATCH_PERFECT; + } + else if (match_from >= 0 && match_to <= 0) + { + return ID_MATCH_ONE_WILDCARD; + } + return ID_MATCH_NONE; +} + +/** + * Match a subnet to an address + */ +static id_match_t matches_subnet_to_addr(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *network, *netmask, *address; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != address_size) + { + return ID_MATCH_NONE; + } + network = this->encoded.ptr; + netmask = this->encoded.ptr + address_size; + address = other_encoding.ptr; + return match_subnets(network, netmask, address, NULL, address_size); +} + +/** + * Match a subnet to an address + */ +static id_match_t matches_range_to_addr(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *from, *to, *address; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != address_size) + { + return ID_MATCH_NONE; + } + from = this->encoded.ptr; + to = this->encoded.ptr + address_size; + address = other_encoding.ptr; + return match_ranges(from, to, address, address, address_size); +} + +/** + * Matches an address to a subnet + */ +static id_match_t matches_addr_to_subnet(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *address, *network, *netmask; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + address = this->encoded.ptr; + network = other_encoding.ptr; + netmask = other_encoding.ptr + address_size; + return match_subnets(address, NULL, network, netmask, address_size); +} + +/** + * Matches a subnet to a subnet + */ +static id_match_t matches_subnet_to_subnet(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *network, *netmask, *other_network, *other_netmask; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + network = this->encoded.ptr; + netmask = this->encoded.ptr + address_size; + other_network = other_encoding.ptr; + other_netmask = other_encoding.ptr + address_size; + return match_subnets(network, netmask, other_network, other_netmask, + address_size); +} + +/** + * Matches a range to a subnet + */ +static id_match_t matches_range_to_subnet(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *from, *to, other_from[address_size], other_to[address_size]; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + + from = this->encoded.ptr; + to = this->encoded.ptr + address_size; + subnet_to_range(other_encoding, address_size, other_from, other_to); + return match_ranges(from, to, other_from, other_to, address_size); +} + +/** + * Match an address to a range + */ +static id_match_t matches_addr_to_range(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *address, *from, *to; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + address = this->encoded.ptr; + from = other_encoding.ptr; + to = other_encoding.ptr + address_size; + return match_ranges(address, address, from, to, address_size); +} + +/** + * Match a subnet to a range + */ +static id_match_t matches_subnet_to_range(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t from[address_size], to[address_size], *other_from, *other_to; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + subnet_to_range(this->encoded, address_size, from, to); + other_from = other_encoding.ptr; + other_to = other_encoding.ptr + address_size; + return match_ranges(from, to, other_from, other_to, address_size); +} + +/** + * Match a range to a range + */ +static id_match_t matches_range_to_range(private_identification_t *this, + identification_t *other, + uint8_t address_size) +{ + chunk_t other_encoding; + uint8_t *from, *to, *other_from, *other_to; + + other_encoding = other->get_encoding(other); + if (this->encoded.len != 2 * address_size || + other_encoding.len != 2 * address_size) + { + return ID_MATCH_NONE; + } + from = this->encoded.ptr; + to = this->encoded.ptr + address_size; + other_from = other_encoding.ptr; + other_to = other_encoding.ptr + address_size; + return match_ranges(from, to, other_from, other_to, address_size); +} + METHOD(identification_t, matches_range, id_match_t, private_identification_t *this, identification_t *other) { - chunk_t other_encoding; - uint8_t *address, *from, *to, *network, *netmask; - size_t address_size = 0; - int netbits, range_sign, i; + id_type_t other_type; + uint8_t address_size = 0; - if (other->get_type(other) == ID_ANY) + other_type = other->get_type(other); + if (other_type == ID_ANY) { return ID_MATCH_ANY; } - if (this->type == other->get_type(other) && + if (this->type == other_type && chunk_equals(this->encoded, other->get_encoding(other))) { return ID_MATCH_PERFECT; } - if ((this->type == ID_IPV4_ADDR && - other->get_type(other) == ID_IPV4_ADDR_SUBNET)) + if (other_type == ID_IPV4_ADDR && + (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV4_ADDR_RANGE)) { address_size = sizeof(struct in_addr); } - else if ((this->type == ID_IPV6_ADDR && - other->get_type(other) == ID_IPV6_ADDR_SUBNET)) + else if (other_type == ID_IPV6_ADDR && + (this->type == ID_IPV6_ADDR_SUBNET || this->type == ID_IPV6_ADDR_RANGE)) { address_size = sizeof(struct in6_addr); } if (address_size) { - other_encoding = other->get_encoding(other); - if (this->encoded.len != address_size || - other_encoding.len != 2 * address_size) + if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET) { - return ID_MATCH_NONE; + return matches_subnet_to_addr(this, other, address_size); } - address = this->encoded.ptr; - network = other_encoding.ptr; - netmask = other_encoding.ptr + address_size; - netbits = netmask_to_cidr(netmask, address_size); - - if (netbits == 0) - { - return ID_MATCH_MAX_WILDCARDS; - } - if (netbits == 8 * address_size) + return matches_range_to_addr(this, other, address_size); + } + if (other_type == ID_IPV4_ADDR_SUBNET && + (this->type == ID_IPV4_ADDR || this->type == ID_IPV4_ADDR_SUBNET || + this->type == ID_IPV4_ADDR_RANGE)) + { + address_size = sizeof(struct in_addr); + } + else if (other_type == ID_IPV6_ADDR_SUBNET && + (this->type == ID_IPV6_ADDR || this->type == ID_IPV6_ADDR_SUBNET || + this->type == ID_IPV6_ADDR_RANGE)) + { + address_size = sizeof(struct in6_addr); + } + if (address_size) + { + if (this->type == ID_IPV4_ADDR || this->type == ID_IPV6_ADDR) { - return memeq(address, network, address_size) ? - ID_MATCH_PERFECT : ID_MATCH_NONE; + return matches_addr_to_subnet(this, other, address_size); } - for (i = 0; i < (netbits + 7)/8; i++) + else if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET) { - if ((address[i] ^ network[i]) & netmask[i]) - { - return ID_MATCH_NONE; - } + return matches_subnet_to_subnet(this, other, address_size); } - return ID_MATCH_ONE_WILDCARD; + return matches_range_to_subnet(this, other, address_size); } - if ((this->type == ID_IPV4_ADDR && - other->get_type(other) == ID_IPV4_ADDR_RANGE)) + if (other_type == ID_IPV4_ADDR_RANGE && + (this->type == ID_IPV4_ADDR || this->type == ID_IPV4_ADDR_SUBNET || + this->type == ID_IPV4_ADDR_RANGE)) { address_size = sizeof(struct in_addr); } - else if ((this->type == ID_IPV6_ADDR && - other->get_type(other) == ID_IPV6_ADDR_RANGE)) + else if (other_type == ID_IPV6_ADDR_RANGE && + (this->type == ID_IPV6_ADDR || this->type == ID_IPV6_ADDR_SUBNET || + this->type == ID_IPV6_ADDR_RANGE)) { address_size = sizeof(struct in6_addr); } if (address_size) { - other_encoding = other->get_encoding(other); - if (this->encoded.len != address_size || - other_encoding.len != 2 * address_size) + if (this->type == ID_IPV4_ADDR || this->type == ID_IPV6_ADDR) { - return ID_MATCH_NONE; + return matches_addr_to_range(this, other, address_size); } - address = this->encoded.ptr; - from = other_encoding.ptr; - to = other_encoding.ptr + address_size; - - range_sign = memcmp(to, from, address_size); - if (range_sign < 0) - { /* to is smaller than from */ - return ID_MATCH_NONE; - } - - /* check lower bound */ - for (i = 0; i < address_size; i++) + else if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET) { - if (address[i] != from[i]) - { - if (address[i] < from[i]) - { - return ID_MATCH_NONE; - } - break; - } + return matches_subnet_to_range(this, other, address_size); } - - /* check upper bound */ - for (i = 0; i < address_size; i++) - { - if (address[i] != to[i]) - { - if (address[i] > to[i]) - { - return ID_MATCH_NONE; - } - break; - } - } - return range_sign ? ID_MATCH_ONE_WILDCARD : ID_MATCH_PERFECT; + return matches_range_to_range(this, other, address_size); } return ID_MATCH_NONE; } @@ -1200,7 +1450,8 @@ int identification_printf_hook(printf_hook_data_t *data, private_identification_t *this = *((private_identification_t**)(args[0])); chunk_t proper; char buf[BUF_LEN], *pos; - size_t len, address_size; + uint8_t address_size; + size_t len; int written; if (this == NULL) @@ -1402,6 +1653,10 @@ static private_identification_t *identification_create(id_type_t type) break; case ID_IPV4_ADDR: case ID_IPV6_ADDR: + case ID_IPV4_ADDR_SUBNET: + case ID_IPV6_ADDR_SUBNET: + case ID_IPV4_ADDR_RANGE: + case ID_IPV6_ADDR_RANGE: this->public.hash = _hash_binary; this->public.equals = _equals_binary; this->public.matches = _matches_range;