]> 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 e5f779d72c2fb1982fe0a4d76cba345379e5d3a4..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;
 }
@@ -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;
@@ -1238,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);
@@ -1301,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) {
@@ -1314,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;
                }
@@ -1325,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;
                        }
@@ -1341,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;
                }
@@ -1350,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;
@@ -1379,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
@@ -1397,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;
@@ -1431,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;
+
+               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;
 
-               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);
+                       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
@@ -1459,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(