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