]> git.ipfire.org Git - location/libloc.git/blob - src/network.c
network: Drop redundant loc_network_match_flag
[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_match_asn(struct loc_network* network, uint32_t asn) {
409 return network->asn == asn;
410 }
411
412 LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
413 return network->flags & flag;
414 }
415
416 LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
417 network->flags |= flag;
418
419 return 0;
420 }
421
422 LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
423 // Compare address
424 int r = in6_addr_cmp(&self->first_address, &other->first_address);
425 if (r)
426 return r;
427
428 // Compare prefix
429 if (self->prefix > other->prefix)
430 return 1;
431 else if (self->prefix < other->prefix)
432 return -1;
433
434 // Both networks are equal
435 return 0;
436 }
437
438 LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
439 // Either of the start addresses must be in the other subnet
440 if (loc_network_match_address(self, &other->first_address))
441 return 1;
442
443 if (loc_network_match_address(other, &self->first_address))
444 return 1;
445
446 // Or either of the end addresses is in the other subnet
447 if (loc_network_match_address(self, &other->last_address))
448 return 1;
449
450 if (loc_network_match_address(other, &self->last_address))
451 return 1;
452
453 return 0;
454 }
455
456 LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
457 // The prefix must be smaller (this avoids the more complex comparisons later)
458 if (self->prefix > other->prefix)
459 return 0;
460
461 // If the start address of the other network is smaller than this network,
462 // it cannot be a subnet.
463 if (in6_addr_cmp(&self->first_address, &other->first_address) > 0)
464 return 0;
465
466 // If the end address of the other network is greater than this network,
467 // it cannot be a subnet.
468 if (in6_addr_cmp(&self->last_address, &other->last_address) < 0)
469 return 0;
470
471 return 1;
472 }
473
474 LOC_EXPORT int loc_network_subnets(struct loc_network* network,
475 struct loc_network** subnet1, struct loc_network** subnet2) {
476 int r;
477 *subnet1 = NULL;
478 *subnet2 = NULL;
479
480 // New prefix length
481 unsigned int prefix = network->prefix + 1;
482
483 // Check if the new prefix is valid
484 if (valid_prefix(&network->first_address, prefix))
485 return -1;
486
487 // Create the first half of the network
488 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
489 if (r)
490 return r;
491
492 // The next subnet starts after the first one
493 struct in6_addr first_address = address_increment(&(*subnet1)->last_address);
494
495 // Create the second half of the network
496 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
497 if (r)
498 return r;
499
500 // Copy country code
501 const char* country_code = loc_network_get_country_code(network);
502 if (country_code) {
503 loc_network_set_country_code(*subnet1, country_code);
504 loc_network_set_country_code(*subnet2, country_code);
505 }
506
507 // Copy ASN
508 uint32_t asn = loc_network_get_asn(network);
509 if (asn) {
510 loc_network_set_asn(*subnet1, asn);
511 loc_network_set_asn(*subnet2, asn);
512 }
513
514 // Copy flags
515 loc_network_set_flag(*subnet1, network->flags);
516 loc_network_set_flag(*subnet2, network->flags);
517
518 return 0;
519 }
520
521 static int __loc_network_exclude(struct loc_network* network,
522 struct loc_network* other, struct loc_network_list* list) {
523 struct loc_network* subnet1 = NULL;
524 struct loc_network* subnet2 = NULL;
525
526 int r = loc_network_subnets(network, &subnet1, &subnet2);
527 if (r)
528 goto ERROR;
529
530 if (loc_network_cmp(other, subnet1) == 0) {
531 r = loc_network_list_push(list, subnet2);
532 if (r)
533 goto ERROR;
534
535 } else if (loc_network_cmp(other, subnet2) == 0) {
536 r = loc_network_list_push(list, subnet1);
537 if (r)
538 goto ERROR;
539
540 } else if (loc_network_is_subnet(subnet1, other)) {
541 r = loc_network_list_push(list, subnet2);
542 if (r)
543 goto ERROR;
544
545 r = __loc_network_exclude(subnet1, other, list);
546 if (r)
547 goto ERROR;
548
549 } else if (loc_network_is_subnet(subnet2, other)) {
550 r = loc_network_list_push(list, subnet1);
551 if (r)
552 goto ERROR;
553
554 r = __loc_network_exclude(subnet2, other, list);
555 if (r)
556 goto ERROR;
557
558 } else {
559 ERROR(network->ctx, "We should never get here\n");
560 r = 1;
561 goto ERROR;
562 }
563
564 ERROR:
565 if (subnet1)
566 loc_network_unref(subnet1);
567
568 if (subnet2)
569 loc_network_unref(subnet2);
570
571 return r;
572 }
573
574 static int __loc_network_exclude_to_list(struct loc_network* self,
575 struct loc_network* other, struct loc_network_list* list) {
576 // Other must be a subnet of self
577 if (!loc_network_is_subnet(self, other)) {
578 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
579
580 // Exit silently
581 return 0;
582 }
583
584 // We cannot perform this operation if both networks equal
585 if (loc_network_cmp(self, other) == 0) {
586 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
587
588 // Exit silently
589 return 0;
590 }
591
592 return __loc_network_exclude(self, other, list);
593 }
594
595 LOC_EXPORT struct loc_network_list* loc_network_exclude(
596 struct loc_network* self, struct loc_network* other) {
597 struct loc_network_list* list;
598
599 #ifdef ENABLE_DEBUG
600 char* n1 = loc_network_str(self);
601 char* n2 = loc_network_str(other);
602
603 DEBUG(self->ctx, "Returning %s excluding %s...\n", n1, n2);
604
605 free(n1);
606 free(n2);
607 #endif
608
609 // Create a new list with the result
610 int r = loc_network_list_new(self->ctx, &list);
611 if (r) {
612 ERROR(self->ctx, "Could not create network list: %d\n", r);
613
614 return NULL;
615 }
616
617 r = __loc_network_exclude_to_list(self, other, list);
618 if (r) {
619 loc_network_list_unref(list);
620
621 return NULL;
622 }
623
624 // Return the result
625 return list;
626 }
627
628 LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
629 struct loc_network* network, struct loc_network_list* list) {
630 struct loc_network_list* to_check;
631
632 // Create a new list with all networks to look at
633 int r = loc_network_list_new(network->ctx, &to_check);
634 if (r)
635 return NULL;
636
637 struct loc_network* subnet = NULL;
638 struct loc_network_list* subnets = NULL;
639
640 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
641 subnet = loc_network_list_get(list, i);
642
643 // Find all excluded networks
644 if (!loc_network_list_contains(to_check, subnet)) {
645 r = __loc_network_exclude_to_list(network, subnet, to_check);
646 if (r) {
647 loc_network_list_unref(to_check);
648 loc_network_unref(subnet);
649
650 return NULL;
651 }
652 }
653
654 // Cleanup
655 loc_network_unref(subnet);
656 }
657
658 r = loc_network_list_new(network->ctx, &subnets);
659 if (r) {
660 loc_network_list_unref(to_check);
661 return NULL;
662 }
663
664 off_t smallest_subnet = 0;
665
666 while (!loc_network_list_empty(to_check)) {
667 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
668
669 // Check whether the subnet to check is part of the input list
670 if (loc_network_list_contains(list, subnet_to_check)) {
671 loc_network_unref(subnet_to_check);
672 continue;
673 }
674
675 // Marks whether this subnet passed all checks
676 int passed = 1;
677
678 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
679 subnet = loc_network_list_get(list, i);
680
681 // Drop this subnet if is a subnet of another subnet
682 if (loc_network_is_subnet(subnet, subnet_to_check)) {
683 passed = 0;
684 loc_network_unref(subnet);
685 break;
686 }
687
688 // Break it down if it overlaps
689 if (loc_network_overlaps(subnet, subnet_to_check)) {
690 passed = 0;
691
692 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
693
694 loc_network_unref(subnet);
695 break;
696 }
697
698 // If the subnet is strictly greater, we do not need to continue the search
699 r = loc_network_cmp(subnet, subnet_to_check);
700 if (r > 0) {
701 loc_network_unref(subnet);
702 break;
703
704 // If it is strictly smaller, we can continue the search from here next
705 // time because all networks that are to be checked can only be larger
706 // than this one.
707 } else if (r < 0) {
708 smallest_subnet = i;
709 }
710
711 loc_network_unref(subnet);
712 }
713
714 if (passed) {
715 r = loc_network_list_push(subnets, subnet_to_check);
716 }
717
718 loc_network_unref(subnet_to_check);
719 }
720
721 loc_network_list_unref(to_check);
722
723 return subnets;
724 }
725
726 LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
727 // Add country code
728 loc_country_code_copy(dbobj->country_code, network->country_code);
729
730 // Add ASN
731 dbobj->asn = htobe32(network->asn);
732
733 // Flags
734 dbobj->flags = htobe16(network->flags);
735
736 return 0;
737 }
738
739 LOC_EXPORT int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
740 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
741 char country_code[3] = "\0\0";
742
743 int r = loc_network_new(ctx, network, address, prefix);
744 if (r) {
745 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
746 return r;
747 }
748
749 // Import country code
750 loc_country_code_copy(country_code, dbobj->country_code);
751
752 r = loc_network_set_country_code(*network, country_code);
753 if (r) {
754 ERROR(ctx, "Could not set country code: %s\n", country_code);
755 return r;
756 }
757
758 // Import ASN
759 uint32_t asn = be32toh(dbobj->asn);
760 r = loc_network_set_asn(*network, asn);
761 if (r) {
762 ERROR(ctx, "Could not set ASN: %d\n", asn);
763 return r;
764 }
765
766 // Import flags
767 int flags = be16toh(dbobj->flags);
768 r = loc_network_set_flag(*network, flags);
769 if (r) {
770 ERROR(ctx, "Could not set flags: %d\n", flags);
771 return r;
772 }
773
774 return 0;
775 }
776
777 struct loc_network_tree {
778 struct loc_ctx* ctx;
779 int refcount;
780
781 struct loc_network_tree_node* root;
782 };
783
784 struct loc_network_tree_node {
785 struct loc_ctx* ctx;
786 int refcount;
787
788 struct loc_network_tree_node* zero;
789 struct loc_network_tree_node* one;
790
791 struct loc_network* network;
792 };
793
794 int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
795 struct loc_network_tree* t = calloc(1, sizeof(*t));
796 if (!t)
797 return -ENOMEM;
798
799 t->ctx = loc_ref(ctx);
800 t->refcount = 1;
801
802 // Create the root node
803 int r = loc_network_tree_node_new(ctx, &t->root);
804 if (r) {
805 loc_network_tree_unref(t);
806 return r;
807 }
808
809 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
810 *tree = t;
811 return 0;
812 }
813
814 struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
815 return loc_network_tree_node_ref(tree->root);
816 }
817
818 static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
819 struct loc_network_tree_node** n;
820
821 if (path == 0)
822 n = &node->zero;
823 else
824 n = &node->one;
825
826 // If the desired node doesn't exist, yet, we will create it
827 if (*n == NULL) {
828 int r = loc_network_tree_node_new(node->ctx, n);
829 if (r)
830 return NULL;
831 }
832
833 return *n;
834 }
835
836 static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
837 struct loc_network_tree_node* node = tree->root;
838
839 for (unsigned int i = 0; i < prefix; i++) {
840 // Check if the ith bit is one or zero
841 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
842 }
843
844 return node;
845 }
846
847 static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
848 int(*filter_callback)(struct loc_network* network, void* data),
849 int(*callback)(struct loc_network* network, void* data), void* data) {
850 int r;
851
852 // Finding a network ends the walk here
853 if (node->network) {
854 if (filter_callback) {
855 int f = filter_callback(node->network, data);
856 if (f < 0)
857 return f;
858
859 // Skip network if filter function returns value greater than zero
860 if (f > 0)
861 return 0;
862 }
863
864 r = callback(node->network, data);
865 if (r)
866 return r;
867 }
868
869 // Walk down on the left side of the tree first
870 if (node->zero) {
871 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
872 if (r)
873 return r;
874 }
875
876 // Then walk on the other side
877 if (node->one) {
878 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
879 if (r)
880 return r;
881 }
882
883 return 0;
884 }
885
886 int loc_network_tree_walk(struct loc_network_tree* tree,
887 int(*filter_callback)(struct loc_network* network, void* data),
888 int(*callback)(struct loc_network* network, void* data), void* data) {
889 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
890 }
891
892 static void loc_network_tree_free(struct loc_network_tree* tree) {
893 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
894
895 loc_network_tree_node_unref(tree->root);
896
897 loc_unref(tree->ctx);
898 free(tree);
899 }
900
901 struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
902 if (--tree->refcount > 0)
903 return tree;
904
905 loc_network_tree_free(tree);
906 return NULL;
907 }
908
909 static int __loc_network_tree_dump(struct loc_network* network, void* data) {
910 DEBUG(network->ctx, "Dumping network at %p\n", network);
911
912 char* s = loc_network_str(network);
913 if (!s)
914 return 1;
915
916 INFO(network->ctx, "%s\n", s);
917 free(s);
918
919 return 0;
920 }
921
922 int loc_network_tree_dump(struct loc_network_tree* tree) {
923 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
924
925 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
926 }
927
928 int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
929 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
930
931 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
932 &network->first_address, network->prefix);
933 if (!node) {
934 ERROR(tree->ctx, "Could not find a node\n");
935 return -ENOMEM;
936 }
937
938 // Check if node has not been set before
939 if (node->network) {
940 DEBUG(tree->ctx, "There is already a network at this path\n");
941 return -EBUSY;
942 }
943
944 // Point node to the network
945 node->network = loc_network_ref(network);
946
947 return 0;
948 }
949
950 static int __loc_network_tree_count(struct loc_network* network, void* data) {
951 size_t* counter = (size_t*)data;
952
953 // Increase the counter for each network
954 counter++;
955
956 return 0;
957 }
958
959 size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
960 size_t counter = 0;
961
962 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
963 if (r)
964 return r;
965
966 return counter;
967 }
968
969 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
970 size_t counter = 1;
971
972 if (node->zero)
973 counter += __loc_network_tree_count_nodes(node->zero);
974
975 if (node->one)
976 counter += __loc_network_tree_count_nodes(node->one);
977
978 return counter;
979 }
980
981 size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
982 return __loc_network_tree_count_nodes(tree->root);
983 }
984
985 int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
986 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
987 if (!n)
988 return -ENOMEM;
989
990 n->ctx = loc_ref(ctx);
991 n->refcount = 1;
992
993 n->zero = n->one = NULL;
994
995 DEBUG(n->ctx, "Network node allocated at %p\n", n);
996 *node = n;
997 return 0;
998 }
999
1000 struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
1001 if (node)
1002 node->refcount++;
1003
1004 return node;
1005 }
1006
1007 static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
1008 DEBUG(node->ctx, "Releasing network node at %p\n", node);
1009
1010 if (node->network)
1011 loc_network_unref(node->network);
1012
1013 if (node->zero)
1014 loc_network_tree_node_unref(node->zero);
1015
1016 if (node->one)
1017 loc_network_tree_node_unref(node->one);
1018
1019 loc_unref(node->ctx);
1020 free(node);
1021 }
1022
1023 struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
1024 if (!node)
1025 return NULL;
1026
1027 if (--node->refcount > 0)
1028 return node;
1029
1030 loc_network_tree_node_free(node);
1031 return NULL;
1032 }
1033
1034 struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
1035 if (index == 0)
1036 node = node->zero;
1037 else
1038 node = node->one;
1039
1040 if (!node)
1041 return NULL;
1042
1043 return loc_network_tree_node_ref(node);
1044 }
1045
1046 int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
1047 return (!!node->network);
1048 }
1049
1050 struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
1051 return loc_network_ref(node->network);
1052 }