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