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