]> git.ipfire.org Git - people/ms/libloc.git/commitdiff
location: Implement listing bogons
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 2 Sep 2021 13:25:37 +0000 (13:25 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 2 Sep 2021 13:25:37 +0000 (13:25 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
man/location.txt
src/database.c
src/loc/database.h
src/loc/network-list.h
src/network-list.c
src/python/database.c
src/python/location.in

index b38f21cf1ab8d7e5f8ff7bd445fd155d7b2a82f5..3878cb813abf409dff8871b67fbdfa567b474fbc 100644 (file)
@@ -78,6 +78,11 @@ or countries.
        +
        See above for usage of the '--family' and '--format' parameters.
 
+'list-bogons [--family=[ipv6|ipv4]] [--format=FORMAT]'::
+       Lists all bogons (i.e. networks that are unknown to the database).
+       +
+       See above for usage of the '--family' and '--format' parameters.
+
 'lookup ADDRESS [ADDRESS...]'::
        This command returns the network the given IP address has been found in
        as well as its Autonomous System if that information is available.
index b92a803d5d3db9a8be49049ee803d7a06f6180e2..6cfefee6ec7eea26cc96f527ac4d7248b915e321 100644 (file)
@@ -45,6 +45,7 @@
 #include <loc/database.h>
 #include <loc/format.h>
 #include <loc/network.h>
+#include <loc/network-list.h>
 #include <loc/private.h>
 #include <loc/stringpool.h>
 
@@ -121,8 +122,9 @@ 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* last_network;
 };
 
 static int loc_database_read_magic(struct loc_database* db) {
@@ -979,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->last_network)
+               loc_network_unref(enumerator->last_network);
+
        free(enumerator);
 }
 
