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