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