]> git.ipfire.org Git - location/libloc.git/blame - src/network.c
network: loc_network_subnets: Use correct prefix
[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 28#include <libloc/libloc.h>
0b1fef38 29#include <libloc/address.h>
c12a1385
MT
30#include <libloc/compat.h>
31#include <libloc/country.h>
32#include <libloc/network.h>
33#include <libloc/network-list.h>
34#include <libloc/private.h>
3b5f4af2
MT
35
36struct loc_network {
37 struct loc_ctx* ctx;
38 int refcount;
39
c3068be1 40 int family;
ce4f5752 41 struct in6_addr first_address;
b43edb61 42 struct in6_addr last_address;
3b5f4af2
MT
43 unsigned int prefix;
44
45 char country_code[3];
71ff3e69 46 uint32_t asn;
a293f829 47 enum loc_network_flags flags;
0a0a289a
MT
48
49 char string[INET6_ADDRSTRLEN + 4];
3b5f4af2
MT
50};
51
52LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 53 struct in6_addr* address, unsigned int prefix) {
6183c0f2 54 // Validate the prefix
1fd09d0b 55 if (!loc_address_valid_prefix(address, prefix)) {
95b6a8e4 56 ERROR(ctx, "Invalid prefix in %s: %u\n", loc_address_str(address), prefix);
5559e086
MT
57 errno = EINVAL;
58 return 1;
6183c0f2
MT
59 }
60
3b5f4af2 61 struct loc_network* n = calloc(1, sizeof(*n));
5559e086
MT
62 if (!n) {
63 errno = ENOMEM;
64 return 1;
65 }
3b5f4af2
MT
66
67 n->ctx = loc_ref(ctx);
68 n->refcount = 1;
69
b43edb61 70 // Store the prefix
1fd09d0b
MT
71 if (IN6_IS_ADDR_V4MAPPED(address))
72 n->prefix = prefix + 96;
73 else
74 n->prefix = prefix;
3b5f4af2 75
b43edb61 76 // Convert the prefix into a bitmask
30f0f0ed 77 struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
b43edb61
MT
78
79 // Store the first and last address in the network
30f0f0ed
MT
80 n->first_address = loc_address_and(address, &bitmask);
81 n->last_address = loc_address_or(&n->first_address, &bitmask);
b43edb61 82
c3068be1 83 // Set family
71877f5f 84 n->family = loc_address_family(&n->first_address);
c3068be1 85
3b5f4af2
MT
86 DEBUG(n->ctx, "Network allocated at %p\n", n);
87 *network = n;
88 return 0;
89}
90
95b6a8e4
MT
91LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx,
92 struct loc_network** network, const char* string) {
93 struct in6_addr address;
94 unsigned int prefix;
13ad6e69 95
95b6a8e4
MT
96 // Parse the input
97 int r = loc_address_parse(&address, &prefix, string);
13ad6e69 98 if (r) {
95b6a8e4 99 ERROR(ctx, "Could not parse network %s: %m\n", string);
13ad6e69 100 return r;
95b6a8e4 101 }
13ad6e69 102
13ad6e69 103 // Create a new network
95b6a8e4 104 return loc_network_new(ctx, network, &address, prefix);
3b5f4af2
MT
105}
106
107LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
108 network->refcount++;
109
110 return network;
111}
112
113static void loc_network_free(struct loc_network* network) {
114 DEBUG(network->ctx, "Releasing network at %p\n", network);
115
3b5f4af2
MT
116 loc_unref(network->ctx);
117 free(network);
118}
119
120LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
2a30e4de
MT
121 if (!network)
122 return NULL;
123
3b5f4af2
MT
124 if (--network->refcount > 0)
125 return network;
126
127 loc_network_free(network);
128 return NULL;
129}
130
0a0a289a
MT
131LOC_EXPORT const char* loc_network_str(struct loc_network* network) {
132 if (!*network->string) {
133 // Format the address
134 const char* address = loc_address_str(&network->first_address);
135 if (!address)
136 return NULL;
d3409788 137
0a0a289a
MT
138 // Fetch the prefix
139 unsigned int prefix = loc_network_prefix(network);
d3409788 140
0a0a289a
MT
141 // Format the string
142 int r = snprintf(network->string, sizeof(network->string) - 1,
143 "%s/%u", address, prefix);
144 if (r < 0) {
145 ERROR(network->ctx, "Could not format network string: %m\n");
146 *network->string = '\0';
147 return NULL;
148 }
3b5f4af2
MT
149 }
150
0a0a289a 151 return network->string;
3b5f4af2
MT
152}
153
44e5ef71 154LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
c3068be1 155 return network->family;
44e5ef71
MT
156}
157
7fe6a218
MT
158LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
159 switch (network->family) {
160 case AF_INET6:
161 return network->prefix;
162
163 case AF_INET:
164 return network->prefix - 96;
165 }
166
167 return 0;
168}
169
a1a00053
MT
170LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
171 return &network->first_address;
172}
173
0a0a289a
MT
174LOC_EXPORT const char* loc_network_format_first_address(struct loc_network* network) {
175 return loc_address_str(&network->first_address);
2b9338ea
MT
176}
177
a1a00053
MT
178LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
179 return &network->last_address;
180}
181
0a0a289a
MT
182LOC_EXPORT const char* loc_network_format_last_address(struct loc_network* network) {
183 return loc_address_str(&network->last_address);
2b9338ea
MT
184}
185
0258d3c9 186LOC_EXPORT int loc_network_matches_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 187 // Address must be larger than the start address
9c1e2943 188 if (loc_address_cmp(&network->first_address, address) > 0)
fc692a58 189 return 0;
2a30e4de 190
2a30e4de 191 // Address must be smaller than the last address
9c1e2943 192 if (loc_address_cmp(&network->last_address, address) < 0)
fc692a58 193 return 0;
2a30e4de
MT
194
195 // The address is inside this network
fc692a58 196 return 1;
2a30e4de
MT
197}
198
3b5f4af2
MT
199LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
200 return network->country_code;
201}
202
203LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
204 // Set empty country code
205 if (!country_code || !*country_code) {
206 *network->country_code = '\0';
207 return 0;
208 }
209
57146963
MT
210 // Check country code
211 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
212 return -EINVAL;
213
a7a3d958 214 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
215
216 return 0;
217}
218
0258d3c9 219LOC_EXPORT int loc_network_matches_country_code(struct loc_network* network, const char* country_code) {
0b27a567
MT
220 // Search for any special flags
221 const int flag = loc_country_special_code_to_flag(country_code);
222
223 // If we found a flag, we will return whether it is set or not
224 if (flag)
225 return loc_network_has_flag(network, flag);
226
57146963
MT
227 // Check country code
228 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
229 return -EINVAL;
230
0b27a567 231 // Check for an exact match
e3f696c1
MT
232 return (network->country_code[0] == country_code[0])
233 && (network->country_code[1] == country_code[1]);
234}
235
71ff3e69
MT
236LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
237 return network->asn;
238}
239
240LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
241 network->asn = asn;
242
243 return 0;
244}
245
a99e7c2b
MT
246LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
247 return network->flags & flag;
248}
249
250LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
251 network->flags |= flag;
252
253 return 0;
254}
255
af4689bf 256LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
af4689bf 257 // Compare address
9c1e2943 258 int r = loc_address_cmp(&self->first_address, &other->first_address);
af4689bf
MT
259 if (r)
260 return r;
261
262 // Compare prefix
263 if (self->prefix > other->prefix)
264 return 1;
265 else if (self->prefix < other->prefix)
266 return -1;
267
268 // Both networks are equal
269 return 0;
270}
271
6159d384 272LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
fc692a58 273 // Either of the start addresses must be in the other subnet
0258d3c9 274 if (loc_network_matches_address(self, &other->first_address))
6159d384
MT
275 return 1;
276
0258d3c9 277 if (loc_network_matches_address(other, &self->first_address))
6159d384
MT
278 return 1;
279
fc692a58 280 // Or either of the end addresses is in the other subnet
0258d3c9 281 if (loc_network_matches_address(self, &other->last_address))
6159d384
MT
282 return 1;
283
0258d3c9 284 if (loc_network_matches_address(other, &self->last_address))
6159d384
MT
285 return 1;
286
287 return 0;
288}
289
33a051e0 290LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
cf8d3c64
MT
291 // The prefix must be smaller (this avoids the more complex comparisons later)
292 if (self->prefix > other->prefix)
293 return 0;
294
33a051e0
MT
295 // If the start address of the other network is smaller than this network,
296 // it cannot be a subnet.
9c1e2943 297 if (loc_address_cmp(&self->first_address, &other->first_address) > 0)
33a051e0
MT
298 return 0;
299
300 // If the end address of the other network is greater than this network,
301 // it cannot be a subnet.
9c1e2943 302 if (loc_address_cmp(&self->last_address, &other->last_address) < 0)
33a051e0
MT
303 return 0;
304
305 return 1;
306}
307
a5967330
MT
308LOC_EXPORT int loc_network_subnets(struct loc_network* network,
309 struct loc_network** subnet1, struct loc_network** subnet2) {
310 int r;
311 *subnet1 = NULL;
312 *subnet2 = NULL;
850e7516
MT
313
314 // New prefix length
47b55e70 315 unsigned int prefix = loc_network_prefix(network) + 1;
850e7516
MT
316
317 // Check if the new prefix is valid
92ba094c
MT
318 if (!loc_address_valid_prefix(&network->first_address, prefix)) {
319 ERROR(network->ctx, "Invalid prefix: %d\n", prefix);
320 errno = EINVAL;
321 return 1;
322 }
850e7516
MT
323
324 // Create the first half of the network
a5967330 325 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
850e7516 326 if (r)
a5967330 327 return r;
850e7516
MT
328
329 // The next subnet starts after the first one
5b72642c
MT
330 struct in6_addr first_address = (*subnet1)->last_address;
331 loc_address_increment(&first_address);
850e7516
MT
332
333 // Create the second half of the network
a5967330 334 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
850e7516 335 if (r)
a5967330 336 return r;
850e7516 337
594ca328
MT
338 // Copy country code
339 const char* country_code = loc_network_get_country_code(network);
340 if (country_code) {
a5967330
MT
341 loc_network_set_country_code(*subnet1, country_code);
342 loc_network_set_country_code(*subnet2, country_code);
594ca328
MT
343 }
344
345 // Copy ASN
346 uint32_t asn = loc_network_get_asn(network);
347 if (asn) {
a5967330
MT
348 loc_network_set_asn(*subnet1, asn);
349 loc_network_set_asn(*subnet2, asn);
594ca328
MT
350 }
351
3e8d86ab
MT
352 // Copy flags
353 loc_network_set_flag(*subnet1, network->flags);
354 loc_network_set_flag(*subnet2, network->flags);
355
a5967330
MT
356 return 0;
357}
850e7516 358
a5967330
MT
359static int __loc_network_exclude(struct loc_network* network,
360 struct loc_network* other, struct loc_network_list* list) {
361 struct loc_network* subnet1 = NULL;
362 struct loc_network* subnet2 = NULL;
363
364 int r = loc_network_subnets(network, &subnet1, &subnet2);
365 if (r)
366 goto ERROR;
367
da101d55 368 if (loc_network_cmp(other, subnet1) == 0) {
a5967330
MT
369 r = loc_network_list_push(list, subnet2);
370 if (r)
371 goto ERROR;
372
da101d55 373 } else if (loc_network_cmp(other, subnet2) == 0) {
a5967330
MT
374 r = loc_network_list_push(list, subnet1);
375 if (r)
376 goto ERROR;
377
d6a5092f 378 } else if (loc_network_is_subnet(subnet1, other)) {
a5967330
MT
379 r = loc_network_list_push(list, subnet2);
380 if (r)
381 goto ERROR;
382
383 r = __loc_network_exclude(subnet1, other, list);
384 if (r)
385 goto ERROR;
386
d6a5092f 387 } else if (loc_network_is_subnet(subnet2, other)) {
a5967330
MT
388 r = loc_network_list_push(list, subnet1);
389 if (r)
390 goto ERROR;
391
392 r = __loc_network_exclude(subnet2, other, list);
393 if (r)
394 goto ERROR;
395
396 } else {
397 ERROR(network->ctx, "We should never get here\n");
398 r = 1;
399 goto ERROR;
400 }
850e7516
MT
401
402ERROR:
403 if (subnet1)
404 loc_network_unref(subnet1);
405
406 if (subnet2)
407 loc_network_unref(subnet2);
408
725718e1
MT
409 if (r)
410 DEBUG(network->ctx, "%s has failed with %d\n", __FUNCTION__, r);
411
a5967330 412 return r;
850e7516
MT
413}
414
c650008e
MT
415static int __loc_network_exclude_to_list(struct loc_network* self,
416 struct loc_network* other, struct loc_network_list* list) {
850e7516 417 // Other must be a subnet of self
d6a5092f 418 if (!loc_network_is_subnet(self, other)) {
850e7516
MT
419 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
420
abf55926
MT
421 // Exit silently
422 return 0;
850e7516
MT
423 }
424
425 // We cannot perform this operation if both networks equal
da101d55 426 if (loc_network_cmp(self, other) == 0) {
850e7516
MT
427 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
428
abf55926
MT
429 // Exit silently
430 return 0;
850e7516
MT
431 }
432
c650008e
MT
433 return __loc_network_exclude(self, other, list);
434}
435
436LOC_EXPORT struct loc_network_list* loc_network_exclude(
437 struct loc_network* self, struct loc_network* other) {
438 struct loc_network_list* list;
439
0a0a289a
MT
440 DEBUG(self->ctx, "Returning %s excluding %s...\n",
441 loc_network_str(self), loc_network_str(other));
c650008e 442
850e7516
MT
443 // Create a new list with the result
444 int r = loc_network_list_new(self->ctx, &list);
445 if (r) {
446 ERROR(self->ctx, "Could not create network list: %d\n", r);
c650008e 447
850e7516
MT
448 return NULL;
449 }
450
c650008e 451 r = __loc_network_exclude_to_list(self, other, list);
a5967330
MT
452 if (r) {
453 loc_network_list_unref(list);
850e7516 454
a5967330 455 return NULL;
850e7516
MT
456 }
457
850e7516
MT
458 // Return the result
459 return list;
850e7516
MT
460}
461
add5bb65
MT
462LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
463 struct loc_network* network, struct loc_network_list* list) {
464 struct loc_network_list* to_check;
465
466 // Create a new list with all networks to look at
467 int r = loc_network_list_new(network->ctx, &to_check);
468 if (r)
469 return NULL;
470
471 struct loc_network* subnet = NULL;
472 struct loc_network_list* subnets = NULL;
473
474 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
475 subnet = loc_network_list_get(list, i);
476
477 // Find all excluded networks
c650008e
MT
478 if (!loc_network_list_contains(to_check, subnet)) {
479 r = __loc_network_exclude_to_list(network, subnet, to_check);
480 if (r) {
481 loc_network_list_unref(to_check);
482 loc_network_unref(subnet);
483
484 return NULL;
485 }
add5bb65
MT
486 }
487
488 // Cleanup
489 loc_network_unref(subnet);
490 }
491
492 r = loc_network_list_new(network->ctx, &subnets);
493 if (r) {
494 loc_network_list_unref(to_check);
495 return NULL;
496 }
497
058e7800
MT
498 off_t smallest_subnet = 0;
499
add5bb65 500 while (!loc_network_list_empty(to_check)) {
058e7800 501 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
add5bb65 502
06177d8c
MT
503 // Check whether the subnet to check is part of the input list
504 if (loc_network_list_contains(list, subnet_to_check)) {
505 loc_network_unref(subnet_to_check);
506 continue;
507 }
508
add5bb65
MT
509 // Marks whether this subnet passed all checks
510 int passed = 1;
511
058e7800 512 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
add5bb65
MT
513 subnet = loc_network_list_get(list, i);
514
add5bb65 515 // Drop this subnet if is a subnet of another subnet
4de8ff8e 516 if (loc_network_is_subnet(subnet, subnet_to_check)) {
add5bb65
MT
517 passed = 0;
518 loc_network_unref(subnet);
519 break;
520 }
521
522 // Break it down if it overlaps
4de8ff8e 523 if (loc_network_overlaps(subnet, subnet_to_check)) {
add5bb65
MT
524 passed = 0;
525
4fc034e2 526 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
add5bb65
MT
527
528 loc_network_unref(subnet);
529 break;
530 }
531
058e7800
MT
532 // If the subnet is strictly greater, we do not need to continue the search
533 r = loc_network_cmp(subnet, subnet_to_check);
534 if (r > 0) {
535 loc_network_unref(subnet);
536 break;
537
538 // If it is strictly smaller, we can continue the search from here next
539 // time because all networks that are to be checked can only be larger
540 // than this one.
541 } else if (r < 0) {
542 smallest_subnet = i;
543 }
544
add5bb65
MT
545 loc_network_unref(subnet);
546 }
547
548 if (passed) {
549 r = loc_network_list_push(subnets, subnet_to_check);
550 }
551
552 loc_network_unref(subnet_to_check);
553 }
554
555 loc_network_list_unref(to_check);
556
557 return subnets;
558}
559
8298728e 560int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
f3e02bc5 561 // Add country code
a7a3d958 562 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
563
564 // Add ASN
71ff3e69 565 dbobj->asn = htobe32(network->asn);
f3e02bc5 566
a99e7c2b 567 // Flags
eee6346d 568 dbobj->flags = htobe16(network->flags);
a99e7c2b 569
f3e02bc5
MT
570 return 0;
571}
572
8298728e 573int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
b904896a 574 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
3dc8adfb 575 char country_code[3] = "\0\0";
a7a3d958 576
1fd09d0b
MT
577 // Adjust prefix for IPv4
578 if (IN6_IS_ADDR_V4MAPPED(address))
579 prefix -= 96;
580
39a55353 581 int r = loc_network_new(ctx, network, address, prefix);
94a3f329
MT
582 if (r) {
583 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
10778041 584 return r;
94a3f329 585 }
10778041
MT
586
587 // Import country code
a7a3d958 588 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
589
590 r = loc_network_set_country_code(*network, country_code);
94a3f329
MT
591 if (r) {
592 ERROR(ctx, "Could not set country code: %s\n", country_code);
10778041 593 return r;
94a3f329 594 }
10778041
MT
595
596 // Import ASN
94a3f329
MT
597 uint32_t asn = be32toh(dbobj->asn);
598 r = loc_network_set_asn(*network, asn);
599 if (r) {
600 ERROR(ctx, "Could not set ASN: %d\n", asn);
10778041 601 return r;
94a3f329 602 }
10778041 603
a99e7c2b 604 // Import flags
94a3f329
MT
605 int flags = be16toh(dbobj->flags);
606 r = loc_network_set_flag(*network, flags);
607 if (r) {
608 ERROR(ctx, "Could not set flags: %d\n", flags);
a99e7c2b 609 return r;
94a3f329 610 }
a99e7c2b 611
10778041
MT
612 return 0;
613}
614
3b5f4af2
MT
615struct loc_network_tree {
616 struct loc_ctx* ctx;
617 int refcount;
618
619 struct loc_network_tree_node* root;
620};
621
622struct loc_network_tree_node {
438db08c
MT
623 struct loc_ctx* ctx;
624 int refcount;
625
3b5f4af2
MT
626 struct loc_network_tree_node* zero;
627 struct loc_network_tree_node* one;
628
629 struct loc_network* network;
630};
631
7933f5bf 632int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
3b5f4af2
MT
633 struct loc_network_tree* t = calloc(1, sizeof(*t));
634 if (!t)
635 return -ENOMEM;
636
637 t->ctx = loc_ref(ctx);
638 t->refcount = 1;
639
640 // Create the root node
438db08c
MT
641 int r = loc_network_tree_node_new(ctx, &t->root);
642 if (r) {
643 loc_network_tree_unref(t);
644 return r;
645 }
3b5f4af2
MT
646
647 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
648 *tree = t;
649 return 0;
650}
651
7933f5bf 652struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
438db08c 653 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
654}
655
656static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
657 struct loc_network_tree_node** n;
658
2a30e4de 659 if (path == 0)
3b5f4af2 660 n = &node->zero;
3eda9cab
MT
661 else
662 n = &node->one;
3b5f4af2
MT
663
664 // If the desired node doesn't exist, yet, we will create it
665 if (*n == NULL) {
438db08c 666 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
667 if (r)
668 return NULL;
669 }
670
671 return *n;
672}
673
39a55353 674static 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
675 struct loc_network_tree_node* node = tree->root;
676
39a55353 677 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 678 // Check if the ith bit is one or zero
d698ca09 679 node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
3b5f4af2
MT
680 }
681
682 return node;
683}
684
685static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
686 int(*filter_callback)(struct loc_network* network, void* data),
687 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
688 int r;
689
690 // Finding a network ends the walk here
691 if (node->network) {
692 if (filter_callback) {
f3e02bc5 693 int f = filter_callback(node->network, data);
3b5f4af2
MT
694 if (f < 0)
695 return f;
696
697 // Skip network if filter function returns value greater than zero
698 if (f > 0)
699 return 0;
700 }
701
f3e02bc5 702 r = callback(node->network, data);
3b5f4af2
MT
703 if (r)
704 return r;
705 }
706
707 // Walk down on the left side of the tree first
708 if (node->zero) {
f3e02bc5 709 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
710 if (r)
711 return r;
712 }
713
714 // Then walk on the other side
715 if (node->one) {
f3e02bc5 716 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
717 if (r)
718 return r;
719 }
720
721 return 0;
722}
723
7933f5bf 724int loc_network_tree_walk(struct loc_network_tree* tree,
f3e02bc5
MT
725 int(*filter_callback)(struct loc_network* network, void* data),
726 int(*callback)(struct loc_network* network, void* data), void* data) {
727 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
728}
729
3b5f4af2
MT
730static void loc_network_tree_free(struct loc_network_tree* tree) {
731 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
732
438db08c 733 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
734
735 loc_unref(tree->ctx);
736 free(tree);
737}
738
7933f5bf 739struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
3b5f4af2
MT
740 if (--tree->refcount > 0)
741 return tree;
742
743 loc_network_tree_free(tree);
744 return NULL;
745}
746
e9b4e2a8 747static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
748 DEBUG(network->ctx, "Dumping network at %p\n", network);
749
0a0a289a 750 const char* s = loc_network_str(network);
3b5f4af2
MT
751 if (!s)
752 return 1;
753
754 INFO(network->ctx, "%s\n", s);
3b5f4af2
MT
755
756 return 0;
757}
758
7933f5bf 759int loc_network_tree_dump(struct loc_network_tree* tree) {
3b5f4af2
MT
760 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
761
f3e02bc5 762 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
763}
764
7933f5bf 765int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
3b5f4af2
MT
766 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
767
39a55353 768 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
ce4f5752 769 &network->first_address, network->prefix);
3b5f4af2
MT
770 if (!node) {
771 ERROR(tree->ctx, "Could not find a node\n");
772 return -ENOMEM;
773 }
774
775 // Check if node has not been set before
776 if (node->network) {
777 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 778 return -EBUSY;
3b5f4af2
MT
779 }
780
781 // Point node to the network
782 node->network = loc_network_ref(network);
783
784 return 0;
785}
f3e02bc5
MT
786
787static int __loc_network_tree_count(struct loc_network* network, void* data) {
788 size_t* counter = (size_t*)data;
789
790 // Increase the counter for each network
791 counter++;
792
793 return 0;
794}
795
7933f5bf 796size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
f3e02bc5
MT
797 size_t counter = 0;
798
799 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
800 if (r)
801 return r;
802
803 return counter;
804}
940f9c2b
MT
805
806static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
807 size_t counter = 1;
808
809 if (node->zero)
810 counter += __loc_network_tree_count_nodes(node->zero);
811
812 if (node->one)
813 counter += __loc_network_tree_count_nodes(node->one);
814
815 return counter;
816}
817
7933f5bf 818size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
940f9c2b
MT
819 return __loc_network_tree_count_nodes(tree->root);
820}
438db08c 821
7933f5bf 822int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
438db08c
MT
823 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
824 if (!n)
825 return -ENOMEM;
826
827 n->ctx = loc_ref(ctx);
828 n->refcount = 1;
829
830 n->zero = n->one = NULL;
831
832 DEBUG(n->ctx, "Network node allocated at %p\n", n);
833 *node = n;
834 return 0;
835}
836
7933f5bf 837struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
438db08c
MT
838 if (node)
839 node->refcount++;
840
841 return node;
842}
843
844static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
845 DEBUG(node->ctx, "Releasing network node at %p\n", node);
846
847 if (node->network)
848 loc_network_unref(node->network);
849
850 if (node->zero)
851 loc_network_tree_node_unref(node->zero);
852
853 if (node->one)
854 loc_network_tree_node_unref(node->one);
855
856 loc_unref(node->ctx);
857 free(node);
858}
859
7933f5bf 860struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
438db08c
MT
861 if (!node)
862 return NULL;
863
864 if (--node->refcount > 0)
865 return node;
866
867 loc_network_tree_node_free(node);
868 return NULL;
869}
870
7933f5bf 871struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
438db08c
MT
872 if (index == 0)
873 node = node->zero;
874 else
875 node = node->one;
876
877 if (!node)
878 return NULL;
879
880 return loc_network_tree_node_ref(node);
881}
882
7933f5bf 883int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
438db08c
MT
884 return (!!node->network);
885}
886
7933f5bf 887struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
438db08c
MT
888 return loc_network_ref(node->network);
889}