From: Michael Tremer Date: Thu, 2 Sep 2021 13:25:37 +0000 (+0000) Subject: location: Implement listing bogons X-Git-Tag: 0.9.8~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8c37d8a7910af1b8cfbd6e7cb11b90ee9c913bc4;p=people%2Fms%2Flibloc.git location: Implement listing bogons Signed-off-by: Michael Tremer --- diff --git a/man/location.txt b/man/location.txt index b38f21c..3878cb8 100644 --- a/man/location.txt +++ b/man/location.txt @@ -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. diff --git a/src/database.c b/src/database.c index b92a803..6cfefee 100644 --- a/src/database.c +++ b/src/database.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -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( diff --git a/src/loc/database.h b/src/loc/database.h index 70801f0..9d1f71a 100644 --- a/src/loc/database.h +++ b/src/loc/database.h @@ -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 { diff --git a/src/loc/network-list.h b/src/loc/network-list.h index bee21c4..308b262 100644 --- a/src/loc/network-list.h +++ b/src/loc/network-list.h @@ -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 + +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 diff --git a/src/network-list.c b/src/network-list.c index 698d3ab..224b345 100644 --- a/src/network-list.c +++ b/src/network-list.c @@ -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; +} diff --git a/src/python/database.c b/src/python/database.c index f8bd94d..8de721f 100644 --- a/src/python/database.c +++ b/src/python/database.c @@ -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, diff --git a/src/python/location.in b/src/python/location.in index 0c89d75..1026946 100644 --- a/src/python/location.in +++ b/src/python/location.in @@ -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 = [], []