#include <openssl/pem.h>
#include <libloc/libloc.h>
+#include <libloc/address.h>
#include <libloc/as.h>
#include <libloc/as-list.h>
#include <libloc/compat.h>
// For subnet search and bogons
struct loc_network_list* stack;
- struct loc_network* last_network;
+ struct loc_network_list* subnets;
+
+ // For bogons
+ struct in6_addr gap6_start;
+ struct in6_addr gap4_start;
};
static int loc_database_read_magic(struct loc_database* db) {
return -1;
}
-#ifdef ENABLE_DEBUG
- if (r == 0) {
- char* string = loc_network_str(*network);
- DEBUG(db->ctx, "Got network %s\n", string);
- free(string);
- }
-#endif
+ if (r == 0)
+ DEBUG(db->ctx, "Got network %s\n", loc_network_str(*network));
return r;
}
}
// Check if the given IP address is inside the network
- if (!loc_network_match_address(*network, address)) {
+ if (!loc_network_matches_address(*network, address)) {
DEBUG(db->ctx, "Searched address is not part of the network\n");
loc_network_unref(*network);
off_t node_index;
// Follow the path
- int bit = in6_addr_get_bit(address, level);
- in6_addr_set_bit(network_address, level, bit);
+ int bit = loc_address_get_bit(address, level);
+ loc_address_set_bit(network_address, level, bit);
if (bit == 0)
node_index = be32toh(node->zero);
const char* string, struct loc_network** network) {
struct in6_addr address;
- int r = loc_parse_address(db->ctx, string, &address);
+ int r = loc_address_parse(&address, NULL, string);
if (r)
return r;
if (enumerator->stack)
loc_network_list_unref(enumerator->stack);
- if (enumerator->last_network)
- loc_network_unref(enumerator->last_network);
+ if (enumerator->subnets)
+ loc_network_list_unref(enumerator->subnets);
free(enumerator);
}
return r;
}
+ // Initialize bogon search
+ loc_address_reset(&e->gap6_start, AF_INET6);
+ loc_address_reset(&e->gap4_start, AF_INET);
+
DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
*enumerator = e;
return 0;
}
-static int loc_database_enumerator_filter_network(
+static int loc_database_enumerator_match_network(
struct loc_database_enumerator* enumerator, struct loc_network* network) {
- // Skip if the family does not match
+ // If family is set, it must match
if (enumerator->family && loc_network_address_family(network) != enumerator->family) {
DEBUG(enumerator->ctx, "Filtered network %p because of family not matching\n", network);
- return 1;
+ return 0;
}
- // Skip if the country code does not match
+ // Match if no filter criteria is configured
+ if (!enumerator->countries && !enumerator->asns && !enumerator->flags)
+ return 1;
+
+ // Check if the country code matches
if (enumerator->countries && !loc_country_list_empty(enumerator->countries)) {
const char* country_code = loc_network_get_country_code(network);
- if (!loc_country_list_contains_code(enumerator->countries, country_code)) {
- DEBUG(enumerator->ctx, "Filtered network %p because of country code not matching\n", network);
+ if (loc_country_list_contains_code(enumerator->countries, country_code)) {
+ DEBUG(enumerator->ctx, "Matched network %p because of its country code\n", network);
return 1;
}
}
- // Skip if the ASN does not match
+ // Check if the ASN matches
if (enumerator->asns && !loc_as_list_empty(enumerator->asns)) {
uint32_t asn = loc_network_get_asn(network);
- if (!loc_as_list_contains_number(enumerator->asns, asn)) {
- DEBUG(enumerator->ctx, "Filtered network %p because of ASN not matching\n", network);
+ if (loc_as_list_contains_number(enumerator->asns, asn)) {
+ DEBUG(enumerator->ctx, "Matched network %p because of its ASN\n", network);
return 1;
}
}
- // Skip if flags do not match
- if (enumerator->flags && !loc_network_has_flag(network, enumerator->flags)) {
- DEBUG(enumerator->ctx, "Filtered network %p because of flags not matching\n", network);
+ // Check if flags match
+ if (enumerator->flags && loc_network_has_flag(network, enumerator->flags)) {
+ DEBUG(enumerator->ctx, "Matched network %p because of its flags\n", network);
return 1;
}
- // Do not filter
+ // Not a match
return 0;
}
if (!*network)
break;
- // Throw away any networks by filter
- if (filter && loc_database_enumerator_filter_network(enumerator, *network)) {
- loc_network_unref(*network);
- *network = NULL;
- continue;
- }
+ // Return everything if filter isn't enabled, or only return matches
+ if (!filter || loc_database_enumerator_match_network(enumerator, *network))
+ return 0;
- // Return result
- return 0;
+ // Throw away anything that doesn't match
+ loc_network_unref(*network);
+ *network = NULL;
}
DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
}
// Mark the bits on the path correctly
- in6_addr_set_bit(&enumerator->network_address,
+ loc_address_set_bit(&enumerator->network_address,
(node->depth > 0) ? node->depth - 1 : 0, node->i);
DEBUG(enumerator->ctx, "Looking at node %jd\n", (intmax_t)node->offset);
if (r)
return r;
- // Return all networks when the filter is disabled
- if (!filter)
+ // Return all networks when the filter is disabled, or check for match
+ if (!filter || loc_database_enumerator_match_network(enumerator, *network))
return 0;
- // Check if we are interested in this network
- if (loc_database_enumerator_filter_network(enumerator, *network)) {
- loc_network_unref(*network);
- *network = NULL;
-
- continue;
- }
-
- return 0;
+ // Does not seem to be a match, so we cleanup and move on
+ loc_network_unref(*network);
+ *network = NULL;
}
}
return 0;
struct loc_network* subnet = NULL;
- struct loc_network_list* subnets;
// Create a list with all subnets
- r = loc_network_list_new(enumerator->ctx, &subnets);
- if (r)
- return r;
+ if (!enumerator->subnets) {
+ r = loc_network_list_new(enumerator->ctx, &enumerator->subnets);
+ if (r)
+ return r;
+ }
// Search all subnets from the database
while (1) {
r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
if (r) {
loc_network_unref(subnet);
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
return r;
}
// Collect all subnets in a list
if (loc_network_is_subnet(*network, subnet)) {
- r = loc_network_list_push(subnets, subnet);
+ r = loc_network_list_push(enumerator->subnets, subnet);
if (r) {
loc_network_unref(subnet);
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
return r;
}
r = loc_network_list_push(enumerator->stack, subnet);
if (r) {
loc_network_unref(subnet);
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
return r;
}
break;
}
- DEBUG(enumerator->ctx, "Found %zu subnet(s)\n", loc_network_list_size(subnets));
+ DEBUG(enumerator->ctx, "Found %zu subnet(s)\n",
+ loc_network_list_size(enumerator->subnets));
// We can abort here if the network has no subnets
- if (loc_network_list_empty(subnets)) {
- loc_network_list_unref(subnets);
+ if (loc_network_list_empty(enumerator->subnets)) {
+ loc_network_list_clear(enumerator->subnets);
return 0;
}
// If the network has any subnets, we will break it into smaller parts
// without the subnets.
- struct loc_network_list* excluded = loc_network_exclude_list(*network, subnets);
+ struct loc_network_list* excluded = loc_network_exclude_list(*network, enumerator->subnets);
if (!excluded) {
- loc_network_list_unref(subnets);
- return -1;
+ loc_network_list_clear(enumerator->subnets);
+ return 1;
}
// Merge subnets onto the stack
- r = loc_network_list_merge(enumerator->stack, subnets);
+ r = loc_network_list_merge(enumerator->stack, enumerator->subnets);
if (r) {
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
loc_network_list_unref(excluded);
return r;
// Push excluded list onto the stack
r = loc_network_list_merge(enumerator->stack, excluded);
if (r) {
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
loc_network_list_unref(excluded);
return r;
}
- loc_network_list_unref(subnets);
+ loc_network_list_clear(enumerator->subnets);
loc_network_list_unref(excluded);
// Drop the network and restart the whole process again to pick the next network
/*
This function finds all bogons (i.e. gaps) between the input networks
*/
-static int __loc_database_enumerator_find_bogons(struct loc_ctx* ctx,
- struct loc_network* first, struct loc_network* second, struct loc_network_list* bogons) {
- // We do not need to check if first < second because the graph is always ordered
-
- // The last address of the first network + 1 is the start of the gap
- struct in6_addr first_address = address_increment(loc_network_get_last_address(first));
-
- // The first address of the second network - 1 is the end of the gap
- struct in6_addr last_address = address_decrement(loc_network_get_first_address(second));
-
- // If first address is now greater than last address, there is no gap
- if (in6_addr_cmp(&first_address, &last_address) >= 0)
- return 0;
-
- return loc_network_list_summarize(ctx, &first_address, &last_address, &bogons);
-}
-
static int __loc_database_enumerator_next_bogon(
struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
int r;
}
struct loc_network* network = NULL;
+ struct in6_addr* gap_start = NULL;
+ struct in6_addr gap_end = IN6ADDR_ANY_INIT;
while (1) {
r = __loc_database_enumerator_next_network(enumerator, &network, 1);
if (r)
- goto ERROR;
+ return r;
+ // We have read the last network
if (!network)
- break;
+ goto FINISH;
- if (enumerator->last_network && loc_network_address_family(enumerator->last_network) == loc_network_address_family(network)) {
- r = __loc_database_enumerator_find_bogons(enumerator->ctx,
- enumerator->last_network, network, enumerator->stack);
+ const char* country_code = loc_network_get_country_code(network);
+
+ /*
+ Skip anything that does not have a country code
+
+ Even if a network is part of the routing table, and the database provides
+ an ASN, this does not mean that this is a legitimate announcement.
+ */
+ if (country_code && !*country_code) {
+ loc_network_unref(network);
+ continue;
+ }
+
+ // Determine the network family
+ int family = loc_network_address_family(network);
+
+ switch (family) {
+ case AF_INET6:
+ gap_start = &enumerator->gap6_start;
+ break;
+
+ case AF_INET:
+ gap_start = &enumerator->gap4_start;
+ break;
+
+ default:
+ ERROR(enumerator->ctx, "Unsupported network family %d\n", family);
+ errno = ENOTSUP;
+ return 1;
+ }
+
+ const struct in6_addr* first_address = loc_network_get_first_address(network);
+ const struct in6_addr* last_address = loc_network_get_last_address(network);
+
+ // Skip if this network is a subnet of a former one
+ if (loc_address_cmp(gap_start, last_address) >= 0) {
+ loc_network_unref(network);
+ continue;
+ }
+
+ // Search where the gap could end
+ gap_end = *first_address;
+ loc_address_decrement(&gap_end);
+
+ // There is a gap
+ if (loc_address_cmp(gap_start, &gap_end) <= 0) {
+ r = loc_network_list_summarize(enumerator->ctx,
+ gap_start, &gap_end, &enumerator->stack);
if (r) {
loc_network_unref(network);
- goto ERROR;
+ return r;
}
}
- // Remember network for next iteration
- enumerator->last_network = loc_network_ref(network);
+ // The gap now starts after this network
+ *gap_start = *last_address;
+ loc_address_increment(gap_start);
+
loc_network_unref(network);
// Try to return something
break;
}
-ERROR:
- return r;
+ return 0;
+
+FINISH:
+
+ if (!loc_address_all_zeroes(&enumerator->gap6_start)) {
+ r = loc_address_reset_last(&gap_end, AF_INET6);
+ if (r)
+ return r;
+
+ if (loc_address_cmp(&enumerator->gap6_start, &gap_end) <= 0) {
+ r = loc_network_list_summarize(enumerator->ctx,
+ &enumerator->gap6_start, &gap_end, &enumerator->stack);
+ if (r)
+ return r;
+ }
+
+ // Reset start
+ loc_address_reset(&enumerator->gap6_start, AF_INET6);
+ }
+
+ if (!loc_address_all_zeroes(&enumerator->gap4_start)) {
+ r = loc_address_reset_last(&gap_end, AF_INET);
+ if (r)
+ return r;
+
+ if (loc_address_cmp(&enumerator->gap4_start, &gap_end) <= 0) {
+ r = loc_network_list_summarize(enumerator->ctx,
+ &enumerator->gap4_start, &gap_end, &enumerator->stack);
+ if (r)
+ return r;
+ }
+
+ // Reset start
+ loc_address_reset(&enumerator->gap4_start, AF_INET);
+ }
+
+ // Try to return something
+ *bogon = loc_network_list_pop_first(enumerator->stack);
+
+ return 0;
}
LOC_EXPORT int loc_database_enumerator_next_network(