]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
libloc: Import recent patches from upstream
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Nov 2020 13:30:15 +0000 (13:30 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 18 Nov 2020 19:18:40 +0000 (19:18 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
config/rootfiles/common/libloc
lfs/libloc
src/patches/libloc-0.9.4-upstream.patch [new file with mode: 0644]

index b120520748491fbec535cddb2bdb2a9fbf606703..e8776e56ef6e2c3027bc6f3e2781513f5b3bc9f3 100644 (file)
@@ -1,12 +1,15 @@
 usr/bin/location
 #usr/bin/location-importer
 #usr/include/libloc
+#usr/include/libloc/as-list.h
 #usr/include/libloc/as.h
 #usr/include/libloc/compat.h
+#usr/include/libloc/country-list.h
 #usr/include/libloc/country.h
 #usr/include/libloc/database.h
 #usr/include/libloc/format.h
 #usr/include/libloc/libloc.h
+#usr/include/libloc/network-list.h
 #usr/include/libloc/network.h
 #usr/include/libloc/private.h
 #usr/include/libloc/resolv.h
index da53e5149479593101340a77f248fb6f942c1aa2..65270eb24b262878bbaeac37c4c2100b16064e0c 100644 (file)
@@ -78,6 +78,9 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        @$(PREBUILD)
        @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar xvf $(DIR_DL)/$(DL_FILE)
 
+       # Import changes from upstream
+       cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.4-upstream.patch
+
        # Add patch for i586 to disable strong stack protector.
 ifeq "$(BUILD_ARCH)" "i586"
        cd $(DIR_APP) && patch -Np1 -i $(DIR_SRC)/src/patches/libloc-0.9.3-perl-i586-regular-stack-protector.patch
diff --git a/src/patches/libloc-0.9.4-upstream.patch b/src/patches/libloc-0.9.4-upstream.patch
new file mode 100644 (file)
index 0000000..28d569d
--- /dev/null
@@ -0,0 +1,7629 @@
+From ee6ea3986dc80183157f67275dc9f28231b5d5b2 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 24 Sep 2020 10:17:58 +0000
+Subject: [PATCH 01/70] Revert "importer: Purge any redundant entries"
+
+This reverts commit c2cc55d5a6875c3838f060032eaed89dcfb92ef6.
+
+The query stalls the database and therefore the automatic
+scripts are no longer able to generate a new version of the
+database.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 22 +---------------------
+ 1 file changed, 1 insertion(+), 21 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 1467923..e3a07a0 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -374,27 +374,7 @@ class CLI(object):
+                               INSERT INTO autnums(number, name)
+                                       SELECT _autnums.number, _organizations.name FROM _autnums
+                                               JOIN _organizations ON _autnums.organization = _organizations.handle
+-                              ON CONFLICT (number) DO UPDATE SET name = excluded.name
+-                      """)
+-
+-                      self.db.execute("""
+-                              --- Purge any redundant entries
+-                              CREATE TEMPORARY TABLE _garbage ON COMMIT DROP
+-                              AS
+-                              SELECT network FROM networks candidates
+-                              WHERE EXISTS (
+-                                      SELECT FROM networks
+-                                      WHERE
+-                                              networks.network << candidates.network
+-                                      AND
+-                                              networks.country = candidates.country
+-                              );
+-
+-                              CREATE UNIQUE INDEX _garbage_search ON _garbage USING BTREE(network);
+-
+-                              DELETE FROM networks WHERE EXISTS (
+-                                      SELECT FROM _garbage WHERE networks.network = _garbage.network
+-                              );
++                              ON CONFLICT (number) DO UPDATE SET name = excluded.name;
+                       """)
+               # Download all extended sources
+-- 
+2.20.1
+
+From 92f6abf4e272672bb0a71cfe991261b95ebe2fef Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 24 Sep 2020 10:18:58 +0000
+Subject: [PATCH 02/70] Revert "importer: Import raw sources for inetnum's
+ again"
+
+This reverts commit 64e95fa903edec8b4e4e59830b395e2e4a411853.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/importer.py          | 14 ++++----
+ src/python/location-importer.in | 63 ---------------------------------
+ 2 files changed, 7 insertions(+), 70 deletions(-)
+
+diff --git a/src/python/importer.py b/src/python/importer.py
+index f19db4b..de20f37 100644
+--- a/src/python/importer.py
++++ b/src/python/importer.py
+@@ -30,8 +30,8 @@ WHOIS_SOURCES = (
+       "https://ftp.afrinic.net/pub/pub/dbase/afrinic.db.gz",
+       # Asia Pacific Network Information Centre
+-      "https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
+-      "https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
++      #"https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
++      #"https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route6.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route.gz",
+       "https://ftp.apnic.net/apnic/whois/apnic.db.aut-num.gz",
+@@ -45,8 +45,8 @@ WHOIS_SOURCES = (
+       # XXX ???
+       # Réseaux IP Européens
+-      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
+-      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
++      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
++      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route6.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route.gz",
+       "https://ftp.ripe.net/ripe/dbase/split/ripe.db.aut-num.gz",
+@@ -55,10 +55,10 @@ WHOIS_SOURCES = (
+ EXTENDED_SOURCES = (
+       # African Network Information Centre
+-      #"https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
++      "https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
+       # Asia Pacific Network Information Centre
+-      #"https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
++      "https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
+       # American Registry for Internet Numbers
+       "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
+@@ -67,7 +67,7 @@ EXTENDED_SOURCES = (
+       "http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
+       # Réseaux IP Européens
+-      #"https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
++      "https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
+ )
+ class Downloader(object):
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index e3a07a0..77952f2 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -393,10 +393,6 @@ class CLI(object):
+               if line.startswith("aut-num:"):
+                       return self._parse_autnum_block(block)
+-              # inetnum
+-              if line.startswith("inet6num:") or line.startswith("inetnum:"):
+-                      return self._parse_inetnum_block(block)
+-
+               # organisation
+               elif line.startswith("organisation:"):
+                       return self._parse_org_block(block)
+@@ -426,65 +422,6 @@ class CLI(object):
+                       autnum.get("asn"), autnum.get("org"),
+               )
+-      def _parse_inetnum_block(self, block):
+-              logging.debug("Parsing inetnum block:")
+-
+-              inetnum = {}
+-              for line in block:
+-                      logging.debug(line)
+-
+-                      # Split line
+-                      key, val = split_line(line)
+-
+-                      if key == "inetnum":
+-                              start_address, delim, end_address = val.partition("-")
+-
+-                              # Strip any excess space
+-                              start_address, end_address = start_address.rstrip(), end_address.strip()
+-
+-                              # Convert to IP address
+-                              try:
+-                                      start_address = ipaddress.ip_address(start_address)
+-                                      end_address   = ipaddress.ip_address(end_address)
+-                              except ValueError:
+-                                      logging.warning("Could not parse line: %s" % line)
+-                                      return
+-
+-                              # Set prefix to default
+-                              prefix = 32
+-
+-                              # Count number of addresses in this subnet
+-                              num_addresses = int(end_address) - int(start_address)
+-                              if num_addresses:
+-                                      prefix -= math.log(num_addresses, 2)
+-
+-                              inetnum["inetnum"] = "%s/%.0f" % (start_address, prefix)
+-
+-                      elif key == "inet6num":
+-                              inetnum[key] = val
+-
+-                      elif key == "country":
+-                              if val == "UNITED STATES":
+-                                      val = "US"
+-
+-                              inetnum[key] = val.upper()
+-
+-              # Skip empty objects
+-              if not inetnum:
+-                      return
+-
+-              network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
+-
+-              # Bail out in case we have processed a non-public IP network
+-              if network.is_private:
+-                      logging.warning("Skipping non-globally routable network: %s" % network)
+-                      return
+-
+-              self.db.execute("INSERT INTO networks(network, country) \
+-                      VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
+-                      "%s" % network, inetnum.get("country"),
+-              )
+-
+       def _parse_org_block(self, block):
+               org = {}
+               for line in block:
+-- 
+2.20.1
+
+From f532841e9197ce2f40aad8c086d786b2cb783a54 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Mon, 12 Oct 2020 20:53:31 +0000
+Subject: [PATCH 03/70] Revert "Revert "importer: Import raw sources for
+ inetnum's again""
+
+This reverts commit 92f6abf4e272672bb0a71cfe991261b95ebe2fef.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/importer.py          | 14 ++++----
+ src/python/location-importer.in | 63 +++++++++++++++++++++++++++++++++
+ 2 files changed, 70 insertions(+), 7 deletions(-)
+
+diff --git a/src/python/importer.py b/src/python/importer.py
+index de20f37..f19db4b 100644
+--- a/src/python/importer.py
++++ b/src/python/importer.py
+@@ -30,8 +30,8 @@ WHOIS_SOURCES = (
+       "https://ftp.afrinic.net/pub/pub/dbase/afrinic.db.gz",
+       # Asia Pacific Network Information Centre
+-      #"https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
+-      #"https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
++      "https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
++      "https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route6.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route.gz",
+       "https://ftp.apnic.net/apnic/whois/apnic.db.aut-num.gz",
+@@ -45,8 +45,8 @@ WHOIS_SOURCES = (
+       # XXX ???
+       # Réseaux IP Européens
+-      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
+-      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
++      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
++      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route6.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route.gz",
+       "https://ftp.ripe.net/ripe/dbase/split/ripe.db.aut-num.gz",
+@@ -55,10 +55,10 @@ WHOIS_SOURCES = (
+ EXTENDED_SOURCES = (
+       # African Network Information Centre
+-      "https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
++      #"https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
+       # Asia Pacific Network Information Centre
+-      "https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
++      #"https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
+       # American Registry for Internet Numbers
+       "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
+@@ -67,7 +67,7 @@ EXTENDED_SOURCES = (
+       "http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
+       # Réseaux IP Européens
+-      "https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
++      #"https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
+ )
+ class Downloader(object):
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 77952f2..e3a07a0 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -393,6 +393,10 @@ class CLI(object):
+               if line.startswith("aut-num:"):
+                       return self._parse_autnum_block(block)
++              # inetnum
++              if line.startswith("inet6num:") or line.startswith("inetnum:"):
++                      return self._parse_inetnum_block(block)
++
+               # organisation
+               elif line.startswith("organisation:"):
+                       return self._parse_org_block(block)
+@@ -422,6 +426,65 @@ class CLI(object):
+                       autnum.get("asn"), autnum.get("org"),
+               )
++      def _parse_inetnum_block(self, block):
++              logging.debug("Parsing inetnum block:")
++
++              inetnum = {}
++              for line in block:
++                      logging.debug(line)
++
++                      # Split line
++                      key, val = split_line(line)
++
++                      if key == "inetnum":
++                              start_address, delim, end_address = val.partition("-")
++
++                              # Strip any excess space
++                              start_address, end_address = start_address.rstrip(), end_address.strip()
++
++                              # Convert to IP address
++                              try:
++                                      start_address = ipaddress.ip_address(start_address)
++                                      end_address   = ipaddress.ip_address(end_address)
++                              except ValueError:
++                                      logging.warning("Could not parse line: %s" % line)
++                                      return
++
++                              # Set prefix to default
++                              prefix = 32
++
++                              # Count number of addresses in this subnet
++                              num_addresses = int(end_address) - int(start_address)
++                              if num_addresses:
++                                      prefix -= math.log(num_addresses, 2)
++
++                              inetnum["inetnum"] = "%s/%.0f" % (start_address, prefix)
++
++                      elif key == "inet6num":
++                              inetnum[key] = val
++
++                      elif key == "country":
++                              if val == "UNITED STATES":
++                                      val = "US"
++
++                              inetnum[key] = val.upper()
++
++              # Skip empty objects
++              if not inetnum:
++                      return
++
++              network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
++
++              # Bail out in case we have processed a non-public IP network
++              if network.is_private:
++                      logging.warning("Skipping non-globally routable network: %s" % network)
++                      return
++
++              self.db.execute("INSERT INTO networks(network, country) \
++                      VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
++                      "%s" % network, inetnum.get("country"),
++              )
++
+       def _parse_org_block(self, block):
+               org = {}
+               for line in block:
+-- 
+2.20.1
+
+From a36bc686865fc87ea386fd90b389338bdcb80cbc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Mon, 12 Oct 2020 20:53:32 +0000
+Subject: [PATCH 04/70] location-importer.in: only import relevant data from
+ AFRINIC, APNIC and RIPE
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In contrast to ARIN and LACNIC, we are able to process more detailled
+feeds from those RIRs, avoiding storage of obviously unnecessary data.
+
+Thanks to various SQL optimisations, doing so now takes less time than
+the first version of this did.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 89 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 87 insertions(+), 2 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index e3a07a0..093f325 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -165,6 +165,7 @@ class CLI(object):
+                               -- networks
+                               CREATE TABLE IF NOT EXISTS networks(network inet, country text);
+                               CREATE UNIQUE INDEX IF NOT EXISTS networks_network ON networks(network);
++                              CREATE INDEX IF NOT EXISTS networks_family ON networks USING BTREE(family(network));
+                               CREATE INDEX IF NOT EXISTS networks_search ON networks USING GIST(network inet_ops);
+                               -- overrides
+@@ -363,6 +364,16 @@ class CLI(object):
+                               CREATE TEMPORARY TABLE _organizations(handle text, name text NOT NULL)
+                                       ON COMMIT DROP;
+                               CREATE UNIQUE INDEX _organizations_handle ON _organizations(handle);
++
++                              CREATE TEMPORARY TABLE _rirdata(network inet NOT NULL, country text NOT NULL)
++                                      ON COMMIT DROP;
++                              CREATE INDEX _rirdata_search ON _rirdata USING BTREE(family(network), masklen(network));
++                              CREATE UNIQUE INDEX _rirdata_network ON _rirdata(network);
++                      """)
++
++                      # Remove all previously imported content
++                      self.db.execute("""
++                              TRUNCATE TABLE networks;
+                       """)
+                       for source in location.importer.WHOIS_SOURCES:
+@@ -370,6 +381,67 @@ class CLI(object):
+                                       for block in f:
+                                               self._parse_block(block)
++                      # Process all parsed networks from every RIR we happen to have access to,
++                      # insert the largest network chunks into the networks table immediately...
++                      families = self.db.query("SELECT DISTINCT family(network) AS family FROM _rirdata ORDER BY family(network)")
++
++                      for family in (row.family for row in families):
++                              smallest = self.db.get("SELECT MIN(masklen(network)) AS prefix FROM _rirdata WHERE family(network) = %s", family)
++
++                              self.db.execute("INSERT INTO networks(network, country) \
++                                      SELECT network, country FROM _rirdata WHERE masklen(network) = %s AND family(network) = %s", smallest.prefix, family)
++
++                              # ... determine any other prefixes for this network family, ...
++                              prefixes = self.db.query("SELECT DISTINCT masklen(network) AS prefix FROM _rirdata \
++                                      WHERE family(network) = %s ORDER BY masklen(network) ASC OFFSET 1", family)
++
++                              # ... and insert networks with this prefix in case they provide additional
++                              # information (i. e. subnet of a larger chunk with a different country)
++                              for prefix in (row.prefix for row in prefixes):
++                                      self.db.execute("""
++                                              WITH candidates AS (
++                                                      SELECT
++                                                              _rirdata.network,
++                                                              _rirdata.country
++                                                      FROM
++                                                              _rirdata
++                                                      WHERE
++                                                              family(_rirdata.network) = %s
++                                                      AND
++                                                              masklen(_rirdata.network) = %s
++                                              ),
++                                              filtered AS (
++                                                      SELECT
++                                                              DISTINCT ON (c.network)
++                                                              c.network,
++                                                              c.country,
++                                                              masklen(networks.network),
++                                                              networks.country AS parent_country
++                                                      FROM
++                                                              candidates c
++                                                      LEFT JOIN
++                                                              networks
++                                                      ON
++                                                              c.network << networks.network
++                                                      ORDER BY
++                                                              c.network,
++                                                              masklen(networks.network) DESC NULLS LAST
++                                              )
++                                              INSERT INTO
++                                                      networks(network, country)
++                                              SELECT
++                                                      network,
++                                                      country
++                                              FROM
++                                                      filtered
++                                              WHERE
++                                                      parent_country IS NULL
++                                              OR
++                                                      country <> parent_country
++                                              ON CONFLICT DO NOTHING""",
++                                              family, prefix,
++                                      )
++
+                       self.db.execute("""
+                               INSERT INTO autnums(number, name)
+                                       SELECT _autnums.number, _organizations.name FROM _autnums
+@@ -470,17 +542,30 @@ class CLI(object):
+                               inetnum[key] = val.upper()
+               # Skip empty objects
+-              if not inetnum:
++              if not inetnum or not "country" in inetnum:
+                       return
+               network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
++              # Bail out in case we have processed a network covering the entire IP range, which
++              # is necessary to work around faulty (?) IPv6 network processing
++              if network.prefixlen == 0:
++                      logging.warning("Skipping network covering the entire IP adress range: %s" % network)
++                      return
++
++              # Bail out in case we have processed a network whose prefix length indicates it is
++              # not globally routable (we have decided not to process them at the moment, as they
++              # significantly enlarge our database without providing very helpful additional information)
++              if (network.prefixlen > 24 and network.version == 4) or (network.prefixlen > 48 and network.version == 6):
++                      logging.info("Skipping network too small to be publicly announced: %s" % network)
++                      return
++
+               # Bail out in case we have processed a non-public IP network
+               if network.is_private:
+                       logging.warning("Skipping non-globally routable network: %s" % network)
+                       return
+-              self.db.execute("INSERT INTO networks(network, country) \
++              self.db.execute("INSERT INTO _rirdata(network, country) \
+                       VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
+                       "%s" % network, inetnum.get("country"),
+               )
+-- 
+2.20.1
+
+From 2373de384f10f5573bbd7570f5522545df70c0e3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 16 Oct 2020 12:24:58 +0000
+Subject: [PATCH 05/70] location-importer: Include all overridden networks
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 093f325..d249a35 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -240,6 +240,8 @@ class CLI(object):
+                                       SELECT network FROM announcements
+                               UNION
+                                       SELECT network FROM networks
++                              UNION
++                                      SELECT network FROM network_overrides
+                               ORDER BY network
+                       )
+-- 
+2.20.1
+
+From 13f67f285856e8eabfeff2daf1be3aeaa36a82cc Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 16 Oct 2020 12:26:38 +0000
+Subject: [PATCH 06/70] Revert "location-importer.in: only import relevant data
+ from AFRINIC, APNIC and RIPE"
+
+This reverts commit a36bc686865fc87ea386fd90b389338bdcb80cbc.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 89 +--------------------------------
+ 1 file changed, 2 insertions(+), 87 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index d249a35..b220eaf 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -165,7 +165,6 @@ class CLI(object):
+                               -- networks
+                               CREATE TABLE IF NOT EXISTS networks(network inet, country text);
+                               CREATE UNIQUE INDEX IF NOT EXISTS networks_network ON networks(network);
+-                              CREATE INDEX IF NOT EXISTS networks_family ON networks USING BTREE(family(network));
+                               CREATE INDEX IF NOT EXISTS networks_search ON networks USING GIST(network inet_ops);
+                               -- overrides
+@@ -366,16 +365,6 @@ class CLI(object):
+                               CREATE TEMPORARY TABLE _organizations(handle text, name text NOT NULL)
+                                       ON COMMIT DROP;
+                               CREATE UNIQUE INDEX _organizations_handle ON _organizations(handle);
+-
+-                              CREATE TEMPORARY TABLE _rirdata(network inet NOT NULL, country text NOT NULL)
+-                                      ON COMMIT DROP;
+-                              CREATE INDEX _rirdata_search ON _rirdata USING BTREE(family(network), masklen(network));
+-                              CREATE UNIQUE INDEX _rirdata_network ON _rirdata(network);
+-                      """)
+-
+-                      # Remove all previously imported content
+-                      self.db.execute("""
+-                              TRUNCATE TABLE networks;
+                       """)
+                       for source in location.importer.WHOIS_SOURCES:
+@@ -383,67 +372,6 @@ class CLI(object):
+                                       for block in f:
+                                               self._parse_block(block)
+-                      # Process all parsed networks from every RIR we happen to have access to,
+-                      # insert the largest network chunks into the networks table immediately...
+-                      families = self.db.query("SELECT DISTINCT family(network) AS family FROM _rirdata ORDER BY family(network)")
+-
+-                      for family in (row.family for row in families):
+-                              smallest = self.db.get("SELECT MIN(masklen(network)) AS prefix FROM _rirdata WHERE family(network) = %s", family)
+-
+-                              self.db.execute("INSERT INTO networks(network, country) \
+-                                      SELECT network, country FROM _rirdata WHERE masklen(network) = %s AND family(network) = %s", smallest.prefix, family)
+-
+-                              # ... determine any other prefixes for this network family, ...
+-                              prefixes = self.db.query("SELECT DISTINCT masklen(network) AS prefix FROM _rirdata \
+-                                      WHERE family(network) = %s ORDER BY masklen(network) ASC OFFSET 1", family)
+-
+-                              # ... and insert networks with this prefix in case they provide additional
+-                              # information (i. e. subnet of a larger chunk with a different country)
+-                              for prefix in (row.prefix for row in prefixes):
+-                                      self.db.execute("""
+-                                              WITH candidates AS (
+-                                                      SELECT
+-                                                              _rirdata.network,
+-                                                              _rirdata.country
+-                                                      FROM
+-                                                              _rirdata
+-                                                      WHERE
+-                                                              family(_rirdata.network) = %s
+-                                                      AND
+-                                                              masklen(_rirdata.network) = %s
+-                                              ),
+-                                              filtered AS (
+-                                                      SELECT
+-                                                              DISTINCT ON (c.network)
+-                                                              c.network,
+-                                                              c.country,
+-                                                              masklen(networks.network),
+-                                                              networks.country AS parent_country
+-                                                      FROM
+-                                                              candidates c
+-                                                      LEFT JOIN
+-                                                              networks
+-                                                      ON
+-                                                              c.network << networks.network
+-                                                      ORDER BY
+-                                                              c.network,
+-                                                              masklen(networks.network) DESC NULLS LAST
+-                                              )
+-                                              INSERT INTO
+-                                                      networks(network, country)
+-                                              SELECT
+-                                                      network,
+-                                                      country
+-                                              FROM
+-                                                      filtered
+-                                              WHERE
+-                                                      parent_country IS NULL
+-                                              OR
+-                                                      country <> parent_country
+-                                              ON CONFLICT DO NOTHING""",
+-                                              family, prefix,
+-                                      )
+-
+                       self.db.execute("""
+                               INSERT INTO autnums(number, name)
+                                       SELECT _autnums.number, _organizations.name FROM _autnums
+@@ -544,30 +472,17 @@ class CLI(object):
+                               inetnum[key] = val.upper()
+               # Skip empty objects
+-              if not inetnum or not "country" in inetnum:
++              if not inetnum:
+                       return
+               network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
+-              # Bail out in case we have processed a network covering the entire IP range, which
+-              # is necessary to work around faulty (?) IPv6 network processing
+-              if network.prefixlen == 0:
+-                      logging.warning("Skipping network covering the entire IP adress range: %s" % network)
+-                      return
+-
+-              # Bail out in case we have processed a network whose prefix length indicates it is
+-              # not globally routable (we have decided not to process them at the moment, as they
+-              # significantly enlarge our database without providing very helpful additional information)
+-              if (network.prefixlen > 24 and network.version == 4) or (network.prefixlen > 48 and network.version == 6):
+-                      logging.info("Skipping network too small to be publicly announced: %s" % network)
+-                      return
+-
+               # Bail out in case we have processed a non-public IP network
+               if network.is_private:
+                       logging.warning("Skipping non-globally routable network: %s" % network)
+                       return
+-              self.db.execute("INSERT INTO _rirdata(network, country) \
++              self.db.execute("INSERT INTO networks(network, country) \
+                       VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
+                       "%s" % network, inetnum.get("country"),
+               )
+-- 
+2.20.1
+
+From 44341478233115b26bb27fdb24da5b0a1eedb173 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 16 Oct 2020 12:26:43 +0000
+Subject: [PATCH 07/70] Revert "Revert "Revert "importer: Import raw sources
+ for inetnum's again"""
+
+This reverts commit f532841e9197ce2f40aad8c086d786b2cb783a54.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/importer.py          | 14 ++++----
+ src/python/location-importer.in | 63 ---------------------------------
+ 2 files changed, 7 insertions(+), 70 deletions(-)
+
+diff --git a/src/python/importer.py b/src/python/importer.py
+index f19db4b..de20f37 100644
+--- a/src/python/importer.py
++++ b/src/python/importer.py
+@@ -30,8 +30,8 @@ WHOIS_SOURCES = (
+       "https://ftp.afrinic.net/pub/pub/dbase/afrinic.db.gz",
+       # Asia Pacific Network Information Centre
+-      "https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
+-      "https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
++      #"https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
++      #"https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route6.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route.gz",
+       "https://ftp.apnic.net/apnic/whois/apnic.db.aut-num.gz",
+@@ -45,8 +45,8 @@ WHOIS_SOURCES = (
+       # XXX ???
+       # Réseaux IP Européens
+-      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
+-      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
++      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
++      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route6.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route.gz",
+       "https://ftp.ripe.net/ripe/dbase/split/ripe.db.aut-num.gz",
+@@ -55,10 +55,10 @@ WHOIS_SOURCES = (
+ EXTENDED_SOURCES = (
+       # African Network Information Centre
+-      #"https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
++      "https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
+       # Asia Pacific Network Information Centre
+-      #"https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
++      "https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
+       # American Registry for Internet Numbers
+       "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
+@@ -67,7 +67,7 @@ EXTENDED_SOURCES = (
+       "http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
+       # Réseaux IP Européens
+-      #"https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
++      "https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
+ )
+ class Downloader(object):
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index b220eaf..e87d378 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -395,10 +395,6 @@ class CLI(object):
+               if line.startswith("aut-num:"):
+                       return self._parse_autnum_block(block)
+-              # inetnum
+-              if line.startswith("inet6num:") or line.startswith("inetnum:"):
+-                      return self._parse_inetnum_block(block)
+-
+               # organisation
+               elif line.startswith("organisation:"):
+                       return self._parse_org_block(block)
+@@ -428,65 +424,6 @@ class CLI(object):
+                       autnum.get("asn"), autnum.get("org"),
+               )
+-      def _parse_inetnum_block(self, block):
+-              logging.debug("Parsing inetnum block:")
+-
+-              inetnum = {}
+-              for line in block:
+-                      logging.debug(line)
+-
+-                      # Split line
+-                      key, val = split_line(line)
+-
+-                      if key == "inetnum":
+-                              start_address, delim, end_address = val.partition("-")
+-
+-                              # Strip any excess space
+-                              start_address, end_address = start_address.rstrip(), end_address.strip()
+-
+-                              # Convert to IP address
+-                              try:
+-                                      start_address = ipaddress.ip_address(start_address)
+-                                      end_address   = ipaddress.ip_address(end_address)
+-                              except ValueError:
+-                                      logging.warning("Could not parse line: %s" % line)
+-                                      return
+-
+-                              # Set prefix to default
+-                              prefix = 32
+-
+-                              # Count number of addresses in this subnet
+-                              num_addresses = int(end_address) - int(start_address)
+-                              if num_addresses:
+-                                      prefix -= math.log(num_addresses, 2)
+-
+-                              inetnum["inetnum"] = "%s/%.0f" % (start_address, prefix)
+-
+-                      elif key == "inet6num":
+-                              inetnum[key] = val
+-
+-                      elif key == "country":
+-                              if val == "UNITED STATES":
+-                                      val = "US"
+-
+-                              inetnum[key] = val.upper()
+-
+-              # Skip empty objects
+-              if not inetnum:
+-                      return
+-
+-              network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
+-
+-              # Bail out in case we have processed a non-public IP network
+-              if network.is_private:
+-                      logging.warning("Skipping non-globally routable network: %s" % network)
+-                      return
+-
+-              self.db.execute("INSERT INTO networks(network, country) \
+-                      VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
+-                      "%s" % network, inetnum.get("country"),
+-              )
+-
+       def _parse_org_block(self, block):
+               org = {}
+               for line in block:
+-- 
+2.20.1
+
+From a7d3a7a0565a0e09d3442e5829a0f30f016993b9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 20 Oct 2020 20:44:43 +0000
+Subject: [PATCH 08/70] as: Fix dereferencing NULL pointer when setting AS name
+
+Reported-by: Gisle Vanem <gisle.vanem@gmail.com>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/as.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/as.c b/src/as.c
+index e1fbb01..8421ac8 100644
+--- a/src/as.c
++++ b/src/as.c
+@@ -90,7 +90,13 @@ LOC_EXPORT const char* loc_as_get_name(struct loc_as* as) {
+ }
+ LOC_EXPORT int loc_as_set_name(struct loc_as* as, const char* name) {
+-      as->name = strdup(name);
++      if (as->name)
++              free(as->name);
++
++      if (name)
++              as->name = strdup(name);
++      else
++              as->name = NULL;
+       return 0;
+ }
+-- 
+2.20.1
+
+From ddb326ad38a7c7202315dd2c6f938313db04ee22 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 09:18:08 +0000
+Subject: [PATCH 09/70] as: Do not attempt to match name when it wasn't set
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/as.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/as.c b/src/as.c
+index 8421ac8..757bf3d 100644
+--- a/src/as.c
++++ b/src/as.c
+@@ -145,6 +145,10 @@ int loc_as_match_string(struct loc_as* as, const char* string) {
+       if (!string)
+               return 1;
++      // Cannot match anything when name is not set
++      if (!as->name)
++              return 1;
++
+       // Search if string is in name
+       if (strcasestr(as->name, string) != NULL)
+               return 1;
+-- 
+2.20.1
+
+From d226ad2d97cbcd42ce807d9308569b1b9c5d4e2f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 09:28:39 +0000
+Subject: [PATCH 10/70] writer: Free array with pointer to ASes, too
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/writer.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/writer.c b/src/writer.c
+index 5939cff..160650f 100644
+--- a/src/writer.c
++++ b/src/writer.c
+@@ -147,8 +147,11 @@ static void loc_writer_free(struct loc_writer* writer) {
+               EVP_PKEY_free(writer->private_key2);
+       // Unref all AS
+-      for (unsigned int i = 0; i < writer->as_count; i++) {
+-              loc_as_unref(writer->as[i]);
++      if (writer->as) {
++              for (unsigned int i = 0; i < writer->as_count; i++) {
++                      loc_as_unref(writer->as[i]);
++              }
++              free(writer->as);
+       }
+       // Release network tree
+-- 
+2.20.1
+
+From d89a7d62772048ae1bd18d03f69df46b7e1a3d3c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 09:31:29 +0000
+Subject: [PATCH 11/70] writer: Free countries when the writer is being
+ destroyed
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/writer.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/writer.c b/src/writer.c
+index 160650f..2f09b56 100644
+--- a/src/writer.c
++++ b/src/writer.c
+@@ -154,6 +154,14 @@ static void loc_writer_free(struct loc_writer* writer) {
+               free(writer->as);
+       }
++      // Unref all countries
++      if (writer->countries) {
++              for (unsigned int i = 0; i < writer->countries_count; i++) {
++                      loc_country_unref(writer->countries[i]);
++              }
++              free(writer->countries);
++      }
++
+       // Release network tree
+       if (writer->networks)
+               loc_network_tree_unref(writer->networks);
+-- 
+2.20.1
+
+From 0f1aedbc68e3945770c93e0ebd83eed0f555d6f0 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:19:44 +0000
+Subject: [PATCH 12/70] tests: Try adding an invalid network
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/test-network.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/src/test-network.c b/src/test-network.c
+index d38f13d..e908b57 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -160,6 +160,14 @@ int main(int argc, char** argv) {
+       // Set ASN
+       loc_network_set_asn(network4, 1024);
++      // Try adding an invalid network
++      struct loc_network* network;
++      err = loc_writer_add_network(writer, &network, "xxxx:xxxx::/32");
++      if (err != -EINVAL) {
++              fprintf(stderr, "It was possible to add an invalid network (err = %d)\n", err);
++              exit(EXIT_FAILURE);
++      }
++
+       FILE* f = tmpfile();
+       if (!f) {
+               fprintf(stderr, "Could not open file for writing: %s\n", strerror(errno));
+-- 
+2.20.1
+
+From 13ad6e695f9ffc7847b3afe3e9cbcea8fb3a443f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:36:35 +0000
+Subject: [PATCH 13/70] networks: Improve parsing IP addresses
+
+loc_network_new_from_string() seem to have had some unexpected
+behaviour for invalid inputs.
+
+The function has been tidied up slightly and returns as soon as
+some invalid input was detected.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c      | 52 +++++++++++++++++++++++++++++++---------------
+ src/test-network.c |  7 +++++++
+ 2 files changed, 42 insertions(+), 17 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index 366caa2..c112a41 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -160,9 +160,10 @@ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network
+ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
+               const char* address_string) {
+       struct in6_addr first_address;
+-      unsigned int prefix = 0;
+       char* prefix_string;
+-      int r = 1;
++      int r = -EINVAL;
++
++      DEBUG(ctx, "Attempting to parse network %s\n", address_string);
+       // Make a copy of the string to work on it
+       char* buffer = strdup(address_string);
+@@ -171,29 +172,46 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo
+       // Split address and prefix
+       address_string = strsep(&prefix_string, "/");
+-      // Did we find a prefix?
+-      if (prefix_string) {
+-              // Convert prefix to integer
+-              prefix = strtol(prefix_string, NULL, 10);
++      DEBUG(ctx, "  Split into address = %s, prefix = %s\n", address_string, prefix_string);
+-              if (prefix) {
+-                      // Parse the address
+-                      r = loc_parse_address(ctx, address_string, &first_address);
++      // We need to have a prefix
++      if (!prefix_string) {
++              DEBUG(ctx, "No prefix set\n");
++              goto FAIL;
++      }
+-                      // Map the prefix to IPv6 if needed
+-                      if (IN6_IS_ADDR_V4MAPPED(&first_address))
+-                              prefix += 96;
+-              }
++      // Convert prefix to integer
++      unsigned int prefix = strtol(prefix_string, NULL, 10);
++
++      // End if the prefix was invalid
++      if (!prefix) {
++              DEBUG(ctx, "The prefix is zero or not a number\n");
++              goto FAIL;
++      }
++
++      // Parse the address
++      r = loc_parse_address(ctx, address_string, &first_address);
++      if (r) {
++              DEBUG(ctx, "The address could not be parsed\n");
++              goto FAIL;
+       }
++      // Map the prefix to IPv6 if needed
++      if (IN6_IS_ADDR_V4MAPPED(&first_address))
++              prefix += 96;
++
++FAIL:
+       // Free temporary buffer
+       free(buffer);
+-      if (r == 0) {
+-              r = loc_network_new(ctx, network, &first_address, prefix);
+-      }
++      // Exit if the parsing was unsuccessful
++      if (r)
++              return r;
++
++      DEBUG(ctx, "GOT HERE\n");
+-      return r;
++      // Create a new network
++      return loc_network_new(ctx, network, &first_address, prefix);
+ }
+ LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
+diff --git a/src/test-network.c b/src/test-network.c
+index e908b57..85eca00 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -168,6 +168,13 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++      // Try adding a single address
++      err = loc_writer_add_network(writer, &network, "2001:db8::");
++      if (err != -EINVAL) {
++              fprintf(stderr, "It was possible to add an invalid network (err = %d)\n", err);
++              exit(EXIT_FAILURE);
++      }
++
+       FILE* f = tmpfile();
+       if (!f) {
+               fprintf(stderr, "Could not open file for writing: %s\n", strerror(errno));
+-- 
+2.20.1
+
+From 6a467e9345bb5a3d37911c9aaac30019eaa4492b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:43:21 +0000
+Subject: [PATCH 14/70] networks: Test if we can add localhost (IPv6)
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/test-network.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/test-network.c b/src/test-network.c
+index 85eca00..8c7e898 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -175,6 +175,13 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++      // Try adding localhost
++      err = loc_writer_add_network(writer, &network, "::1/128");
++      if (err != -EINVAL) {
++              fprintf(stderr, "It was possible to add localhost (::1/128): %d\n", err);
++              exit(EXIT_FAILURE);
++      }
++
+       FILE* f = tmpfile();
+       if (!f) {
+               fprintf(stderr, "Could not open file for writing: %s\n", strerror(errno));
+-- 
+2.20.1
+
+From fc1190aa11e3ff3d2dbf5f4d408c298e7916f46f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:44:50 +0000
+Subject: [PATCH 15/70] networks: Remove accidentially committed debug line
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index c112a41..be88d75 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -208,8 +208,6 @@ FAIL:
+       if (r)
+               return r;
+-      DEBUG(ctx, "GOT HERE\n");
+-
+       // Create a new network
+       return loc_network_new(ctx, network, &first_address, prefix);
+ }
+-- 
+2.20.1
+
+From a1707d8983898b6878cdd5c68744bcc444e278ed Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:53:36 +0000
+Subject: [PATCH 16/70] importer: Add search index to announcements table
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index e87d378..d0fe5a6 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -152,6 +152,7 @@ class CLI(object):
+                                       last_seen_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP);
+                               CREATE UNIQUE INDEX IF NOT EXISTS announcements_networks ON announcements(network);
+                               CREATE INDEX IF NOT EXISTS announcements_family ON announcements(family(network));
++                              CREATE INDEX IF NOT EXISTS announcements_search ON announcements USING GIST(network inet_ops);
+                               -- autnums
+                               CREATE TABLE IF NOT EXISTS autnums(number bigint, name text NOT NULL);
+-- 
+2.20.1
+
+From 991baf530d47adb2ed7a15b65dc4565d07fa6d07 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 13:54:45 +0000
+Subject: [PATCH 17/70] importer: Add search index to network_overrides table
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index d0fe5a6..fe21d73 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -189,6 +189,8 @@ class CLI(object):
+                               );
+                               CREATE UNIQUE INDEX IF NOT EXISTS network_overrides_network
+                                       ON network_overrides(network);
++                              CREATE INDEX IF NOT EXISTS network_overrides_search
++                                      ON network_overrides USING GIST(network inet_ops);
+                       """)
+               return db
+-- 
+2.20.1
+
+From bbea93a74651df10e2ffdbd09eb434dc6a0471bc Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 21 Oct 2020 16:01:57 +0000
+Subject: [PATCH 18/70] importer: Restructure SQL query to be executed in
+ parallel
+
+There are no functional changes, this just runs quicker now.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 67 ++++++++++++++++++---------------
+ 1 file changed, 37 insertions(+), 30 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index fe21d73..3c1e5e2 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -237,34 +237,24 @@ class CLI(object):
+               # Select all known networks
+               rows = self.db.query("""
+-                      -- Get a (sorted) list of all known networks
+-                      WITH known_networks AS (
+-                                      SELECT network FROM announcements
+-                              UNION
+-                                      SELECT network FROM networks
+-                              UNION
+-                                      SELECT network FROM network_overrides
+-                              ORDER BY network
+-                      )
+-
+                       -- Return a list of those networks enriched with all
+                       -- other information that we store in the database
+                       SELECT
+-                              DISTINCT ON (known_networks.network)
+-                              known_networks.network AS network,
+-                              announcements.autnum AS autnum,
++                              DISTINCT ON (network)
++                              network,
++                              autnum,
+                               -- Country
+                               COALESCE(
+                                       (
+                                               SELECT country FROM network_overrides overrides
+-                                                      WHERE announcements.network <<= overrides.network
++                                                      WHERE networks.network <<= overrides.network
+                                                       ORDER BY masklen(overrides.network) DESC
+                                                       LIMIT 1
+                                       ),
+                                       (
+                                               SELECT country FROM autnum_overrides overrides
+-                                                      WHERE announcements.autnum = overrides.number
++                                                      WHERE networks.autnum = overrides.number
+                                       ),
+                                       networks.country
+                               ) AS country,
+@@ -273,50 +263,67 @@ class CLI(object):
+                               COALESCE(
+                                       (
+                                               SELECT is_anonymous_proxy FROM network_overrides overrides
+-                                                      WHERE announcements.network <<= overrides.network
++                                                      WHERE networks.network <<= overrides.network
+                                                       ORDER BY masklen(overrides.network) DESC
+                                                       LIMIT 1
+                                       ),
+                                       (
+                                               SELECT is_anonymous_proxy FROM autnum_overrides overrides
+-                                                      WHERE announcements.autnum = overrides.number
++                                                      WHERE networks.autnum = overrides.number
+                                       ),
+                                       FALSE
+                               ) AS is_anonymous_proxy,
+                               COALESCE(
+                                       (
+                                               SELECT is_satellite_provider FROM network_overrides overrides
+-                                                      WHERE announcements.network <<= overrides.network
++                                                      WHERE networks.network <<= overrides.network
+                                                       ORDER BY masklen(overrides.network) DESC
+                                                       LIMIT 1
+                                       ),
+                                       (
+                                               SELECT is_satellite_provider FROM autnum_overrides overrides
+-                                                      WHERE announcements.autnum = overrides.number
++                                                      WHERE networks.autnum = overrides.number
+                                       ),
+                                       FALSE
+                               ) AS is_satellite_provider,
+                               COALESCE(
+                                       (
+                                               SELECT is_anycast FROM network_overrides overrides
+-                                                      WHERE announcements.network <<= overrides.network
++                                                      WHERE networks.network <<= overrides.network
+                                                       ORDER BY masklen(overrides.network) DESC
+                                                       LIMIT 1
+                                       ),
+                                       (
+                                               SELECT is_anycast FROM autnum_overrides overrides
+-                                                      WHERE announcements.autnum = overrides.number
++                                                      WHERE networks.autnum = overrides.number
+                                       ),
+                                       FALSE
+-                              ) AS is_anycast,
+-
+-                              -- Must be part of returned values for ORDER BY clause
+-                              masklen(announcements.network) AS sort_a,
+-                              masklen(networks.network) AS sort_b
+-                      FROM known_networks
+-                              LEFT JOIN announcements ON known_networks.network <<= announcements.network
+-                              LEFT JOIN networks ON known_networks.network <<= networks.network
+-                      ORDER BY known_networks.network, sort_a DESC, sort_b DESC
++                              ) AS is_anycast
++                      FROM (
++                              SELECT
++                                      known_networks.network AS network,
++                                      announcements.autnum AS autnum,
++                                      networks.country AS country,
++
++                                      -- Must be part of returned values for ORDER BY clause
++                                      masklen(announcements.network) AS sort_a,
++                                      masklen(networks.network) AS sort_b
++                              FROM (
++                                              SELECT network FROM announcements
++                                      UNION ALL
++                                              SELECT network FROM networks
++                                      UNION ALL
++                                              SELECT network FROM network_overrides
++                                      ) known_networks
++                              LEFT JOIN
++                                      announcements ON known_networks.network <<= announcements.network
++                              LEFT JOIN
++                                      networks ON known_networks.network <<= networks.network
++                              ORDER BY
++                                      known_networks.network,
++                                      sort_a DESC,
++                                      sort_b DESC
++                      ) networks
+               """)
+               for row in rows:
+-- 
+2.20.1
+
+From 26ab419b68d166f932db1f97c38cb9d793d04187 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 22 Oct 2020 12:24:34 +0000
+Subject: [PATCH 19/70] network: Allow adding single IP addresses and
+ automatically add the prefix
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c      | 33 +++++++++++++++------------------
+ src/test-network.c |  4 ++--
+ 2 files changed, 17 insertions(+), 20 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index be88d75..d7b1645 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -161,6 +161,7 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo
+               const char* address_string) {
+       struct in6_addr first_address;
+       char* prefix_string;
++      unsigned int prefix = 128;
+       int r = -EINVAL;
+       DEBUG(ctx, "Attempting to parse network %s\n", address_string);
+@@ -174,21 +175,6 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo
+       DEBUG(ctx, "  Split into address = %s, prefix = %s\n", address_string, prefix_string);
+-      // We need to have a prefix
+-      if (!prefix_string) {
+-              DEBUG(ctx, "No prefix set\n");
+-              goto FAIL;
+-      }
+-
+-      // Convert prefix to integer
+-      unsigned int prefix = strtol(prefix_string, NULL, 10);
+-
+-      // End if the prefix was invalid
+-      if (!prefix) {
+-              DEBUG(ctx, "The prefix is zero or not a number\n");
+-              goto FAIL;
+-      }
+-
+       // Parse the address
+       r = loc_parse_address(ctx, address_string, &first_address);
+       if (r) {
+@@ -196,9 +182,20 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo
+               goto FAIL;
+       }
+-      // Map the prefix to IPv6 if needed
+-      if (IN6_IS_ADDR_V4MAPPED(&first_address))
+-              prefix += 96;
++      // If a prefix was given, we will try to parse it
++      if (prefix_string) {
++              // Convert prefix to integer
++              prefix = strtol(prefix_string, NULL, 10);
++
++              if (!prefix) {
++                      DEBUG(ctx, "The prefix was not parsable: %s\n", prefix_string);
++                      goto FAIL;
++              }
++
++              // Map the prefix to IPv6 if needed
++              if (IN6_IS_ADDR_V4MAPPED(&first_address))
++                      prefix += 96;
++      }
+ FAIL:
+       // Free temporary buffer
+diff --git a/src/test-network.c b/src/test-network.c
+index 8c7e898..b6776b4 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -170,8 +170,8 @@ int main(int argc, char** argv) {
+       // Try adding a single address
+       err = loc_writer_add_network(writer, &network, "2001:db8::");
+-      if (err != -EINVAL) {
+-              fprintf(stderr, "It was possible to add an invalid network (err = %d)\n", err);
++      if (err) {
++              fprintf(stderr, "It was impossible to add an single IP address (err = %d)\n", err);
+               exit(EXIT_FAILURE);
+       }
+-- 
+2.20.1
+
+From aadac4c569e921be1d28dd3b2377ac7f3732213e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:36 +0000
+Subject: [PATCH 20/70] Revert "Revert "Revert "Revert "importer: Import raw
+ sources for inetnum's again""""
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 44341478233115b26bb27fdb24da5b0a1eedb173.
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/importer.py          | 14 ++++----
+ src/python/location-importer.in | 63 +++++++++++++++++++++++++++++++++
+ 2 files changed, 70 insertions(+), 7 deletions(-)
+
+diff --git a/src/python/importer.py b/src/python/importer.py
+index de20f37..f19db4b 100644
+--- a/src/python/importer.py
++++ b/src/python/importer.py
+@@ -30,8 +30,8 @@ WHOIS_SOURCES = (
+       "https://ftp.afrinic.net/pub/pub/dbase/afrinic.db.gz",
+       # Asia Pacific Network Information Centre
+-      #"https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
+-      #"https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
++      "https://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz",
++      "https://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route6.gz",
+       #"https://ftp.apnic.net/apnic/whois/apnic.db.route.gz",
+       "https://ftp.apnic.net/apnic/whois/apnic.db.aut-num.gz",
+@@ -45,8 +45,8 @@ WHOIS_SOURCES = (
+       # XXX ???
+       # Réseaux IP Européens
+-      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
+-      #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
++      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inet6num.gz",
++      "https://ftp.ripe.net/ripe/dbase/split/ripe.db.inetnum.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route6.gz",
+       #"https://ftp.ripe.net/ripe/dbase/split/ripe.db.route.gz",
+       "https://ftp.ripe.net/ripe/dbase/split/ripe.db.aut-num.gz",
+@@ -55,10 +55,10 @@ WHOIS_SOURCES = (
+ EXTENDED_SOURCES = (
+       # African Network Information Centre
+-      "https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
++      #"https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest",
+       # Asia Pacific Network Information Centre
+-      "https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
++      #"https://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-extended-latest",
+       # American Registry for Internet Numbers
+       "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
+@@ -67,7 +67,7 @@ EXTENDED_SOURCES = (
+       "http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
+       # Réseaux IP Européens
+-      "https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
++      #"https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
+ )
+ class Downloader(object):
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 3c1e5e2..e8a4fc5 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -405,6 +405,10 @@ class CLI(object):
+               if line.startswith("aut-num:"):
+                       return self._parse_autnum_block(block)
++              # inetnum
++              if line.startswith("inet6num:") or line.startswith("inetnum:"):
++                      return self._parse_inetnum_block(block)
++
+               # organisation
+               elif line.startswith("organisation:"):
+                       return self._parse_org_block(block)
+@@ -434,6 +438,65 @@ class CLI(object):
+                       autnum.get("asn"), autnum.get("org"),
+               )
++      def _parse_inetnum_block(self, block):
++              logging.debug("Parsing inetnum block:")
++
++              inetnum = {}
++              for line in block:
++                      logging.debug(line)
++
++                      # Split line
++                      key, val = split_line(line)
++
++                      if key == "inetnum":
++                              start_address, delim, end_address = val.partition("-")
++
++                              # Strip any excess space
++                              start_address, end_address = start_address.rstrip(), end_address.strip()
++
++                              # Convert to IP address
++                              try:
++                                      start_address = ipaddress.ip_address(start_address)
++                                      end_address   = ipaddress.ip_address(end_address)
++                              except ValueError:
++                                      logging.warning("Could not parse line: %s" % line)
++                                      return
++
++                              # Set prefix to default
++                              prefix = 32
++
++                              # Count number of addresses in this subnet
++                              num_addresses = int(end_address) - int(start_address)
++                              if num_addresses:
++                                      prefix -= math.log(num_addresses, 2)
++
++                              inetnum["inetnum"] = "%s/%.0f" % (start_address, prefix)
++
++                      elif key == "inet6num":
++                              inetnum[key] = val
++
++                      elif key == "country":
++                              if val == "UNITED STATES":
++                                      val = "US"
++
++                              inetnum[key] = val.upper()
++
++              # Skip empty objects
++              if not inetnum:
++                      return
++
++              network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
++
++              # Bail out in case we have processed a non-public IP network
++              if network.is_private:
++                      logging.warning("Skipping non-globally routable network: %s" % network)
++                      return
++
++              self.db.execute("INSERT INTO networks(network, country) \
++                      VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
++                      "%s" % network, inetnum.get("country"),
++              )
++
+       def _parse_org_block(self, block):
+               org = {}
+               for line in block:
+-- 
+2.20.1
+
+From 002deb6b42ac0b3624c07e3352cebd72dc0685a2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:37 +0000
+Subject: [PATCH 21/70] Revert "Revert "location-importer.in: only import
+ relevant data from AFRINIC, APNIC and RIPE""
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reverts commit 13f67f285856e8eabfeff2daf1be3aeaa36a82cc.
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 89 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 87 insertions(+), 2 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index e8a4fc5..5656c41 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -166,6 +166,7 @@ class CLI(object):
+                               -- networks
+                               CREATE TABLE IF NOT EXISTS networks(network inet, country text);
+                               CREATE UNIQUE INDEX IF NOT EXISTS networks_network ON networks(network);
++                              CREATE INDEX IF NOT EXISTS networks_family ON networks USING BTREE(family(network));
+                               CREATE INDEX IF NOT EXISTS networks_search ON networks USING GIST(network inet_ops);
+                               -- overrides
+@@ -375,6 +376,16 @@ class CLI(object):
+                               CREATE TEMPORARY TABLE _organizations(handle text, name text NOT NULL)
+                                       ON COMMIT DROP;
+                               CREATE UNIQUE INDEX _organizations_handle ON _organizations(handle);
++
++                              CREATE TEMPORARY TABLE _rirdata(network inet NOT NULL, country text NOT NULL)
++                                      ON COMMIT DROP;
++                              CREATE INDEX _rirdata_search ON _rirdata USING BTREE(family(network), masklen(network));
++                              CREATE UNIQUE INDEX _rirdata_network ON _rirdata(network);
++                      """)
++
++                      # Remove all previously imported content
++                      self.db.execute("""
++                              TRUNCATE TABLE networks;
+                       """)
+                       for source in location.importer.WHOIS_SOURCES:
+@@ -382,6 +393,67 @@ class CLI(object):
+                                       for block in f:
+                                               self._parse_block(block)
++                      # Process all parsed networks from every RIR we happen to have access to,
++                      # insert the largest network chunks into the networks table immediately...
++                      families = self.db.query("SELECT DISTINCT family(network) AS family FROM _rirdata ORDER BY family(network)")
++
++                      for family in (row.family for row in families):
++                              smallest = self.db.get("SELECT MIN(masklen(network)) AS prefix FROM _rirdata WHERE family(network) = %s", family)
++
++                              self.db.execute("INSERT INTO networks(network, country) \
++                                      SELECT network, country FROM _rirdata WHERE masklen(network) = %s AND family(network) = %s", smallest.prefix, family)
++
++                              # ... determine any other prefixes for this network family, ...
++                              prefixes = self.db.query("SELECT DISTINCT masklen(network) AS prefix FROM _rirdata \
++                                      WHERE family(network) = %s ORDER BY masklen(network) ASC OFFSET 1", family)
++
++                              # ... and insert networks with this prefix in case they provide additional
++                              # information (i. e. subnet of a larger chunk with a different country)
++                              for prefix in (row.prefix for row in prefixes):
++                                      self.db.execute("""
++                                              WITH candidates AS (
++                                                      SELECT
++                                                              _rirdata.network,
++                                                              _rirdata.country
++                                                      FROM
++                                                              _rirdata
++                                                      WHERE
++                                                              family(_rirdata.network) = %s
++                                                      AND
++                                                              masklen(_rirdata.network) = %s
++                                              ),
++                                              filtered AS (
++                                                      SELECT
++                                                              DISTINCT ON (c.network)
++                                                              c.network,
++                                                              c.country,
++                                                              masklen(networks.network),
++                                                              networks.country AS parent_country
++                                                      FROM
++                                                              candidates c
++                                                      LEFT JOIN
++                                                              networks
++                                                      ON
++                                                              c.network << networks.network
++                                                      ORDER BY
++                                                              c.network,
++                                                              masklen(networks.network) DESC NULLS LAST
++                                              )
++                                              INSERT INTO
++                                                      networks(network, country)
++                                              SELECT
++                                                      network,
++                                                      country
++                                              FROM
++                                                      filtered
++                                              WHERE
++                                                      parent_country IS NULL
++                                              OR
++                                                      country <> parent_country
++                                              ON CONFLICT DO NOTHING""",
++                                              family, prefix,
++                                      )
++
+                       self.db.execute("""
+                               INSERT INTO autnums(number, name)
+                                       SELECT _autnums.number, _organizations.name FROM _autnums
+@@ -482,17 +554,30 @@ class CLI(object):
+                               inetnum[key] = val.upper()
+               # Skip empty objects
+-              if not inetnum:
++              if not inetnum or not "country" in inetnum:
+                       return
+               network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
++              # Bail out in case we have processed a network covering the entire IP range, which
++              # is necessary to work around faulty (?) IPv6 network processing
++              if network.prefixlen == 0:
++                      logging.warning("Skipping network covering the entire IP adress range: %s" % network)
++                      return
++
++              # Bail out in case we have processed a network whose prefix length indicates it is
++              # not globally routable (we have decided not to process them at the moment, as they
++              # significantly enlarge our database without providing very helpful additional information)
++              if (network.prefixlen > 24 and network.version == 4) or (network.prefixlen > 48 and network.version == 6):
++                      logging.info("Skipping network too small to be publicly announced: %s" % network)
++                      return
++
+               # Bail out in case we have processed a non-public IP network
+               if network.is_private:
+                       logging.warning("Skipping non-globally routable network: %s" % network)
+                       return
+-              self.db.execute("INSERT INTO networks(network, country) \
++              self.db.execute("INSERT INTO _rirdata(network, country) \
+                       VALUES(%s, %s) ON CONFLICT (network) DO UPDATE SET country = excluded.country",
+                       "%s" % network, inetnum.get("country"),
+               )
+-- 
+2.20.1
+
+From 28c73fa3f4257e0a41e52af8a9643da414a6cb6f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:38 +0000
+Subject: [PATCH 22/70] export.py: fix exporting IP networks for crappy
+ xt_geoip module
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In contrast to the location database itself, the xt_geoip module
+consumes a list of IP networks for each country, and returns after the
+first match.
+
+We therefore need to...
+
+(a) sort IP networks by their size, allow as precise matches as possible
+(b) export _any_ IP networks - including inverted subnets - to prevent
+    undefined overlaps
+(c) do the entire thing as fast as possible, consuming as less disk
+    space as possible, which is why we can't just iterate over all /24
+    chunks
+
+Partially fixes: #12499
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 69 ++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 54 insertions(+), 15 deletions(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index d15c6f0..5eaf43f 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -39,8 +39,8 @@ class OutputWriter(object):
+       suffix = "networks"
+       mode = "w"
+-      def __init__(self, f, prefix=None, flatten=True):
+-              self.f, self.prefix, self.flatten = f, prefix, flatten
++      def __init__(self, db, f, prefix=None, flatten=True):
++              self.db, self.f, self.prefix, self.flatten = db, f, prefix, flatten
+               # The previously written network
+               self._last_network = None
+@@ -49,13 +49,13 @@ class OutputWriter(object):
+               self._write_header()
+       @classmethod
+-      def open(cls, filename, **kwargs):
++      def open(cls, db, filename, **kwargs):
+               """
+                       Convenience function to open a file
+               """
+               f = open(filename, cls.mode)
+-              return cls(f, **kwargs)
++              return cls(db, f, **kwargs)
+       def __repr__(self):
+               return "<%s f=%s>" % (self.__class__.__name__, self.f)
+@@ -87,13 +87,31 @@ class OutputWriter(object):
+       def _write_network(self, network):
+               self.f.write("%s\n" % network)
+-      def write(self, network):
++      def write(self, network, subnets):
+               if self.flatten and self._flatten(network):
+                       log.debug("Skipping writing network %s" % network)
+                       return
+-              # Write the network to file
+-              self._write_network(network)
++              # Write the network when it has no subnets
++              if not subnets:
++                      network = ipaddress.ip_network("%s" % network)
++                      return self._write_network(network)
++
++              # Collect all matching subnets
++              matching_subnets = []
++
++              for subnet in sorted(subnets):
++                      # Try to find the subnet in the database
++                      n = self.db.lookup("%s" % subnet.network_address)
++
++                      # No entry or matching country means those IP addresses belong here
++                      if not n or n.country_code == network.country_code:
++                              matching_subnets.append(subnet)
++
++              # Write all networks as compact as possible
++              for network in ipaddress.collapse_addresses(matching_subnets):
++                      log.debug("Writing %s to database" % network)
++                      self._write_network(network)
+       def finish(self):
+               """
+@@ -143,10 +161,10 @@ class XTGeoIPOutputWriter(OutputWriter):
+       mode = "wb"
+       def _write_network(self, network):
+-              for address in (network.first_address, network.last_address):
++              for address in (network.network_address, network.broadcast_address):
+                       # Convert this into a string of bits
+                       bytes = socket.inet_pton(
+-                              network.family, address,
++                              socket.AF_INET6 if network.version == 6 else socket.AF_INET, "%s" % address,
+                       )
+                       self.f.write(bytes)
+@@ -175,7 +193,7 @@ class Exporter(object):
+                                       directory, prefix=country_code, suffix=self.writer.suffix, family=family,
+                               )
+-                              writers[country_code] = self.writer.open(filename, prefix="CC_%s" % country_code)
++                              writers[country_code] = self.writer.open(self.db, filename, prefix="CC_%s" % country_code)
+                       # Create writers for ASNs
+                       for asn in asns:
+@@ -183,22 +201,43 @@ class Exporter(object):
+                                       directory, "AS%s" % asn, suffix=self.writer.suffix, family=family,
+                               )
+-                              writers[asn] = self.writer.open(filename, prefix="AS%s" % asn)
++                              writers[asn] = self.writer.open(self.db, filename, prefix="AS%s" % asn)
+                       # Get all networks that match the family
+                       networks = self.db.search_networks(family=family)
++                      # Materialise the generator into a list (uses quite some memory)
++                      networks = list(networks)
++
+                       # Walk through all networks
+-                      for network in networks:
++                      for i, network in enumerate(networks):
++                              _network = ipaddress.ip_network("%s" % network)
++
++                              # Search for all subnets
++                              subnets = set()
++
++                              while i < len(networks):
++                                      subnet = networks[i+1]
++
++                                      if subnet.is_subnet_of(network):
++                                              _subnet = ipaddress.ip_network("%s" % subnet)
++
++                                              subnets.add(_subnet)
++                                              subnets.update(_network.address_exclude(_subnet))
++
++                                              i += 1
++                                      else:
++                                              break
++
+                               # Write matching countries
+                               try:
+-                                      writers[network.country_code].write(network)
++                                      writers[network.country_code].write(network, subnets)
+                               except KeyError:
+                                       pass
+                               # Write matching ASNs
+                               try:
+-                                      writers[network.asn].write(network)
++                                      writers[network.asn].write(network, subnets)
+                               except KeyError:
+                                       pass
+@@ -209,7 +248,7 @@ class Exporter(object):
+                                               country = flags[flag]
+                                               try:
+-                                                      writers[country].write(network)
++                                                      writers[country].write(network, subnets)
+                                               except KeyError:
+                                                       pass
+-- 
+2.20.1
+
+From bd341642fc6bbcc050e9b4ec5124585c83cab84d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:39 +0000
+Subject: [PATCH 23/70] location-importer.in: filter bogus IP networks for both
+ Whois and extended sources
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Sanity checks for parsed networks have been put into a separate function
+to avoid boilerplate code for extended sources. This makes the location
+database less vulnerable to garbage written into RIR databases on
+purpose or by chance.
+
+Fixes: #12500
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 83 ++++++++++++++++++++++++++-------
+ 1 file changed, 67 insertions(+), 16 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 5656c41..f24d357 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -469,6 +469,69 @@ class CLI(object):
+                                       for line in f:
+                                               self._parse_line(line)
++      def _check_parsed_network(self, network):
++              """
++                      Assistive function to detect and subsequently sort out parsed
++                      networks from RIR data (both Whois and so-called "extended sources"),
++                      which are or have...
++
++                      (a) not globally routable (RFC 1918 space, et al.)
++                      (b) covering a too large chunk of the IP address space (prefix length
++                              is < 7 for IPv4 networks, and < 10 for IPv6)
++                      (c) "0.0.0.0" or "::" as a network address
++                      (d) are too small for being publicly announced (we have decided not to
++                              process them at the moment, as they significantly enlarge our
++                              database without providing very helpful additional information)
++
++                      This unfortunately is necessary due to brain-dead clutter across
++                      various RIR databases, causing mismatches and eventually disruptions.
++
++                      We will return False in case a network is not suitable for adding
++                      it to our database, and True otherwise.
++              """
++
++              if not network or not (isinstance(network, ipaddress.IPv4Network) or isinstance(network, ipaddress.IPv6Network)):
++                      return False
++
++              if not network.is_global:
++                      logging.warning("Skipping non-globally routable network: %s" % network)
++                      return False
++
++              if network.version == 4:
++                      if network.prefixlen < 7:
++                              logging.warning("Skipping too big IP chunk: %s" % network)
++                              return False
++
++                      if network.prefixlen > 24:
++                              logging.info("Skipping network too small to be publicly announced: %s" % network)
++                              return False
++
++                      if str(network.network_address) == "0.0.0.0":
++                              logging.warning("Skipping network based on 0.0.0.0: %s" % network)
++                              return False
++
++              elif network.version == 6:
++                      if network.prefixlen < 10:
++                              logging.warning("Skipping too big IP chunk: %s" % network)
++                              return False
++
++                      if network.prefixlen > 48:
++                              logging.info("Skipping network too small to be publicly announced: %s" % network)
++                              return False
++
++                      if str(network.network_address) == "::":
++                              logging.warning("Skipping network based on '::': %s" % network)
++                              return False
++
++              else:
++                      # This should not happen...
++                      logging.warning("Skipping network of unknown family, this should not happen: %s" % network)
++                      return False
++
++              # In case we have made it here, the network is considered to
++              # be suitable for libloc consumption...
++              return True
++
+       def _parse_block(self, block):
+               # Get first line to find out what type of block this is
+               line = block[0]
+@@ -559,22 +622,7 @@ class CLI(object):
+               network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
+-              # Bail out in case we have processed a network covering the entire IP range, which
+-              # is necessary to work around faulty (?) IPv6 network processing
+-              if network.prefixlen == 0:
+-                      logging.warning("Skipping network covering the entire IP adress range: %s" % network)
+-                      return
+-
+-              # Bail out in case we have processed a network whose prefix length indicates it is
+-              # not globally routable (we have decided not to process them at the moment, as they
+-              # significantly enlarge our database without providing very helpful additional information)
+-              if (network.prefixlen > 24 and network.version == 4) or (network.prefixlen > 48 and network.version == 6):
+-                      logging.info("Skipping network too small to be publicly announced: %s" % network)
+-                      return
+-
+-              # Bail out in case we have processed a non-public IP network
+-              if network.is_private:
+-                      logging.warning("Skipping non-globally routable network: %s" % network)
++              if not self._check_parsed_network(network):
+                       return
+               self.db.execute("INSERT INTO _rirdata(network, country) \
+@@ -658,6 +706,9 @@ class CLI(object):
+                       log.warning("Invalid IP address: %s" % address)
+                       return
++              if not self._check_parsed_network(network):
++                      return
++
+               self.db.execute("INSERT INTO networks(network, country) \
+                       VALUES(%s, %s) ON CONFLICT (network) DO \
+                       UPDATE SET country = excluded.country",
+-- 
+2.20.1
+
+From eee65490a10e0fe89b3834b8be176fc900084fa0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:40 +0000
+Subject: [PATCH 24/70] importer.py: fetch LACNIC data via HTTPS
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/importer.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/python/importer.py b/src/python/importer.py
+index f19db4b..5f46bc3 100644
+--- a/src/python/importer.py
++++ b/src/python/importer.py
+@@ -64,7 +64,7 @@ EXTENDED_SOURCES = (
+       "https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest",
+       # Latin America and Caribbean Network Information Centre
+-      "http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
++      "https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest",
+       # Réseaux IP Européens
+       #"https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest",
+-- 
+2.20.1
+
+From 84187ab5436eb158529d6f5e2a38890b4af3ddb4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:41 +0000
+Subject: [PATCH 25/70] location-importer.in: omit historic/orphaned RIR data
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some RIRs include detailled information regarding networks not managed
+by or allocated to themselves, particually APNIC. We need to filter
+those networks (they usually have a characteristic network name) in
+order to prevent operational quirks or returning wrong country codes.
+
+Fixes: #12501
+Partially fixes: #12499
+
+Cc: Michael Tremer <michael.tremer@ipfire.org>
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 38 +++++++++++++++++++++------------
+ 1 file changed, 24 insertions(+), 14 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index f24d357..a869256 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -494,38 +494,38 @@ class CLI(object):
+                       return False
+               if not network.is_global:
+-                      logging.warning("Skipping non-globally routable network: %s" % network)
++                      log.warning("Skipping non-globally routable network: %s" % network)
+                       return False
+               if network.version == 4:
+                       if network.prefixlen < 7:
+-                              logging.warning("Skipping too big IP chunk: %s" % network)
++                              log.warning("Skipping too big IP chunk: %s" % network)
+                               return False
+                       if network.prefixlen > 24:
+-                              logging.info("Skipping network too small to be publicly announced: %s" % network)
++                              log.info("Skipping network too small to be publicly announced: %s" % network)
+                               return False
+                       if str(network.network_address) == "0.0.0.0":
+-                              logging.warning("Skipping network based on 0.0.0.0: %s" % network)
++                              log.warning("Skipping network based on 0.0.0.0: %s" % network)
+                               return False
+               elif network.version == 6:
+                       if network.prefixlen < 10:
+-                              logging.warning("Skipping too big IP chunk: %s" % network)
++                              log.warning("Skipping too big IP chunk: %s" % network)
+                               return False
+                       if network.prefixlen > 48:
+-                              logging.info("Skipping network too small to be publicly announced: %s" % network)
++                              log.info("Skipping network too small to be publicly announced: %s" % network)
+                               return False
+                       if str(network.network_address) == "::":
+-                              logging.warning("Skipping network based on '::': %s" % network)
++                              log.warning("Skipping network based on '::': %s" % network)
+                               return False
+               else:
+                       # This should not happen...
+-                      logging.warning("Skipping network of unknown family, this should not happen: %s" % network)
++                      log.warning("Skipping network of unknown family, this should not happen: %s" % network)
+                       return False
+               # In case we have made it here, the network is considered to
+@@ -574,15 +574,22 @@ class CLI(object):
+               )
+       def _parse_inetnum_block(self, block):
+-              logging.debug("Parsing inetnum block:")
++              log.debug("Parsing inetnum block:")
+               inetnum = {}
+               for line in block:
+-                      logging.debug(line)
++                      log.debug(line)
+                       # Split line
+                       key, val = split_line(line)
++                      # Filter any inetnum records which are only referring to IP space
++                      # not managed by that specific RIR...
++                      if key == "netname":
++                              if re.match(r"(ERX-NETBLOCK|(AFRINIC|ARIN|LACNIC|RIPE)-CIDR-BLOCK|IANA-NETBLOCK-\d{1,3}|NON-RIPE-NCC-MANAGED-ADDRESS-BLOCK)", val.strip()):
++                                      log.warning("Skipping record indicating historic/orphaned data: %s" % val.strip())
++                                      return
++
+                       if key == "inetnum":
+                               start_address, delim, end_address = val.partition("-")
+@@ -594,7 +601,7 @@ class CLI(object):
+                                       start_address = ipaddress.ip_address(start_address)
+                                       end_address   = ipaddress.ip_address(end_address)
+                               except ValueError:
+-                                      logging.warning("Could not parse line: %s" % line)
++                                      log.warning("Could not parse line: %s" % line)
+                                       return
+                               # Set prefix to default
+@@ -611,15 +618,18 @@ class CLI(object):
+                               inetnum[key] = val
+                       elif key == "country":
+-                              if val == "UNITED STATES":
+-                                      val = "US"
+-
+                               inetnum[key] = val.upper()
+               # Skip empty objects
+               if not inetnum or not "country" in inetnum:
+                       return
++              # Skip objects with bogus country code 'ZZ'
++              if inetnum.get("country") == "ZZ":
++                      log.warning("Skipping network with bogus country 'ZZ': %s" % \
++                              (inetnum.get("inet6num") or inetnum.get("inetnum")))
++                      return
++
+               network = ipaddress.ip_network(inetnum.get("inet6num") or inetnum.get("inetnum"), strict=False)
+               if not self._check_parsed_network(network):
+-- 
+2.20.1
+
+From ebb087cfa30ec5ca0c96dcce66a91245c1ffc271 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Wed, 21 Oct 2020 14:47:43 +0000
+Subject: [PATCH 26/70] location-importer.in: avoid log spam for too small
+ networks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index a869256..864eab1 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -503,7 +503,7 @@ class CLI(object):
+                               return False
+                       if network.prefixlen > 24:
+-                              log.info("Skipping network too small to be publicly announced: %s" % network)
++                              log.debug("Skipping network too small to be publicly announced: %s" % network)
+                               return False
+                       if str(network.network_address) == "0.0.0.0":
+@@ -516,7 +516,7 @@ class CLI(object):
+                               return False
+                       if network.prefixlen > 48:
+-                              log.info("Skipping network too small to be publicly announced: %s" % network)
++                              log.debug("Skipping network too small to be publicly announced: %s" % network)
+                               return False
+                       if str(network.network_address) == "::":
+-- 
+2.20.1
+
+From bbed1fd2330e8efa6b413dc152a1a6ef2d771aac Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 27 Oct 2020 17:14:30 +0000
+Subject: [PATCH 27/70] export: Flatten the tree before exporting it
+
+This patch removes the possibility that any IP address ranges
+might show up in multiple exported files.
+
+If this was content from the database:
+
+  * 10.0.0.0/16 - DE
+  * 10.0.1.0/24 - FR
+
+Then the IP address 10.0.1.1 would match for both countries.
+
+The algorithm will now break the larger /16 subnet apart into
+smaller subnets so that 10.0.1.0/24 is no longer overlapped.
+
+There was some time spent on this to make this as efficient
+as possible.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 154 ++++++++++++++++++++++++++++++-------------
+ 1 file changed, 110 insertions(+), 44 deletions(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index 5eaf43f..dd44332 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -89,28 +89,55 @@ class OutputWriter(object):
+       def write(self, network, subnets):
+               if self.flatten and self._flatten(network):
+-                      log.debug("Skipping writing network %s" % network)
++                      log.debug("Skipping writing network %s (last one was %s)" % (network, self._last_network))
+                       return
++              # Convert network into a Python object
++              _network = ipaddress.ip_network("%s" % network)
++
+               # Write the network when it has no subnets
+               if not subnets:
+-                      network = ipaddress.ip_network("%s" % network)
+-                      return self._write_network(network)
++                      log.debug("Writing %s to %s" % (_network, self.f))
++                      return self._write_network(_network)
++
++              # Convert subnets into Python objects
++              _subnets = [ipaddress.ip_network("%s" % subnet) for subnet in subnets]
++
++              # Split the network into smaller bits so that
++              # we can accomodate for any gaps in it later
++              to_check = set()
++              for _subnet in _subnets:
++                      to_check.update(
++                              _network.address_exclude(_subnet)
++                      )
++
++              # Clear the list of all subnets
++              subnets = []
++
++              # Check if all subnets to not overlap with anything else
++              while to_check:
++                      subnet_to_check = to_check.pop()
+-              # Collect all matching subnets
+-              matching_subnets = []
++                      for _subnet in _subnets:
++                              # Drop this subnet if it equals one of the subnets
++                              # or if it is subnet of one of them
++                              if subnet_to_check == _subnet or subnet_to_check.subnet_of(_subnet):
++                                      break
+-              for subnet in sorted(subnets):
+-                      # Try to find the subnet in the database
+-                      n = self.db.lookup("%s" % subnet.network_address)
++                              # Break it down if it overlaps
++                              if subnet_to_check.overlaps(_subnet):
++                                      to_check.update(
++                                              subnet_to_check.address_exclude(_subnet)
++                                      )
++                                      break
+-                      # No entry or matching country means those IP addresses belong here
+-                      if not n or n.country_code == network.country_code:
+-                              matching_subnets.append(subnet)
++                      # Add the subnet again as it passed the check
++                      else:
++                              subnets.append(subnet_to_check)
+               # Write all networks as compact as possible
+-              for network in ipaddress.collapse_addresses(matching_subnets):
+-                      log.debug("Writing %s to database" % network)
++              for network in ipaddress.collapse_addresses(subnets):
++                      log.debug("Writing %s to %s" % (network, self.f))
+                       self._write_network(network)
+       def finish(self):
+@@ -206,40 +233,40 @@ class Exporter(object):
+                       # Get all networks that match the family
+                       networks = self.db.search_networks(family=family)
+-                      # Materialise the generator into a list (uses quite some memory)
+-                      networks = list(networks)
++                      # Create a stack with all networks in order where we can put items back
++                      # again and retrieve them in the next iteration.
++                      networks = BufferedStack(networks)
+                       # Walk through all networks
+-                      for i, network in enumerate(networks):
+-                              _network = ipaddress.ip_network("%s" % network)
+-
+-                              # Search for all subnets
+-                              subnets = set()
+-
+-                              while i < len(networks):
+-                                      subnet = networks[i+1]
+-
+-                                      if subnet.is_subnet_of(network):
+-                                              _subnet = ipaddress.ip_network("%s" % subnet)
+-
+-                                              subnets.add(_subnet)
+-                                              subnets.update(_network.address_exclude(_subnet))
+-
+-                                              i += 1
+-                                      else:
++                      for network in networks:
++                              # Collect all networks which are a subnet of network
++                              subnets = []
++                              for subnet in networks:
++                                      # If the next subnet was not a subnet, we have to push
++                                      # it back on the stack and break this loop
++                                      if not subnet.is_subnet_of(network):
++                                              networks.push(subnet)
+                                               break
++                                      subnets.append(subnet)
++
+                               # Write matching countries
+-                              try:
+-                                      writers[network.country_code].write(network, subnets)
+-                              except KeyError:
+-                                      pass
++                              if network.country_code and network.country_code in writers:
++                                      # Mismatching subnets
++                                      gaps = [
++                                              subnet for subnet in subnets if not network.country_code == subnet.country_code
++                                      ]
++
++                                      writers[network.country_code].write(network, gaps)
+                               # Write matching ASNs
+-                              try:
+-                                      writers[network.asn].write(network, subnets)
+-                              except KeyError:
+-                                      pass
++                              if network.asn and network.asn in writers:
++                                      # Mismatching subnets
++                                      gaps = [
++                                              subnet for subnet in subnets if not network.asn == subnet.asn
++                                      ]
++
++                                      writers[network.asn].write(network, gaps)
+                               # Handle flags
+                               for flag in flags:
+@@ -247,10 +274,19 @@ class Exporter(object):
+                                               # Fetch the "fake" country code
+                                               country = flags[flag]
+-                                              try:
+-                                                      writers[country].write(network, subnets)
+-                                              except KeyError:
+-                                                      pass
++                                              if not country in writers:
++                                                      continue
++
++                                              gaps = [
++                                                      subnet for subnet in subnets
++                                                              if not subnet.has_flag(flag)
++                                              ]
++
++                                              writers[country].write(network, gaps)
++
++                              # Push all subnets back onto the stack
++                              for subnet in reversed(subnets):
++                                      networks.push(subnet)
+                       # Write everything to the filesystem
+                       for writer in writers.values():
+@@ -262,3 +298,33 @@ class Exporter(object):
+               )
+               return os.path.join(directory, filename)
++
++
++class BufferedStack(object):
++      """
++              This class takes an iterator and when being iterated
++              over it returns objects from that iterator for as long
++              as there are any.
++
++              It additionally has a function to put an item back on
++              the back so that it will be returned again at the next
++              iteration.
++      """
++      def __init__(self, iterator):
++              self.iterator = iterator
++              self.stack = []
++
++      def __iter__(self):
++              return self
++
++      def __next__(self):
++              if self.stack:
++                      return self.stack.pop(0)
++
++              return next(self.iterator)
++
++      def push(self, elem):
++              """
++                      Takes an element and puts it on the stack
++              """
++              self.stack.insert(0, elem)
+-- 
+2.20.1
+
+From e99a72265c1ba2194b61663eda7e9f14e0083016 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 28 Oct 2020 09:52:36 +0000
+Subject: [PATCH 28/70] location: Fix Python syntax error in verify()
+
+The database is now being opened before the requested
+method is called and handle_verify() wasn't updated.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location.in | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/src/python/location.in b/src/python/location.in
+index 44ad726..b5e5758 100644
+--- a/src/python/location.in
++++ b/src/python/location.in
+@@ -453,13 +453,7 @@ class CLI(object):
+               return 0
+-      def handle_verify(self, ns):
+-              try:
+-                      db = location.Database(ns.database)
+-              except FileNotFoundError as e:
+-                      log.error("%s: %s" % (ns.database, e))
+-                      return 127
+-
++      def handle_verify(self, db, ns):
+               # Verify the database
+               with open(ns.public_key, "r") as f:
+                       if not db.verify(f):
+-- 
+2.20.1
+
+From 0c74f6b1a3bdce5ebdc2ee452b9baf3e421dd3d1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Thu, 29 Oct 2020 07:25:53 -0700
+Subject: [PATCH 29/70] location update: Remove double conversion of timestamps
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This caused that the timestamp in DNS was misinterpreted
+as local time and often, databases could not be downloaded.
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+---
+ src/python/location.in | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/src/python/location.in b/src/python/location.in
+index b5e5758..070640c 100644
+--- a/src/python/location.in
++++ b/src/python/location.in
+@@ -421,11 +421,8 @@ class CLI(object):
+               # Fetch the timestamp we need from DNS
+               t = location.discover_latest_version()
+-              # Parse timestamp into datetime format
+-              timestamp = datetime.datetime.utcfromtimestamp(t) if t else None
+-
+               # Check the version of the local database
+-              if db and timestamp and db.created_at >= timestamp.timestamp():
++              if db and t and db.created_at >= t:
+                       log.info("Already on the latest version")
+                       return
+-- 
+2.20.1
+
+From 60c1ac0307312614bd6980d30b44bb59b5a6ab6e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Thu, 29 Oct 2020 07:36:46 -0700
+Subject: [PATCH 30/70] location.in: do not confuse UTC with local time zones
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+---
+ src/python/location.in | 12 +++++-------
+ 1 file changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/src/python/location.in b/src/python/location.in
+index 070640c..0d09210 100644
+--- a/src/python/location.in
++++ b/src/python/location.in
+@@ -398,10 +398,7 @@ class CLI(object):
+       def handle_update(self, db, ns):
+               if ns.cron and db:
+-                      now = datetime.datetime.utcnow()
+-
+-                      # Parse the database timestamp
+-                      t = datetime.datetime.utcfromtimestamp(db.created_at)
++                      now = time.time()
+                       if ns.cron == "daily":
+                               delta = datetime.timedelta(days=1)
+@@ -410,11 +407,12 @@ class CLI(object):
+                       elif ns.cron == "monthly":
+                               delta = datetime.timedelta(days=30)
++                      delta = delta.total_seconds()
++
+                       # Check if the database has recently been updated
+-                      if t >= (now - delta):
++                      if db.created_at >= (now - delta):
+                               log.info(
+-                                      _("The database has been updated recently (%s)") % \
+-                                              format_timedelta(now - t),
++                                      _("The database has been updated recently"),
+                               )
+                               return 3
+-- 
+2.20.1
+
+From e7d612e5219ef9ba612ed404e4e2c174110d3dd7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Peter=20M=C3=BCller?= <peter.mueller@ipfire.org>
+Date: Tue, 3 Nov 2020 15:31:08 +0000
+Subject: [PATCH 31/70] location-importer.in: always convert organisation
+ handles into upper cases
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Fixes: #12523
+
+Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/location-importer.in | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/python/location-importer.in b/src/python/location-importer.in
+index 864eab1..2dec89e 100644
+--- a/src/python/location-importer.in
++++ b/src/python/location-importer.in
+@@ -560,7 +560,7 @@ class CLI(object):
+                                       autnum["asn"] = m.group(2)
+                       elif key == "org":
+-                              autnum[key] = val
++                              autnum[key] = val.upper()
+               # Skip empty objects
+               if not autnum:
+@@ -646,7 +646,9 @@ class CLI(object):
+                       # Split line
+                       key, val = split_line(line)
+-                      if key in ("organisation", "org-name"):
++                      if key == "organisation":
++                              org[key] = val.upper()
++                      elif key == "org-name":
+                               org[key] = val
+               # Skip empty objects
+-- 
+2.20.1
+
+From e96704f43acca1a8f56d9a680cce281f5e587ec5 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 11 Nov 2020 21:16:45 +0000
+Subject: [PATCH 32/70] test: Add tests for database enumerator
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/test-database.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 53 insertions(+)
+
+diff --git a/src/test-database.c b/src/test-database.c
+index b4a75c4..4aef94e 100644
+--- a/src/test-database.c
++++ b/src/test-database.c
+@@ -38,6 +38,14 @@ const char* DESCRIPTION =
+       "Maecenas ut venenatis nunc.";
+ const char* LICENSE = "CC";
++const char* networks[] = {
++      "2001:db8::/32",
++      "2001:db8:1000::/48",
++      "2001:db8:2000::/48",
++      "2001:db8:2020::/48",
++      NULL,
++};
++
+ static int attempt_to_open(struct loc_ctx* ctx, char* path) {
+       FILE* f = fopen(path, "r");
+       if (!f)
+@@ -139,6 +147,24 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++      struct loc_network* network = NULL;
++
++      // Add some networks
++      const char** n = networks;
++      while (*n) {
++              err = loc_writer_add_network(writer, &network, *n);
++              if (err) {
++                      fprintf(stderr, "Could not add network %s\n", *n);
++                      exit(EXIT_FAILURE);
++              }
++
++              // Set a country
++              loc_network_set_country_code(network, "XX");
++
++              // Next one
++              n++;
++      }
++
+       FILE* f = tmpfile();
+       if (!f) {
+               fprintf(stderr, "Could not open file for writing: %s\n", strerror(errno));
+@@ -170,6 +196,33 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++      // Enumerator
++      struct loc_database_enumerator* enumerator;
++      err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_NETWORKS);
++      if (err) {
++              fprintf(stderr, "Could not initialise the enumerator: %d\n", err);
++              exit(EXIT_FAILURE);
++      }
++
++      // Walk through all networks
++      while (1) {
++              err = loc_database_enumerator_next_network(enumerator, &network);
++              if (err) {
++                      fprintf(stderr, "Error fetching the next network: %d\n", err);
++                      exit(EXIT_FAILURE);
++              }
++
++              if (!network)
++                      break;
++
++              char* s = loc_network_str(network);
++              printf("Got network: %s\n", s);
++              free(s);
++      }
++
++      // Free the enumerator
++      loc_database_enumerator_unref(enumerator);
++
+       // Close the database
+       loc_database_unref(db);
+       loc_unref(ctx);
+-- 
+2.20.1
+
+From ecce288da39a2c0eb60076050ca21e9619f61844 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 11 Nov 2020 23:01:19 +0000
+Subject: [PATCH 33/70] networks: Add list to manage groups of networks
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    | 11 ++++++
+ src/loc/network.h | 12 ++++++
+ src/network.c     | 96 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 119 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index b8296eb..6ef2d27 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -98,6 +98,17 @@ global:
+       loc_network_str;
+       loc_network_unref;
++      # Network List
++      loc_network_list_clear;
++      loc_network_list_empty;
++      loc_network_list_get;
++      loc_network_list_new;
++      loc_network_list_pop;
++      loc_network_list_push;
++      loc_network_list_ref;
++      loc_network_list_size;
++      loc_network_list_unref;
++
+       # Writer
+       loc_writer_add_as;
+       loc_writer_add_country;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 70c3803..fd20812 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -56,6 +56,18 @@ int loc_network_match_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other);
++// List
++struct loc_network_list;
++int loc_network_list_new(struct loc_ctx* ctx, struct loc_network_list** list);
++struct loc_network_list* loc_network_list_ref(struct loc_network_list* list);
++struct loc_network_list* loc_network_list_unref(struct loc_network_list* list);
++size_t loc_network_list_size(struct loc_network_list* list);
++int loc_network_list_empty(struct loc_network_list* list);
++void loc_network_list_clear(struct loc_network_list* list);
++struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
++int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
++struct loc_network* loc_network_list_pop(struct loc_network_list* list);
++
+ #ifdef LIBLOC_PRIVATE
+ int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj);
+diff --git a/src/network.c b/src/network.c
+index d7b1645..c9e7979 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -746,3 +746,99 @@ LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node)
+ LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
+       return loc_network_ref(node->network);
+ }
++
++// List
++
++struct loc_network_list {
++      struct loc_ctx* ctx;
++      int refcount;
++
++      struct loc_network* list[1024];
++      size_t size;
++      size_t max_size;
++};
++
++LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
++              struct loc_network_list** list) {
++      struct loc_network_list* l = calloc(1, sizeof(*l));
++      if (!l)
++              return -ENOMEM;
++
++      l->ctx = loc_ref(ctx);
++      l->refcount = 1;
++
++      // Do not allow this list to grow larger than this
++      l->max_size = 1024;
++
++      DEBUG(l->ctx, "Network list allocated at %p\n", l);
++      *list = l;
++      return 0;
++}
++
++LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
++      list->refcount++;
++
++      return list;
++}
++
++static void loc_network_list_free(struct loc_network_list* list) {
++      DEBUG(list->ctx, "Releasing network list at %p\n", list);
++
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_network_unref(list->list[i]);
++
++      loc_unref(list->ctx);
++      free(list);
++}
++
++LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
++      if (!list)
++              return NULL;
++
++      if (--list->refcount > 0)
++              return list;
++
++      loc_network_list_free(list);
++      return NULL;
++}
++
++LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
++      return list->size;
++}
++
++LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
++      return list->size == 0;
++}
++
++LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_network_unref(list->list[i]);
++
++      list->size = 0;
++}
++
++LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
++      // Check index
++      if (index >= list->size)
++              return NULL;
++
++      return loc_network_ref(list->list[index]);
++}
++
++LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
++      // Check if we have space left
++      if (list->size == list->max_size)
++              return -ENOMEM;
++
++      list->list[list->size++] = loc_network_ref(network);
++
++      return 0;
++}
++
++LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
++      // Return nothing when empty
++      if (loc_network_list_empty(list))
++              return NULL;
++
++      return list->list[list->size--];
++}
+-- 
+2.20.1
+
+From 8b2205272b7872a1458ad87811abf58609f38ad4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 13:57:35 +0000
+Subject: [PATCH 34/70] networks: Add function to dump lists
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  1 +
+ src/network.c     | 14 ++++++++++++++
+ 3 files changed, 16 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 6ef2d27..a5641c6 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -100,6 +100,7 @@ global:
+       # Network List
+       loc_network_list_clear;
++      loc_network_list_dump;
+       loc_network_list_empty;
+       loc_network_list_get;
+       loc_network_list_new;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index fd20812..44c50a4 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -64,6 +64,7 @@ struct loc_network_list* loc_network_list_unref(struct loc_network_list* list);
+ size_t loc_network_list_size(struct loc_network_list* list);
+ int loc_network_list_empty(struct loc_network_list* list);
+ void loc_network_list_clear(struct loc_network_list* list);
++void loc_network_list_dump(struct loc_network_list* list);
+ struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
+ int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
+ struct loc_network* loc_network_list_pop(struct loc_network_list* list);
+diff --git a/src/network.c b/src/network.c
+index c9e7979..0977406 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -817,6 +817,20 @@ LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
+       list->size = 0;
+ }
++LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
++      struct loc_network* network;
++      char* s;
++
++      for (unsigned int i = 0; i < list->size; i++) {
++              network = list->list[i];
++
++              s = loc_network_str(network);
++
++              INFO(list->ctx, "%s\n", s);
++              free(s);
++      }
++}
++
+ LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
+       // Check index
+       if (index >= list->size)
+-- 
+2.20.1
+
+From 850e75167e8e03fe8b951992c9f7bc2ccb9fb711 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 14:18:40 +0000
+Subject: [PATCH 35/70] network: Add functions to break network into subnets
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym     |   5 +
+ src/loc/network.h  |   6 +
+ src/network.c      | 290 ++++++++++++++++++++++++++++++++++++++++++++-
+ src/test-network.c |  61 ++++++++++
+ 4 files changed, 361 insertions(+), 1 deletion(-)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index a5641c6..fcb9ea5 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -80,6 +80,8 @@ global:
+       # Network
+       loc_network_address_family;
++      loc_network_eq;
++      loc_network_exclude;
+       loc_network_format_first_address;
+       loc_network_format_last_address;
+       loc_network_get_asn;
+@@ -96,6 +98,7 @@ global:
+       loc_network_set_country_code;
+       loc_network_set_flag;
+       loc_network_str;
++      loc_network_subnets;
+       loc_network_unref;
+       # Network List
+@@ -107,7 +110,9 @@ global:
+       loc_network_list_pop;
+       loc_network_list_push;
+       loc_network_list_ref;
++      loc_network_list_reverse;
+       loc_network_list_size;
++      loc_network_list_sort;
+       loc_network_list_unref;
+       # Writer
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 44c50a4..4e51a62 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -54,7 +54,11 @@ int loc_network_has_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_set_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_match_flag(struct loc_network* network, uint32_t flag);
++int loc_network_eq(struct loc_network* self, struct loc_network* other);
+ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other);
++struct loc_network_list* loc_network_subnets(struct loc_network* network);
++struct loc_network_list* loc_network_exclude(
++              struct loc_network* self, struct loc_network* other);
+ // List
+ struct loc_network_list;
+@@ -68,6 +72,8 @@ void loc_network_list_dump(struct loc_network_list* list);
+ struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
+ int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
+ struct loc_network* loc_network_list_pop(struct loc_network_list* list);
++void loc_network_list_sort(struct loc_network_list* list);
++void loc_network_list_reverse(struct loc_network_list* list);
+ #ifdef LIBLOC_PRIVATE
+diff --git a/src/network.c b/src/network.c
+index 0977406..6c08070 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -97,6 +97,21 @@ static struct in6_addr make_last_address(const struct in6_addr* address, const s
+       return a;
+ }
++static struct in6_addr address_increment(const struct in6_addr* address) {
++      struct in6_addr a = *address;
++
++      for (int octet = 15; octet >= 0; octet--) {
++              if (a.s6_addr[octet] < 255) {
++                      a.s6_addr[octet]++;
++                      break;
++              } else {
++                      a.s6_addr[octet] = 0;
++              }
++      }
++
++      return a;
++}
++
+ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
+               struct in6_addr* address, unsigned int prefix) {
+       // Address cannot be unspecified
+@@ -405,6 +420,69 @@ LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag
+       return loc_network_has_flag(network, flag);
+ }
++LOC_EXPORT int loc_network_eq(struct loc_network* self, struct loc_network* other) {
++#ifdef ENABLE_DEBUG
++      char* n1 = loc_network_str(self);
++      char* n2 = loc_network_str(other);
++
++      DEBUG(self->ctx, "Is %s equal to %s?\n", n1, n2);
++
++      free(n1);
++      free(n2);
++#endif
++
++      // Family must be the same
++      if (self->family != other->family) {
++              DEBUG(self->ctx, "  Family mismatch\n");
++
++              return 0;
++      }
++
++      // The start address must be the same
++      if (in6_addr_cmp(&self->first_address, &other->first_address) != 0) {
++              DEBUG(self->ctx, "  Address mismatch\n");
++
++              return 0;
++      }
++
++      // The prefix length must be the same
++      if (self->prefix != other->prefix) {
++              DEBUG(self->ctx, "  Prefix mismatch\n");
++              return 0;
++      }
++
++      DEBUG(self->ctx, "  Yes!\n");
++
++      return 1;
++}
++
++static int loc_network_gt(struct loc_network* self, struct loc_network* other) {
++      // Families must match
++      if (self->family != other->family)
++              return -1;
++
++      int r = in6_addr_cmp(&self->first_address, &other->first_address);
++
++      switch (r) {
++              // Smaller
++              case -1:
++                      return 0;
++
++              // Larger
++              case 1:
++                      return 1;
++
++              default:
++                      break;
++      }
++
++      if (self->prefix > other->prefix)
++              return 1;
++
++      // Dunno
++      return 0;
++}
++
+ LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other) {
+       // If the start address of the other network is smaller than this network,
+       // it cannot be a subnet.
+@@ -419,6 +497,175 @@ LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_net
+       return 1;
+ }
++LOC_EXPORT struct loc_network_list* loc_network_subnets(struct loc_network* network) {
++      struct loc_network_list* list;
++
++      // New prefix length
++      unsigned int prefix = network->prefix + 1;
++
++      // Check if the new prefix is valid
++      if (valid_prefix(&network->first_address, prefix))
++              return NULL;
++
++      // Create a new list with the result
++      int r = loc_network_list_new(network->ctx, &list);
++      if (r) {
++              ERROR(network->ctx, "Could not create network list: %d\n", r);
++              return NULL;
++      }
++
++      struct loc_network* subnet1 = NULL;
++      struct loc_network* subnet2 = NULL;
++
++      // Create the first half of the network
++      r = loc_network_new(network->ctx, &subnet1, &network->first_address, prefix);
++      if (r)
++              goto ERROR;
++
++      // The next subnet starts after the first one
++      struct in6_addr first_address = address_increment(&subnet1->last_address);
++
++      // Create the second half of the network
++      r = loc_network_new(network->ctx, &subnet2, &first_address, prefix);
++      if (r)
++              goto ERROR;
++
++      // Push the both onto the stack (in reverse order)
++      r = loc_network_list_push(list, subnet2);
++      if (r)
++              goto ERROR;
++
++      r = loc_network_list_push(list, subnet1);
++      if (r)
++              goto ERROR;
++
++      loc_network_unref(subnet1);
++      loc_network_unref(subnet2);
++
++      return list;
++
++ERROR:
++      if (subnet1)
++              loc_network_unref(subnet1);
++
++      if (subnet2)
++              loc_network_unref(subnet2);
++
++      if (list)
++              loc_network_list_unref(list);
++
++      return NULL;
++}
++
++LOC_EXPORT struct loc_network_list* loc_network_exclude(
++              struct loc_network* self, struct loc_network* other) {
++      struct loc_network_list* list;
++
++#ifdef ENABLE_DEBUG
++      char* n1 = loc_network_str(self);
++      char* n2 = loc_network_str(other);
++
++      DEBUG(self->ctx, "Returning %s excluding %s...\n", n1, n2);
++
++      free(n1);
++      free(n2);
++#endif
++
++      // Family must match
++      if (self->family != other->family) {
++              DEBUG(self->ctx, "Family mismatch\n");
++
++              return NULL;
++      }
++
++      // Other must be a subnet of self
++      if (!loc_network_is_subnet_of(other, self)) {
++              DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
++
++              return NULL;
++      }
++
++      // We cannot perform this operation if both networks equal
++      if (loc_network_eq(self, other)) {
++              DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
++
++              return NULL;
++      }
++
++      // Create a new list with the result
++      int r = loc_network_list_new(self->ctx, &list);
++      if (r) {
++              ERROR(self->ctx, "Could not create network list: %d\n", r);
++              return NULL;
++      }
++
++      struct loc_network_list* subnets = loc_network_subnets(self);
++
++      struct loc_network* subnet1 = NULL;
++      struct loc_network* subnet2 = NULL;
++
++      while (subnets) {
++              // Fetch both subnets
++              subnet1 = loc_network_list_get(subnets, 0);
++              subnet2 = loc_network_list_get(subnets, 1);
++
++              // Free list
++              loc_network_list_unref(subnets);
++              subnets = NULL;
++
++              if (loc_network_eq(other, subnet1)) {
++                      r = loc_network_list_push(list, subnet2);
++                      if (r)
++                              goto ERROR;
++
++              } else if (loc_network_eq(other, subnet2)) {
++                      r = loc_network_list_push(list, subnet1);
++                      if (r)
++                              goto ERROR;
++
++              } else  if (loc_network_is_subnet_of(other, subnet1)) {
++                      r = loc_network_list_push(list, subnet2);
++                      if (r)
++                              goto ERROR;
++
++                      subnets = loc_network_subnets(subnet1);
++
++              } else if (loc_network_is_subnet_of(other, subnet2)) {
++                      r = loc_network_list_push(list, subnet1);
++                      if (r)
++                              goto ERROR;
++
++                      subnets = loc_network_subnets(subnet2);
++
++              } else {
++                      ERROR(self->ctx, "We should never get here\n");
++                      goto ERROR;
++              }
++
++              loc_network_unref(subnet1);
++              loc_network_unref(subnet2);
++      }
++
++#ifdef ENABLE_DEBUG
++      loc_network_list_dump(list);
++#endif
++
++      // Return the result
++      return list;
++
++ERROR:
++      if (subnet1)
++              loc_network_unref(subnet1);
++
++      if (subnet2)
++              loc_network_unref(subnet2);
++
++      if (list)
++              loc_network_list_unref(list);
++
++      return NULL;
++}
++
+ LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
+       // Add country code
+       loc_country_code_copy(dbobj->country_code, network->country_code);
+@@ -854,5 +1101,46 @@ LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* lis
+       if (loc_network_list_empty(list))
+               return NULL;
+-      return list->list[list->size--];
++      return list->list[--list->size];
++}
++
++static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1, unsigned int i2) {
++      // Do nothing for invalid indices
++      if (i1 >= list->size || i2 >= list->size)
++              return;
++
++      DEBUG(list->ctx, "Swapping %u with %u\n", i1, i2);
++
++      struct loc_network* network1 = list->list[i1];
++      struct loc_network* network2 = list->list[i2];
++
++      list->list[i1] = network2;
++      list->list[i2] = network1;
++}
++
++LOC_EXPORT void loc_network_list_reverse(struct loc_network_list* list) {
++      unsigned int i = 0;
++      unsigned int j = list->size - 1;
++
++      while (i < j) {
++              loc_network_list_swap(list, i++, j--);
++      }
++}
++
++LOC_EXPORT void loc_network_list_sort(struct loc_network_list* list) {
++      unsigned int n = list->size;
++      int swapped;
++
++      do {
++              swapped = 0;
++
++              for (unsigned int i = 1; i < n; i++) {
++                      if (loc_network_gt(list->list[i-1], list->list[i]) > 0) {
++                              loc_network_list_swap(list, i-1, i);
++                              swapped = 1;
++                      }
++              }
++
++              n--;
++      } while (swapped);
+ }
+diff --git a/src/test-network.c b/src/test-network.c
+index b6776b4..af1b2e6 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -118,6 +118,19 @@ int main(int argc, char** argv) {
+       size_t nodes = loc_network_tree_count_nodes(tree);
+       printf("The tree has %zu nodes\n", nodes);
++      // Check equals function
++      err = loc_network_eq(network1, network1);
++      if (!err) {
++              fprintf(stderr, "Network is not equal with itself\n");
++              exit(EXIT_FAILURE);
++      }
++
++      err = loc_network_eq(network1, network2);
++      if (err) {
++              fprintf(stderr, "Networks equal unexpectedly\n");
++              exit(EXIT_FAILURE);
++      }
++
+       // Check subnet function
+       err = loc_network_is_subnet_of(network1, network2);
+       if (err != 0) {
+@@ -131,6 +144,54 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++      // Make list of subnets
++      struct loc_network_list* subnets = loc_network_subnets(network1);
++      if (!subnets) {
++              fprintf(stderr, "Could not find subnets of network\n");
++              exit(EXIT_FAILURE);
++      }
++
++      loc_network_list_dump(subnets);
++
++      while (!loc_network_list_empty(subnets)) {
++              struct loc_network* subnet = loc_network_list_pop(subnets);
++              if (!subnet) {
++                      fprintf(stderr, "Received an empty subnet\n");
++                      exit(EXIT_FAILURE);
++              }
++
++              char* s = loc_network_str(subnet);
++              printf("Received subnet %s\n", s);
++              free(s);
++
++              if (!loc_network_is_subnet_of(subnet, network1)) {
++                      fprintf(stderr, "Not a subnet\n");
++                      exit(EXIT_FAILURE);
++              }
++
++              loc_network_unref(subnet);
++      }
++
++      loc_network_list_unref(subnets);
++
++      struct loc_network_list* excluded = loc_network_exclude(network1, network2);
++      if (!excluded) {
++              fprintf(stderr, "Could not create excluded list\n");
++              exit(EXIT_FAILURE);
++      }
++
++      loc_network_list_dump(excluded);
++
++      // Reverse it
++      loc_network_list_reverse(excluded);
++      loc_network_list_dump(excluded);
++
++      // Sort them and dump them again
++      loc_network_list_sort(excluded);
++      loc_network_list_dump(excluded);
++
++      loc_network_list_unref(excluded);
++
+       // Create a database
+       struct loc_writer* writer;
+       err = loc_writer_new(ctx, &writer, NULL, NULL);
+-- 
+2.20.1
+
+From 6159d384c4a98fe45ec52522e2950719e4982d80 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 14:24:58 +0000
+Subject: [PATCH 36/70] networks: Add function to check if two networks overlap
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  1 +
+ src/network.c     | 16 ++++++++++++++++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index fcb9ea5..0c2835b 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -93,6 +93,7 @@ global:
+       loc_network_match_flag;
+       loc_network_new;
+       loc_network_new_from_string;
++      loc_network_overlaps;
+       loc_network_ref;
+       loc_network_set_asn;
+       loc_network_set_country_code;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 4e51a62..ef13756 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -55,6 +55,7 @@ int loc_network_set_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_match_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_eq(struct loc_network* self, struct loc_network* other);
++int loc_network_overlaps(struct loc_network* self, struct loc_network* other);
+ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other);
+ struct loc_network_list* loc_network_subnets(struct loc_network* network);
+ struct loc_network_list* loc_network_exclude(
+diff --git a/src/network.c b/src/network.c
+index 6c08070..d826511 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -483,6 +483,22 @@ static int loc_network_gt(struct loc_network* self, struct loc_network* other) {
+       return 0;
+ }
++LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
++      if (loc_network_match_address(self, &other->first_address) == 0)
++              return 1;
++
++      if (loc_network_match_address(self, &other->last_address) == 0)
++              return 1;
++
++      if (loc_network_match_address(other, &self->first_address) == 0)
++              return 1;
++
++      if (loc_network_match_address(other, &self->last_address) == 0)
++              return 1;
++
++      return 0;
++}
++
+ LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other) {
+       // If the start address of the other network is smaller than this network,
+       // it cannot be a subnet.
+-- 
+2.20.1
+
+From e52ba21761f27e592040a2793b2a26bbeeeecc05 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 14:28:15 +0000
+Subject: [PATCH 37/70] networks: Add function to check if network is part of a
+ list
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    | 1 +
+ src/loc/network.h | 1 +
+ src/network.c     | 9 +++++++++
+ 3 files changed, 11 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 0c2835b..f1b63a2 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -104,6 +104,7 @@ global:
+       # Network List
+       loc_network_list_clear;
++      loc_network_list_contains;
+       loc_network_list_dump;
+       loc_network_list_empty;
+       loc_network_list_get;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index ef13756..7804512 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -73,6 +73,7 @@ void loc_network_list_dump(struct loc_network_list* list);
+ struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
+ int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
+ struct loc_network* loc_network_list_pop(struct loc_network_list* list);
++int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network);
+ void loc_network_list_sort(struct loc_network_list* list);
+ void loc_network_list_reverse(struct loc_network_list* list);
+diff --git a/src/network.c b/src/network.c
+index d826511..fcbdc59 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1120,6 +1120,15 @@ LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* lis
+       return list->list[--list->size];
+ }
++LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
++      for (unsigned int i = 0; i < list->size; i++) {
++              if (loc_network_eq(list->list[i], network))
++                      return 1;
++      }
++
++      return 0;
++}
++
+ static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1, unsigned int i2) {
+       // Do nothing for invalid indices
+       if (i1 >= list->size || i2 >= list->size)
+-- 
+2.20.1
+
+From f802f3a4decf4827ecc8bcabe269ae9f94f7f32d Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 14:33:22 +0000
+Subject: [PATCH 38/70] networks: Add function to merge two lists
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  1 +
+ src/network.c     | 13 +++++++++++++
+ 3 files changed, 15 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index f1b63a2..c0b6b1f 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -108,6 +108,7 @@ global:
+       loc_network_list_dump;
+       loc_network_list_empty;
+       loc_network_list_get;
++      loc_network_list_merge;
+       loc_network_list_new;
+       loc_network_list_pop;
+       loc_network_list_push;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 7804512..e30d91c 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -76,6 +76,7 @@ struct loc_network* loc_network_list_pop(struct loc_network_list* list);
+ int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network);
+ void loc_network_list_sort(struct loc_network_list* list);
+ void loc_network_list_reverse(struct loc_network_list* list);
++int loc_network_list_merge(struct loc_network_list* self, struct loc_network_list* other);
+ #ifdef LIBLOC_PRIVATE
+diff --git a/src/network.c b/src/network.c
+index fcbdc59..541286d 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1169,3 +1169,16 @@ LOC_EXPORT void loc_network_list_sort(struct loc_network_list* list) {
+               n--;
+       } while (swapped);
+ }
++
++LOC_EXPORT int loc_network_list_merge(
++              struct loc_network_list* self, struct loc_network_list* other) {
++      int r;
++
++      for (unsigned int i = 0; i < other->size; i++) {
++              r = loc_network_list_push(self, other->list[i]);
++              if (r)
++                      return r;
++      }
++
++      return 0;
++}
+-- 
+2.20.1
+
+From 6d22a179dffd08fcf2a44aafb361725ab22486d4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 14:35:43 +0000
+Subject: [PATCH 39/70] network: Make lists unique
+
+Networks that are in the list won't be added again
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/network.c b/src/network.c
+index 541286d..44571b3 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1103,6 +1103,10 @@ LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* lis
+ }
+ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
++      // Do not add networks that are already on the list
++      if (loc_network_list_contains(list, network))
++              return 0;
++
+       // Check if we have space left
+       if (list->size == list->max_size)
+               return -ENOMEM;
+-- 
+2.20.1
+
+From 681ff05cb7cdf230d38abf09a330a31498e265a4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 19:21:13 +0000
+Subject: [PATCH 40/70] database: Pass flag to enumerator to flatten output
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c        |  8 +++++++-
+ src/loc/database.h    |  6 +++++-
+ src/perl/Location.xs  |  2 +-
+ src/python/database.c | 25 ++++++++++++++++++-------
+ 4 files changed, 31 insertions(+), 10 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index fa1dad0..9baab33 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -104,6 +104,9 @@ struct loc_database_enumerator {
+       enum loc_network_flags flags;
+       int family;
++      // Flatten output?
++      int flatten;
++
+       // Index of the AS we are looking at
+       unsigned int as_index;
+@@ -933,7 +936,7 @@ LOC_EXPORT int loc_database_get_country(struct loc_database* db,
+ // Enumerator
+ LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
+-              struct loc_database* db, enum loc_database_enumerator_mode mode) {
++              struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
+       struct loc_database_enumerator* e = calloc(1, sizeof(*e));
+       if (!e)
+               return -ENOMEM;
+@@ -944,6 +947,9 @@ LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enum
+       e->mode = mode;
+       e->refcount = 1;
++      // Flatten output?
++      e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
++
+       // Initialise graph search
+       //e->network_stack[++e->network_stack_depth] = 0;
+       e->network_stack_depth = 1;
+diff --git a/src/loc/database.h b/src/loc/database.h
+index 43173dd..14eb5ea 100644
+--- a/src/loc/database.h
++++ b/src/loc/database.h
+@@ -55,9 +55,13 @@ enum loc_database_enumerator_mode {
+       LOC_DB_ENUMERATE_COUNTRIES = 3,
+ };
++enum loc_database_enumerator_flags {
++      LOC_DB_ENUMERATOR_FLAGS_FLATTEN = (1 << 0),
++};
++
+ struct loc_database_enumerator;
+ int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
+-      struct loc_database* db, enum loc_database_enumerator_mode mode);
++      struct loc_database* db, enum loc_database_enumerator_mode mode, int flags);
+ struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_enumerator* enumerator);
+ struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator);
+diff --git a/src/perl/Location.xs b/src/perl/Location.xs
+index dcf3f0d..b7676d2 100644
+--- a/src/perl/Location.xs
++++ b/src/perl/Location.xs
+@@ -125,7 +125,7 @@ database_countries(db)
+       PPCODE:
+               // Create Database enumerator
+               struct loc_database_enumerator* enumerator;
+-              int err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_COUNTRIES);
++              int err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_COUNTRIES, 0);
+               if (err) {
+                       croak("Could not create a database enumerator\n");
+diff --git a/src/python/database.c b/src/python/database.c
+index 1013a58..7f8c2c2 100644
+--- a/src/python/database.c
++++ b/src/python/database.c
+@@ -207,10 +207,10 @@ static PyObject* new_database_enumerator(PyTypeObject* type, struct loc_database
+       return (PyObject*)self;
+ }
+-static PyObject* Database_iterate_all(DatabaseObject* self, enum loc_database_enumerator_mode what) {
++static PyObject* Database_iterate_all(DatabaseObject* self, enum loc_database_enumerator_mode what, int flags) {
+       struct loc_database_enumerator* enumerator;
+-      int r = loc_database_enumerator_new(&enumerator, self->db, what);
++      int r = loc_database_enumerator_new(&enumerator, self->db, what, flags);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_SystemError);
+               return NULL;
+@@ -223,7 +223,7 @@ static PyObject* Database_iterate_all(DatabaseObject* self, enum loc_database_en
+ }
+ static PyObject* Database_ases(DatabaseObject* self) {
+-      return Database_iterate_all(self, LOC_DB_ENUMERATE_ASES);
++      return Database_iterate_all(self, LOC_DB_ENUMERATE_ASES, 0);
+ }
+ static PyObject* Database_search_as(DatabaseObject* self, PyObject* args) {
+@@ -234,7 +234,7 @@ static PyObject* Database_search_as(DatabaseObject* self, PyObject* args) {
+       struct loc_database_enumerator* enumerator;
+-      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_ASES);
++      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_ASES, 0);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_SystemError);
+               return NULL;
+@@ -250,7 +250,11 @@ static PyObject* Database_search_as(DatabaseObject* self, PyObject* args) {
+ }
+ static PyObject* Database_networks(DatabaseObject* self) {
+-      return Database_iterate_all(self, LOC_DB_ENUMERATE_NETWORKS);
++      return Database_iterate_all(self, LOC_DB_ENUMERATE_NETWORKS, 0);
++}
++
++static PyObject* Database_networks_flattened(DatabaseObject *self) {
++      return Database_iterate_all(self, LOC_DB_ENUMERATE_NETWORKS, LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
+ }
+ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
+@@ -264,7 +268,7 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
+               return NULL;
+       struct loc_database_enumerator* enumerator;
+-      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS);
++      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS, 0);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_SystemError);
+               return NULL;
+@@ -317,7 +321,7 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
+ }
+ static PyObject* Database_countries(DatabaseObject* self) {
+-      return Database_iterate_all(self, LOC_DB_ENUMERATE_COUNTRIES);
++      return Database_iterate_all(self, LOC_DB_ENUMERATE_COUNTRIES, 0);
+ }
+ static struct PyMethodDef Database_methods[] = {
+@@ -403,6 +407,13 @@ static struct PyGetSetDef Database_getsetters[] = {
+               NULL,
+               NULL,
+       },
++      {
++              "networks_flattened",
++              (getter)Database_networks_flattened,
++              NULL,
++              NULL,
++              NULL,
++      },
+       {
+               "vendor",
+               (getter)Database_get_vendor,
+-- 
+2.20.1
+
+From f5e50a47e37e9b29d0d2ee9e5a41e5a5fe5aea7f Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 19:21:58 +0000
+Subject: [PATCH 41/70] network: Reduce debugging output
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c | 28 +++-------------------------
+ 1 file changed, 3 insertions(+), 25 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index 44571b3..f7071a6 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -421,37 +421,17 @@ LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag
+ }
+ LOC_EXPORT int loc_network_eq(struct loc_network* self, struct loc_network* other) {
+-#ifdef ENABLE_DEBUG
+-      char* n1 = loc_network_str(self);
+-      char* n2 = loc_network_str(other);
+-
+-      DEBUG(self->ctx, "Is %s equal to %s?\n", n1, n2);
+-
+-      free(n1);
+-      free(n2);
+-#endif
+-
+       // Family must be the same
+-      if (self->family != other->family) {
+-              DEBUG(self->ctx, "  Family mismatch\n");
+-
++      if (self->family != other->family)
+               return 0;
+-      }
+       // The start address must be the same
+-      if (in6_addr_cmp(&self->first_address, &other->first_address) != 0) {
+-              DEBUG(self->ctx, "  Address mismatch\n");
+-
++      if (in6_addr_cmp(&self->first_address, &other->first_address) != 0)
+               return 0;
+-      }
+       // The prefix length must be the same
+-      if (self->prefix != other->prefix) {
+-              DEBUG(self->ctx, "  Prefix mismatch\n");
++      if (self->prefix != other->prefix)
+               return 0;
+-      }
+-
+-      DEBUG(self->ctx, "  Yes!\n");
+       return 1;
+ }
+@@ -1138,8 +1118,6 @@ static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1
+       if (i1 >= list->size || i2 >= list->size)
+               return;
+-      DEBUG(list->ctx, "Swapping %u with %u\n", i1, i2);
+-
+       struct loc_network* network1 = list->list[i1];
+       struct loc_network* network2 = list->list[i2];
+-- 
+2.20.1
+
+From 037c65d3a07ec6d37ff063f0645adda6b483b407 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 19:36:38 +0000
+Subject: [PATCH 42/70] python: Export networks exclude function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/network.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+diff --git a/src/python/network.c b/src/python/network.c
+index 5496d1e..11f672b 100644
+--- a/src/python/network.c
++++ b/src/python/network.c
+@@ -24,6 +24,24 @@
+ #include "locationmodule.h"
+ #include "network.h"
++static PyObject* PyList_FromNetworkList(struct loc_network_list* networks) {
++      PyObject* list = PyList_New(0);
++      if (!networks)
++              return list;
++
++      while (!loc_network_list_empty(networks)) {
++              struct loc_network* network = loc_network_list_pop(networks);
++
++              PyObject* n = new_network(&NetworkType, network);
++              PyList_Append(list, n);
++
++              loc_network_unref(network);
++              Py_DECREF(n);
++      }
++
++      return list;
++}
++
+ PyObject* new_network(PyTypeObject* type, struct loc_network* network) {
+       NetworkObject* self = (NetworkObject*)type->tp_alloc(type, 0);
+       if (self) {
+@@ -154,6 +172,21 @@ static PyObject* Network_set_flag(NetworkObject* self, PyObject* args) {
+       Py_RETURN_NONE;
+ }
++static PyObject* Network_exclude(NetworkObject* self, PyObject* args) {
++      NetworkObject* other = NULL;
++
++      if (!PyArg_ParseTuple(args, "O!", &NetworkType, &other))
++              return NULL;
++
++      struct loc_network_list* list = loc_network_exclude(self->network, other->network);
++
++      // Convert to Python objects
++      PyObject* obj = PyList_FromNetworkList(list);
++      loc_network_list_unref(list);
++
++      return obj;
++}
++
+ static PyObject* Network_is_subnet_of(NetworkObject* self, PyObject* args) {
+       NetworkObject* other = NULL;
+@@ -191,6 +224,12 @@ static PyObject* Network_get_last_address(NetworkObject* self) {
+ }
+ static struct PyMethodDef Network_methods[] = {
++      {
++              "exclude",
++              (PyCFunction)Network_exclude,
++              METH_VARARGS,
++              NULL,
++      },
+       {
+               "has_flag",
+               (PyCFunction)Network_has_flag,
+-- 
+2.20.1
+
+From 9a7732c8679e805d4d2d55ea4750c5d70ca4bd2c Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 19:59:22 +0000
+Subject: [PATCH 43/70] network: Add more debugging output to stacks
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index f7071a6..d41e873 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1088,8 +1088,12 @@ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_n
+               return 0;
+       // Check if we have space left
+-      if (list->size == list->max_size)
++      if (list->size == list->max_size) {
++              ERROR(list->ctx, "%p: Could not push network onto the stack: Stack full\n", list);
+               return -ENOMEM;
++      }
++
++      DEBUG(list->ctx, "%p: Pushing network %p onto stack\n", list, network);
+       list->list[list->size++] = loc_network_ref(network);
+@@ -1098,10 +1102,16 @@ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_n
+ LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
+       // Return nothing when empty
+-      if (loc_network_list_empty(list))
++      if (loc_network_list_empty(list)) {
++              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
+               return NULL;
++      }
+-      return list->list[--list->size];
++      struct loc_network* network = list->list[--list->size];
++
++      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
++
++      return network;
+ }
+ LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
+-- 
+2.20.1
+
+From 33a051e0435f6e78cc936f26f3b9ee16b7851025 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 20:00:09 +0000
+Subject: [PATCH 44/70] network: Add new subnet function
+
+The old one is too difficult to use in terms of order
+of input parameters and return value.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  1 +
+ src/network.c     | 15 +++++++++++++++
+ 3 files changed, 17 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index c0b6b1f..5392437 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -87,6 +87,7 @@ global:
+       loc_network_get_asn;
+       loc_network_get_country_code;
+       loc_network_has_flag;
++      loc_network_is_subnet;
+       loc_network_is_subnet_of;
+       loc_network_match_asn;
+       loc_network_match_country_code;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index e30d91c..2154cdc 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -56,6 +56,7 @@ int loc_network_match_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_eq(struct loc_network* self, struct loc_network* other);
+ int loc_network_overlaps(struct loc_network* self, struct loc_network* other);
++int loc_network_is_subnet(struct loc_network* self, struct loc_network* other);
+ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other);
+ struct loc_network_list* loc_network_subnets(struct loc_network* network);
+ struct loc_network_list* loc_network_exclude(
+diff --git a/src/network.c b/src/network.c
+index d41e873..5719111 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -479,6 +479,21 @@ LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network
+       return 0;
+ }
++LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
++      // If the start address of the other network is smaller than this network,
++      // it cannot be a subnet.
++      if (in6_addr_cmp(&self->first_address, &other->first_address) < 0)
++              return 0;
++
++      // If the end address of the other network is greater than this network,
++      // it cannot be a subnet.
++      if (in6_addr_cmp(&self->last_address, &other->last_address) > 0)
++              return 0;
++
++      return 1;
++}
++
++// XXX DEPRECATED - I find this too difficult to use
+ LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other) {
+       // If the start address of the other network is smaller than this network,
+       // it cannot be a subnet.
+-- 
+2.20.1
+
+From add5bb652ba1dad1127f79cb6a0db2d363a6d5e5 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 20:01:17 +0000
+Subject: [PATCH 45/70] network: Add function to exclude multiple networks at
+ once
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  2 ++
+ src/network.c     | 85 +++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 88 insertions(+)
+
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 5392437..bcd11be 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -82,6 +82,7 @@ global:
+       loc_network_address_family;
+       loc_network_eq;
+       loc_network_exclude;
++      loc_network_exclude_list;
+       loc_network_format_first_address;
+       loc_network_format_last_address;
+       loc_network_get_asn;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 2154cdc..40712b9 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -61,6 +61,8 @@ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other
+ struct loc_network_list* loc_network_subnets(struct loc_network* network);
+ struct loc_network_list* loc_network_exclude(
+               struct loc_network* self, struct loc_network* other);
++struct loc_network_list* loc_network_exclude_list(
++              struct loc_network* network, struct loc_network_list* list);
+ // List
+ struct loc_network_list;
+diff --git a/src/network.c b/src/network.c
+index 5719111..751e8e5 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -677,6 +677,91 @@ ERROR:
+       return NULL;
+ }
++LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
++              struct loc_network* network, struct loc_network_list* list) {
++      struct loc_network_list* to_check;
++
++      // Create a new list with all networks to look at
++      int r = loc_network_list_new(network->ctx, &to_check);
++      if (r)
++              return NULL;
++
++      struct loc_network* subnet = NULL;
++      struct loc_network_list* subnets = NULL;
++
++      for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
++              subnet = loc_network_list_get(list, i);
++
++              // Find all excluded networks
++              struct loc_network_list* excluded = loc_network_exclude(network, subnet);
++              if (excluded) {
++                      // Add them all to the "to check" list
++                      loc_network_list_merge(to_check, excluded);
++                      loc_network_list_unref(excluded);
++              }
++
++              // Cleanup
++              loc_network_unref(subnet);
++      }
++
++      r = loc_network_list_new(network->ctx, &subnets);
++      if (r) {
++              loc_network_list_unref(to_check);
++              return NULL;
++      }
++
++      while (!loc_network_list_empty(to_check)) {
++              struct loc_network* subnet_to_check = loc_network_list_pop(to_check);
++
++              // Marks whether this subnet passed all checks
++              int passed = 1;
++
++              for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
++                      subnet = loc_network_list_get(list, i);
++
++                      // Drop this subnet if is is already in list
++                      if (loc_network_eq(subnet_to_check, subnet)) {
++                              passed = 0;
++                              loc_network_unref(subnet);
++                              break;
++                      }
++
++                      // Drop this subnet if is a subnet of another subnet
++                      if (loc_network_is_subnet_of(subnet, subnet_to_check)) {
++                              passed = 0;
++                              loc_network_unref(subnet);
++                              break;
++                      }
++
++                      // Break it down if it overlaps
++                      if (loc_network_overlaps(subnet_to_check, subnet)) {
++                              passed = 0;
++
++                              struct loc_network_list* excluded = loc_network_exclude(subnet_to_check, subnet);
++                              if (excluded) {
++                                      loc_network_list_merge(to_check, excluded);
++                                      loc_network_list_unref(excluded);
++                              }
++
++                              loc_network_unref(subnet);
++                              break;
++                      }
++
++                      loc_network_unref(subnet);
++              }
++
++              if (passed) {
++                      r = loc_network_list_push(subnets, subnet_to_check);
++              }
++
++              loc_network_unref(subnet_to_check);
++      }
++
++      loc_network_list_unref(to_check);
++
++      return subnets;
++}
++
+ LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
+       // Add country code
+       loc_country_code_copy(dbobj->country_code, network->country_code);
+-- 
+2.20.1
+
+From d87fd7a3d277b4b03222c7d1680e51b3e45e525b Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 20:02:03 +0000
+Subject: [PATCH 46/70] database: Add option to return networks flattened
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 174 ++++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 143 insertions(+), 31 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 9baab33..7a3d1a7 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -118,6 +118,9 @@ struct loc_database_enumerator {
+       struct loc_node_stack network_stack[MAX_STACK_DEPTH];
+       int network_stack_depth;
+       unsigned int* networks_visited;
++
++      // For subnet search
++      struct loc_network_list* stack;
+ };
+ static int loc_database_read_magic(struct loc_database* db) {
+@@ -935,6 +938,26 @@ LOC_EXPORT int loc_database_get_country(struct loc_database* db,
+ // Enumerator
++static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
++      DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
++
++      // Release all references
++      loc_database_unref(enumerator->db);
++      loc_unref(enumerator->ctx);
++
++      if (enumerator->string)
++              free(enumerator->string);
++
++      // Free network search
++      free(enumerator->networks_visited);
++
++      // Free subnet stack
++      if (enumerator->stack)
++              loc_network_list_unref(enumerator->stack);
++
++      free(enumerator);
++}
++
+ LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enumerator,
+               struct loc_database* db, enum loc_database_enumerator_mode mode, int flags) {
+       struct loc_database_enumerator* e = calloc(1, sizeof(*e));
+@@ -951,10 +974,16 @@ LOC_EXPORT int loc_database_enumerator_new(struct loc_database_enumerator** enum
+       e->flatten = (flags & LOC_DB_ENUMERATOR_FLAGS_FLATTEN);
+       // Initialise graph search
+-      //e->network_stack[++e->network_stack_depth] = 0;
+       e->network_stack_depth = 1;
+       e->networks_visited = calloc(db->network_nodes_count, sizeof(*e->networks_visited));
++      // Allocate stack
++      int r = loc_network_list_new(e->ctx, &e->stack);
++      if (r) {
++              loc_database_enumerator_free(e);
++              return r;
++      }
++
+       DEBUG(e->ctx, "Database enumerator object allocated at %p\n", e);
+       *enumerator = e;
+@@ -967,22 +996,6 @@ LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_ref(struct lo
+       return enumerator;
+ }
+-static void loc_database_enumerator_free(struct loc_database_enumerator* enumerator) {
+-      DEBUG(enumerator->ctx, "Releasing database enumerator %p\n", enumerator);
+-
+-      // Release all references
+-      loc_database_unref(enumerator->db);
+-      loc_unref(enumerator->ctx);
+-
+-      if (enumerator->string)
+-              free(enumerator->string);
+-
+-      // Free network search
+-      free(enumerator->networks_visited);
+-
+-      free(enumerator);
+-}
+-
+ LOC_EXPORT struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator) {
+       if (!enumerator)
+               return NULL;
+@@ -1116,17 +1129,13 @@ static int loc_database_enumerator_stack_push_node(
+       return 0;
+ }
+-LOC_EXPORT int loc_database_enumerator_next_network(
+-              struct loc_database_enumerator* enumerator, struct loc_network** network) {
+-      // Reset network
+-      *network = NULL;
+-
+-      // Do not do anything if not in network mode
+-      if (enumerator->mode != LOC_DB_ENUMERATE_NETWORKS)
++static int __loc_database_enumerator_next_network(
++              struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
++      // Return top element from the stack
++      *network = loc_network_list_pop(enumerator->stack);
++      if (*network)
+               return 0;
+-      int r;
+-
+       DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
+               enumerator->network_stack_depth);
+@@ -1155,7 +1164,7 @@ LOC_EXPORT int loc_database_enumerator_next_network(
+                       enumerator->db->network_nodes_v1 + node->offset;
+               // Add edges to stack
+-              r = loc_database_enumerator_stack_push_node(enumerator,
++              int r = loc_database_enumerator_stack_push_node(enumerator,
+                       be32toh(n->one), 1, node->depth + 1);
+               if (r)
+@@ -1181,6 +1190,10 @@ LOC_EXPORT int loc_database_enumerator_next_network(
+                       if (r)
+                               return r;
++                      // Return all networks when the filter is disabled
++                      if (!filter)
++                              return 0;
++
+                       // Check if we are interested in this network
+                       // Skip if the family does not match
+@@ -1223,12 +1236,111 @@ LOC_EXPORT int loc_database_enumerator_next_network(
+       }
+       // Reached the end of the search
++      return 0;
++}
+-      // Mark all nodes as non-visited
+-      for (unsigned int i = 0; i < enumerator->db->network_nodes_count; i++)
+-              enumerator->networks_visited[i] = 0;
++static int __loc_database_enumerator_next_network_flattened(
++              struct loc_database_enumerator* enumerator, struct loc_network** network) {
++      // Fetch the next network
++      int r = __loc_database_enumerator_next_network(enumerator, network, 1);
++      if (r)
++              return r;
+-      return 0;
++      // End if we could not read another network
++      if (!*network)
++              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;
++
++      // Search all subnets from the database
++      while (1) {
++              // Fetch the next network in line
++              r = __loc_database_enumerator_next_network(enumerator, &subnet, 0);
++              if (r)
++                      goto END;
++
++              // End if we did not receive another subnet
++              if (!subnet)
++                      break;
++
++              // Collect all subnets in a list
++              if (loc_network_is_subnet(*network, subnet)) {
++                      r = loc_network_list_push(subnets, subnet);
++                      if (r)
++                              goto END;
++
++                      loc_network_unref(subnet);
++                      continue;
++              }
++
++              // If this is not a subnet, we push it back onto the stack and break
++              r = loc_network_list_push(enumerator->stack, subnet);
++              if (r)
++                      goto END;
++
++              loc_network_unref(subnet);
++              break;
++      }
++
++      DEBUG(enumerator->ctx, "Found %zu subnet(s)\n", loc_network_list_size(subnets));
++
++      // We can abort here if the network has no subnets
++      if (loc_network_list_empty(subnets)) {
++              loc_network_list_unref(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);
++      if (!excluded || loc_network_list_empty(excluded)) {
++              r = 1;
++              goto END;
++      }
++
++      // Sort the result
++      loc_network_list_sort(excluded);
++
++      // Reverse the list
++      loc_network_list_reverse(excluded);
++
++      // Replace network with the first one
++      loc_network_unref(*network);
++
++      *network = loc_network_list_pop(excluded);
++
++      // Push the rest onto the stack
++      loc_network_list_merge(enumerator->stack, excluded);
++
++      loc_network_list_unref(excluded);
++
++END:
++      if (subnet)
++              loc_network_unref(subnet);
++
++      loc_network_list_unref(subnets);
++
++      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;
++
++      // Flatten output?
++      if (enumerator->flatten)
++              return __loc_database_enumerator_next_network_flattened(enumerator, network);
++
++      return __loc_database_enumerator_next_network(enumerator, network, 1);
+ }
+ LOC_EXPORT int loc_database_enumerator_next_country(
+-- 
+2.20.1
+
+From d3ae93c27dcd7f6984fdc29cc141621e277f2e2a Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 20:09:20 +0000
+Subject: [PATCH 47/70] test: Update API
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/test-as.c       | 2 +-
+ src/test-database.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/test-as.c b/src/test-as.c
+index 839a04c..2d61675 100644
+--- a/src/test-as.c
++++ b/src/test-as.c
+@@ -95,7 +95,7 @@ int main(int argc, char** argv) {
+       // Enumerator
+       struct loc_database_enumerator* enumerator;
+-      err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_ASES);
++      err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_ASES, 0);
+       if (err) {
+               fprintf(stderr, "Could not create a database enumerator\n");
+               exit(EXIT_FAILURE);
+diff --git a/src/test-database.c b/src/test-database.c
+index 4aef94e..da4b11c 100644
+--- a/src/test-database.c
++++ b/src/test-database.c
+@@ -198,7 +198,7 @@ int main(int argc, char** argv) {
+       // Enumerator
+       struct loc_database_enumerator* enumerator;
+-      err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_NETWORKS);
++      err = loc_database_enumerator_new(&enumerator, db, LOC_DB_ENUMERATE_NETWORKS, 0);
+       if (err) {
+               fprintf(stderr, "Could not initialise the enumerator: %d\n", err);
+               exit(EXIT_FAILURE);
+-- 
+2.20.1
+
+From 594ca328c6e124d0f1eb543e9c8d9bbfe8a7b628 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Thu, 12 Nov 2020 20:09:37 +0000
+Subject: [PATCH 48/70] networks: Copy all attributes when splitting networks
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/src/network.c b/src/network.c
+index 751e8e5..d67f116 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -550,6 +550,20 @@ LOC_EXPORT struct loc_network_list* loc_network_subnets(struct loc_network* netw
+       if (r)
+               goto ERROR;
++      // Copy country code
++      const char* country_code = loc_network_get_country_code(network);
++      if (country_code) {
++              loc_network_set_country_code(subnet1, country_code);
++              loc_network_set_country_code(subnet2, country_code);
++      }
++
++      // Copy ASN
++      uint32_t asn = loc_network_get_asn(network);
++      if (asn) {
++              loc_network_set_asn(subnet1, asn);
++              loc_network_set_asn(subnet2, asn);
++      }
++
+       loc_network_unref(subnet1);
+       loc_network_unref(subnet2);
+-- 
+2.20.1
+
+From 69248038292e9ea1a4ee8912cdfc8700456753ad Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 11:23:33 +0000
+Subject: [PATCH 49/70] database: Move network filtering into a separate
+ function
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 56 +++++++++++++++++++++++---------------------------
+ 1 file changed, 26 insertions(+), 30 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 7a3d1a7..72bc8eb 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1129,6 +1129,31 @@ static int loc_database_enumerator_stack_push_node(
+       return 0;
+ }
++static int loc_database_enumerator_filter_network(
++              struct loc_database_enumerator* enumerator, struct loc_network* network) {
++      // Skip if the family does not match
++      if (enumerator->family && loc_network_address_family(network) != enumerator->family)
++              return 1;
++
++      // Skip if the country code does not match
++      if (*enumerator->country_code &&
++                      !loc_network_match_country_code(network, enumerator->country_code))
++              return 1;
++
++      // Skip if the ASN does not match
++      if (enumerator->asn &&
++                      !loc_network_match_asn(network, enumerator->asn))
++              return 1;
++
++      // Skip if flags do not match
++      if (enumerator->flags &&
++                      !loc_network_match_flag(network, enumerator->flags))
++              return 1;
++
++      // Do not filter
++      return 0;
++}
++
+ static int __loc_database_enumerator_next_network(
+               struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
+       // Return top element from the stack
+@@ -1195,36 +1220,7 @@ static int __loc_database_enumerator_next_network(
+                               return 0;
+                       // Check if we are interested in this network
+-
+-                      // Skip if the family does not match
+-                      if (enumerator->family && loc_network_address_family(*network) != enumerator->family) {
+-                              loc_network_unref(*network);
+-                              *network = NULL;
+-
+-                              continue;
+-                      }
+-
+-                      // Skip if the country code does not match
+-                      if (*enumerator->country_code &&
+-                                      !loc_network_match_country_code(*network, enumerator->country_code)) {
+-                              loc_network_unref(*network);
+-                              *network = NULL;
+-
+-                              continue;
+-                      }
+-
+-                      // Skip if the ASN does not match
+-                      if (enumerator->asn &&
+-                                      !loc_network_match_asn(*network, enumerator->asn)) {
+-                              loc_network_unref(*network);
+-                              *network = NULL;
+-
+-                              continue;
+-                      }
+-
+-                      // Skip if flags do not match
+-                      if (enumerator->flags &&
+-                                      !loc_network_match_flag(*network, enumerator->flags)) {
++                      if (loc_database_enumerator_filter_network(enumerator, *network)) {
+                               loc_network_unref(*network);
+                               *network = NULL;
+-- 
+2.20.1
+
+From 2113e71bf7b997c82670c5c22cf91aa6442fe6f3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 11:29:02 +0000
+Subject: [PATCH 50/70] database: Filter results coming from stack
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 72bc8eb..0f3cdc2 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1157,9 +1157,23 @@ static int loc_database_enumerator_filter_network(
+ static int __loc_database_enumerator_next_network(
+               struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
+       // Return top element from the stack
+-      *network = loc_network_list_pop(enumerator->stack);
+-      if (*network)
++      while (1) {
++              *network = loc_network_list_pop(enumerator->stack);
++
++              // Stack is empty
++              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 result
+               return 0;
++      }
+       DEBUG(enumerator->ctx, "Called with a stack of %u nodes\n",
+               enumerator->network_stack_depth);
+-- 
+2.20.1
+
+From d33753688138c9938743dafbbdddf220dd2afd14 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 11:29:15 +0000
+Subject: [PATCH 51/70] network: Sort result of excluded lists
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 3 ---
+ src/network.c  | 3 +++
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 0f3cdc2..6849d97 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1315,9 +1315,6 @@ static int __loc_database_enumerator_next_network_flattened(
+               goto END;
+       }
+-      // Sort the result
+-      loc_network_list_sort(excluded);
+-
+       // Reverse the list
+       loc_network_list_reverse(excluded);
+diff --git a/src/network.c b/src/network.c
+index d67f116..9d02bf8 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -773,6 +773,9 @@ LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
+       loc_network_list_unref(to_check);
++      // Sort the result
++      loc_network_list_sort(subnets);
++
+       return subnets;
+ }
+-- 
+2.20.1
+
+From 8d777f12f7ffa4df1b28d197563888296803b727 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 11:38:15 +0000
+Subject: [PATCH 52/70] network: Add function to pop first element from stack
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c    |  6 ++----
+ src/libloc.sym    |  1 +
+ src/loc/network.h |  1 +
+ src/network.c     | 19 +++++++++++++++++++
+ 4 files changed, 23 insertions(+), 4 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 6849d97..b9d870f 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1315,15 +1315,13 @@ static int __loc_database_enumerator_next_network_flattened(
+               goto END;
+       }
+-      // Reverse the list
+-      loc_network_list_reverse(excluded);
+-
+       // Replace network with the first one
+       loc_network_unref(*network);
+-      *network = loc_network_list_pop(excluded);
++      *network = loc_network_list_pop_first(excluded);
+       // Push the rest onto the stack
++      loc_network_list_reverse(excluded);
+       loc_network_list_merge(enumerator->stack, excluded);
+       loc_network_list_unref(excluded);
+diff --git a/src/libloc.sym b/src/libloc.sym
+index bcd11be..6139db6 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -113,6 +113,7 @@ global:
+       loc_network_list_merge;
+       loc_network_list_new;
+       loc_network_list_pop;
++      loc_network_list_pop_first;
+       loc_network_list_push;
+       loc_network_list_ref;
+       loc_network_list_reverse;
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 40712b9..203e61c 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -76,6 +76,7 @@ void loc_network_list_dump(struct loc_network_list* list);
+ struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
+ int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
+ struct loc_network* loc_network_list_pop(struct loc_network_list* list);
++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);
+ void loc_network_list_sort(struct loc_network_list* list);
+ void loc_network_list_reverse(struct loc_network_list* list);
+diff --git a/src/network.c b/src/network.c
+index 9d02bf8..e7dc97e 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -1231,6 +1231,25 @@ LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* lis
+       return network;
+ }
++LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
++      // Return nothing when empty
++      if (loc_network_list_empty(list)) {
++              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
++              return NULL;
++      }
++
++      struct loc_network* network = list->list[0];
++
++      // Move all elements to the top of the stack
++      for (unsigned int i = 0; i < --list->size; i++) {
++              list->list[i] = list->list[i+1];
++      }
++
++      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
++
++      return network;
++}
++
+ LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
+       for (unsigned int i = 0; i < list->size; i++) {
+               if (loc_network_eq(list->list[i], network))
+-- 
+2.20.1
+
+From 7933f5bfb4dd7603cb646e192840762bf6394292 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 11:43:53 +0000
+Subject: [PATCH 53/70] network: Unexport all tree functions
+
+These should not be exported
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network.c      | 28 ++++++++++++++--------------
+ src/test-network.c |  9 +++++++++
+ 2 files changed, 23 insertions(+), 14 deletions(-)
+
+diff --git a/src/network.c b/src/network.c
+index e7dc97e..d015579 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -847,7 +847,7 @@ struct loc_network_tree_node {
+       struct loc_network* network;
+ };
+-LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
++int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
+       struct loc_network_tree* t = calloc(1, sizeof(*t));
+       if (!t)
+               return -ENOMEM;
+@@ -867,7 +867,7 @@ LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree
+       return 0;
+ }
+-LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
++struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
+       return loc_network_tree_node_ref(tree->root);
+ }
+@@ -939,7 +939,7 @@ static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_
+       return 0;
+ }
+-LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
++int loc_network_tree_walk(struct loc_network_tree* tree,
+               int(*filter_callback)(struct loc_network* network, void* data),
+               int(*callback)(struct loc_network* network, void* data), void* data) {
+       return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
+@@ -954,7 +954,7 @@ static void loc_network_tree_free(struct loc_network_tree* tree) {
+       free(tree);
+ }
+-LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
++struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
+       if (--tree->refcount > 0)
+               return tree;
+@@ -975,13 +975,13 @@ static int __loc_network_tree_dump(struct loc_network* network, void* data) {
+       return 0;
+ }
+-LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
++int loc_network_tree_dump(struct loc_network_tree* tree) {
+       DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
+       return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
+ }
+-LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
++int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
+       DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
+       struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
+@@ -1012,7 +1012,7 @@ static int __loc_network_tree_count(struct loc_network* network, void* data) {
+       return 0;
+ }
+-LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
++size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
+       size_t counter = 0;
+       int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
+@@ -1034,11 +1034,11 @@ static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node)
+       return counter;
+ }
+-LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
++size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
+       return __loc_network_tree_count_nodes(tree->root);
+ }
+-LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
++int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
+       struct loc_network_tree_node* n = calloc(1, sizeof(*n));
+       if (!n)
+               return -ENOMEM;
+@@ -1053,7 +1053,7 @@ LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network
+       return 0;
+ }
+-LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
++struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
+       if (node)
+               node->refcount++;
+@@ -1076,7 +1076,7 @@ static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
+       free(node);
+ }
+-LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
++struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
+       if (!node)
+               return NULL;
+@@ -1087,7 +1087,7 @@ LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_
+       return NULL;
+ }
+-LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
++struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
+       if (index == 0)
+               node = node->zero;
+       else
+@@ -1099,11 +1099,11 @@ LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_ne
+       return loc_network_tree_node_ref(node);
+ }
+-LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
++int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
+       return (!!node->network);
+ }
+-LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
++struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
+       return loc_network_ref(node->network);
+ }
+diff --git a/src/test-network.c b/src/test-network.c
+index af1b2e6..7c90224 100644
+--- a/src/test-network.c
++++ b/src/test-network.c
+@@ -37,12 +37,14 @@ int main(int argc, char** argv) {
+       // Enable debug logging
+       loc_set_log_priority(ctx, LOG_DEBUG);
++#if 0
+       struct loc_network_tree* tree;
+       err = loc_network_tree_new(ctx, &tree);
+       if (err) {
+               fprintf(stderr, "Could not create the network tree\n");
+               exit(EXIT_FAILURE);
+       }
++#endif
+       // Create a network
+       struct loc_network* network1;
+@@ -58,12 +60,14 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++#if 0
+       // Adding network to the tree
+       err = loc_network_tree_add_network(tree, network1);
+       if (err) {
+               fprintf(stderr, "Could not add network to the tree\n");
+               exit(EXIT_FAILURE);
+       }
++#endif
+       // Check if the first and last addresses are correct
+       char* string = loc_network_format_first_address(network1);
+@@ -101,6 +105,7 @@ int main(int argc, char** argv) {
+               exit(EXIT_FAILURE);
+       }
++#if 0
+       // Adding network to the tree
+       err = loc_network_tree_add_network(tree, network2);
+       if (err) {
+@@ -117,6 +122,7 @@ int main(int argc, char** argv) {
+       size_t nodes = loc_network_tree_count_nodes(tree);
+       printf("The tree has %zu nodes\n", nodes);
++#endif
+       // Check equals function
+       err = loc_network_eq(network1, network1);
+@@ -260,7 +266,10 @@ int main(int argc, char** argv) {
+       loc_network_unref(network2);
+       loc_network_unref(network3);
+       loc_network_unref(network4);
++
++#if 0
+       loc_network_tree_unref(tree);
++#endif
+       // And open it again from disk
+       struct loc_database* db;
+-- 
+2.20.1
+
+From c242f7325bd6fc4ba26047ac24196d1c161c6e01 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Fri, 13 Nov 2020 12:09:03 +0000
+Subject: [PATCH 54/70] python: Move tree flattening into C
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/database.c |   8 ++-
+ src/python/export.py  | 138 +++++-------------------------------------
+ 2 files changed, 21 insertions(+), 125 deletions(-)
+
+diff --git a/src/python/database.c b/src/python/database.c
+index 7f8c2c2..d169547 100644
+--- a/src/python/database.c
++++ b/src/python/database.c
+@@ -258,17 +258,19 @@ static PyObject* Database_networks_flattened(DatabaseObject *self) {
+ }
+ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
+-      char* kwlist[] = { "country_code", "asn", "flags", "family", NULL };
++      char* kwlist[] = { "country_code", "asn", "flags", "family", "flatten", NULL };
+       const char* country_code = NULL;
+       unsigned int asn = 0;
+       int flags = 0;
+       int family = 0;
++      int flatten = 0;
+-      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siii", kwlist, &country_code, &asn, &flags, &family))
++      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiip", kwlist, &country_code, &asn, &flags, &family, &flatten))
+               return NULL;
+       struct loc_database_enumerator* enumerator;
+-      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS, 0);
++      int r = loc_database_enumerator_new(&enumerator, self->db, LOC_DB_ENUMERATE_NETWORKS,
++              (flatten) ? LOC_DB_ENUMERATOR_FLAGS_FLATTEN : 0);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_SystemError);
+               return NULL;
+diff --git a/src/python/export.py b/src/python/export.py
+index dd44332..be4a68e 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -87,58 +87,12 @@ class OutputWriter(object):
+       def _write_network(self, network):
+               self.f.write("%s\n" % network)
+-      def write(self, network, subnets):
++      def write(self, network):
+               if self.flatten and self._flatten(network):
+                       log.debug("Skipping writing network %s (last one was %s)" % (network, self._last_network))
+                       return
+-              # Convert network into a Python object
+-              _network = ipaddress.ip_network("%s" % network)
+-
+-              # Write the network when it has no subnets
+-              if not subnets:
+-                      log.debug("Writing %s to %s" % (_network, self.f))
+-                      return self._write_network(_network)
+-
+-              # Convert subnets into Python objects
+-              _subnets = [ipaddress.ip_network("%s" % subnet) for subnet in subnets]
+-
+-              # Split the network into smaller bits so that
+-              # we can accomodate for any gaps in it later
+-              to_check = set()
+-              for _subnet in _subnets:
+-                      to_check.update(
+-                              _network.address_exclude(_subnet)
+-                      )
+-
+-              # Clear the list of all subnets
+-              subnets = []
+-
+-              # Check if all subnets to not overlap with anything else
+-              while to_check:
+-                      subnet_to_check = to_check.pop()
+-
+-                      for _subnet in _subnets:
+-                              # Drop this subnet if it equals one of the subnets
+-                              # or if it is subnet of one of them
+-                              if subnet_to_check == _subnet or subnet_to_check.subnet_of(_subnet):
+-                                      break
+-
+-                              # Break it down if it overlaps
+-                              if subnet_to_check.overlaps(_subnet):
+-                                      to_check.update(
+-                                              subnet_to_check.address_exclude(_subnet)
+-                                      )
+-                                      break
+-
+-                      # Add the subnet again as it passed the check
+-                      else:
+-                              subnets.append(subnet_to_check)
+-
+-              # Write all networks as compact as possible
+-              for network in ipaddress.collapse_addresses(subnets):
+-                      log.debug("Writing %s to %s" % (network, self.f))
+-                      self._write_network(network)
++              return self._write_network(network)
+       def finish(self):
+               """
+@@ -188,7 +142,7 @@ class XTGeoIPOutputWriter(OutputWriter):
+       mode = "wb"
+       def _write_network(self, network):
+-              for address in (network.network_address, network.broadcast_address):
++              for address in (network.first_address, network.last_address):
+                       # Convert this into a string of bits
+                       bytes = socket.inet_pton(
+                               socket.AF_INET6 if network.version == 6 else socket.AF_INET, "%s" % address,
+@@ -231,42 +185,21 @@ class Exporter(object):
+                               writers[asn] = self.writer.open(self.db, filename, prefix="AS%s" % asn)
+                       # Get all networks that match the family
+-                      networks = self.db.search_networks(family=family)
+-
+-                      # Create a stack with all networks in order where we can put items back
+-                      # again and retrieve them in the next iteration.
+-                      networks = BufferedStack(networks)
++                      networks = self.db.search_networks(family=family, flatten=True)
+                       # Walk through all networks
+                       for network in networks:
+-                              # Collect all networks which are a subnet of network
+-                              subnets = []
+-                              for subnet in networks:
+-                                      # If the next subnet was not a subnet, we have to push
+-                                      # it back on the stack and break this loop
+-                                      if not subnet.is_subnet_of(network):
+-                                              networks.push(subnet)
+-                                              break
+-
+-                                      subnets.append(subnet)
+-
+                               # Write matching countries
+-                              if network.country_code and network.country_code in writers:
+-                                      # Mismatching subnets
+-                                      gaps = [
+-                                              subnet for subnet in subnets if not network.country_code == subnet.country_code
+-                                      ]
+-
+-                                      writers[network.country_code].write(network, gaps)
++                              try:
++                                      writers[network.country_code].write(network)
++                              except KeyError:
++                                      pass
+                               # Write matching ASNs
+-                              if network.asn and network.asn in writers:
+-                                      # Mismatching subnets
+-                                      gaps = [
+-                                              subnet for subnet in subnets if not network.asn == subnet.asn
+-                                      ]
+-
+-                                      writers[network.asn].write(network, gaps)
++                              try:
++                                      writers[network.asn].write(network)
++                              except KeyError:
++                                      pass
+                               # Handle flags
+                               for flag in flags:
+@@ -274,19 +207,10 @@ class Exporter(object):
+                                               # Fetch the "fake" country code
+                                               country = flags[flag]
+-                                              if not country in writers:
+-                                                      continue
+-
+-                                              gaps = [
+-                                                      subnet for subnet in subnets
+-                                                              if not subnet.has_flag(flag)
+-                                              ]
+-
+-                                              writers[country].write(network, gaps)
+-
+-                              # Push all subnets back onto the stack
+-                              for subnet in reversed(subnets):
+-                                      networks.push(subnet)
++                                              try:
++                                                      writers[country].write(network)
++                                              except KeyError:
++                                                      pass
+                       # Write everything to the filesystem
+                       for writer in writers.values():
+@@ -298,33 +222,3 @@ class Exporter(object):
+               )
+               return os.path.join(directory, filename)
+-
+-
+-class BufferedStack(object):
+-      """
+-              This class takes an iterator and when being iterated
+-              over it returns objects from that iterator for as long
+-              as there are any.
+-
+-              It additionally has a function to put an item back on
+-              the back so that it will be returned again at the next
+-              iteration.
+-      """
+-      def __init__(self, iterator):
+-              self.iterator = iterator
+-              self.stack = []
+-
+-      def __iter__(self):
+-              return self
+-
+-      def __next__(self):
+-              if self.stack:
+-                      return self.stack.pop(0)
+-
+-              return next(self.iterator)
+-
+-      def push(self, elem):
+-              """
+-                      Takes an element and puts it on the stack
+-              """
+-              self.stack.insert(0, elem)
+-- 
+2.20.1
+
+From e0b9ff5f38beb0d560b16db881647e5a75127df1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Sun, 15 Nov 2020 15:02:28 +0000
+Subject: [PATCH 55/70] Move network lists into an own file
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am            |   2 +
+ src/libloc.sym         |   1 +
+ src/loc/network-list.h |  37 +++++++
+ src/loc/network.h      |  20 +---
+ src/network-list.c     | 224 +++++++++++++++++++++++++++++++++++++++++
+ src/network.c          | 207 +------------------------------------
+ src/python/network.c   |   1 +
+ 7 files changed, 269 insertions(+), 223 deletions(-)
+ create mode 100644 src/loc/network-list.h
+ create mode 100644 src/network-list.c
+
+diff --git a/Makefile.am b/Makefile.am
+index a0431a6..f0d8c4c 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -96,6 +96,7 @@ pkginclude_HEADERS = \
+       src/loc/database.h \
+       src/loc/format.h \
+       src/loc/network.h \
++      src/loc/network-list.h \
+       src/loc/private.h \
+       src/loc/stringpool.h \
+       src/loc/resolv.h \
+@@ -110,6 +111,7 @@ src_libloc_la_SOURCES = \
+       src/country.c \
+       src/database.c \
+       src/network.c \
++      src/network-list.c \
+       src/resolv.c \
+       src/stringpool.c \
+       src/writer.c
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 6139db6..453a1be 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -87,6 +87,7 @@ global:
+       loc_network_format_last_address;
+       loc_network_get_asn;
+       loc_network_get_country_code;
++      loc_network_gt;
+       loc_network_has_flag;
+       loc_network_is_subnet;
+       loc_network_is_subnet_of;
+diff --git a/src/loc/network-list.h b/src/loc/network-list.h
+new file mode 100644
+index 0000000..af3b28d
+--- /dev/null
++++ b/src/loc/network-list.h
+@@ -0,0 +1,37 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#ifndef LIBLOC_NETWORK_LIST_H
++#define LIBLOC_NETWORK_LIST_H
++
++struct loc_network_list;
++int loc_network_list_new(struct loc_ctx* ctx, struct loc_network_list** list);
++struct loc_network_list* loc_network_list_ref(struct loc_network_list* list);
++struct loc_network_list* loc_network_list_unref(struct loc_network_list* list);
++size_t loc_network_list_size(struct loc_network_list* list);
++int loc_network_list_empty(struct loc_network_list* list);
++void loc_network_list_clear(struct loc_network_list* list);
++void loc_network_list_dump(struct loc_network_list* list);
++struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
++int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
++struct loc_network* loc_network_list_pop(struct loc_network_list* list);
++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);
++void loc_network_list_sort(struct loc_network_list* list);
++void loc_network_list_reverse(struct loc_network_list* list);
++int loc_network_list_merge(struct loc_network_list* self, struct loc_network_list* other);
++
++#endif
+diff --git a/src/loc/network.h b/src/loc/network.h
+index 203e61c..d86b685 100644
+--- a/src/loc/network.h
++++ b/src/loc/network.h
+@@ -21,6 +21,7 @@
+ #include <loc/libloc.h>
+ #include <loc/format.h>
++#include <loc/network-list.h>
+ enum loc_network_flags {
+       LOC_NETWORK_FLAG_ANONYMOUS_PROXY    = (1 << 0), // A1
+@@ -55,6 +56,7 @@ int loc_network_set_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_match_flag(struct loc_network* network, uint32_t flag);
+ int loc_network_eq(struct loc_network* self, struct loc_network* other);
++int loc_network_gt(struct loc_network* self, struct loc_network* other);
+ int loc_network_overlaps(struct loc_network* self, struct loc_network* other);
+ int loc_network_is_subnet(struct loc_network* self, struct loc_network* other);
+ int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other);
+@@ -64,24 +66,6 @@ struct loc_network_list* loc_network_exclude(
+ struct loc_network_list* loc_network_exclude_list(
+               struct loc_network* network, struct loc_network_list* list);
+-// List
+-struct loc_network_list;
+-int loc_network_list_new(struct loc_ctx* ctx, struct loc_network_list** list);
+-struct loc_network_list* loc_network_list_ref(struct loc_network_list* list);
+-struct loc_network_list* loc_network_list_unref(struct loc_network_list* list);
+-size_t loc_network_list_size(struct loc_network_list* list);
+-int loc_network_list_empty(struct loc_network_list* list);
+-void loc_network_list_clear(struct loc_network_list* list);
+-void loc_network_list_dump(struct loc_network_list* list);
+-struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index);
+-int loc_network_list_push(struct loc_network_list* list, struct loc_network* network);
+-struct loc_network* loc_network_list_pop(struct loc_network_list* list);
+-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);
+-void loc_network_list_sort(struct loc_network_list* list);
+-void loc_network_list_reverse(struct loc_network_list* list);
+-int loc_network_list_merge(struct loc_network_list* self, struct loc_network_list* other);
+-
+ #ifdef LIBLOC_PRIVATE
+ int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj);
+diff --git a/src/network-list.c b/src/network-list.c
+new file mode 100644
+index 0000000..1f6e80e
+--- /dev/null
++++ b/src/network-list.c
+@@ -0,0 +1,224 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#include <errno.h>
++#include <stdlib.h>
++
++#include <loc/libloc.h>
++#include <loc/network.h>
++#include <loc/private.h>
++
++struct loc_network_list {
++      struct loc_ctx* ctx;
++      int refcount;
++
++      struct loc_network* list[1024];
++      size_t size;
++      size_t max_size;
++};
++
++LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
++              struct loc_network_list** list) {
++      struct loc_network_list* l = calloc(1, sizeof(*l));
++      if (!l)
++              return -ENOMEM;
++
++      l->ctx = loc_ref(ctx);
++      l->refcount = 1;
++
++      // Do not allow this list to grow larger than this
++      l->max_size = 1024;
++
++      DEBUG(l->ctx, "Network list allocated at %p\n", l);
++      *list = l;
++      return 0;
++}
++
++LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
++      list->refcount++;
++
++      return list;
++}
++
++static void loc_network_list_free(struct loc_network_list* list) {
++      DEBUG(list->ctx, "Releasing network list at %p\n", list);
++
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_network_unref(list->list[i]);
++
++      loc_unref(list->ctx);
++      free(list);
++}
++
++LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
++      if (!list)
++              return NULL;
++
++      if (--list->refcount > 0)
++              return list;
++
++      loc_network_list_free(list);
++      return NULL;
++}
++
++LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
++      return list->size;
++}
++
++LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
++      return list->size == 0;
++}
++
++LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_network_unref(list->list[i]);
++
++      list->size = 0;
++}
++
++LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
++      struct loc_network* network;
++      char* s;
++
++      for (unsigned int i = 0; i < list->size; i++) {
++              network = list->list[i];
++
++              s = loc_network_str(network);
++
++              INFO(list->ctx, "%s\n", s);
++              free(s);
++      }
++}
++
++LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
++      // Check index
++      if (index >= list->size)
++              return NULL;
++
++      return loc_network_ref(list->list[index]);
++}
++
++LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
++      // Do not add networks that are already on the list
++      if (loc_network_list_contains(list, network))
++              return 0;
++
++      // Check if we have space left
++      if (list->size == list->max_size) {
++              ERROR(list->ctx, "%p: Could not push network onto the stack: Stack full\n", list);
++              return -ENOMEM;
++      }
++
++      DEBUG(list->ctx, "%p: Pushing network %p onto stack\n", list, network);
++
++      list->list[list->size++] = loc_network_ref(network);
++
++      return 0;
++}
++
++LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
++      // Return nothing when empty
++      if (loc_network_list_empty(list)) {
++              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
++              return NULL;
++      }
++
++      struct loc_network* network = list->list[--list->size];
++
++      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
++
++      return network;
++}
++
++LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
++      // Return nothing when empty
++      if (loc_network_list_empty(list)) {
++              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
++              return NULL;
++      }
++
++      struct loc_network* network = list->list[0];
++
++      // Move all elements to the top of the stack
++      for (unsigned int i = 0; i < --list->size; i++) {
++              list->list[i] = list->list[i+1];
++      }
++
++      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
++
++      return network;
++}
++
++LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
++      for (unsigned int i = 0; i < list->size; i++) {
++              if (loc_network_eq(list->list[i], network))
++                      return 1;
++      }
++
++      return 0;
++}
++
++static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1, unsigned int i2) {
++      // Do nothing for invalid indices
++      if (i1 >= list->size || i2 >= list->size)
++              return;
++
++      struct loc_network* network1 = list->list[i1];
++      struct loc_network* network2 = list->list[i2];
++
++      list->list[i1] = network2;
++      list->list[i2] = network1;
++}
++
++LOC_EXPORT void loc_network_list_reverse(struct loc_network_list* list) {
++      unsigned int i = 0;
++      unsigned int j = list->size - 1;
++
++      while (i < j) {
++              loc_network_list_swap(list, i++, j--);
++      }
++}
++
++LOC_EXPORT void loc_network_list_sort(struct loc_network_list* list) {
++      unsigned int n = list->size;
++      int swapped;
++
++      do {
++              swapped = 0;
++
++              for (unsigned int i = 1; i < n; i++) {
++                      if (loc_network_gt(list->list[i-1], list->list[i]) > 0) {
++                              loc_network_list_swap(list, i-1, i);
++                              swapped = 1;
++                      }
++              }
++
++              n--;
++      } while (swapped);
++}
++
++LOC_EXPORT int loc_network_list_merge(
++              struct loc_network_list* self, struct loc_network_list* other) {
++      int r;
++
++      for (unsigned int i = 0; i < other->size; i++) {
++              r = loc_network_list_push(self, other->list[i]);
++              if (r)
++                      return r;
++      }
++
++      return 0;
++}
+diff --git a/src/network.c b/src/network.c
+index d015579..28ca2df 100644
+--- a/src/network.c
++++ b/src/network.c
+@@ -29,6 +29,7 @@
+ #include <loc/compat.h>
+ #include <loc/country.h>
+ #include <loc/network.h>
++#include <loc/network-list.h>
+ #include <loc/private.h>
+ struct loc_network {
+@@ -436,7 +437,7 @@ LOC_EXPORT int loc_network_eq(struct loc_network* self, struct loc_network* othe
+       return 1;
+ }
+-static int loc_network_gt(struct loc_network* self, struct loc_network* other) {
++LOC_EXPORT int loc_network_gt(struct loc_network* self, struct loc_network* other) {
+       // Families must match
+       if (self->family != other->family)
+               return -1;
+@@ -1106,207 +1107,3 @@ int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
+ struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
+       return loc_network_ref(node->network);
+ }
+-
+-// List
+-
+-struct loc_network_list {
+-      struct loc_ctx* ctx;
+-      int refcount;
+-
+-      struct loc_network* list[1024];
+-      size_t size;
+-      size_t max_size;
+-};
+-
+-LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
+-              struct loc_network_list** list) {
+-      struct loc_network_list* l = calloc(1, sizeof(*l));
+-      if (!l)
+-              return -ENOMEM;
+-
+-      l->ctx = loc_ref(ctx);
+-      l->refcount = 1;
+-
+-      // Do not allow this list to grow larger than this
+-      l->max_size = 1024;
+-
+-      DEBUG(l->ctx, "Network list allocated at %p\n", l);
+-      *list = l;
+-      return 0;
+-}
+-
+-LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
+-      list->refcount++;
+-
+-      return list;
+-}
+-
+-static void loc_network_list_free(struct loc_network_list* list) {
+-      DEBUG(list->ctx, "Releasing network list at %p\n", list);
+-
+-      for (unsigned int i = 0; i < list->size; i++)
+-              loc_network_unref(list->list[i]);
+-
+-      loc_unref(list->ctx);
+-      free(list);
+-}
+-
+-LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
+-      if (!list)
+-              return NULL;
+-
+-      if (--list->refcount > 0)
+-              return list;
+-
+-      loc_network_list_free(list);
+-      return NULL;
+-}
+-
+-LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
+-      return list->size;
+-}
+-
+-LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
+-      return list->size == 0;
+-}
+-
+-LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
+-      for (unsigned int i = 0; i < list->size; i++)
+-              loc_network_unref(list->list[i]);
+-
+-      list->size = 0;
+-}
+-
+-LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
+-      struct loc_network* network;
+-      char* s;
+-
+-      for (unsigned int i = 0; i < list->size; i++) {
+-              network = list->list[i];
+-
+-              s = loc_network_str(network);
+-
+-              INFO(list->ctx, "%s\n", s);
+-              free(s);
+-      }
+-}
+-
+-LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
+-      // Check index
+-      if (index >= list->size)
+-              return NULL;
+-
+-      return loc_network_ref(list->list[index]);
+-}
+-
+-LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
+-      // Do not add networks that are already on the list
+-      if (loc_network_list_contains(list, network))
+-              return 0;
+-
+-      // Check if we have space left
+-      if (list->size == list->max_size) {
+-              ERROR(list->ctx, "%p: Could not push network onto the stack: Stack full\n", list);
+-              return -ENOMEM;
+-      }
+-
+-      DEBUG(list->ctx, "%p: Pushing network %p onto stack\n", list, network);
+-
+-      list->list[list->size++] = loc_network_ref(network);
+-
+-      return 0;
+-}
+-
+-LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
+-      // Return nothing when empty
+-      if (loc_network_list_empty(list)) {
+-              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
+-              return NULL;
+-      }
+-
+-      struct loc_network* network = list->list[--list->size];
+-
+-      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
+-
+-      return network;
+-}
+-
+-LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
+-      // Return nothing when empty
+-      if (loc_network_list_empty(list)) {
+-              DEBUG(list->ctx, "%p: Popped empty stack\n", list);
+-              return NULL;
+-      }
+-
+-      struct loc_network* network = list->list[0];
+-
+-      // Move all elements to the top of the stack
+-      for (unsigned int i = 0; i < --list->size; i++) {
+-              list->list[i] = list->list[i+1];
+-      }
+-
+-      DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
+-
+-      return network;
+-}
+-
+-LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
+-      for (unsigned int i = 0; i < list->size; i++) {
+-              if (loc_network_eq(list->list[i], network))
+-                      return 1;
+-      }
+-
+-      return 0;
+-}
+-
+-static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1, unsigned int i2) {
+-      // Do nothing for invalid indices
+-      if (i1 >= list->size || i2 >= list->size)
+-              return;
+-
+-      struct loc_network* network1 = list->list[i1];
+-      struct loc_network* network2 = list->list[i2];
+-
+-      list->list[i1] = network2;
+-      list->list[i2] = network1;
+-}
+-
+-LOC_EXPORT void loc_network_list_reverse(struct loc_network_list* list) {
+-      unsigned int i = 0;
+-      unsigned int j = list->size - 1;
+-
+-      while (i < j) {
+-              loc_network_list_swap(list, i++, j--);
+-      }
+-}
+-
+-LOC_EXPORT void loc_network_list_sort(struct loc_network_list* list) {
+-      unsigned int n = list->size;
+-      int swapped;
+-
+-      do {
+-              swapped = 0;
+-
+-              for (unsigned int i = 1; i < n; i++) {
+-                      if (loc_network_gt(list->list[i-1], list->list[i]) > 0) {
+-                              loc_network_list_swap(list, i-1, i);
+-                              swapped = 1;
+-                      }
+-              }
+-
+-              n--;
+-      } while (swapped);
+-}
+-
+-LOC_EXPORT int loc_network_list_merge(
+-              struct loc_network_list* self, struct loc_network_list* other) {
+-      int r;
+-
+-      for (unsigned int i = 0; i < other->size; i++) {
+-              r = loc_network_list_push(self, other->list[i]);
+-              if (r)
+-                      return r;
+-      }
+-
+-      return 0;
+-}
+diff --git a/src/python/network.c b/src/python/network.c
+index 11f672b..ed91d65 100644
+--- a/src/python/network.c
++++ b/src/python/network.c
+@@ -20,6 +20,7 @@
+ #include <loc/libloc.h>
+ #include <loc/network.h>
++#include <loc/network-list.h>
+ #include "locationmodule.h"
+ #include "network.h"
+-- 
+2.20.1
+
+From e646a8f35ec7eff009414b3fd107c9af5cf39a86 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 16 Nov 2020 15:13:28 +0000
+Subject: [PATCH 56/70] Implement filtering for multiple countries in the
+ enumerator
+
+This will allow us to speed up the export of the database
+if only a few countries should be returned.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am            |   2 +
+ src/country-list.c     | 138 +++++++++++++++++++++++++++++++++++++++++
+ src/country.c          |   3 +
+ src/database.c         |  47 ++++++--------
+ src/libloc.sym         |  15 ++++-
+ src/loc/country-list.h |  43 +++++++++++++
+ src/loc/database.h     |   5 +-
+ src/python/database.c  |  57 ++++++++++++++---
+ 8 files changed, 274 insertions(+), 36 deletions(-)
+ create mode 100644 src/country-list.c
+ create mode 100644 src/loc/country-list.h
+
+diff --git a/Makefile.am b/Makefile.am
+index f0d8c4c..f4ca3c8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -93,6 +93,7 @@ pkginclude_HEADERS = \
+       src/loc/as.h \
+       src/loc/compat.h \
+       src/loc/country.h \
++      src/loc/country-list.h \
+       src/loc/database.h \
+       src/loc/format.h \
+       src/loc/network.h \
+@@ -109,6 +110,7 @@ src_libloc_la_SOURCES = \
+       src/libloc.c \
+       src/as.c \
+       src/country.c \
++      src/country-list.c \
+       src/database.c \
+       src/network.c \
+       src/network-list.c \
+diff --git a/src/country-list.c b/src/country-list.c
+new file mode 100644
+index 0000000..ae0d71a
+--- /dev/null
++++ b/src/country-list.c
+@@ -0,0 +1,138 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#include <errno.h>
++#include <stdlib.h>
++
++#include <loc/country.h>
++#include <loc/country-list.h>
++#include <loc/private.h>
++
++struct loc_country_list {
++      struct loc_ctx* ctx;
++      int refcount;
++
++      struct loc_country* list[1024];
++      size_t size;
++      size_t max_size;
++};
++
++LOC_EXPORT int loc_country_list_new(struct loc_ctx* ctx,
++              struct loc_country_list** list) {
++      struct loc_country_list* l = calloc(1, sizeof(*l));
++      if (!l)
++              return -ENOMEM;
++
++      l->ctx = loc_ref(ctx);
++      l->refcount = 1;
++
++      // Do not allow this list to grow larger than this
++      l->max_size = 1024;
++
++      DEBUG(l->ctx, "Country list allocated at %p\n", l);
++      *list = l;
++
++      return 0;
++}
++
++LOC_EXPORT struct loc_country_list* loc_country_list_ref(struct loc_country_list* list) {
++      list->refcount++;
++
++      return list;
++}
++
++static void loc_country_list_free(struct loc_country_list* list) {
++      DEBUG(list->ctx, "Releasing country list at %p\n", list);
++
++      loc_country_list_clear(list);
++
++      loc_unref(list->ctx);
++      free(list);
++}
++
++LOC_EXPORT struct loc_country_list* loc_country_list_unref(struct loc_country_list* list) {
++      if (!list)
++              return NULL;
++
++      if (--list->refcount > 0)
++              return list;
++
++      loc_country_list_free(list);
++      return NULL;
++}
++
++LOC_EXPORT size_t loc_country_list_size(struct loc_country_list* list) {
++      return list->size;
++}
++
++LOC_EXPORT int loc_country_list_empty(struct loc_country_list* list) {
++      return list->size == 0;
++}
++
++LOC_EXPORT void loc_country_list_clear(struct loc_country_list* list) {
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_country_unref(list->list[i]);
++}
++
++LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index) {
++      // Check index
++      if (index >= list->size)
++              return NULL;
++
++      return loc_country_ref(list->list[index]);
++}
++
++LOC_EXPORT int loc_country_list_append(
++              struct loc_country_list* list, struct loc_country* country) {
++      if (loc_country_list_contains(list, country))
++              return 0;
++
++      // Check if we have space left
++      if (list->size == list->max_size) {
++              ERROR(list->ctx, "%p: Could not append country to the list. List full\n", list);
++              return -ENOMEM;
++      }
++
++      DEBUG(list->ctx, "%p: Appending country %p to list\n", list, country);
++
++      list->list[list->size++] = loc_country_ref(country);
++
++      return 0;
++}
++
++LOC_EXPORT int loc_country_list_contains(
++              struct loc_country_list* list, struct loc_country* country) {
++      for (unsigned int i = 0; i < list->size; i++) {
++              if (loc_country_cmp(country, list->list[i]) == 0)
++                      return 1;
++      }
++
++      return 0;
++}
++
++LOC_EXPORT int loc_country_list_contains_code(
++              struct loc_country_list* list, const char* code) {
++      struct loc_country* country;
++
++      int r = loc_country_new(list->ctx, &country, code);
++      if (r)
++              return -1;
++
++      r = loc_country_list_contains(list, country);
++      loc_country_unref(country);
++
++      return r;
++}
+diff --git a/src/country.c b/src/country.c
+index 2ba93e6..7aac0db 100644
+--- a/src/country.c
++++ b/src/country.c
+@@ -34,6 +34,9 @@ struct loc_country {
+ };
+ LOC_EXPORT int loc_country_new(struct loc_ctx* ctx, struct loc_country** country, const char* country_code) {
++      if (!loc_country_code_is_valid(country_code))
++              return -EINVAL;
++
+       struct loc_country* c = calloc(1, sizeof(*c));
+       if (!c)
+               return -ENOMEM;
+diff --git a/src/database.c b/src/database.c
+index b9d870f..29823b2 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -40,6 +40,7 @@
+ #include <loc/as.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>
+@@ -99,7 +100,7 @@ struct loc_database_enumerator {
+       // Search string
+       char* string;
+-      char country_code[3];
++      struct loc_country_list* countries;
+       uint32_t asn;
+       enum loc_network_flags flags;
+       int family;
+@@ -1017,33 +1018,20 @@ LOC_EXPORT int loc_database_enumerator_set_string(struct loc_database_enumerator
+       return 0;
+ }
+-LOC_EXPORT int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code) {
+-      // Set empty country code
+-      if (!country_code || !*country_code) {
+-              *enumerator->country_code = '\0';
+-              return 0;
+-      }
++LOC_EXPORT struct loc_country_list* loc_database_enumerator_get_countries(
++              struct loc_database_enumerator* enumerator) {
++      if (!enumerator->countries)
++              return NULL;
+-      // Treat A1, A2, A3 as special country codes,
+-      // but perform search for flags instead
+-      if (strcmp(country_code, "A1") == 0) {
+-              return loc_database_enumerator_set_flag(enumerator,
+-                      LOC_NETWORK_FLAG_ANONYMOUS_PROXY);
+-      } else if (strcmp(country_code, "A2") == 0) {
+-              return loc_database_enumerator_set_flag(enumerator,
+-                      LOC_NETWORK_FLAG_SATELLITE_PROVIDER);
+-      } else if (strcmp(country_code, "A3") == 0) {
+-              return loc_database_enumerator_set_flag(enumerator,
+-                      LOC_NETWORK_FLAG_ANYCAST);
+-      }
++      return loc_country_list_ref(enumerator->countries);
++}
+-      // Country codes must be two characters
+-      if (!loc_country_code_is_valid(country_code))
+-              return -EINVAL;
++LOC_EXPORT int loc_database_enumerator_set_countries(
++              struct loc_database_enumerator* enumerator, struct loc_country_list* countries) {
++      if (enumerator->countries)
++              loc_country_list_unref(enumerator->countries);
+-      for (unsigned int i = 0; i < 3; i++) {
+-              enumerator->country_code[i] = country_code[i];
+-      }
++      enumerator->countries = loc_country_list_ref(countries);
+       return 0;
+ }
+@@ -1129,6 +1117,12 @@ static int loc_database_enumerator_stack_push_node(
+       return 0;
+ }
++static int loc_network_match_countries(struct loc_network* network, struct loc_country_list* countries) {
++      const char* country_code = loc_network_get_country_code(network);
++
++      return loc_country_list_contains_code(countries, country_code);
++}
++
+ static int loc_database_enumerator_filter_network(
+               struct loc_database_enumerator* enumerator, struct loc_network* network) {
+       // Skip if the family does not match
+@@ -1136,8 +1130,7 @@ static int loc_database_enumerator_filter_network(
+               return 1;
+       // Skip if the country code does not match
+-      if (*enumerator->country_code &&
+-                      !loc_network_match_country_code(network, enumerator->country_code))
++      if (enumerator->countries && !loc_network_match_countries(network, enumerator->countries))
+               return 1;
+       // Skip if the ASN does not match
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 453a1be..40e9f88 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -49,6 +49,18 @@ global:
+       loc_country_set_name;
+       loc_country_unref;
++      # Country List
++      loc_country_list_append;
++      loc_country_list_clear;
++      loc_country_list_contains;
++      loc_country_list_contains_code;
++      loc_country_list_empty;
++      loc_country_list_get;
++      loc_country_list_new;
++      loc_country_list_ref;
++      loc_country_list_size;
++      loc_country_list_unref;
++
+       # Database
+       loc_database_add_as;
+       loc_database_count_as;
+@@ -66,13 +78,14 @@ global:
+       loc_database_verify;
+       # Database Enumerator
++      loc_database_enumerator_get_countries;
+       loc_database_enumerator_new;
+       loc_database_enumerator_next_as;
+       loc_database_enumerator_next_country;
+       loc_database_enumerator_next_network;
+       loc_database_enumerator_ref;
+       loc_database_enumerator_set_asn;
+-      loc_database_enumerator_set_country_code;
++      loc_database_enumerator_set_countries;
+       loc_database_enumerator_set_family;
+       loc_database_enumerator_set_flag;
+       loc_database_enumerator_set_string;
+diff --git a/src/loc/country-list.h b/src/loc/country-list.h
+new file mode 100644
+index 0000000..a7f818a
+--- /dev/null
++++ b/src/loc/country-list.h
+@@ -0,0 +1,43 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#ifndef LIBLOC_COUNTRY_LIST_H
++#define LIBLOC_COUNTRY_LIST_H
++
++#include <stdlib.h>
++
++#include <loc/libloc.h>
++#include <loc/country.h>
++
++struct loc_country_list;
++
++int loc_country_list_new(struct loc_ctx* ctx, struct loc_country_list** list);
++struct loc_country_list* loc_country_list_ref(struct loc_country_list* list);
++struct loc_country_list* loc_country_list_unref(struct loc_country_list* list);
++
++size_t loc_country_list_size(struct loc_country_list* list);
++int loc_country_list_empty(struct loc_country_list* list);
++void loc_country_list_clear(struct loc_country_list* list);
++
++struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index);
++int loc_country_list_append(struct loc_country_list* list, struct loc_country* country);
++
++int loc_country_list_contains(
++      struct loc_country_list* list, struct loc_country* country);
++int loc_country_list_contains_code(
++      struct loc_country_list* list, const char* code);
++
++#endif
+diff --git a/src/loc/database.h b/src/loc/database.h
+index 14eb5ea..246e5c5 100644
+--- a/src/loc/database.h
++++ b/src/loc/database.h
+@@ -25,6 +25,7 @@
+ #include <loc/network.h>
+ #include <loc/as.h>
+ #include <loc/country.h>
++#include <loc/country-list.h>
+ struct loc_database;
+ int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f);
+@@ -66,7 +67,9 @@ struct loc_database_enumerator* loc_database_enumerator_ref(struct loc_database_
+ struct loc_database_enumerator* loc_database_enumerator_unref(struct loc_database_enumerator* enumerator);
+ int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerator, const char* string);
+-int loc_database_enumerator_set_country_code(struct loc_database_enumerator* enumerator, const char* country_code);
++struct loc_country_list* loc_database_enumerator_get_countries(struct loc_database_enumerator* enumerator);
++int loc_database_enumerator_set_countries(
++      struct loc_database_enumerator* enumerator, struct loc_country_list* countries);
+ int loc_database_enumerator_set_asn(struct loc_database_enumerator* enumerator, unsigned int asn);
+ int loc_database_enumerator_set_flag(struct loc_database_enumerator* enumerator, enum loc_network_flags flag);
+ int loc_database_enumerator_set_family(struct loc_database_enumerator* enumerator, int family);
+diff --git a/src/python/database.c b/src/python/database.c
+index d169547..e6f6f37 100644
+--- a/src/python/database.c
++++ b/src/python/database.c
+@@ -258,14 +258,15 @@ static PyObject* Database_networks_flattened(DatabaseObject *self) {
+ }
+ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
+-      char* kwlist[] = { "country_code", "asn", "flags", "family", "flatten", NULL };
+-      const char* country_code = NULL;
++      char* kwlist[] = { "country_codes", "asn", "flags", "family", "flatten", NULL };
++      PyObject* country_codes = NULL;
+       unsigned int asn = 0;
+       int flags = 0;
+       int family = 0;
+       int flatten = 0;
+-      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|siiip", kwlist, &country_code, &asn, &flags, &family, &flatten))
++      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!iiip", kwlist,
++                      &PyList_Type, &country_codes, &asn, &flags, &family, &flatten))
+               return NULL;
+       struct loc_database_enumerator* enumerator;
+@@ -277,13 +278,55 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
+       }
+       // Set country code we are searching for
+-      if (country_code) {
+-              r = loc_database_enumerator_set_country_code(enumerator, country_code);
+-
++      if (country_codes) {
++              struct loc_country_list* countries;
++              r = loc_country_list_new(loc_ctx, &countries);
+               if (r) {
+-                      PyErr_SetFromErrno(PyExc_SystemError);
++                      PyErr_SetString(PyExc_SystemError, "Could not create country list");
+                       return NULL;
+               }
++
++              for (unsigned int i = 0; i < PyList_Size(country_codes); i++) {
++                      PyObject* item = PyList_GetItem(country_codes, i);
++
++                      if (!PyUnicode_Check(item)) {
++                              PyErr_SetString(PyExc_TypeError, "Country codes must be strings");
++                              loc_country_list_unref(countries);
++                              return NULL;
++                      }
++
++                      const char* country_code = PyUnicode_AsUTF8(item);
++
++                      struct loc_country* country;
++                      r = loc_country_new(loc_ctx, &country, country_code);
++                      if (r) {
++                              if (r == -EINVAL) {
++                                      PyErr_Format(PyExc_ValueError, "Invalid country code: %s", country_code);
++                              } else {
++                                      PyErr_SetString(PyExc_SystemError, "Could not create country");
++                              }
++
++                              loc_country_list_unref(countries);
++                              return NULL;
++                      }
++
++                      // Append it to the list
++                      r = loc_country_list_append(countries, country);
++                      if (r) {
++                              PyErr_SetString(PyExc_SystemError, "Could not append country to the list");
++
++                              loc_country_list_unref(countries);
++                              loc_country_unref(country);
++                              return NULL;
++                      }
++
++                      loc_country_unref(country);
++              }
++
++              loc_database_enumerator_set_countries(enumerator, countries);
++
++              Py_DECREF(country_codes);
++              loc_country_list_unref(countries);
+       }
+       // Set the ASN we are searching for
+-- 
+2.20.1
+
+From 7af51f8a579c79714992a3e175036fb511139310 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 16 Nov 2020 15:20:50 +0000
+Subject: [PATCH 57/70] python: Only return country codes we want
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index be4a68e..5e7fe53 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -184,8 +184,14 @@ class Exporter(object):
+                               writers[asn] = self.writer.open(self.db, filename, prefix="AS%s" % asn)
++                      # Filter countries from special country codes
++                      country_codes = [
++                              country_code for country_code in countries if not country_code in flags.values()
++                      ]
++
+                       # Get all networks that match the family
+-                      networks = self.db.search_networks(family=family, flatten=True)
++                      networks = self.db.search_networks(family=family,
++                              country_codes=country_codes, flatten=True)
+                       # Walk through all networks
+                       for network in networks:
+-- 
+2.20.1
+
+From bd1dc6bf6fe4ce40bf12e7426e283b31afd274e1 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Mon, 16 Nov 2020 15:25:15 +0000
+Subject: [PATCH 58/70] database: Filter flags in C
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index 5e7fe53..739742f 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -29,7 +29,7 @@ import _location
+ log = logging.getLogger("location.export")
+ log.propagate = 1
+-flags = {
++FLAGS = {
+       _location.NETWORK_FLAG_ANONYMOUS_PROXY    : "A1",
+       _location.NETWORK_FLAG_SATELLITE_PROVIDER : "A2",
+       _location.NETWORK_FLAG_ANYCAST            : "A3",
+@@ -186,12 +186,18 @@ class Exporter(object):
+                       # Filter countries from special country codes
+                       country_codes = [
+-                              country_code for country_code in countries if not country_code in flags.values()
++                              country_code for country_code in countries if not country_code in FLAGS.values()
+                       ]
++                      # Collect flags
++                      flags = 0
++                      for flag in FLAGS:
++                              if FLAGS[flag] in countries:
++                                      flags |= flag
++
+                       # Get all networks that match the family
+                       networks = self.db.search_networks(family=family,
+-                              country_codes=country_codes, flatten=True)
++                              country_codes=country_codes, flags=flags, flatten=True)
+                       # Walk through all networks
+                       for network in networks:
+@@ -208,10 +214,10 @@ class Exporter(object):
+                                       pass
+                               # Handle flags
+-                              for flag in flags:
++                              for flag in FLAGS:
+                                       if network.has_flag(flag):
+                                               # Fetch the "fake" country code
+-                                              country = flags[flag]
++                                              country = FLAGS[flag]
+                                               try:
+                                                       writers[country].write(network)
+-- 
+2.20.1
+
+From 84a2f0c2d9cbf8ae4225802c29ccba86561c77ed Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 16:46:48 +0000
+Subject: [PATCH 59/70] as: Add list for easier processing
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ Makefile.am           |   2 +
+ src/as-list.c         | 138 ++++++++++++++++++++++++++++++++++++++++++
+ src/database.c        |  29 +++++++--
+ src/libloc.sym        |  15 ++++-
+ src/loc/as-list.h     |  41 +++++++++++++
+ src/loc/database.h    |   5 +-
+ src/python/database.c |  58 ++++++++++++++++--
+ src/python/export.py  |   2 +-
+ 8 files changed, 275 insertions(+), 15 deletions(-)
+ create mode 100644 src/as-list.c
+ create mode 100644 src/loc/as-list.h
+
+diff --git a/Makefile.am b/Makefile.am
+index f4ca3c8..d0cc793 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -91,6 +91,7 @@ EXTRA_DIST += \
+ pkginclude_HEADERS = \
+       src/loc/libloc.h \
+       src/loc/as.h \
++      src/loc/as-list.h \
+       src/loc/compat.h \
+       src/loc/country.h \
+       src/loc/country-list.h \
+@@ -109,6 +110,7 @@ lib_LTLIBRARIES = \
+ src_libloc_la_SOURCES = \
+       src/libloc.c \
+       src/as.c \
++      src/as-list.c \
+       src/country.c \
+       src/country-list.c \
+       src/database.c \
+diff --git a/src/as-list.c b/src/as-list.c
+new file mode 100644
+index 0000000..7c69eb0
+--- /dev/null
++++ b/src/as-list.c
+@@ -0,0 +1,138 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#include <errno.h>
++#include <stdlib.h>
++
++#include <loc/as.h>
++#include <loc/as-list.h>
++#include <loc/private.h>
++
++struct loc_as_list {
++      struct loc_ctx* ctx;
++      int refcount;
++
++      struct loc_as* list[1024];
++      size_t size;
++      size_t max_size;
++};
++
++LOC_EXPORT int loc_as_list_new(struct loc_ctx* ctx,
++              struct loc_as_list** list) {
++      struct loc_as_list* l = calloc(1, sizeof(*l));
++      if (!l)
++              return -ENOMEM;
++
++      l->ctx = loc_ref(ctx);
++      l->refcount = 1;
++
++      // Do not allow this list to grow larger than this
++      l->max_size = 1024;
++
++      DEBUG(l->ctx, "AS list allocated at %p\n", l);
++      *list = l;
++
++      return 0;
++}
++
++LOC_EXPORT struct loc_as_list* loc_as_list_ref(struct loc_as_list* list) {
++      list->refcount++;
++
++      return list;
++}
++
++static void loc_as_list_free(struct loc_as_list* list) {
++      DEBUG(list->ctx, "Releasing AS list at %p\n", list);
++
++      loc_as_list_clear(list);
++
++      loc_unref(list->ctx);
++      free(list);
++}
++
++LOC_EXPORT struct loc_as_list* loc_as_list_unref(struct loc_as_list* list) {
++      if (!list)
++              return NULL;
++
++      if (--list->refcount > 0)
++              return list;
++
++      loc_as_list_free(list);
++      return NULL;
++}
++
++LOC_EXPORT size_t loc_as_list_size(struct loc_as_list* list) {
++      return list->size;
++}
++
++LOC_EXPORT int loc_as_list_empty(struct loc_as_list* list) {
++      return list->size == 0;
++}
++
++LOC_EXPORT void loc_as_list_clear(struct loc_as_list* list) {
++      for (unsigned int i = 0; i < list->size; i++)
++              loc_as_unref(list->list[i]);
++}
++
++LOC_EXPORT struct loc_as* loc_as_list_get(struct loc_as_list* list, size_t index) {
++      // Check index
++      if (index >= list->size)
++              return NULL;
++
++      return loc_as_ref(list->list[index]);
++}
++
++LOC_EXPORT int loc_as_list_append(
++              struct loc_as_list* list, struct loc_as* as) {
++      if (loc_as_list_contains(list, as))
++              return 0;
++
++      // Check if we have space left
++      if (list->size == list->max_size) {
++              ERROR(list->ctx, "%p: Could not append AS to the list. List full\n", list);
++              return -ENOMEM;
++      }
++
++      DEBUG(list->ctx, "%p: Appending AS %p to list\n", list, as);
++
++      list->list[list->size++] = loc_as_ref(as);
++
++      return 0;
++}
++
++LOC_EXPORT int loc_as_list_contains(
++              struct loc_as_list* list, struct loc_as* as) {
++      for (unsigned int i = 0; i < list->size; i++) {
++              if (loc_as_cmp(as, list->list[i]) == 0)
++                      return 1;
++      }
++
++      return 0;
++}
++
++LOC_EXPORT int loc_as_list_contains_number(
++              struct loc_as_list* list, uint32_t number) {
++      struct loc_as* as;
++
++      int r = loc_as_new(list->ctx, &as, number);
++      if (r)
++              return -1;
++
++      r = loc_as_list_contains(list, as);
++      loc_as_unref(as);
++
++      return r;
++}
+diff --git a/src/database.c b/src/database.c
+index 29823b2..51cb5cd 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -38,6 +38,7 @@
+ #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>
+@@ -101,7 +102,7 @@ struct loc_database_enumerator {
+       // Search string
+       char* string;
+       struct loc_country_list* countries;
+-      uint32_t asn;
++      struct loc_as_list* asns;
+       enum loc_network_flags flags;
+       int family;
+@@ -1036,9 +1037,20 @@ LOC_EXPORT int loc_database_enumerator_set_countries(
+       return 0;
+ }
+-LOC_EXPORT int loc_database_enumerator_set_asn(
+-              struct loc_database_enumerator* enumerator, unsigned int asn) {
+-      enumerator->asn = asn;
++LOC_EXPORT struct loc_as_list* loc_database_enumerator_get_asns(
++              struct loc_database_enumerator* enumerator) {
++      if (!enumerator->asns)
++              return NULL;
++
++      return loc_as_list_ref(enumerator->asns);
++}
++
++LOC_EXPORT int loc_database_enumerator_set_asns(
++              struct loc_database_enumerator* enumerator, struct loc_as_list* asns) {
++      if (enumerator->asns)
++              loc_as_list_unref(enumerator->asns);
++
++      enumerator->asns = loc_as_list_ref(asns);
+       return 0;
+ }
+@@ -1123,6 +1135,12 @@ static int loc_network_match_countries(struct loc_network* network, struct loc_c
+       return loc_country_list_contains_code(countries, country_code);
+ }
++static int loc_network_match_asns(struct loc_network* network, struct loc_as_list* asns) {
++      uint32_t asn = loc_network_get_asn(network);
++
++      return loc_as_list_contains_number(asns, asn);
++}
++
+ static int loc_database_enumerator_filter_network(
+               struct loc_database_enumerator* enumerator, struct loc_network* network) {
+       // Skip if the family does not match
+@@ -1134,8 +1152,7 @@ static int loc_database_enumerator_filter_network(
+               return 1;
+       // Skip if the ASN does not match
+-      if (enumerator->asn &&
+-                      !loc_network_match_asn(network, enumerator->asn))
++      if (enumerator->asns && !loc_network_match_asns(network, enumerator->asns))
+               return 1;
+       // Skip if flags do not match
+diff --git a/src/libloc.sym b/src/libloc.sym
+index 40e9f88..53273cd 100644
+--- a/src/libloc.sym
++++ b/src/libloc.sym
+@@ -37,6 +37,18 @@ global:
+       loc_as_set_name;
+       loc_as_unref;
++      # AS List
++      loc_as_list_append;
++      loc_as_list_clear;
++      loc_as_list_contains;
++      loc_as_list_contains_number;
++      loc_as_list_empty;
++      loc_as_list_get;
++      loc_as_list_new;
++      loc_as_list_ref;
++      loc_as_list_size;
++      loc_as_list_unref;
++
+       # Country
+       loc_country_cmp;
+       loc_country_code_is_valid;
+@@ -78,13 +90,14 @@ global:
+       loc_database_verify;
+       # Database Enumerator
++      loc_database_enumerator_get_asns;
+       loc_database_enumerator_get_countries;
+       loc_database_enumerator_new;
+       loc_database_enumerator_next_as;
+       loc_database_enumerator_next_country;
+       loc_database_enumerator_next_network;
+       loc_database_enumerator_ref;
+-      loc_database_enumerator_set_asn;
++      loc_database_enumerator_set_asns;
+       loc_database_enumerator_set_countries;
+       loc_database_enumerator_set_family;
+       loc_database_enumerator_set_flag;
+diff --git a/src/loc/as-list.h b/src/loc/as-list.h
+new file mode 100644
+index 0000000..7b5c4e8
+--- /dev/null
++++ b/src/loc/as-list.h
+@@ -0,0 +1,41 @@
++/*
++      libloc - A library to determine the location of someone on the Internet
++
++      Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
++
++      This library is free software; you can redistribute it and/or
++      modify it under the terms of the GNU Lesser General Public
++      License as published by the Free Software Foundation; either
++      version 2.1 of the License, or (at your option) any later version.
++
++      This library is distributed in the hope that it will be useful,
++      but WITHOUT ANY WARRANTY; without even the implied warranty of
++      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++      Lesser General Public License for more details.
++*/
++
++#ifndef LIBLOC_AS_LIST_H
++#define LIBLOC_AS_LIST_H
++
++#include <loc/as.h>
++#include <loc/libloc.h>
++
++struct loc_as_list;
++
++int loc_as_list_new(struct loc_ctx* ctx, struct loc_as_list** list);
++struct loc_as_list* loc_as_list_ref(struct loc_as_list* list);
++struct loc_as_list* loc_as_list_unref(struct loc_as_list* list);
++
++size_t loc_as_list_size(struct loc_as_list* list);
++int loc_as_list_empty(struct loc_as_list* list);
++void loc_as_list_clear(struct loc_as_list* list);
++
++struct loc_as* loc_as_list_get(struct loc_as_list* list, size_t index);
++int loc_as_list_append(struct loc_as_list* list, struct loc_as* as);
++
++int loc_as_list_contains(
++      struct loc_as_list* list, struct loc_as* as);
++int loc_as_list_contains_number(
++      struct loc_as_list* list, uint32_t number);
++
++#endif
+diff --git a/src/loc/database.h b/src/loc/database.h
+index 246e5c5..70801f0 100644
+--- a/src/loc/database.h
++++ b/src/loc/database.h
+@@ -70,7 +70,10 @@ int loc_database_enumerator_set_string(struct loc_database_enumerator* enumerato
+ struct loc_country_list* loc_database_enumerator_get_countries(struct loc_database_enumerator* enumerator);
+ int loc_database_enumerator_set_countries(
+       struct loc_database_enumerator* enumerator, struct loc_country_list* countries);
+-int loc_database_enumerator_set_asn(struct loc_database_enumerator* enumerator, unsigned int asn);
++struct loc_as_list* loc_database_enumerator_get_asns(
++      struct loc_database_enumerator* enumerator);
++int loc_database_enumerator_set_asns(
++      struct loc_database_enumerator* enumerator, struct loc_as_list* asns);
+ int loc_database_enumerator_set_flag(struct loc_database_enumerator* enumerator, enum loc_network_flags flag);
+ int loc_database_enumerator_set_family(struct loc_database_enumerator* enumerator, int family);
+ int loc_database_enumerator_next_as(
+diff --git a/src/python/database.c b/src/python/database.c
+index e6f6f37..38a804c 100644
+--- a/src/python/database.c
++++ b/src/python/database.c
+@@ -17,6 +17,8 @@
+ #include <Python.h>
+ #include <loc/libloc.h>
++#include <loc/as.h>
++#include <loc/as-list.h>
+ #include <loc/database.h>
+ #include "locationmodule.h"
+@@ -258,15 +260,15 @@ static PyObject* Database_networks_flattened(DatabaseObject *self) {
+ }
+ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args, PyObject* kwargs) {
+-      char* kwlist[] = { "country_codes", "asn", "flags", "family", "flatten", NULL };
++      char* kwlist[] = { "country_codes", "asns", "flags", "family", "flatten", NULL };
+       PyObject* country_codes = NULL;
+-      unsigned int asn = 0;
++      PyObject* asn_list = NULL;
+       int flags = 0;
+       int family = 0;
+       int flatten = 0;
+-      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!iiip", kwlist,
+-                      &PyList_Type, &country_codes, &asn, &flags, &family, &flatten))
++      if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!O!iip", kwlist,
++                      &PyList_Type, &country_codes, &PyList_Type, &asn_list, &flags, &family, &flatten))
+               return NULL;
+       struct loc_database_enumerator* enumerator;
+@@ -330,13 +332,57 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
+       }
+       // Set the ASN we are searching for
+-      if (asn) {
+-              r = loc_database_enumerator_set_asn(enumerator, asn);
++      if (asn_list) {
++              struct loc_as_list* asns;
++              r = loc_as_list_new(loc_ctx, &asns);
++              if (r) {
++                      PyErr_SetString(PyExc_SystemError, "Could not create AS list");
++                      return NULL;
++              }
++
++              for (unsigned int i = 0; i < PyList_Size(asn_list); i++) {
++                      PyObject* item = PyList_GetItem(asn_list, i);
++
++                      if (!PyLong_Check(item)) {
++                              PyErr_SetString(PyExc_TypeError, "ASNs must be numbers");
++
++                              loc_as_list_unref(asns);
++                              return NULL;
++                      }
++
++                      unsigned long number = PyLong_AsLong(item);
++                      struct loc_as* as;
++                      r = loc_as_new(loc_ctx, &as, number);
++                      if (r) {
++                              PyErr_SetString(PyExc_SystemError, "Could not create AS");
++
++                              loc_as_list_unref(asns);
++                              loc_as_unref(as);
++                              return NULL;
++                      }
++
++                      r = loc_as_list_append(asns, as);
++                      if (r) {
++                              PyErr_SetString(PyExc_SystemError, "Could not append AS to the list");
++
++                              loc_as_list_unref(asns);
++                              loc_as_unref(as);
++                              return NULL;
++                      }
++
++                      loc_as_unref(as);
++              }
++
++              r = loc_database_enumerator_set_asns(enumerator, asns);
+               if (r) {
+                       PyErr_SetFromErrno(PyExc_SystemError);
++
++                      loc_as_list_unref(asns);
+                       return NULL;
+               }
++
++              loc_as_list_unref(asns);
+       }
+       // Set the flags we are searching for
+diff --git a/src/python/export.py b/src/python/export.py
+index 739742f..f675eb3 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -197,7 +197,7 @@ class Exporter(object):
+                       # Get all networks that match the family
+                       networks = self.db.search_networks(family=family,
+-                              country_codes=country_codes, flags=flags, flatten=True)
++                              country_codes=country_codes, asns=asns, flags=flags, flatten=True)
+                       # Walk through all networks
+                       for network in networks:
+-- 
+2.20.1
+
+From 50120b991fc2fa4b7813096de87b42d700faf3e6 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 16:56:43 +0000
+Subject: [PATCH 60/70] database: Simplify network matching code
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 51cb5cd..1a354f6 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1129,12 +1129,6 @@ static int loc_database_enumerator_stack_push_node(
+       return 0;
+ }
+-static int loc_network_match_countries(struct loc_network* network, struct loc_country_list* countries) {
+-      const char* country_code = loc_network_get_country_code(network);
+-
+-      return loc_country_list_contains_code(countries, country_code);
+-}
+-
+ static int loc_network_match_asns(struct loc_network* network, struct loc_as_list* asns) {
+       uint32_t asn = loc_network_get_asn(network);
+@@ -1148,8 +1142,14 @@ static int loc_database_enumerator_filter_network(
+               return 1;
+       // Skip if the country code does not match
+-      if (enumerator->countries && !loc_network_match_countries(network, enumerator->countries))
+-              return 1;
++      if (enumerator->countries) {
++              if (!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))
++                              return 1;
++              }
++      }
+       // Skip if the ASN does not match
+       if (enumerator->asns && !loc_network_match_asns(network, enumerator->asns))
+-- 
+2.20.1
+
+From c1a36c943181da5cd2aef589a972d5027e529eb8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 16:58:55 +0000
+Subject: [PATCH 61/70] database: Simplify AS matching code
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index 1a354f6..be93e00 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1129,12 +1129,6 @@ static int loc_database_enumerator_stack_push_node(
+       return 0;
+ }
+-static int loc_network_match_asns(struct loc_network* network, struct loc_as_list* asns) {
+-      uint32_t asn = loc_network_get_asn(network);
+-
+-      return loc_as_list_contains_number(asns, asn);
+-}
+-
+ static int loc_database_enumerator_filter_network(
+               struct loc_database_enumerator* enumerator, struct loc_network* network) {
+       // Skip if the family does not match
+@@ -1152,8 +1146,14 @@ static int loc_database_enumerator_filter_network(
+       }
+       // Skip if the ASN does not match
+-      if (enumerator->asns && !loc_network_match_asns(network, enumerator->asns))
+-              return 1;
++      if (enumerator->asns) {
++              if (!loc_as_list_empty(enumerator->asns)) {
++                      uint32_t asn = loc_network_get_asn(network);
++
++                      if (!loc_as_list_contains_number(enumerator->asns, asn))
++                              return 1;
++              }
++      }
+       // Skip if flags do not match
+       if (enumerator->flags &&
+-- 
+2.20.1
+
+From d5205091f9cc1ff987e483325d48696459df08d8 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 17:50:17 +0000
+Subject: [PATCH 62/70] countries: Make list grow dynamically
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/country-list.c | 38 ++++++++++++++++++++++++++------------
+ 1 file changed, 26 insertions(+), 12 deletions(-)
+
+diff --git a/src/country-list.c b/src/country-list.c
+index ae0d71a..1ce2d06 100644
+--- a/src/country-list.c
++++ b/src/country-list.c
+@@ -25,11 +25,27 @@ struct loc_country_list {
+       struct loc_ctx* ctx;
+       int refcount;
+-      struct loc_country* list[1024];
++      struct loc_country** elements;
++      size_t elements_size;
++
+       size_t size;
+-      size_t max_size;
+ };
++static int loc_country_list_grow(struct loc_country_list* list, size_t size) {
++      DEBUG(list->ctx, "Growing country list %p by %zu to %zu\n",
++              list, size, list->elements_size + size);
++
++      struct loc_country** elements = reallocarray(list->elements,
++                      list->elements_size + size, sizeof(*list->elements));
++      if (!elements)
++              return -errno;
++
++      list->elements = elements;
++      list->elements_size += size;
++
++      return 0;
++}
++
+ LOC_EXPORT int loc_country_list_new(struct loc_ctx* ctx,
+               struct loc_country_list** list) {
+       struct loc_country_list* l = calloc(1, sizeof(*l));
+@@ -39,9 +55,6 @@ LOC_EXPORT int loc_country_list_new(struct loc_ctx* ctx,
+       l->ctx = loc_ref(ctx);
+       l->refcount = 1;
+-      // Do not allow this list to grow larger than this
+-      l->max_size = 1024;
+-
+       DEBUG(l->ctx, "Country list allocated at %p\n", l);
+       *list = l;
+@@ -84,7 +97,7 @@ LOC_EXPORT int loc_country_list_empty(struct loc_country_list* list) {
+ LOC_EXPORT void loc_country_list_clear(struct loc_country_list* list) {
+       for (unsigned int i = 0; i < list->size; i++)
+-              loc_country_unref(list->list[i]);
++              loc_country_unref(list->elements[i]);
+ }
+ LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index) {
+@@ -92,7 +105,7 @@ LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* lis
+       if (index >= list->size)
+               return NULL;
+-      return loc_country_ref(list->list[index]);
++      return loc_country_ref(list->elements[index]);
+ }
+ LOC_EXPORT int loc_country_list_append(
+@@ -101,14 +114,15 @@ LOC_EXPORT int loc_country_list_append(
+               return 0;
+       // Check if we have space left
+-      if (list->size == list->max_size) {
+-              ERROR(list->ctx, "%p: Could not append country to the list. List full\n", list);
+-              return -ENOMEM;
++      if (list->size >= list->elements_size) {
++              int r = loc_country_list_grow(list, 64);
++              if (r)
++                      return r;
+       }
+       DEBUG(list->ctx, "%p: Appending country %p to list\n", list, country);
+-      list->list[list->size++] = loc_country_ref(country);
++      list->elements[list->size++] = loc_country_ref(country);
+       return 0;
+ }
+@@ -116,7 +130,7 @@ LOC_EXPORT int loc_country_list_append(
+ LOC_EXPORT int loc_country_list_contains(
+               struct loc_country_list* list, struct loc_country* country) {
+       for (unsigned int i = 0; i < list->size; i++) {
+-              if (loc_country_cmp(country, list->list[i]) == 0)
++              if (loc_country_cmp(country, list->elements[i]) == 0)
+                       return 1;
+       }
+-- 
+2.20.1
+
+From 3b44e4211371d2103f89ba8f056b15edb7778fac Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 17:55:51 +0000
+Subject: [PATCH 63/70] networks: Make list grow dynamically
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/network-list.c | 60 ++++++++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 23 deletions(-)
+
+diff --git a/src/network-list.c b/src/network-list.c
+index 1f6e80e..4912c02 100644
+--- a/src/network-list.c
++++ b/src/network-list.c
+@@ -25,11 +25,27 @@ struct loc_network_list {
+       struct loc_ctx* ctx;
+       int refcount;
+-      struct loc_network* list[1024];
++      struct loc_network** elements;
++      size_t elements_size;
++
+       size_t size;
+-      size_t max_size;
+ };
++static int loc_network_list_grow(struct loc_network_list* list, size_t size) {
++      DEBUG(list->ctx, "Growing network list %p by %zu to %zu\n",
++              list, size, list->elements_size + size);
++
++      struct loc_network** elements = reallocarray(list->elements,
++                      list->elements_size + size, sizeof(*list->elements));
++      if (!elements)
++              return -errno;
++
++      list->elements = elements;
++      list->elements_size += size;
++
++      return 0;
++}
++
+ LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
+               struct loc_network_list** list) {
+       struct loc_network_list* l = calloc(1, sizeof(*l));
+@@ -39,9 +55,6 @@ LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
+       l->ctx = loc_ref(ctx);
+       l->refcount = 1;
+-      // Do not allow this list to grow larger than this
+-      l->max_size = 1024;
+-
+       DEBUG(l->ctx, "Network list allocated at %p\n", l);
+       *list = l;
+       return 0;
+@@ -57,7 +70,7 @@ static void loc_network_list_free(struct loc_network_list* list) {
+       DEBUG(list->ctx, "Releasing network list at %p\n", list);
+       for (unsigned int i = 0; i < list->size; i++)
+-              loc_network_unref(list->list[i]);
++              loc_network_unref(list->elements[i]);
+       loc_unref(list->ctx);
+       free(list);
+@@ -84,7 +97,7 @@ LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
+ LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
+       for (unsigned int i = 0; i < list->size; i++)
+-              loc_network_unref(list->list[i]);
++              loc_network_unref(list->elements[i]);
+       list->size = 0;
+ }
+@@ -94,7 +107,7 @@ LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
+       char* s;
+       for (unsigned int i = 0; i < list->size; i++) {
+-              network = list->list[i];
++              network = list->elements[i];
+               s = loc_network_str(network);
+@@ -108,7 +121,7 @@ LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* lis
+       if (index >= list->size)
+               return NULL;
+-      return loc_network_ref(list->list[index]);
++      return loc_network_ref(list->elements[index]);
+ }
+ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
+@@ -117,14 +130,15 @@ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_n
+               return 0;
+       // Check if we have space left
+-      if (list->size == list->max_size) {
+-              ERROR(list->ctx, "%p: Could not push network onto the stack: Stack full\n", list);
+-              return -ENOMEM;
++      if (list->size >= list->elements_size) {
++              int r = loc_network_list_grow(list, 64);
++              if (r)
++                      return r;
+       }
+       DEBUG(list->ctx, "%p: Pushing network %p onto stack\n", list, network);
+-      list->list[list->size++] = loc_network_ref(network);
++      list->elements[list->size++] = loc_network_ref(network);
+       return 0;
+ }
+@@ -136,7 +150,7 @@ LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* lis
+               return NULL;
+       }
+-      struct loc_network* network = list->list[--list->size];
++      struct loc_network* network = list->elements[--list->size];
+       DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
+@@ -150,11 +164,11 @@ LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_lis
+               return NULL;
+       }
+-      struct loc_network* network = list->list[0];
++      struct loc_network* network = list->elements[0];
+       // Move all elements to the top of the stack
+       for (unsigned int i = 0; i < --list->size; i++) {
+-              list->list[i] = list->list[i+1];
++              list->elements[i] = list->elements[i+1];
+       }
+       DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
+@@ -164,7 +178,7 @@ LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_lis
+ LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
+       for (unsigned int i = 0; i < list->size; i++) {
+-              if (loc_network_eq(list->list[i], network))
++              if (loc_network_eq(list->elements[i], network))
+                       return 1;
+       }
+@@ -176,11 +190,11 @@ static void loc_network_list_swap(struct loc_network_list* list, unsigned int i1
+       if (i1 >= list->size || i2 >= list->size)
+               return;
+-      struct loc_network* network1 = list->list[i1];
+-      struct loc_network* network2 = list->list[i2];
++      struct loc_network* network1 = list->elements[i1];
++      struct loc_network* network2 = list->elements[i2];
+-      list->list[i1] = network2;
+-      list->list[i2] = network1;
++      list->elements[i1] = network2;
++      list->elements[i2] = network1;
+ }
+ LOC_EXPORT void loc_network_list_reverse(struct loc_network_list* list) {
+@@ -200,7 +214,7 @@ LOC_EXPORT void loc_network_list_sort(struct loc_network_list* list) {
+               swapped = 0;
+               for (unsigned int i = 1; i < n; i++) {
+-                      if (loc_network_gt(list->list[i-1], list->list[i]) > 0) {
++                      if (loc_network_gt(list->elements[i-1], list->elements[i]) > 0) {
+                               loc_network_list_swap(list, i-1, i);
+                               swapped = 1;
+                       }
+@@ -215,7 +229,7 @@ LOC_EXPORT int loc_network_list_merge(
+       int r;
+       for (unsigned int i = 0; i < other->size; i++) {
+-              r = loc_network_list_push(self, other->list[i]);
++              r = loc_network_list_push(self, other->elements[i]);
+               if (r)
+                       return r;
+       }
+-- 
+2.20.1
+
+From 1a415f8c555f4fe9a68eb2a897c4a1fc0d33db25 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 17:57:55 +0000
+Subject: [PATCH 64/70] as: Make lists grow dynamically
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/as-list.c | 38 ++++++++++++++++++++++++++------------
+ 1 file changed, 26 insertions(+), 12 deletions(-)
+
+diff --git a/src/as-list.c b/src/as-list.c
+index 7c69eb0..17de23e 100644
+--- a/src/as-list.c
++++ b/src/as-list.c
+@@ -25,11 +25,27 @@ struct loc_as_list {
+       struct loc_ctx* ctx;
+       int refcount;
+-      struct loc_as* list[1024];
++      struct loc_as** elements;
++      size_t elements_size;
++
+       size_t size;
+-      size_t max_size;
+ };
++static int loc_as_list_grow(struct loc_as_list* list, size_t size) {
++      DEBUG(list->ctx, "Growing AS list %p by %zu to %zu\n",
++              list, size, list->elements_size + size);
++
++      struct loc_as** elements = reallocarray(list->elements,
++                      list->elements_size + size, sizeof(*list->elements));
++      if (!elements)
++              return -errno;
++
++      list->elements = elements;
++      list->elements_size += size;
++
++      return 0;
++}
++
+ LOC_EXPORT int loc_as_list_new(struct loc_ctx* ctx,
+               struct loc_as_list** list) {
+       struct loc_as_list* l = calloc(1, sizeof(*l));
+@@ -39,9 +55,6 @@ LOC_EXPORT int loc_as_list_new(struct loc_ctx* ctx,
+       l->ctx = loc_ref(ctx);
+       l->refcount = 1;
+-      // Do not allow this list to grow larger than this
+-      l->max_size = 1024;
+-
+       DEBUG(l->ctx, "AS list allocated at %p\n", l);
+       *list = l;
+@@ -84,7 +97,7 @@ LOC_EXPORT int loc_as_list_empty(struct loc_as_list* list) {
+ LOC_EXPORT void loc_as_list_clear(struct loc_as_list* list) {
+       for (unsigned int i = 0; i < list->size; i++)
+-              loc_as_unref(list->list[i]);
++              loc_as_unref(list->elements[i]);
+ }
+ LOC_EXPORT struct loc_as* loc_as_list_get(struct loc_as_list* list, size_t index) {
+@@ -92,7 +105,7 @@ LOC_EXPORT struct loc_as* loc_as_list_get(struct loc_as_list* list, size_t index
+       if (index >= list->size)
+               return NULL;
+-      return loc_as_ref(list->list[index]);
++      return loc_as_ref(list->elements[index]);
+ }
+ LOC_EXPORT int loc_as_list_append(
+@@ -101,14 +114,15 @@ LOC_EXPORT int loc_as_list_append(
+               return 0;
+       // Check if we have space left
+-      if (list->size == list->max_size) {
+-              ERROR(list->ctx, "%p: Could not append AS to the list. List full\n", list);
+-              return -ENOMEM;
++      if (list->size >= list->elements_size) {
++              int r = loc_as_list_grow(list, 64);
++              if (r)
++                      return r;
+       }
+       DEBUG(list->ctx, "%p: Appending AS %p to list\n", list, as);
+-      list->list[list->size++] = loc_as_ref(as);
++      list->elements[list->size++] = loc_as_ref(as);
+       return 0;
+ }
+@@ -116,7 +130,7 @@ LOC_EXPORT int loc_as_list_append(
+ LOC_EXPORT int loc_as_list_contains(
+               struct loc_as_list* list, struct loc_as* as) {
+       for (unsigned int i = 0; i < list->size; i++) {
+-              if (loc_as_cmp(as, list->list[i]) == 0)
++              if (loc_as_cmp(as, list->elements[i]) == 0)
+                       return 1;
+       }
+-- 
+2.20.1
+
+From e6592434ee7836507c1f436ec3b0db3bc81a81b9 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 18:13:49 +0000
+Subject: [PATCH 65/70] export: Change back to use Network objects
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index f675eb3..67e437f 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -144,9 +144,7 @@ class XTGeoIPOutputWriter(OutputWriter):
+       def _write_network(self, network):
+               for address in (network.first_address, network.last_address):
+                       # Convert this into a string of bits
+-                      bytes = socket.inet_pton(
+-                              socket.AF_INET6 if network.version == 6 else socket.AF_INET, "%s" % address,
+-                      )
++                      bytes = socket.inet_pton(network.family, address)
+                       self.f.write(bytes)
+-- 
+2.20.1
+
+From 248f5e0419f2349253b8ea96e477c15649fe2173 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 18:14:15 +0000
+Subject: [PATCH 66/70] Actually clear all lists
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/as-list.c      | 8 ++++++++
+ src/country-list.c | 8 ++++++++
+ src/network-list.c | 6 ++++++
+ 3 files changed, 22 insertions(+)
+
+diff --git a/src/as-list.c b/src/as-list.c
+index 17de23e..76620c7 100644
+--- a/src/as-list.c
++++ b/src/as-list.c
+@@ -96,8 +96,16 @@ LOC_EXPORT int loc_as_list_empty(struct loc_as_list* list) {
+ }
+ LOC_EXPORT void loc_as_list_clear(struct loc_as_list* list) {
++      if (!list->elements)
++              return;
++
+       for (unsigned int i = 0; i < list->size; i++)
+               loc_as_unref(list->elements[i]);
++
++      free(list->elements);
++      list->elements_size = 0;
++
++      list->size = 0;
+ }
+ LOC_EXPORT struct loc_as* loc_as_list_get(struct loc_as_list* list, size_t index) {
+diff --git a/src/country-list.c b/src/country-list.c
+index 1ce2d06..1c49c47 100644
+--- a/src/country-list.c
++++ b/src/country-list.c
+@@ -96,8 +96,16 @@ LOC_EXPORT int loc_country_list_empty(struct loc_country_list* list) {
+ }
+ LOC_EXPORT void loc_country_list_clear(struct loc_country_list* list) {
++      if (!list->elements)
++              return;
++
+       for (unsigned int i = 0; i < list->size; i++)
+               loc_country_unref(list->elements[i]);
++
++      free(list->elements);
++      list->elements_size = 0;
++
++      list->size = 0;
+ }
+ LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index) {
+diff --git a/src/network-list.c b/src/network-list.c
+index 4912c02..9cb4547 100644
+--- a/src/network-list.c
++++ b/src/network-list.c
+@@ -96,9 +96,15 @@ LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
+ }
+ LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
++      if (!list->elements)
++              return;
++
+       for (unsigned int i = 0; i < list->size; i++)
+               loc_network_unref(list->elements[i]);
++      free(list->elements);
++      list->elements_size = 0;
++
+       list->size = 0;
+ }
+-- 
+2.20.1
+
+From c98ebf8aae2aa141193db52cd9429b1ded5b09c4 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 18:34:51 +0000
+Subject: [PATCH 67/70] database: Do not clean up python list
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/database.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/src/python/database.c b/src/python/database.c
+index 38a804c..ed22275 100644
+--- a/src/python/database.c
++++ b/src/python/database.c
+@@ -325,9 +325,14 @@ static PyObject* Database_search_networks(DatabaseObject* self, PyObject* args,
+                       loc_country_unref(country);
+               }
+-              loc_database_enumerator_set_countries(enumerator, countries);
++              r = loc_database_enumerator_set_countries(enumerator, countries);
++              if (r) {
++                      PyErr_SetFromErrno(PyExc_SystemError);
++
++                      loc_as_list_unref(countries);
++                      return NULL;
++              }
+-              Py_DECREF(country_codes);
+               loc_country_list_unref(countries);
+       }
+-- 
+2.20.1
+
+From 5470d06cb59027f4e04b6d576763dbf7f1093fde Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Tue, 17 Nov 2020 19:01:04 +0000
+Subject: [PATCH 68/70] database: Free filter lists in enumerator
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/src/database.c b/src/database.c
+index be93e00..ca35fe1 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -950,6 +950,12 @@ static void loc_database_enumerator_free(struct loc_database_enumerator* enumera
+       if (enumerator->string)
+               free(enumerator->string);
++      if (enumerator->countries)
++              loc_country_list_unref(enumerator->countries);
++
++      if (enumerator->asns)
++              loc_as_list_unref(enumerator->asns);
++
+       // Free network search
+       free(enumerator->networks_visited);
+-- 
+2.20.1
+
+From e0e96878d3df51c4a265d51d088005dedf9335e3 Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 18 Nov 2020 13:18:52 +0000
+Subject: [PATCH 69/70] database: Add debug output to filtering
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/database.c | 29 ++++++++++++++++-------------
+ 1 file changed, 16 insertions(+), 13 deletions(-)
+
+diff --git a/src/database.c b/src/database.c
+index ca35fe1..83dd752 100644
+--- a/src/database.c
++++ b/src/database.c
+@@ -1138,33 +1138,36 @@ static int loc_database_enumerator_stack_push_node(
+ static int loc_database_enumerator_filter_network(
+               struct loc_database_enumerator* enumerator, struct loc_network* network) {
+       // Skip if the family does not match
+-      if (enumerator->family && loc_network_address_family(network) != enumerator->family)
++      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;
++      }
+       // Skip if the country code does not match
+-      if (enumerator->countries) {
+-              if (!loc_country_list_empty(enumerator->countries)) {
+-                      const char* country_code = loc_network_get_country_code(network);
++      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))
+-                              return 1;
++              if (!loc_country_list_contains_code(enumerator->countries, country_code)) {
++                      DEBUG(enumerator->ctx, "Filtered network %p because of country code not matching\n", network);
++                      return 1;
+               }
+       }
+       // Skip if the ASN does not match
+-      if (enumerator->asns) {
+-              if (!loc_as_list_empty(enumerator->asns)) {
+-                      uint32_t asn = loc_network_get_asn(network);
++      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))
+-                              return 1;
++              if (!loc_as_list_contains_number(enumerator->asns, asn)) {
++                      DEBUG(enumerator->ctx, "Filtered network %p because of ASN not matching\n", network);
++                      return 1;
+               }
+       }
+       // Skip if flags do not match
+-      if (enumerator->flags &&
+-                      !loc_network_match_flag(network, enumerator->flags))
++      if (enumerator->flags && !loc_network_match_flag(network, enumerator->flags)) {
++              DEBUG(enumerator->ctx, "Filtered network %p because of flags not matching\n", network);
+               return 1;
++      }
+       // Do not filter
+       return 0;
+-- 
+2.20.1
+
+From bce0c9295ff8ff9488f24babe01ce851228d0b1e Mon Sep 17 00:00:00 2001
+From: Michael Tremer <michael.tremer@ipfire.org>
+Date: Wed, 18 Nov 2020 13:19:04 +0000
+Subject: [PATCH 70/70] export: Remove filtering for flags
+
+The filter is an AND filter and if we set the flags from
+the special country codes, we won't get back much.
+
+Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
+---
+ src/python/export.py | 8 +-------
+ 1 file changed, 1 insertion(+), 7 deletions(-)
+
+diff --git a/src/python/export.py b/src/python/export.py
+index 67e437f..4219957 100644
+--- a/src/python/export.py
++++ b/src/python/export.py
+@@ -187,15 +187,9 @@ class Exporter(object):
+                               country_code for country_code in countries if not country_code in FLAGS.values()
+                       ]
+-                      # Collect flags
+-                      flags = 0
+-                      for flag in FLAGS:
+-                              if FLAGS[flag] in countries:
+-                                      flags |= flag
+-
+                       # Get all networks that match the family
+                       networks = self.db.search_networks(family=family,
+-                              country_codes=country_codes, asns=asns, flags=flags, flatten=True)
++                              country_codes=country_codes, asns=asns, flatten=True)
+                       # Walk through all networks
+                       for network in networks:
+-- 
+2.20.1
+