]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network-tree.c
tree: Be smarter when removing networks from the stack
[people/ms/libloc.git] / src / network-tree.c
CommitLineData
fe8277ed
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2024 IPFire Development Team <info@ipfire.org>
5
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.
10
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.
15*/
16
17#include <stddef.h>
18#include <stdlib.h>
19#include <errno.h>
20
21#include <libloc/libloc.h>
22#include <libloc/address.h>
23#include <libloc/network-tree.h>
24#include <libloc/private.h>
25
26struct loc_network_tree {
27 struct loc_ctx* ctx;
28 int refcount;
29
30 struct loc_network_tree_node* root;
31};
32
33struct loc_network_tree_node {
34 struct loc_ctx* ctx;
35 int refcount;
36
37 struct loc_network_tree_node* zero;
38 struct loc_network_tree_node* one;
39
40 struct loc_network* network;
41
42 // Set if deleted
43 int deleted:1;
44};
45
46int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
47 struct loc_network_tree* t = calloc(1, sizeof(*t));
48 if (!t)
49 return 1;
50
51 t->ctx = loc_ref(ctx);
52 t->refcount = 1;
53
54 // Create the root node
55 int r = loc_network_tree_node_new(ctx, &t->root);
56 if (r) {
57 loc_network_tree_unref(t);
58 return r;
59 }
60
61 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
62 *tree = t;
63 return 0;
64}
65
66struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
67 return loc_network_tree_node_ref(tree->root);
68}
69
70static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
71 struct loc_network_tree_node** n = NULL;
72 int r;
73
74 switch (path) {
75 case 0:
76 n = &node->zero;
77 break;
78
79 case 1:
80 n = &node->one;
81 break;
82
83 default:
84 errno = EINVAL;
85 return NULL;
86 }
87
88 // If the node existed, but has been deleted, we undelete it
89 if (*n && (*n)->deleted) {
90 (*n)->deleted = 0;
91
92 // If the desired node doesn't exist, yet, we will create it
93 } else if (!*n) {
94 r = loc_network_tree_node_new(node->ctx, n);
95 if (r)
96 return NULL;
97 }
98
99 return *n;
100}
101
102static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
103 struct loc_network_tree_node* node = tree->root;
104
105 for (unsigned int i = 0; i < prefix; i++) {
106 // Check if the ith bit is one or zero
107 node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
108 }
109
110 return node;
111}
112
113static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
114 int(*filter_callback)(struct loc_network* network, void* data),
115 int(*callback)(struct loc_network* network, void* data), void* data) {
116 int r;
117
118 // If the node has been deleted, don't process it
119 if (node->deleted)
120 return 0;
121
122 // Finding a network ends the walk here
123 if (node->network) {
124 if (filter_callback) {
125 int f = filter_callback(node->network, data);
126 if (f < 0)
127 return f;
128
129 // Skip network if filter function returns value greater than zero
130 if (f > 0)
131 return 0;
132 }
133
134 r = callback(node->network, data);
135 if (r)
136 return r;
137 }
138
139 // Walk down on the left side of the tree first
140 if (node->zero) {
141 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
142 if (r)
143 return r;
144 }
145
146 // Then walk on the other side
147 if (node->one) {
148 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
149 if (r)
150 return r;
151 }
152
153 return 0;
154}
155
156int loc_network_tree_walk(struct loc_network_tree* tree,
157 int(*filter_callback)(struct loc_network* network, void* data),
158 int(*callback)(struct loc_network* network, void* data), void* data) {
159 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
160}
161
162static void loc_network_tree_free(struct loc_network_tree* tree) {
163 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
164
165 loc_network_tree_node_unref(tree->root);
166
167 loc_unref(tree->ctx);
168 free(tree);
169}
170
171struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
172 if (--tree->refcount > 0)
173 return tree;
174
175 loc_network_tree_free(tree);
176 return NULL;
177}
178
179static int __loc_network_tree_dump(struct loc_network* network, void* data) {
180 struct loc_ctx* ctx = data;
181
182 DEBUG(ctx, "Dumping network at %p\n", network);
183
184 const char* s = loc_network_str(network);
185 if (!s)
186 return 1;
187
188 INFO(ctx, "%s\n", s);
189
190 return 0;
191}
192
193int loc_network_tree_dump(struct loc_network_tree* tree) {
194 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
195
196 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, tree->ctx);
197}
198
199int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
200 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
201
202 const struct in6_addr* first_address = loc_network_get_first_address(network);
a92139f1 203 const unsigned int prefix = loc_network_raw_prefix(network);
fe8277ed
MT
204
205 struct loc_network_tree_node* node = loc_network_tree_get_path(tree, first_address, prefix);
206 if (!node) {
207 ERROR(tree->ctx, "Could not find a node\n");
208 return -ENOMEM;
209 }
210
211 // Check if node has not been set before
212 if (node->network) {
213 DEBUG(tree->ctx, "There is already a network at this path: %s\n",
214 loc_network_str(node->network));
215 return -EBUSY;
216 }
217
218 // Point node to the network
219 node->network = loc_network_ref(network);
220
221 return 0;
222}
223
224static int loc_network_tree_delete_network(
225 struct loc_network_tree* tree, struct loc_network* network) {
226 struct loc_network_tree_node* node = NULL;
227
228 DEBUG(tree->ctx, "Deleting network %s from tree...\n", loc_network_str(network));
229
230 const struct in6_addr* first_address = loc_network_get_first_address(network);
a92139f1 231 const unsigned int prefix = loc_network_raw_prefix(network);
fe8277ed
MT
232
233 node = loc_network_tree_get_path(tree, first_address, prefix);
234 if (!node) {
235 ERROR(tree->ctx, "Network was not found in tree %s\n", loc_network_str(network));
236 return 1;
237 }
238
239 // Drop the network
240 if (node->network) {
241 loc_network_unref(node->network);
242 node->network = NULL;
243 }
244
245 // Mark the node as deleted if it was a leaf
246 if (!node->zero && !node->one)
247 node->deleted = 1;
248
249 return 0;
250}
251
252static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
253 size_t counter = 1;
254
255 // Don't count deleted nodes
256 if (node->deleted)
257 return 0;
258
259 if (node->zero)
260 counter += __loc_network_tree_count_nodes(node->zero);
261
262 if (node->one)
263 counter += __loc_network_tree_count_nodes(node->one);
264
265 return counter;
266}
267
268size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
269 return __loc_network_tree_count_nodes(tree->root);
270}
271
272int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
273 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
274 if (!n)
275 return -ENOMEM;
276
277 n->ctx = loc_ref(ctx);
278 n->refcount = 1;
279
280 n->zero = n->one = NULL;
281
282 DEBUG(n->ctx, "Network node allocated at %p\n", n);
283 *node = n;
284 return 0;
285}
286
287struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
288 if (node)
289 node->refcount++;
290
291 return node;
292}
293
294static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
295 DEBUG(node->ctx, "Releasing network node at %p\n", node);
296
297 if (node->network)
298 loc_network_unref(node->network);
299
300 if (node->zero)
301 loc_network_tree_node_unref(node->zero);
302
303 if (node->one)
304 loc_network_tree_node_unref(node->one);
305
306 loc_unref(node->ctx);
307 free(node);
308}
309
310struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
311 if (--node->refcount > 0)
312 return node;
313
314 loc_network_tree_node_free(node);
315 return NULL;
316}
317
318struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
319 if (index == 0)
320 node = node->zero;
321 else
322 node = node->one;
323
324 if (!node)
325 return NULL;
326
327 return loc_network_tree_node_ref(node);
328}
329
330int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
331 return (!!node->network);
332}
333
334struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
335 return loc_network_ref(node->network);
336}
337
338/*
339 Merge the tree!
340*/
341
342struct loc_network_tree_merge_ctx {
343 struct loc_network_tree* tree;
344 struct loc_network_list* networks;
345 unsigned int merged;
346};
347
348static int loc_network_tree_merge_step(struct loc_network* network, void* data) {
349 struct loc_network_tree_merge_ctx* ctx = (struct loc_network_tree_merge_ctx*)data;
350 struct loc_network* n = NULL;
351 struct loc_network* m = NULL;
352 int r;
353
354 // How many networks do we have?
355 size_t i = loc_network_list_size(ctx->networks);
356
357 // If the list is empty, just add the network
358 if (i == 0)
359 return loc_network_list_push(ctx->networks, network);
360
361 while (i--) {
362 // Fetch the last network of the list
363 n = loc_network_list_get(ctx->networks, i);
364
365 // Try to merge the two networks
366 r = loc_network_merge(&m, n, network);
367 if (r)
368 goto ERROR;
369
370 // Did we get a result?
371 if (m) {
372 DEBUG(ctx->tree->ctx, "Merged networks %s + %s -> %s\n",
373 loc_network_str(n), loc_network_str(network), loc_network_str(m));
374
375 // Add the new network
376 r = loc_network_tree_add_network(ctx->tree, m);
377 switch (r) {
378 case 0:
379 break;
380
381 // There might already be a network
382 case -EBUSY:
383 r = 0;
384 goto ERROR;
385
386 default:
387 goto ERROR;
388 }
389
390 // Remove the merge networks
391 r = loc_network_tree_delete_network(ctx->tree, network);
392 if (r)
393 goto ERROR;
394
395 r = loc_network_tree_delete_network(ctx->tree, n);
396 if (r)
397 goto ERROR;
398
399 // Add the new network to the stack
400 r = loc_network_list_push(ctx->networks, m);
401 if (r)
402 goto ERROR;
403
404 // Remove the previous network from the stack
405 r = loc_network_list_remove(ctx->networks, n);
406 if (r)
407 goto ERROR;
408
409 // Count merges
410 ctx->merged++;
411
412 // Try merging the new network with others
413 r = loc_network_tree_merge_step(m, data);
414 if (r)
415 goto ERROR;
416
417 loc_network_unref(m);
418 m = NULL;
419
420 // Once we have found a merge, we are done
421 break;
422
423 // If we could not merge the two networks, we add the current one
424 } else {
425 r = loc_network_list_push(ctx->networks, network);
426 if (r)
427 goto ERROR;
428 }
429
430 loc_network_unref(n);
431 n = NULL;
432 }
433
434 const unsigned int prefix = loc_network_prefix(network);
435
436 // Remove any networks that we cannot merge
437 loc_network_list_remove_with_prefix_smaller_than(ctx->networks, prefix);
438
439ERROR:
440 if (m)
441 loc_network_unref(m);
442 if (n)
443 loc_network_unref(n);
444
445 return r;
446}
447
448static int loc_network_tree_merge(struct loc_network_tree* tree) {
449 struct loc_network_tree_merge_ctx ctx = {
450 .tree = tree,
451 .networks = NULL,
452 .merged = 0,
453 };
454 int r;
455
456 // Create a new list
457 r = loc_network_list_new(tree->ctx, &ctx.networks);
458 if (r)
459 goto ERROR;
460
461 // Walk through the entire tree
462 r = loc_network_tree_walk(tree, NULL, loc_network_tree_merge_step, &ctx);
463 if (r)
464 goto ERROR;
465
466 DEBUG(tree->ctx, "%u network(s) have been merged\n", ctx.merged);
467
468ERROR:
469 if (ctx.networks)
470 loc_network_list_unref(ctx.networks);
471
472 return r;
473}
474
475/*
476 Deduplicate the tree
477*/
478
479struct loc_network_tree_dedup_ctx {
480 struct loc_network_tree* tree;
481 struct loc_network_list* stack;
578fda4e
MT
482 unsigned int* removed;
483 int family;
fe8277ed
MT
484};
485
486static int loc_network_tree_dedup_step(struct loc_network* network, void* data) {
487 struct loc_network_tree_dedup_ctx* ctx = (struct loc_network_tree_dedup_ctx*)data;
488 struct loc_network* n = NULL;
489 int r;
490
491 // First call when we have not seen any networks, yet
492 if (loc_network_list_empty(ctx->stack))
493 return loc_network_list_push(ctx->stack, network);
494
e5e53b59 495 // Walk through all networks on the stack...
fe8277ed
MT
496 for (int i = loc_network_list_size(ctx->stack) - 1; i >= 0; i--) {
497 n = loc_network_list_get(ctx->stack, i);
498
499 // Is network a subnet?
500 if (loc_network_is_subnet(n, network)) {
501 // Do all properties match?
502 if (loc_network_properties_cmp(n, network) == 0) {
503 r = loc_network_tree_delete_network(ctx->tree, network);
504 if (r)
9153d1e7 505 goto END;
fe8277ed
MT
506
507 // Count
578fda4e 508 (*ctx->removed)++;
fe8277ed
MT
509
510 // Once we removed the subnet, we are done
511 goto END;
512 }
513
514 // Once we found a subnet, we are done
515 break;
516 }
9153d1e7 517
e5e53b59
MT
518 // If the network wasn't a subnet, we can remove it,
519 // because we won't ever see a subnet again.
520 r = loc_network_list_remove(ctx->stack, n);
521 if (r)
522 goto END;
523
9153d1e7
MT
524 loc_network_unref(n);
525 n = NULL;
fe8277ed
MT
526 }
527
528 // If network did not get removed, we push it into the stack
529 r = loc_network_list_push(ctx->stack, network);
530 if (r)
531 return r;
532
533END:
534 if (n)
535 loc_network_unref(n);
536
537 return r;
538}
539
578fda4e
MT
540static int loc_network_tree_dedup_filter(struct loc_network* network, void* data) {
541 const struct loc_network_tree_dedup_ctx* ctx = data;
542
543 // Match address family
544 return ctx->family == loc_network_address_family(network);
545}
546
547static int loc_network_tree_dedup_one(struct loc_network_tree* tree,
548 const int family, unsigned int* removed) {
fe8277ed
MT
549 struct loc_network_tree_dedup_ctx ctx = {
550 .tree = tree,
551 .stack = NULL,
578fda4e
MT
552 .removed = removed,
553 .family = family,
fe8277ed
MT
554 };
555 int r;
556
557 r = loc_network_list_new(tree->ctx, &ctx.stack);
558 if (r)
559 return r;
560
561 // Walk through the entire tree
578fda4e
MT
562 r = loc_network_tree_walk(tree,
563 loc_network_tree_dedup_filter, loc_network_tree_dedup_step, &ctx);
fe8277ed
MT
564 if (r)
565 goto ERROR;
566
fe8277ed
MT
567ERROR:
568 if (ctx.stack)
569 loc_network_list_unref(ctx.stack);
570
571 return r;
572}
573
578fda4e
MT
574static int loc_network_tree_dedup(struct loc_network_tree* tree) {
575 unsigned int removed = 0;
576 int r;
577
578 r = loc_network_tree_dedup_one(tree, AF_INET6, &removed);
579 if (r)
580 return r;
581
582 r = loc_network_tree_dedup_one(tree, AF_INET, &removed);
583 if (r)
584 return r;
585
586 DEBUG(tree->ctx, "%u network(s) have been removed\n", removed);
587
588 return 0;
589}
590
fe8277ed
MT
591static int loc_network_tree_delete_node(struct loc_network_tree* tree,
592 struct loc_network_tree_node** node) {
593 struct loc_network_tree_node* n = *node;
594 int r0 = 1;
595 int r1 = 1;
596
597 // Return for nodes that have already been deleted
598 if (n->deleted)
599 goto DELETE;
600
601 // Delete zero
602 if (n->zero) {
603 r0 = loc_network_tree_delete_node(tree, &n->zero);
604 if (r0 < 0)
605 return r0;
606 }
607
608 // Delete one
609 if (n->one) {
610 r1 = loc_network_tree_delete_node(tree, &n->one);
611 if (r1 < 0)
612 return r1;
613 }
614
615 // Don't delete this node if we are a leaf
616 if (n->network)
617 return 0;
618
619 // Don't delete this node if has child nodes that we need
620 if (!r0 || !r1)
621 return 0;
622
623 // Don't delete root
624 if (tree->root == n)
625 return 0;
626
627DELETE:
628 // It is now safe to delete the node
629 loc_network_tree_node_unref(n);
630 *node = NULL;
631
632 return 1;
633}
634
635static int loc_network_tree_delete_nodes(struct loc_network_tree* tree) {
636 int r;
637
638 r = loc_network_tree_delete_node(tree, &tree->root);
639 if (r < 0)
640 return r;
641
642 return 0;
643}
644
645int loc_network_tree_cleanup(struct loc_network_tree* tree) {
646 int r;
647
648 // Deduplicate the tree
649 r = loc_network_tree_dedup(tree);
650 if (r)
651 return r;
652
653 // Merge networks
654 r = loc_network_tree_merge(tree);
655 if (r) {
656 ERROR(tree->ctx, "Could not merge networks: %m\n");
657 return r;
658 }
659
660 // Delete any unneeded nodes
661 r = loc_network_tree_delete_nodes(tree);
662 if (r)
663 return r;
664
665 return 0;
666}