]> git.ipfire.org Git - location/libloc.git/blobdiff - src/database.c
Make sources around that we can run tests without location installed
[location/libloc.git] / src / database.c
index d37e70fb3bac6eb3a2f4cc9a2012c43b1db7cd2a..b57407eb860250ad634a9a5922e94d9c18c4f255 100644 (file)
@@ -37,6 +37,7 @@
 #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>
@@ -124,7 +125,11 @@ struct loc_database_enumerator {
 
        // 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) {
@@ -754,13 +759,8 @@ static int loc_database_fetch_network(struct loc_database* db, struct loc_networ
                        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;
 }
@@ -785,7 +785,7 @@ static int __loc_database_lookup_handle_leaf(struct loc_database* db, const stru
        }
 
        // 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);
@@ -805,8 +805,8 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr*
        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);
@@ -877,7 +877,7 @@ LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
                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;
 
@@ -985,8 +985,8 @@ static void loc_database_enumerator_free(struct loc_database_enumerator* enumera
        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);
 }
@@ -1017,6 +1017,10 @@ LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enum
                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;
@@ -1160,41 +1164,45 @@ static int loc_database_enumerator_stack_push_node(
        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;
 }
 
@@ -1208,15 +1216,13 @@ static int __loc_database_enumerator_next_network(
                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",
@@ -1236,7 +1242,7 @@ static int __loc_database_enumerator_next_network(
                }
 
                // 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);
@@ -1273,19 +1279,13 @@ static int __loc_database_enumerator_next_network(
                        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;
                }
        }
 
@@ -1305,12 +1305,13 @@ static int __loc_database_enumerator_next_network_flattened(
                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) {
@@ -1318,7 +1319,7 @@ static int __loc_database_enumerator_next_network_flattened(
                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;
                }
@@ -1329,10 +1330,10 @@ static int __loc_database_enumerator_next_network_flattened(
 
                // 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;
                        }
@@ -1345,7 +1346,7 @@ static int __loc_database_enumerator_next_network_flattened(
                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;
                }
@@ -1354,27 +1355,28 @@ static int __loc_database_enumerator_next_network_flattened(
                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;
@@ -1383,13 +1385,13 @@ static int __loc_database_enumerator_next_network_flattened(
        // 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
@@ -1401,23 +1403,6 @@ static int __loc_database_enumerator_next_network_flattened(
 /*
        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;
@@ -1435,26 +1420,76 @@ static int __loc_database_enumerator_next_bogon(
        }
 
        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
@@ -1463,8 +1498,46 @@ static int __loc_database_enumerator_next_bogon(
                        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(