]> git.ipfire.org Git - people/ms/libloc.git/blobdiff - src/network-list.c
lua: Fix raising an exception if no network was found
[people/ms/libloc.git] / src / network-list.c
index 224b34508e3bcd865fb6acae9fad7fd9873d7bdb..bca44221c011ec881bc9c95d242057add83f2fa8 100644 (file)
 #include <stdlib.h>
 #include <time.h>
 
-#include <loc/libloc.h>
-#include <loc/network.h>
-#include <loc/private.h>
+#include <libloc/address.h>
+#include <libloc/libloc.h>
+#include <libloc/network.h>
+#include <libloc/private.h>
 
 struct loc_network_list {
        struct loc_ctx* ctx;
@@ -32,14 +33,18 @@ struct loc_network_list {
        size_t size;
 };
 
-static int loc_network_list_grow(struct loc_network_list* list, size_t size) {
+static int loc_network_list_grow(struct loc_network_list* list) {
+       size_t size = list->elements_size * 2;
+       if (size < 1024)
+               size = 1024;
+
        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;
+               return 1;
 
        list->elements = elements;
        list->elements_size += size;
@@ -78,9 +83,6 @@ static void loc_network_list_free(struct loc_network_list* 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;
 
@@ -112,15 +114,12 @@ LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
 
 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->elements[i];
 
-               s = loc_network_str(network);
-
-               INFO(list->ctx, "%4d: %s\n", i, s);
-               free(s);
+               INFO(list->ctx, "%4d: %s\n",
+                       i, loc_network_str(network));
        }
 }
 
@@ -223,7 +222,7 @@ LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_n
 
        // Check if we have space left
        if (list->size >= list->elements_size) {
-               int r = loc_network_list_grow(list, 64);
+               int r = loc_network_list_grow(list);
                if (r)
                        return r;
        }
@@ -277,6 +276,29 @@ LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_lis
        return network;
 }
 
+int loc_network_list_remove(struct loc_network_list* list, struct loc_network* network) {
+       int found = 0;
+
+       // Find the network on the list
+       off_t index = loc_network_list_find(list, network, &found);
+
+       // Nothing to do if the network wasn't found
+       if (!found)
+               return 0;
+
+       // Dereference the network at the position
+       loc_network_unref(list->elements[index]);
+
+       // Move all other elements back
+       for (unsigned int i = index; i < list->size - 1; i++)
+               list->elements[i] = list->elements[i+1];
+
+       // The list is shorter now
+       --list->size;
+
+       return 0;
+}
+
 LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
        int found = 0;
 
@@ -307,60 +329,111 @@ int loc_network_list_summarize(struct loc_ctx* ctx,
                return 1;
        }
 
-       int family = loc_address_family(first);
+       DEBUG(ctx, "Summarizing %s - %s\n", loc_address_str(first), loc_address_str(last));
+
+       const int family1 = loc_address_family(first);
+       const int family2 = loc_address_family(last);
 
-       // Families must match
-       if (family != loc_address_family(last)) {
+       // Check if address families match
+       if (family1 != family2) {
                ERROR(ctx, "Address families do not match\n");
                errno = EINVAL;
                return 1;
        }
 
        // Check if the last address is larger than the first address
-       if (in6_addr_cmp(first, last) >= 0) {
+       if (loc_address_cmp(first, last) >= 0) {
                ERROR(ctx, "The first address must be smaller than the last address\n");
                errno = EINVAL;
                return 1;
        }
 
        struct loc_network* network = NULL;
-
        struct in6_addr start = *first;
-       const struct in6_addr* end = NULL;
-
-       while (in6_addr_cmp(&start, last) <= 0) {
-               // Guess the prefix
-               int prefix = 128 - loc_address_count_trailing_zero_bits(&start);
 
-               while (1) {
-                       // Create a new network object
-                       r = loc_network_new(ctx, &network, &start, prefix);
-                       if (r)
-                               return r;
+       const int family_bit_length = loc_address_family_bit_length(family1);
 
-                       // Is this network within bounds?
-                       end = loc_network_get_last_address(network);
-                       if (in6_addr_cmp(last, end) <= 0)
-                               break;
+       while (loc_address_cmp(&start, last) <= 0) {
+               struct in6_addr num;
+               int bits1;
 
-                       // Drop network and decrease prefix
-                       loc_network_unref(network);
-                       prefix--;
+               // Find the number of trailing zeroes of the start address
+               if (loc_address_all_zeroes(&start))
+                       bits1 = family_bit_length;
+               else {
+                       bits1 = loc_address_count_trailing_zero_bits(&start);
+                       if (bits1 > family_bit_length)
+                               bits1 = family_bit_length;
                }
 
-               // Push it on the list
+               // Subtract the start address from the last address and add one
+               // (i.e. how many addresses are in this network?)
+               r = loc_address_sub(&num, last, &start);
+               if (r)
+                       return r;
+
+               loc_address_increment(&num);
+
+               // How many bits do we need to represent this address?
+               int bits2 = loc_address_bit_length(&num) - 1;
+
+               // Select the smaller one
+               int bits = (bits1 > bits2) ? bits2 : bits1;
+
+               // Create a network
+               r = loc_network_new(ctx, &network, &start, family_bit_length - bits);
+               if (r)
+                       return r;
+
+               DEBUG(ctx, "Found network %s\n", loc_network_str(network));
+
+               // Push network on the list
                r = loc_network_list_push(*list, network);
                if (r) {
                        loc_network_unref(network);
                        return r;
                }
 
-               // Reset addr to the next start address
-               start = address_increment(end);
+               // The next network starts right after this one
+               start = *loc_network_get_last_address(network);
+
+               // If we have reached the end of possible IP addresses, we stop
+               if (loc_address_all_ones(&start))
+                       break;
 
-               // Cleanup
-               loc_network_unref(network);
+               loc_address_increment(&start);
        }
 
        return 0;
 }
+
+void loc_network_list_remove_with_prefix_smaller_than(
+               struct loc_network_list* list, const unsigned int prefix) {
+       unsigned int p = 0;
+
+       // Count how many networks were removed
+       unsigned int removed = 0;
+
+       for (unsigned int i = 0; i < list->size; i++) {
+               // Fetch the prefix
+               p = loc_network_prefix(list->elements[i]);
+
+               if (p > prefix) {
+                       // Drop this network
+                       loc_network_unref(list->elements[i]);
+
+                       // Increment counter
+                       removed++;
+
+                       continue;
+               }
+
+               // Move pointers backwards to keep the list filled
+               list->elements[i - removed] = list->elements[i];
+       }
+
+       // Adjust size
+       list->size -= removed;
+
+       return;
+}