2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
17 #include <arpa/inet.h>
28 #include <libloc/libloc.h>
29 #include <libloc/address.h>
30 #include <libloc/compat.h>
31 #include <libloc/country.h>
32 #include <libloc/network.h>
33 #include <libloc/network-list.h>
34 #include <libloc/private.h>
41 struct in6_addr first_address
;
42 struct in6_addr last_address
;
47 enum loc_network_flags flags
;
49 char string
[INET6_ADDRSTRLEN
+ 4];
52 LOC_EXPORT
int loc_network_new(struct loc_ctx
* ctx
, struct loc_network
** network
,
53 struct in6_addr
* address
, unsigned int prefix
) {
54 // Validate the prefix
55 if (!loc_address_valid_prefix(address
, prefix
)) {
56 ERROR(ctx
, "Invalid prefix in %s: %u\n", loc_address_str(address
), prefix
);
61 struct loc_network
* n
= calloc(1, sizeof(*n
));
65 n
->ctx
= loc_ref(ctx
);
69 if (IN6_IS_ADDR_V4MAPPED(address
))
70 n
->prefix
= prefix
+ 96;
74 // Convert the prefix into a bitmask
75 struct in6_addr bitmask
= loc_prefix_to_bitmask(n
->prefix
);
77 // Store the first and last address in the network
78 n
->first_address
= loc_address_and(address
, &bitmask
);
79 n
->last_address
= loc_address_or(&n
->first_address
, &bitmask
);
82 n
->family
= loc_address_family(&n
->first_address
);
84 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
89 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
,
90 struct loc_network
** network
, const char* string
) {
91 struct in6_addr address
;
95 int r
= loc_address_parse(&address
, &prefix
, string
);
97 ERROR(ctx
, "Could not parse network %s: %m\n", string
);
101 // Create a new network
102 return loc_network_new(ctx
, network
, &address
, prefix
);
105 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
111 static void loc_network_free(struct loc_network
* network
) {
112 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
114 loc_unref(network
->ctx
);
118 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
119 if (--network
->refcount
> 0)
122 loc_network_free(network
);
126 LOC_EXPORT
const char* loc_network_str(struct loc_network
* network
) {
127 if (!*network
->string
) {
128 // Format the address
129 const char* address
= loc_address_str(&network
->first_address
);
134 unsigned int prefix
= loc_network_prefix(network
);
137 int r
= snprintf(network
->string
, sizeof(network
->string
) - 1,
138 "%s/%u", address
, prefix
);
140 ERROR(network
->ctx
, "Could not format network string: %m\n");
141 *network
->string
= '\0';
146 return network
->string
;
149 LOC_EXPORT
int loc_network_address_family(struct loc_network
* network
) {
150 return network
->family
;
153 LOC_EXPORT
unsigned int loc_network_prefix(struct loc_network
* network
) {
154 switch (network
->family
) {
156 return network
->prefix
;
159 return network
->prefix
- 96;
165 LOC_EXPORT
const struct in6_addr
* loc_network_get_first_address(struct loc_network
* network
) {
166 return &network
->first_address
;
169 LOC_EXPORT
const char* loc_network_format_first_address(struct loc_network
* network
) {
170 return loc_address_str(&network
->first_address
);
173 LOC_EXPORT
const struct in6_addr
* loc_network_get_last_address(struct loc_network
* network
) {
174 return &network
->last_address
;
177 LOC_EXPORT
const char* loc_network_format_last_address(struct loc_network
* network
) {
178 return loc_address_str(&network
->last_address
);
181 LOC_EXPORT
int loc_network_matches_address(struct loc_network
* network
, const struct in6_addr
* address
) {
182 // Address must be larger than the start address
183 if (loc_address_cmp(&network
->first_address
, address
) > 0)
186 // Address must be smaller than the last address
187 if (loc_address_cmp(&network
->last_address
, address
) < 0)
190 // The address is inside this network
194 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
195 return network
->country_code
;
198 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
199 // Set empty country code
200 if (!country_code
|| !*country_code
) {
201 *network
->country_code
= '\0';
205 // Check country code
206 if (!loc_country_code_is_valid(country_code
))
209 loc_country_code_copy(network
->country_code
, country_code
);
214 LOC_EXPORT
int loc_network_matches_country_code(struct loc_network
* network
, const char* country_code
) {
215 // Search for any special flags
216 const int flag
= loc_country_special_code_to_flag(country_code
);
218 // If we found a flag, we will return whether it is set or not
220 return loc_network_has_flag(network
, flag
);
222 // Check country code
223 if (!loc_country_code_is_valid(country_code
))
226 // Check for an exact match
227 return (network
->country_code
[0] == country_code
[0])
228 && (network
->country_code
[1] == country_code
[1]);
231 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
235 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
241 LOC_EXPORT
int loc_network_has_flag(struct loc_network
* network
, uint32_t flag
) {
242 return network
->flags
& flag
;
245 LOC_EXPORT
int loc_network_set_flag(struct loc_network
* network
, uint32_t flag
) {
246 network
->flags
|= flag
;
251 LOC_EXPORT
int loc_network_cmp(struct loc_network
* self
, struct loc_network
* other
) {
253 int r
= loc_address_cmp(&self
->first_address
, &other
->first_address
);
258 if (self
->prefix
> other
->prefix
)
260 else if (self
->prefix
< other
->prefix
)
263 // Both networks are equal
267 static int loc_network_properties_cmp(struct loc_network
* self
, struct loc_network
* other
) {
270 // Check country code
271 r
= loc_country_code_cmp(self
->country_code
, other
->country_code
);
276 if (self
->asn
> other
->asn
)
278 else if (self
->asn
< other
->asn
)
282 if (self
->flags
> other
->flags
)
284 else if (self
->flags
< other
->flags
)
290 LOC_EXPORT
int loc_network_overlaps(struct loc_network
* self
, struct loc_network
* other
) {
291 // Either of the start addresses must be in the other subnet
292 if (loc_network_matches_address(self
, &other
->first_address
))
295 if (loc_network_matches_address(other
, &self
->first_address
))
298 // Or either of the end addresses is in the other subnet
299 if (loc_network_matches_address(self
, &other
->last_address
))
302 if (loc_network_matches_address(other
, &self
->last_address
))
308 LOC_EXPORT
int loc_network_is_subnet(struct loc_network
* self
, struct loc_network
* other
) {
309 // The prefix must be smaller (this avoids the more complex comparisons later)
310 if (self
->prefix
> other
->prefix
)
313 // If the start address of the other network is smaller than this network,
314 // it cannot be a subnet.
315 if (loc_address_cmp(&self
->first_address
, &other
->first_address
) > 0)
318 // If the end address of the other network is greater than this network,
319 // it cannot be a subnet.
320 if (loc_address_cmp(&self
->last_address
, &other
->last_address
) < 0)
326 LOC_EXPORT
int loc_network_subnets(struct loc_network
* network
,
327 struct loc_network
** subnet1
, struct loc_network
** subnet2
) {
333 unsigned int prefix
= loc_network_prefix(network
) + 1;
335 // Check if the new prefix is valid
336 if (!loc_address_valid_prefix(&network
->first_address
, prefix
)) {
337 ERROR(network
->ctx
, "Invalid prefix: %d\n", prefix
);
342 // Create the first half of the network
343 r
= loc_network_new(network
->ctx
, subnet1
, &network
->first_address
, prefix
);
347 // The next subnet starts after the first one
348 struct in6_addr first_address
= (*subnet1
)->last_address
;
349 loc_address_increment(&first_address
);
351 // Create the second half of the network
352 r
= loc_network_new(network
->ctx
, subnet2
, &first_address
, prefix
);
357 const char* country_code
= loc_network_get_country_code(network
);
359 loc_network_set_country_code(*subnet1
, country_code
);
360 loc_network_set_country_code(*subnet2
, country_code
);
364 uint32_t asn
= loc_network_get_asn(network
);
366 loc_network_set_asn(*subnet1
, asn
);
367 loc_network_set_asn(*subnet2
, asn
);
371 loc_network_set_flag(*subnet1
, network
->flags
);
372 loc_network_set_flag(*subnet2
, network
->flags
);
377 static int __loc_network_exclude(struct loc_network
* network
,
378 struct loc_network
* other
, struct loc_network_list
* list
) {
379 struct loc_network
* subnet1
= NULL
;
380 struct loc_network
* subnet2
= NULL
;
382 int r
= loc_network_subnets(network
, &subnet1
, &subnet2
);
386 if (loc_network_cmp(other
, subnet1
) == 0) {
387 r
= loc_network_list_push(list
, subnet2
);
391 } else if (loc_network_cmp(other
, subnet2
) == 0) {
392 r
= loc_network_list_push(list
, subnet1
);
396 } else if (loc_network_is_subnet(subnet1
, other
)) {
397 r
= loc_network_list_push(list
, subnet2
);
401 r
= __loc_network_exclude(subnet1
, other
, list
);
405 } else if (loc_network_is_subnet(subnet2
, other
)) {
406 r
= loc_network_list_push(list
, subnet1
);
410 r
= __loc_network_exclude(subnet2
, other
, list
);
415 ERROR(network
->ctx
, "We should never get here\n");
422 loc_network_unref(subnet1
);
425 loc_network_unref(subnet2
);
428 DEBUG(network
->ctx
, "%s has failed with %d\n", __FUNCTION__
, r
);
433 static int __loc_network_exclude_to_list(struct loc_network
* self
,
434 struct loc_network
* other
, struct loc_network_list
* list
) {
435 // Other must be a subnet of self
436 if (!loc_network_is_subnet(self
, other
)) {
437 DEBUG(self
->ctx
, "Network %p is not contained in network %p\n", other
, self
);
443 // We cannot perform this operation if both networks equal
444 if (loc_network_cmp(self
, other
) == 0) {
445 DEBUG(self
->ctx
, "Networks %p and %p are equal\n", self
, other
);
451 return __loc_network_exclude(self
, other
, list
);
454 LOC_EXPORT
struct loc_network_list
* loc_network_exclude(
455 struct loc_network
* self
, struct loc_network
* other
) {
456 struct loc_network_list
* list
;
458 DEBUG(self
->ctx
, "Returning %s excluding %s...\n",
459 loc_network_str(self
), loc_network_str(other
));
461 // Create a new list with the result
462 int r
= loc_network_list_new(self
->ctx
, &list
);
464 ERROR(self
->ctx
, "Could not create network list: %d\n", r
);
469 r
= __loc_network_exclude_to_list(self
, other
, list
);
471 loc_network_list_unref(list
);
480 LOC_EXPORT
struct loc_network_list
* loc_network_exclude_list(
481 struct loc_network
* network
, struct loc_network_list
* list
) {
482 struct loc_network_list
* to_check
;
484 // Create a new list with all networks to look at
485 int r
= loc_network_list_new(network
->ctx
, &to_check
);
489 struct loc_network
* subnet
= NULL
;
490 struct loc_network_list
* subnets
= NULL
;
492 for (unsigned int i
= 0; i
< loc_network_list_size(list
); i
++) {
493 subnet
= loc_network_list_get(list
, i
);
495 // Find all excluded networks
496 if (!loc_network_list_contains(to_check
, subnet
)) {
497 r
= __loc_network_exclude_to_list(network
, subnet
, to_check
);
499 loc_network_list_unref(to_check
);
500 loc_network_unref(subnet
);
507 loc_network_unref(subnet
);
510 r
= loc_network_list_new(network
->ctx
, &subnets
);
512 loc_network_list_unref(to_check
);
516 off_t smallest_subnet
= 0;
518 while (!loc_network_list_empty(to_check
)) {
519 struct loc_network
* subnet_to_check
= loc_network_list_pop_first(to_check
);
521 // Check whether the subnet to check is part of the input list
522 if (loc_network_list_contains(list
, subnet_to_check
)) {
523 loc_network_unref(subnet_to_check
);
527 // Marks whether this subnet passed all checks
530 for (unsigned int i
= smallest_subnet
; i
< loc_network_list_size(list
); i
++) {
531 subnet
= loc_network_list_get(list
, i
);
533 // Drop this subnet if is a subnet of another subnet
534 if (loc_network_is_subnet(subnet
, subnet_to_check
)) {
536 loc_network_unref(subnet
);
540 // Break it down if it overlaps
541 if (loc_network_overlaps(subnet
, subnet_to_check
)) {
544 __loc_network_exclude_to_list(subnet_to_check
, subnet
, to_check
);
546 loc_network_unref(subnet
);
550 // If the subnet is strictly greater, we do not need to continue the search
551 r
= loc_network_cmp(subnet
, subnet_to_check
);
553 loc_network_unref(subnet
);
556 // If it is strictly smaller, we can continue the search from here next
557 // time because all networks that are to be checked can only be larger
563 loc_network_unref(subnet
);
567 r
= loc_network_list_push(subnets
, subnet_to_check
);
570 loc_network_unref(subnet_to_check
);
573 loc_network_list_unref(to_check
);
578 static int loc_network_merge(struct loc_network
** n
,
579 struct loc_network
* n1
, struct loc_network
* n2
) {
580 struct loc_network
* network
= NULL
;
581 struct in6_addr address
;
588 if (n1
->family
!= n2
->family
)
591 // The prefix must match, too
592 if (n1
->prefix
!= n2
->prefix
)
595 // Cannot merge ::/0 or 0.0.0.0/0
596 if (!n1
->prefix
|| !n2
->prefix
)
599 const unsigned int prefix
= loc_network_prefix(n1
);
601 // How many bits do we need to represent this address?
602 const size_t bitlength
= loc_address_bit_length(&n1
->first_address
) - 1;
604 // We cannot shorten this any more
605 if (bitlength
== prefix
)
608 // Increment the last address of the first network
609 address
= n1
->last_address
;
610 loc_address_increment(&address
);
612 // If they don't match they are not neighbours
613 if (loc_address_cmp(&address
, &n2
->first_address
) != 0)
616 // All properties must match, too
617 if (loc_network_properties_cmp(n1
, n2
) != 0)
620 // Create a new network object
621 r
= loc_network_new(n1
->ctx
, &network
, &n1
->first_address
, prefix
- 1);
625 // Copy everything else
626 loc_country_code_copy(network
->country_code
, n1
->country_code
);
627 network
->asn
= n1
->asn
;
628 network
->flags
= n1
->flags
;
636 int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
638 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
641 dbobj
->asn
= htobe32(network
->asn
);
644 dbobj
->flags
= htobe16(network
->flags
);
649 int loc_network_new_from_database_v1(struct loc_ctx
* ctx
, struct loc_network
** network
,
650 struct in6_addr
* address
, unsigned int prefix
, const struct loc_database_network_v1
* dbobj
) {
651 char country_code
[3] = "\0\0";
653 // Adjust prefix for IPv4
654 if (IN6_IS_ADDR_V4MAPPED(address
))
657 int r
= loc_network_new(ctx
, network
, address
, prefix
);
659 ERROR(ctx
, "Could not allocate a new network: %m\n");
663 // Import country code
664 loc_country_code_copy(country_code
, dbobj
->country_code
);
666 r
= loc_network_set_country_code(*network
, country_code
);
668 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
673 uint32_t asn
= be32toh(dbobj
->asn
);
674 r
= loc_network_set_asn(*network
, asn
);
676 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
681 int flags
= be16toh(dbobj
->flags
);
682 r
= loc_network_set_flag(*network
, flags
);
684 ERROR(ctx
, "Could not set flags: %d\n", flags
);
691 struct loc_network_tree
{
695 struct loc_network_tree_node
* root
;
698 struct loc_network_tree_node
{
702 struct loc_network_tree_node
* zero
;
703 struct loc_network_tree_node
* one
;
705 struct loc_network
* network
;
711 int loc_network_tree_new(struct loc_ctx
* ctx
, struct loc_network_tree
** tree
) {
712 struct loc_network_tree
* t
= calloc(1, sizeof(*t
));
716 t
->ctx
= loc_ref(ctx
);
719 // Create the root node
720 int r
= loc_network_tree_node_new(ctx
, &t
->root
);
722 loc_network_tree_unref(t
);
726 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
731 struct loc_network_tree_node
* loc_network_tree_get_root(struct loc_network_tree
* tree
) {
732 return loc_network_tree_node_ref(tree
->root
);
735 static struct loc_network_tree_node
* loc_network_tree_get_node(struct loc_network_tree_node
* node
, int path
) {
736 struct loc_network_tree_node
** n
= NULL
;
753 // If the node existed, but has been deleted, we undelete it
754 if (*n
&& (*n
)->deleted
) {
757 // If the desired node doesn't exist, yet, we will create it
759 r
= loc_network_tree_node_new(node
->ctx
, n
);
767 static struct loc_network_tree_node
* loc_network_tree_get_path(struct loc_network_tree
* tree
, const struct in6_addr
* address
, unsigned int prefix
) {
768 struct loc_network_tree_node
* node
= tree
->root
;
770 for (unsigned int i
= 0; i
< prefix
; i
++) {
771 // Check if the ith bit is one or zero
772 node
= loc_network_tree_get_node(node
, loc_address_get_bit(address
, i
));
778 static int __loc_network_tree_walk(struct loc_ctx
* ctx
, struct loc_network_tree_node
* node
,
779 int(*filter_callback
)(struct loc_network
* network
, void* data
),
780 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
783 // If the node has been deleted, don't process it
787 // Finding a network ends the walk here
789 if (filter_callback
) {
790 int f
= filter_callback(node
->network
, data
);
794 // Skip network if filter function returns value greater than zero
799 r
= callback(node
->network
, data
);
804 // Walk down on the left side of the tree first
806 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
811 // Then walk on the other side
813 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
821 int loc_network_tree_walk(struct loc_network_tree
* tree
,
822 int(*filter_callback
)(struct loc_network
* network
, void* data
),
823 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
824 return __loc_network_tree_walk(tree
->ctx
, tree
->root
, filter_callback
, callback
, data
);
827 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
828 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
830 loc_network_tree_node_unref(tree
->root
);
832 loc_unref(tree
->ctx
);
836 struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
837 if (--tree
->refcount
> 0)
840 loc_network_tree_free(tree
);
844 static int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
845 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
847 const char* s
= loc_network_str(network
);
851 INFO(network
->ctx
, "%s\n", s
);
856 int loc_network_tree_dump(struct loc_network_tree
* tree
) {
857 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
859 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
862 int loc_network_tree_add_network(struct loc_network_tree
* tree
, struct loc_network
* network
) {
863 DEBUG(tree
->ctx
, "Adding network %p to tree %p\n", network
, tree
);
865 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
,
866 &network
->first_address
, network
->prefix
);
868 ERROR(tree
->ctx
, "Could not find a node\n");
872 // Check if node has not been set before
874 DEBUG(tree
->ctx
, "There is already a network at this path: %s\n",
875 loc_network_str(node
->network
));
879 // Point node to the network
880 node
->network
= loc_network_ref(network
);
885 static int loc_network_tree_delete_network(
886 struct loc_network_tree
* tree
, struct loc_network
* network
) {
887 struct loc_network_tree_node
* node
= NULL
;
889 node
= loc_network_tree_get_path(tree
, &network
->first_address
, network
->prefix
);
891 DEBUG(tree
->ctx
, "Network was not found in tree\n");
897 loc_network_unref(node
->network
);
898 node
->network
= NULL
;
901 // Mark the node as deleted if it was a leaf
902 if (!node
->zero
&& !node
->one
)
908 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node
* node
) {
911 // Don't count deleted nodes
916 counter
+= __loc_network_tree_count_nodes(node
->zero
);
919 counter
+= __loc_network_tree_count_nodes(node
->one
);
924 size_t loc_network_tree_count_nodes(struct loc_network_tree
* tree
) {
925 return __loc_network_tree_count_nodes(tree
->root
);
928 int loc_network_tree_node_new(struct loc_ctx
* ctx
, struct loc_network_tree_node
** node
) {
929 struct loc_network_tree_node
* n
= calloc(1, sizeof(*n
));
933 n
->ctx
= loc_ref(ctx
);
936 n
->zero
= n
->one
= NULL
;
938 DEBUG(n
->ctx
, "Network node allocated at %p\n", n
);
943 struct loc_network_tree_node
* loc_network_tree_node_ref(struct loc_network_tree_node
* node
) {
950 static void loc_network_tree_node_free(struct loc_network_tree_node
* node
) {
951 DEBUG(node
->ctx
, "Releasing network node at %p\n", node
);
954 loc_network_unref(node
->network
);
957 loc_network_tree_node_unref(node
->zero
);
960 loc_network_tree_node_unref(node
->one
);
962 loc_unref(node
->ctx
);
966 struct loc_network_tree_node
* loc_network_tree_node_unref(struct loc_network_tree_node
* node
) {
967 if (--node
->refcount
> 0)
970 loc_network_tree_node_free(node
);
974 struct loc_network_tree_node
* loc_network_tree_node_get(struct loc_network_tree_node
* node
, unsigned int index
) {
983 return loc_network_tree_node_ref(node
);
986 int loc_network_tree_node_is_leaf(struct loc_network_tree_node
* node
) {
987 return (!!node
->network
);
990 struct loc_network
* loc_network_tree_node_get_network(struct loc_network_tree_node
* node
) {
991 return loc_network_ref(node
->network
);
998 struct loc_network_tree_merge_ctx
{
999 struct loc_network_tree
* tree
;
1000 struct loc_network_list
* networks
;
1001 unsigned int merged
;
1004 static int loc_network_tree_merge_step(struct loc_network
* network
, void* data
) {
1005 struct loc_network_tree_merge_ctx
* ctx
= (struct loc_network_tree_merge_ctx
*)data
;
1006 struct loc_network
* n
= NULL
;
1007 struct loc_network
* m
= NULL
;
1010 // How many networks do we have?
1011 size_t i
= loc_network_list_size(ctx
->networks
);
1013 // If the list is empty, just add the network
1015 return loc_network_list_push(ctx
->networks
, network
);
1018 // Fetch the last network of the list
1019 n
= loc_network_list_get(ctx
->networks
, i
);
1021 // Try to merge the two networks
1022 r
= loc_network_merge(&m
, n
, network
);
1026 // Did we get a result?
1028 DEBUG(ctx
->tree
->ctx
, "Merged networks %s + %s -> %s\n",
1029 loc_network_str(n
), loc_network_str(network
), loc_network_str(m
));
1031 // Add the new network
1032 r
= loc_network_tree_add_network(ctx
->tree
, m
);
1037 // There might already be a network
1046 // It would be nice if we could remove the networks that we just merged,
1047 // but removing objects from the tree while walking through it is something
1048 // we currently don't support. The deduplication check will however find
1049 // and remove these networks.
1051 // Add the new network to the stack
1052 r
= loc_network_list_push(ctx
->networks
, m
);
1056 // Remove the previous network from the stack
1057 r
= loc_network_list_remove(ctx
->networks
, n
);
1064 // Try merging the new network with others
1065 r
= loc_network_tree_merge_step(m
, data
);
1069 loc_network_unref(m
);
1072 // Once we have found a merge, we are done
1075 // If we could not merge the two networks, we add the current one
1077 r
= loc_network_list_push(ctx
->networks
, network
);
1082 loc_network_unref(n
);
1086 const unsigned int prefix
= loc_network_prefix(network
);
1088 // Remove any networks that we cannot merge
1089 loc_network_list_remove_with_prefix_smaller_than(ctx
->networks
, prefix
);
1093 loc_network_unref(m
);
1095 loc_network_unref(n
);
1100 static int loc_network_tree_merge(struct loc_network_tree
* tree
) {
1101 struct loc_network_tree_merge_ctx ctx
= {
1108 // Create a new list
1109 r
= loc_network_list_new(tree
->ctx
, &ctx
.networks
);
1113 // Walk through the entire tree
1114 r
= loc_network_tree_walk(tree
, NULL
, loc_network_tree_merge_step
, &ctx
);
1118 DEBUG(tree
->ctx
, "%u network(s) have been merged\n", ctx
.merged
);
1122 loc_network_list_unref(ctx
.networks
);
1128 Deduplicate the tree
1131 struct loc_network_tree_dedup_ctx
{
1132 struct loc_network_tree
* tree
;
1133 struct loc_network
* network
;
1134 unsigned int removed
;
1137 static int loc_network_tree_dedup_step(struct loc_network
* network
, void* data
) {
1138 struct loc_network_tree_dedup_ctx
* ctx
= (struct loc_network_tree_dedup_ctx
*)data
;
1140 // First call when we have not seen any networks, yet
1141 if (!ctx
->network
) {
1142 ctx
->network
= loc_network_ref(network
);
1146 // If network is a subnet of ctx->network, and all properties match,
1147 // we can drop the network.
1148 if (loc_network_is_subnet(ctx
->network
, network
)) {
1149 if (loc_network_properties_cmp(ctx
->network
, network
) == 0) {
1150 // Increment counter
1153 // Remove the network
1154 return loc_network_tree_delete_network(ctx
->tree
, network
);
1160 // Drop the reference to the previous network
1162 loc_network_unref(ctx
->network
);
1163 ctx
->network
= loc_network_ref(network
);
1168 static int loc_network_tree_dedup(struct loc_network_tree
* tree
) {
1169 struct loc_network_tree_dedup_ctx ctx
= {
1176 // Walk through the entire tree
1177 r
= loc_network_tree_walk(tree
, NULL
, loc_network_tree_dedup_step
, &ctx
);
1181 DEBUG(tree
->ctx
, "%u network(s) have been removed\n", ctx
.removed
);
1185 loc_network_unref(ctx
.network
);
1190 static int loc_network_tree_delete_node(struct loc_network_tree_node
** node
) {
1191 struct loc_network_tree_node
* n
= *node
;
1195 // Return for nodes that have already been deleted
1201 r0
= loc_network_tree_delete_node(&n
->zero
);
1208 r1
= loc_network_tree_delete_node(&n
->one
);
1213 // Don't delete this node if we are a leaf
1217 // Don't delete this node if has child nodes that we need
1222 // It is now safe to delete the node
1223 loc_network_tree_node_unref(n
);
1229 static int loc_network_tree_delete_nodes(struct loc_network_tree
* tree
) {
1232 r
= loc_network_tree_delete_node(&tree
->root
);
1236 // Undelete the root node in case the entire tree got deleted
1237 tree
->root
->deleted
= 0;
1242 int loc_network_tree_cleanup(struct loc_network_tree
* tree
) {
1245 // Deduplicate the tree so finding merges is quicker
1246 r
= loc_network_tree_dedup(tree
);
1251 r
= loc_network_tree_merge(tree
);
1253 ERROR(tree
->ctx
, "Could not merge networks: %m\n");
1257 // Remove duplicates once again
1258 r
= loc_network_tree_dedup(tree
);
1262 // Delete any unneeded nodes
1263 r
= loc_network_tree_delete_nodes(tree
);