]> git.ipfire.org Git - people/ms/libloc.git/blame_incremental - src/network.c
network: Do not execute with an error when the excluded result will be empty
[people/ms/libloc.git] / src / network.c
... / ...
CommitLineData
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 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 <arpa/inet.h>
18#include <assert.h>
19#include <errno.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#ifdef HAVE_ENDIAN_H
25# include <endian.h>
26#endif
27
28#include <loc/libloc.h>
29#include <loc/compat.h>
30#include <loc/country.h>
31#include <loc/network.h>
32#include <loc/network-list.h>
33#include <loc/private.h>
34
35struct loc_network {
36 struct loc_ctx* ctx;
37 int refcount;
38
39 int family;
40 struct in6_addr first_address;
41 struct in6_addr last_address;
42 unsigned int prefix;
43
44 char country_code[3];
45 uint32_t asn;
46 enum loc_network_flags flags;
47};
48
49static int valid_prefix(struct in6_addr* address, unsigned int prefix) {
50 // The prefix cannot be larger than 128 bits
51 if (prefix > 128)
52 return 1;
53
54 // And the prefix cannot be zero
55 if (prefix == 0)
56 return 1;
57
58 // For IPv4-mapped addresses the prefix has to be 96 or lager
59 if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96)
60 return 1;
61
62 return 0;
63}
64
65static struct in6_addr prefix_to_bitmask(unsigned int prefix) {
66 struct in6_addr bitmask;
67
68 for (unsigned int i = 0; i < 16; i++)
69 bitmask.s6_addr[i] = 0;
70
71 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
72 if (i >= 8)
73 bitmask.s6_addr[j] = 0xff;
74 else
75 bitmask.s6_addr[j] = 0xff << (8 - i);
76 }
77
78 return bitmask;
79}
80
81static struct in6_addr make_first_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
82 struct in6_addr a;
83
84 // Perform bitwise AND
85 for (unsigned int i = 0; i < 4; i++)
86 a.s6_addr32[i] = address->s6_addr32[i] & bitmask->s6_addr32[i];
87
88 return a;
89}
90
91static struct in6_addr make_last_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
92 struct in6_addr a;
93
94 // Perform bitwise OR
95 for (unsigned int i = 0; i < 4; i++)
96 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask->s6_addr32[i];
97
98 return a;
99}
100
101static struct in6_addr address_increment(const struct in6_addr* address) {
102 struct in6_addr a = *address;
103
104 for (int octet = 15; octet >= 0; octet--) {
105 if (a.s6_addr[octet] < 255) {
106 a.s6_addr[octet]++;
107 break;
108 } else {
109 a.s6_addr[octet] = 0;
110 }
111 }
112
113 return a;
114}
115
116LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
117 struct in6_addr* address, unsigned int prefix) {
118 // Address cannot be unspecified
119 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
120 DEBUG(ctx, "Start address is unspecified\n");
121 return -EINVAL;
122 }
123
124 // Address cannot be loopback
125 if (IN6_IS_ADDR_LOOPBACK(address)) {
126 DEBUG(ctx, "Start address is loopback address\n");
127 return -EINVAL;
128 }
129
130 // Address cannot be link-local
131 if (IN6_IS_ADDR_LINKLOCAL(address)) {
132 DEBUG(ctx, "Start address cannot be link-local\n");
133 return -EINVAL;
134 }
135
136 // Address cannot be site-local
137 if (IN6_IS_ADDR_SITELOCAL(address)) {
138 DEBUG(ctx, "Start address cannot be site-local\n");
139 return -EINVAL;
140 }
141
142 // Validate the prefix
143 if (valid_prefix(address, prefix) != 0) {
144 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
145 return -EINVAL;
146 }
147
148 struct loc_network* n = calloc(1, sizeof(*n));
149 if (!n)
150 return -ENOMEM;
151
152 n->ctx = loc_ref(ctx);
153 n->refcount = 1;
154
155 // Store the prefix
156 n->prefix = prefix;
157
158 // Convert the prefix into a bitmask
159 struct in6_addr bitmask = prefix_to_bitmask(n->prefix);
160
161 // Store the first and last address in the network
162 n->first_address = make_first_address(address, &bitmask);
163 n->last_address = make_last_address(&n->first_address, &bitmask);
164
165 // Set family
166 if (IN6_IS_ADDR_V4MAPPED(&n->first_address))
167 n->family = AF_INET;
168 else
169 n->family = AF_INET6;
170
171 DEBUG(n->ctx, "Network allocated at %p\n", n);
172 *network = n;
173 return 0;
174}
175
176LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
177 const char* address_string) {
178 struct in6_addr first_address;
179 char* prefix_string;
180 unsigned int prefix = 128;
181 int r = -EINVAL;
182
183 DEBUG(ctx, "Attempting to parse network %s\n", address_string);
184
185 // Make a copy of the string to work on it
186 char* buffer = strdup(address_string);
187 address_string = prefix_string = buffer;
188
189 // Split address and prefix
190 address_string = strsep(&prefix_string, "/");
191
192 DEBUG(ctx, " Split into address = %s, prefix = %s\n", address_string, prefix_string);
193
194 // Parse the address
195 r = loc_parse_address(ctx, address_string, &first_address);
196 if (r) {
197 DEBUG(ctx, "The address could not be parsed\n");
198 goto FAIL;
199 }
200
201 // If a prefix was given, we will try to parse it
202 if (prefix_string) {
203 // Convert prefix to integer
204 prefix = strtol(prefix_string, NULL, 10);
205
206 if (!prefix) {
207 DEBUG(ctx, "The prefix was not parsable: %s\n", prefix_string);
208 goto FAIL;
209 }
210
211 // Map the prefix to IPv6 if needed
212 if (IN6_IS_ADDR_V4MAPPED(&first_address))
213 prefix += 96;
214 }
215
216FAIL:
217 // Free temporary buffer
218 free(buffer);
219
220 // Exit if the parsing was unsuccessful
221 if (r)
222 return r;
223
224 // Create a new network
225 return loc_network_new(ctx, network, &first_address, prefix);
226}
227
228LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
229 network->refcount++;
230
231 return network;
232}
233
234static void loc_network_free(struct loc_network* network) {
235 DEBUG(network->ctx, "Releasing network at %p\n", network);
236
237 loc_unref(network->ctx);
238 free(network);
239}
240
241LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
242 if (!network)
243 return NULL;
244
245 if (--network->refcount > 0)
246 return network;
247
248 loc_network_free(network);
249 return NULL;
250}
251
252static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) {
253 const char* ret = inet_ntop(AF_INET6, address, string, length);
254 if (!ret)
255 return -1;
256
257 return 0;
258}
259
260static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
261 struct in_addr ipv4_address;
262 ipv4_address.s_addr = address->s6_addr32[3];
263
264 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
265 if (!ret)
266 return -1;
267
268 return 0;
269}
270
271LOC_EXPORT char* loc_network_str(struct loc_network* network) {
272 int r;
273 const size_t length = INET6_ADDRSTRLEN + 4;
274
275 char* string = malloc(length);
276 if (!string)
277 return NULL;
278
279 unsigned int prefix = network->prefix;
280
281 switch (network->family) {
282 case AF_INET6:
283 r = format_ipv6_address(&network->first_address, string, length);
284 break;
285
286 case AF_INET:
287 r = format_ipv4_address(&network->first_address, string, length);
288 prefix -= 96;
289 break;
290
291 default:
292 r = -1;
293 break;
294 }
295
296 if (r) {
297 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
298 free(string);
299
300 return NULL;
301 }
302
303 // Append prefix
304 sprintf(string + strlen(string), "/%u", prefix);
305
306 return string;
307}
308
309LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
310 return network->family;
311}
312
313LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
314 switch (network->family) {
315 case AF_INET6:
316 return network->prefix;
317
318 case AF_INET:
319 return network->prefix - 96;
320 }
321
322 return 0;
323}
324
325static char* loc_network_format_address(struct loc_network* network, const struct in6_addr* address) {
326 const size_t length = INET6_ADDRSTRLEN;
327
328 char* string = malloc(length);
329 if (!string)
330 return NULL;
331
332 int r = 0;
333
334 switch (network->family) {
335 case AF_INET6:
336 r = format_ipv6_address(address, string, length);
337 break;
338
339 case AF_INET:
340 r = format_ipv4_address(address, string, length);
341 break;
342
343 default:
344 r = -1;
345 break;
346 }
347
348 if (r) {
349 ERROR(network->ctx, "Could not format IP address to string: %s\n", strerror(errno));
350 free(string);
351
352 return NULL;
353 }
354
355 return string;
356}
357
358LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
359 return &network->first_address;
360}
361
362LOC_EXPORT char* loc_network_format_first_address(struct loc_network* network) {
363 return loc_network_format_address(network, &network->first_address);
364}
365
366LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
367 return &network->last_address;
368}
369
370LOC_EXPORT char* loc_network_format_last_address(struct loc_network* network) {
371 return loc_network_format_address(network, &network->last_address);
372}
373
374LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
375 // Address must be larger than the start address
376 if (in6_addr_cmp(&network->first_address, address) > 0)
377 return 0;
378
379 // Address must be smaller than the last address
380 if (in6_addr_cmp(&network->last_address, address) < 0)
381 return 0;
382
383 // The address is inside this network
384 return 1;
385}
386
387LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
388 return network->country_code;
389}
390
391LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
392 // Set empty country code
393 if (!country_code || !*country_code) {
394 *network->country_code = '\0';
395 return 0;
396 }
397
398 // Check country code
399 if (!loc_country_code_is_valid(country_code))
400 return -EINVAL;
401
402 loc_country_code_copy(network->country_code, country_code);
403
404 return 0;
405}
406
407LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) {
408 // Check country code
409 if (!loc_country_code_is_valid(country_code))
410 return -EINVAL;
411
412 return (network->country_code[0] == country_code[0])
413 && (network->country_code[1] == country_code[1]);
414}
415
416LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
417 return network->asn;
418}
419
420LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
421 network->asn = asn;
422
423 return 0;
424}
425
426LOC_EXPORT int loc_network_match_asn(struct loc_network* network, uint32_t asn) {
427 return network->asn == asn;
428}
429
430LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
431 return network->flags & flag;
432}
433
434LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
435 network->flags |= flag;
436
437 return 0;
438}
439
440LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag) {
441 return loc_network_has_flag(network, flag);
442}
443
444LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
445 // Compare address
446 int r = in6_addr_cmp(&self->first_address, &other->first_address);
447 if (r)
448 return r;
449
450 // Compare prefix
451 if (self->prefix > other->prefix)
452 return 1;
453 else if (self->prefix < other->prefix)
454 return -1;
455
456 // Both networks are equal
457 return 0;
458}
459
460LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
461 // Either of the start addresses must be in the other subnet
462 if (loc_network_match_address(self, &other->first_address))
463 return 1;
464
465 if (loc_network_match_address(other, &self->first_address))
466 return 1;
467
468 // Or either of the end addresses is in the other subnet
469 if (loc_network_match_address(self, &other->last_address))
470 return 1;
471
472 if (loc_network_match_address(other, &self->last_address))
473 return 1;
474
475 return 0;
476}
477
478LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
479 // The prefix must be smaller (this avoids the more complex comparisons later)
480 if (self->prefix > other->prefix)
481 return 0;
482
483 // If the start address of the other network is smaller than this network,
484 // it cannot be a subnet.
485 if (in6_addr_cmp(&self->first_address, &other->first_address) > 0)
486 return 0;
487
488 // If the end address of the other network is greater than this network,
489 // it cannot be a subnet.
490 if (in6_addr_cmp(&self->last_address, &other->last_address) < 0)
491 return 0;
492
493 return 1;
494}
495
496LOC_EXPORT int loc_network_subnets(struct loc_network* network,
497 struct loc_network** subnet1, struct loc_network** subnet2) {
498 int r;
499 *subnet1 = NULL;
500 *subnet2 = NULL;
501
502 // New prefix length
503 unsigned int prefix = network->prefix + 1;
504
505 // Check if the new prefix is valid
506 if (valid_prefix(&network->first_address, prefix))
507 return -1;
508
509 // Create the first half of the network
510 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
511 if (r)
512 return r;
513
514 // The next subnet starts after the first one
515 struct in6_addr first_address = address_increment(&(*subnet1)->last_address);
516
517 // Create the second half of the network
518 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
519 if (r)
520 return r;
521
522 // Copy country code
523 const char* country_code = loc_network_get_country_code(network);
524 if (country_code) {
525 loc_network_set_country_code(*subnet1, country_code);
526 loc_network_set_country_code(*subnet2, country_code);
527 }
528
529 // Copy ASN
530 uint32_t asn = loc_network_get_asn(network);
531 if (asn) {
532 loc_network_set_asn(*subnet1, asn);
533 loc_network_set_asn(*subnet2, asn);
534 }
535
536 return 0;
537}
538
539static int __loc_network_exclude(struct loc_network* network,
540 struct loc_network* other, struct loc_network_list* list) {
541 struct loc_network* subnet1 = NULL;
542 struct loc_network* subnet2 = NULL;
543
544 int r = loc_network_subnets(network, &subnet1, &subnet2);
545 if (r)
546 goto ERROR;
547
548 if (loc_network_cmp(other, subnet1) == 0) {
549 r = loc_network_list_push(list, subnet2);
550 if (r)
551 goto ERROR;
552
553 } else if (loc_network_cmp(other, subnet2) == 0) {
554 r = loc_network_list_push(list, subnet1);
555 if (r)
556 goto ERROR;
557
558 } else if (loc_network_is_subnet(subnet1, other)) {
559 r = loc_network_list_push(list, subnet2);
560 if (r)
561 goto ERROR;
562
563 r = __loc_network_exclude(subnet1, other, list);
564 if (r)
565 goto ERROR;
566
567 } else if (loc_network_is_subnet(subnet2, other)) {
568 r = loc_network_list_push(list, subnet1);
569 if (r)
570 goto ERROR;
571
572 r = __loc_network_exclude(subnet2, other, list);
573 if (r)
574 goto ERROR;
575
576 } else {
577 ERROR(network->ctx, "We should never get here\n");
578 r = 1;
579 goto ERROR;
580 }
581
582ERROR:
583 if (subnet1)
584 loc_network_unref(subnet1);
585
586 if (subnet2)
587 loc_network_unref(subnet2);
588
589 return r;
590}
591
592static int __loc_network_exclude_to_list(struct loc_network* self,
593 struct loc_network* other, struct loc_network_list* list) {
594 // Other must be a subnet of self
595 if (!loc_network_is_subnet(self, other)) {
596 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
597
598 // Exit silently
599 return 0;
600 }
601
602 // We cannot perform this operation if both networks equal
603 if (loc_network_cmp(self, other) == 0) {
604 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
605
606 // Exit silently
607 return 0;
608 }
609
610 return __loc_network_exclude(self, other, list);
611}
612
613LOC_EXPORT struct loc_network_list* loc_network_exclude(
614 struct loc_network* self, struct loc_network* other) {
615 struct loc_network_list* list;
616
617#ifdef ENABLE_DEBUG
618 char* n1 = loc_network_str(self);
619 char* n2 = loc_network_str(other);
620
621 DEBUG(self->ctx, "Returning %s excluding %s...\n", n1, n2);
622
623 free(n1);
624 free(n2);
625#endif
626
627 // Create a new list with the result
628 int r = loc_network_list_new(self->ctx, &list);
629 if (r) {
630 ERROR(self->ctx, "Could not create network list: %d\n", r);
631
632 return NULL;
633 }
634
635 r = __loc_network_exclude_to_list(self, other, list);
636 if (r) {
637 loc_network_list_unref(list);
638
639 return NULL;
640 }
641
642 // Return the result
643 return list;
644}
645
646LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
647 struct loc_network* network, struct loc_network_list* list) {
648 struct loc_network_list* to_check;
649
650 // Create a new list with all networks to look at
651 int r = loc_network_list_new(network->ctx, &to_check);
652 if (r)
653 return NULL;
654
655 struct loc_network* subnet = NULL;
656 struct loc_network_list* subnets = NULL;
657
658 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
659 subnet = loc_network_list_get(list, i);
660
661 // Find all excluded networks
662 if (!loc_network_list_contains(to_check, subnet)) {
663 r = __loc_network_exclude_to_list(network, subnet, to_check);
664 if (r) {
665 loc_network_list_unref(to_check);
666 loc_network_unref(subnet);
667
668 return NULL;
669 }
670 }
671
672 // Cleanup
673 loc_network_unref(subnet);
674 }
675
676 r = loc_network_list_new(network->ctx, &subnets);
677 if (r) {
678 loc_network_list_unref(to_check);
679 return NULL;
680 }
681
682 while (!loc_network_list_empty(to_check)) {
683 struct loc_network* subnet_to_check = loc_network_list_pop(to_check);
684
685 // Check whether the subnet to check is part of the input list
686 if (loc_network_list_contains(list, subnet_to_check)) {
687 loc_network_unref(subnet_to_check);
688 continue;
689 }
690
691 // Marks whether this subnet passed all checks
692 int passed = 1;
693
694 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
695 subnet = loc_network_list_get(list, i);
696
697 // Drop this subnet if is a subnet of another subnet
698 if (loc_network_is_subnet(subnet_to_check, subnet)) {
699 passed = 0;
700 loc_network_unref(subnet);
701 break;
702 }
703
704 // Break it down if it overlaps
705 if (loc_network_overlaps(subnet_to_check, subnet)) {
706 passed = 0;
707
708 struct loc_network_list* excluded = loc_network_exclude(subnet_to_check, subnet);
709 if (excluded) {
710 loc_network_list_merge(to_check, excluded);
711 loc_network_list_unref(excluded);
712 }
713
714 loc_network_unref(subnet);
715 break;
716 }
717
718 loc_network_unref(subnet);
719 }
720
721 if (passed) {
722 r = loc_network_list_push(subnets, subnet_to_check);
723 }
724
725 loc_network_unref(subnet_to_check);
726 }
727
728 loc_network_list_unref(to_check);
729
730 // Sort the result
731 loc_network_list_sort(subnets);
732
733 return subnets;
734}
735
736LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
737 // Add country code
738 loc_country_code_copy(dbobj->country_code, network->country_code);
739
740 // Add ASN
741 dbobj->asn = htobe32(network->asn);
742
743 // Flags
744 dbobj->flags = htobe16(network->flags);
745
746 return 0;
747}
748
749LOC_EXPORT int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
750 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
751 char country_code[3] = "\0\0";
752
753 int r = loc_network_new(ctx, network, address, prefix);
754 if (r) {
755 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
756 return r;
757 }
758
759 // Import country code
760 loc_country_code_copy(country_code, dbobj->country_code);
761
762 r = loc_network_set_country_code(*network, country_code);
763 if (r) {
764 ERROR(ctx, "Could not set country code: %s\n", country_code);
765 return r;
766 }
767
768 // Import ASN
769 uint32_t asn = be32toh(dbobj->asn);
770 r = loc_network_set_asn(*network, asn);
771 if (r) {
772 ERROR(ctx, "Could not set ASN: %d\n", asn);
773 return r;
774 }
775
776 // Import flags
777 int flags = be16toh(dbobj->flags);
778 r = loc_network_set_flag(*network, flags);
779 if (r) {
780 ERROR(ctx, "Could not set flags: %d\n", flags);
781 return r;
782 }
783
784 return 0;
785}
786
787struct loc_network_tree {
788 struct loc_ctx* ctx;
789 int refcount;
790
791 struct loc_network_tree_node* root;
792};
793
794struct loc_network_tree_node {
795 struct loc_ctx* ctx;
796 int refcount;
797
798 struct loc_network_tree_node* zero;
799 struct loc_network_tree_node* one;
800
801 struct loc_network* network;
802};
803
804int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
805 struct loc_network_tree* t = calloc(1, sizeof(*t));
806 if (!t)
807 return -ENOMEM;
808
809 t->ctx = loc_ref(ctx);
810 t->refcount = 1;
811
812 // Create the root node
813 int r = loc_network_tree_node_new(ctx, &t->root);
814 if (r) {
815 loc_network_tree_unref(t);
816 return r;
817 }
818
819 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
820 *tree = t;
821 return 0;
822}
823
824struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
825 return loc_network_tree_node_ref(tree->root);
826}
827
828static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
829 struct loc_network_tree_node** n;
830
831 if (path == 0)
832 n = &node->zero;
833 else
834 n = &node->one;
835
836 // If the desired node doesn't exist, yet, we will create it
837 if (*n == NULL) {
838 int r = loc_network_tree_node_new(node->ctx, n);
839 if (r)
840 return NULL;
841 }
842
843 return *n;
844}
845
846static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
847 struct loc_network_tree_node* node = tree->root;
848
849 for (unsigned int i = 0; i < prefix; i++) {
850 // Check if the ith bit is one or zero
851 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
852 }
853
854 return node;
855}
856
857static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
858 int(*filter_callback)(struct loc_network* network, void* data),
859 int(*callback)(struct loc_network* network, void* data), void* data) {
860 int r;
861
862 // Finding a network ends the walk here
863 if (node->network) {
864 if (filter_callback) {
865 int f = filter_callback(node->network, data);
866 if (f < 0)
867 return f;
868
869 // Skip network if filter function returns value greater than zero
870 if (f > 0)
871 return 0;
872 }
873
874 r = callback(node->network, data);
875 if (r)
876 return r;
877 }
878
879 // Walk down on the left side of the tree first
880 if (node->zero) {
881 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
882 if (r)
883 return r;
884 }
885
886 // Then walk on the other side
887 if (node->one) {
888 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
889 if (r)
890 return r;
891 }
892
893 return 0;
894}
895
896int loc_network_tree_walk(struct loc_network_tree* tree,
897 int(*filter_callback)(struct loc_network* network, void* data),
898 int(*callback)(struct loc_network* network, void* data), void* data) {
899 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
900}
901
902static void loc_network_tree_free(struct loc_network_tree* tree) {
903 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
904
905 loc_network_tree_node_unref(tree->root);
906
907 loc_unref(tree->ctx);
908 free(tree);
909}
910
911struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
912 if (--tree->refcount > 0)
913 return tree;
914
915 loc_network_tree_free(tree);
916 return NULL;
917}
918
919static int __loc_network_tree_dump(struct loc_network* network, void* data) {
920 DEBUG(network->ctx, "Dumping network at %p\n", network);
921
922 char* s = loc_network_str(network);
923 if (!s)
924 return 1;
925
926 INFO(network->ctx, "%s\n", s);
927 free(s);
928
929 return 0;
930}
931
932int loc_network_tree_dump(struct loc_network_tree* tree) {
933 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
934
935 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
936}
937
938int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
939 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
940
941 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
942 &network->first_address, network->prefix);
943 if (!node) {
944 ERROR(tree->ctx, "Could not find a node\n");
945 return -ENOMEM;
946 }
947
948 // Check if node has not been set before
949 if (node->network) {
950 DEBUG(tree->ctx, "There is already a network at this path\n");
951 return -EBUSY;
952 }
953
954 // Point node to the network
955 node->network = loc_network_ref(network);
956
957 return 0;
958}
959
960static int __loc_network_tree_count(struct loc_network* network, void* data) {
961 size_t* counter = (size_t*)data;
962
963 // Increase the counter for each network
964 counter++;
965
966 return 0;
967}
968
969size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
970 size_t counter = 0;
971
972 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
973 if (r)
974 return r;
975
976 return counter;
977}
978
979static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
980 size_t counter = 1;
981
982 if (node->zero)
983 counter += __loc_network_tree_count_nodes(node->zero);
984
985 if (node->one)
986 counter += __loc_network_tree_count_nodes(node->one);
987
988 return counter;
989}
990
991size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
992 return __loc_network_tree_count_nodes(tree->root);
993}
994
995int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
996 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
997 if (!n)
998 return -ENOMEM;
999
1000 n->ctx = loc_ref(ctx);
1001 n->refcount = 1;
1002
1003 n->zero = n->one = NULL;
1004
1005 DEBUG(n->ctx, "Network node allocated at %p\n", n);
1006 *node = n;
1007 return 0;
1008}
1009
1010struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
1011 if (node)
1012 node->refcount++;
1013
1014 return node;
1015}
1016
1017static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
1018 DEBUG(node->ctx, "Releasing network node at %p\n", node);
1019
1020 if (node->network)
1021 loc_network_unref(node->network);
1022
1023 if (node->zero)
1024 loc_network_tree_node_unref(node->zero);
1025
1026 if (node->one)
1027 loc_network_tree_node_unref(node->one);
1028
1029 loc_unref(node->ctx);
1030 free(node);
1031}
1032
1033struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
1034 if (!node)
1035 return NULL;
1036
1037 if (--node->refcount > 0)
1038 return node;
1039
1040 loc_network_tree_node_free(node);
1041 return NULL;
1042}
1043
1044struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
1045 if (index == 0)
1046 node = node->zero;
1047 else
1048 node = node->one;
1049
1050 if (!node)
1051 return NULL;
1052
1053 return loc_network_tree_node_ref(node);
1054}
1055
1056int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
1057 return (!!node->network);
1058}
1059
1060struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
1061 return loc_network_ref(node->network);
1062}