@@ -1393,17 +1398,91 @@ static int __loc_database_enumerator_next_network_flattened(
        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_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;
+
+       // 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;
+
+       while (1) {
+               r = __loc_database_enumerator_next_network(enumerator, &network, 1);
+               if (r)
+                       goto ERROR;
+
+               if (!network)
+                       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);
+                       if (r) {
+                               loc_network_unref(network);
+                               goto ERROR;
+                       }
+               }
+
+               // Remember network for next iteration
+               enumerator->last_network = loc_network_ref(network);
+               loc_network_unref(network);
+
+               // Try to return something
+               *bogon = loc_network_list_pop_first(enumerator->stack);
+               if (*bogon)
+                       break;
+       }
+
+ERROR:
+       return r;
+}
+
 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(
index 70801f0d66db5a34515e1fa8b5f42c1951b8013b..9d1f71a2ae456df98e2806b6f0a7a1610b03d7c5 100644 (file)
@@ -54,6 +54,7 @@ enum loc_database_enumerator_mode {
        LOC_DB_ENUMERATE_NETWORKS  = 1,
        LOC_DB_ENUMERATE_ASES      = 2,
        LOC_DB_ENUMERATE_COUNTRIES = 3,
+       LOC_DB_ENUMERATE_BOGONS    = 4,
 };
 
 enum loc_database_enumerator_flags {
index bee21c4bdbbc5b6bbd7d42f1540787335d06684f..308b262f883ff8ebcf8e0078fed21bbc5b4bb89a 100644 (file)
@@ -34,4 +34,13 @@ struct loc_network* loc_network_list_pop_first(struct loc_network_list* list);
 int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network);
 int loc_network_list_merge(struct loc_network_list* self, struct loc_network_list* other);
 
+#ifdef LIBLOC_PRIVATE
+
+#include <netinet/in.h>
+
+int loc_network_list_summarize(struct loc_ctx* ctx,
+       const struct in6_addr* first, const struct in6_addr* last, struct loc_network_list** list);
+
+#endif /* LOC_PRIVATE */
+
 #endif
index 698d3abce98bd3380205e1987881fcef91d0e6df..224b34508e3bcd865fb6acae9fad7fd9873d7bdb 100644 (file)
@@ -297,3 +297,70 @@ LOC_EXPORT int loc_network_list_merge(
 
        return 0;
 }
+
+int loc_network_list_summarize(struct loc_ctx* ctx,
+               const struct in6_addr* first, const struct in6_addr* last, struct loc_network_list** list) {
+       int r;
+
+       if (!list) {
+               errno = EINVAL;
+               return 1;
+       }
+
+       int family = loc_address_family(first);
+
+       // Families must match
+       if (family != loc_address_family(last)) {
+               ERROR(ctx, "Address families do not match\n");
+               errno = EINVAL;
+               return 1;
+       }
+
+       // Check if the last address is larger than the first address
+       if (in6_addr_cmp(first, last) >= 0) {
+               ERROR(ctx, "The first address must be smaller than the last address\n");
+               errno = EINVAL;
+               return 1;
+       }
+
+       struct loc_network* network = NULL;
+
+       struct in6_addr start = *first;
+       const struct in6_addr* end = NULL;
+
+       while (in6_addr_cmp(&start, last) <= 0) {
+               // Guess the prefix
+               int prefix = 128 - loc_address_count_trailing_zero_bits(&start);
+
+               while (1) {
+                       // Create a new network object
+                       r = loc_network_new(ctx, &network, &start, prefix);
+                       if (r)
+                               return r;
+
+                       // Is this network within bounds?
+                       end = loc_network_get_last_address(network);
+                       if (in6_addr_cmp(last, end) <= 0)
+                               break;
+
+                       // Drop network and decrease prefix
+                       loc_network_unref(network);
+                       prefix--;
+               }
+
+               // Push it on the list
+               r = loc_network_list_push(*list, network);
+               if (r) {
+                       loc_network_unref(network);
+                       return r;
+               }
+
+               // Reset addr to the next start address
+               start = address_increment(end);
+
+               // Cleanup
+               loc_network_unref(network);
+       }
+
+       return 0;
+}
index f8bd94deebd1fb1e35b264cc362167d207afa4eb..8de721fff8f4d47b872a2dd5c772396273dddec8 100644 (file)
@@ -426,6 +426,17 @@ static PyObject* Database_countries(DatabaseObject* self) {
        return Database_iterate_all(self, LOC_DB_ENUMERATE_COUNTRIES, AF_UNSPEC, 0);
 }
 
+static PyObject* Database_list_bogons(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
+       char* kwlist[] = { "family", NULL };
+       int family = AF_UNSPEC;
+
+       // Parse arguments
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &family))
+               return NULL;
+
+       return Database_iterate_all(self, LOC_DB_ENUMERATE_BOGONS, family, 0);
+}
+
 static struct PyMethodDef Database_methods[] = {
        {
                "get_as",
@@ -439,6 +450,12 @@ static struct PyMethodDef Database_methods[] = {
                METH_VARARGS,
                NULL,
        },
+       {
+               "list_bogons",
+               (PyCFunction)Database_list_bogons,
+               METH_VARARGS|METH_KEYWORDS,
+               NULL,
+       },
        {
                "lookup",
                (PyCFunction)Database_lookup,
index 0c89d75263370952259255a5470aa4884685b1b1..10269468be95dd99d9a67d3ec6e787dfc421f2dc 100644 (file)
@@ -154,6 +154,15 @@ class CLI(object):
                        choices=location.export.formats.keys(), default="list")
                list_networks_by_flags.set_defaults(func=self.handle_list_networks_by_flags)
 
+               # List bogons
+               list_bogons = subparsers.add_parser("list-bogons",
+                       help=_("Lists all bogons"),
+               )
+               list_bogons.add_argument("--family", choices=("ipv6", "ipv4"))
+               list_bogons.add_argument("--format",
+                       choices=location.export.formats.keys(), default="list")
+               list_bogons.set_defaults(func=self.handle_list_bogons)
+
                # List countries
                list_countries = subparsers.add_parser("list-countries",
                        help=_("Lists all countries"),
@@ -547,6 +556,15 @@ class CLI(object):
 
                f.finish()
 
+       def handle_list_bogons(self, db, ns):
+               writer = self.__get_output_formatter(ns)
+               f = writer(sys.stdout, prefix="bogons")
+
+               for n in db.list_bogons(family=ns.family):
+                       f.write(n)
+
+               f.finish()
+
        def handle_export(self, db, ns):
                countries, asns = [], []