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