]> 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 7100298b3de866f467b4e2ca3cbb567da32eec0c..b57407eb860250ad634a9a5922e94d9c18c4f255 100644 (file)
 #include <openssl/evp.h>
 #include <openssl/pem.h>
 
-#include <loc/libloc.h>
-#include <loc/as.h>
-#include <loc/as-list.h>
-#include <loc/compat.h>
-#include <loc/country.h>
-#include <loc/country-list.h>
-#include <loc/database.h>
-#include <loc/format.h>
-#include <loc/network.h>
-#include <loc/private.h>
-#include <loc/stringpool.h>
+#include <libloc/libloc.h>
+#include <libloc/address.h>
+#include <libloc/as.h>
+#include <libloc/as-list.h>
+#include <libloc/compat.h>
+#include <libloc/country.h>
+#include <libloc/country-list.h>
+#include <libloc/database.h>
+#include <libloc/format.h>
+#include <libloc/network.h>
+#include <libloc/network-list.h>
+#include <libloc/private.h>
+#include <libloc/stringpool.h>
 
 struct loc_database {
        struct loc_ctx* ctx;
@@ -121,8 +123,13 @@ struct loc_database_enumerator {
        int network_stack_depth;
        unsigned int* networks_visited;
 
-       // For subnet search
+       // For subnet search and bogons
        struct loc_network_list* stack;
+       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) {
@@ -446,6 +453,13 @@ static void loc_database_free(struct loc_database* db) {
                        ERROR(db->ctx, "Could not unmap network nodes section: %s\n", strerror(errno));
        }
 
+       // Remove mapped countries section
+       if (db->countries_v1) {
+               r = munmap(db->countries_v1, db->countries_count * sizeof(*db->countries_v1));
+               if (r)
+                       ERROR(db->ctx, "Could not unmap countries section: %s\n", strerror(errno));
+       }
+
        if (db->pool)
                loc_stringpool_unref(db->pool);
 
@@ -745,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;
 }
@@ -776,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);
@@ -796,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);
@@ -839,7 +848,7 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr*
 }
 
 LOC_EXPORT int loc_database_lookup(struct loc_database* db,
-               struct in6_addr* address, struct loc_network** network) {
+               const struct in6_addr* address, struct loc_network** network) {
        struct in6_addr network_address;
        memset(&network_address, 0, sizeof(network_address));
 
@@ -868,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;
 
@@ -972,10 +981,13 @@ static void loc_database_enumerator_free(struct loc_database_enumerator* enumera
        // Free network search
        free(enumerator->networks_visited);
 
-       // Free subnet stack
+       // Free subnet/bogons stack
        if (enumerator->stack)
                loc_network_list_unref(enumerator->stack);
 
+       if (enumerator->subnets)
+               loc_network_list_unref(enumerator->subnets);
+
        free(enumerator);
 }
 
@@ -1005,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;
@@ -1148,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_match_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;
 }
 
@@ -1196,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",
@@ -1224,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);
@@ -1261,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;
                }
        }
 
@@ -1293,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) {
@@ -1306,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;
                }
@@ -1317,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;
                        }
@@ -1333,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;
                }
@@ -1342,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;
@@ -1371,33 +1385,177 @@ 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);
 
-       // Replace network with the first one from the stack
+       // Drop the network and restart the whole process again to pick the next network
        loc_network_unref(*network);
-       *network = loc_network_list_pop_first(enumerator->stack);
+
+       return __loc_database_enumerator_next_network_flattened(enumerator, network);
+}
+
+/*
+       This function finds all bogons (i.e. gaps) between the input networks
+*/
+static int __loc_database_enumerator_next_bogon(
+               struct loc_database_enumerator* enumerator, struct loc_network** bogon) {
+       int r;
+
+       // Return top element from the stack
+       while (1) {
+               *bogon = loc_network_list_pop_first(enumerator->stack);
+
+               // Stack is empty
+               if (!*bogon)
+                       break;
+
+               // Return result
+               return 0;
+       }
+
+       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)
+                       return r;
+
+               // We have read the last network
+               if (!network)
+                       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;
+
+                       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);
+                               return r;
+                       }
+               }
+
+               // The gap now starts after this network
+               *gap_start = *last_address;
+               loc_address_increment(gap_start);
+
+               loc_network_unref(network);
+
+               // Try to return something
+               *bogon = loc_network_list_pop_first(enumerator->stack);
+               if (*bogon)
+                       break;
+       }
+
+       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(
                struct loc_database_enumerator* enumerator, struct loc_network** network) {
-       // Do not do anything if not in network mode
-       if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
-               return 0;
+       switch (enumerator->mode) {
+               case LOC_DB_ENUMERATE_NETWORKS:
+                       // Flatten output?
+                       if (enumerator->flatten)
+                               return __loc_database_enumerator_next_network_flattened(enumerator, network);
 
-       // Flatten output?
-       if (enumerator->flatten)
-               return __loc_database_enumerator_next_network_flattened(enumerator, network);
+                       return __loc_database_enumerator_next_network(enumerator, network, 1);
+
+               case LOC_DB_ENUMERATE_BOGONS:
+                       return __loc_database_enumerator_next_bogon(enumerator, network);
 
-       return __loc_database_enumerator_next_network(enumerator, network, 1);
+               default:
+                       return 0;
+       }
 }
 
 LOC_EXPORT int loc_database_enumerator_next_country(