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