]> git.ipfire.org Git - location/libloc.git/blame - src/network.c
Refactor parsing IP addresses
[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
315 unsigned int prefix = network->prefix + 1;
316
317 // Check if the new prefix is valid
1fd09d0b 318 if (!loc_address_valid_prefix(&network->first_address, prefix))
a5967330 319 return -1;
850e7516
MT
320
321 // Create the first half of the network
a5967330 322 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
850e7516 323 if (r)
a5967330 324 return r;
850e7516
MT
325
326 // The next subnet starts after the first one
5b72642c
MT
327 struct in6_addr first_address = (*subnet1)->last_address;
328 loc_address_increment(&first_address);
850e7516
MT
329
330 // Create the second half of the network
a5967330 331 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
850e7516 332 if (r)
a5967330 333 return r;
850e7516 334
594ca328
MT
335 // Copy country code
336 const char* country_code = loc_network_get_country_code(network);
337 if (country_code) {
a5967330
MT
338 loc_network_set_country_code(*subnet1, country_code);
339 loc_network_set_country_code(*subnet2, country_code);
594ca328
MT
340 }
341
342 // Copy ASN
343 uint32_t asn = loc_network_get_asn(network);
344 if (asn) {
a5967330
MT
345 loc_network_set_asn(*subnet1, asn);
346 loc_network_set_asn(*subnet2, asn);
594ca328
MT
347 }
348
3e8d86ab
MT
349 // Copy flags
350 loc_network_set_flag(*subnet1, network->flags);
351 loc_network_set_flag(*subnet2, network->flags);
352
a5967330
MT
353 return 0;
354}
850e7516 355
a5967330
MT
356static int __loc_network_exclude(struct loc_network* network,
357 struct loc_network* other, struct loc_network_list* list) {
358 struct loc_network* subnet1 = NULL;
359 struct loc_network* subnet2 = NULL;
360
361 int r = loc_network_subnets(network, &subnet1, &subnet2);
362 if (r)
363 goto ERROR;
364
da101d55 365 if (loc_network_cmp(other, subnet1) == 0) {
a5967330
MT
366 r = loc_network_list_push(list, subnet2);
367 if (r)
368 goto ERROR;
369
da101d55 370 } else if (loc_network_cmp(other, subnet2) == 0) {
a5967330
MT
371 r = loc_network_list_push(list, subnet1);
372 if (r)
373 goto ERROR;
374
d6a5092f 375 } else if (loc_network_is_subnet(subnet1, other)) {
a5967330
MT
376 r = loc_network_list_push(list, subnet2);
377 if (r)
378 goto ERROR;
379
380 r = __loc_network_exclude(subnet1, other, list);
381 if (r)
382 goto ERROR;
383
d6a5092f 384 } else if (loc_network_is_subnet(subnet2, other)) {
a5967330
MT
385 r = loc_network_list_push(list, subnet1);
386 if (r)
387 goto ERROR;
388
389 r = __loc_network_exclude(subnet2, other, list);
390 if (r)
391 goto ERROR;
392
393 } else {
394 ERROR(network->ctx, "We should never get here\n");
395 r = 1;
396 goto ERROR;
397 }
850e7516
MT
398
399ERROR:
400 if (subnet1)
401 loc_network_unref(subnet1);
402
403 if (subnet2)
404 loc_network_unref(subnet2);
405
a5967330 406 return r;
850e7516
MT
407}
408
c650008e
MT
409static int __loc_network_exclude_to_list(struct loc_network* self,
410 struct loc_network* other, struct loc_network_list* list) {
850e7516 411 // Other must be a subnet of self
d6a5092f 412 if (!loc_network_is_subnet(self, other)) {
850e7516
MT
413 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
414
abf55926
MT
415 // Exit silently
416 return 0;
850e7516
MT
417 }
418
419 // We cannot perform this operation if both networks equal
da101d55 420 if (loc_network_cmp(self, other) == 0) {
850e7516
MT
421 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
422
abf55926
MT
423 // Exit silently
424 return 0;
850e7516
MT
425 }
426
c650008e
MT
427 return __loc_network_exclude(self, other, list);
428}
429
430LOC_EXPORT struct loc_network_list* loc_network_exclude(
431 struct loc_network* self, struct loc_network* other) {
432 struct loc_network_list* list;
433
0a0a289a
MT
434 DEBUG(self->ctx, "Returning %s excluding %s...\n",
435 loc_network_str(self), loc_network_str(other));
c650008e 436
850e7516
MT
437 // Create a new list with the result
438 int r = loc_network_list_new(self->ctx, &list);
439 if (r) {
440 ERROR(self->ctx, "Could not create network list: %d\n", r);
c650008e 441
850e7516
MT
442 return NULL;
443 }
444
c650008e 445 r = __loc_network_exclude_to_list(self, other, list);
a5967330
MT
446 if (r) {
447 loc_network_list_unref(list);
850e7516 448
a5967330 449 return NULL;
850e7516
MT
450 }
451
850e7516
MT
452 // Return the result
453 return list;
850e7516
MT
454}
455
add5bb65
MT
456LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
457 struct loc_network* network, struct loc_network_list* list) {
458 struct loc_network_list* to_check;
459
460 // Create a new list with all networks to look at
461 int r = loc_network_list_new(network->ctx, &to_check);
462 if (r)
463 return NULL;
464
465 struct loc_network* subnet = NULL;
466 struct loc_network_list* subnets = NULL;
467
468 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
469 subnet = loc_network_list_get(list, i);
470
471 // Find all excluded networks
c650008e
MT
472 if (!loc_network_list_contains(to_check, subnet)) {
473 r = __loc_network_exclude_to_list(network, subnet, to_check);
474 if (r) {
475 loc_network_list_unref(to_check);
476 loc_network_unref(subnet);
477
478 return NULL;
479 }
add5bb65
MT
480 }
481
482 // Cleanup
483 loc_network_unref(subnet);
484 }
485
486 r = loc_network_list_new(network->ctx, &subnets);
487 if (r) {
488 loc_network_list_unref(to_check);
489 return NULL;
490 }
491
058e7800
MT
492 off_t smallest_subnet = 0;
493
add5bb65 494 while (!loc_network_list_empty(to_check)) {
058e7800 495 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
add5bb65 496
06177d8c
MT
497 // Check whether the subnet to check is part of the input list
498 if (loc_network_list_contains(list, subnet_to_check)) {
499 loc_network_unref(subnet_to_check);
500 continue;
501 }
502
add5bb65
MT
503 // Marks whether this subnet passed all checks
504 int passed = 1;
505
058e7800 506 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
add5bb65
MT
507 subnet = loc_network_list_get(list, i);
508
add5bb65 509 // Drop this subnet if is a subnet of another subnet
4de8ff8e 510 if (loc_network_is_subnet(subnet, subnet_to_check)) {
add5bb65
MT
511 passed = 0;
512 loc_network_unref(subnet);
513 break;
514 }
515
516 // Break it down if it overlaps
4de8ff8e 517 if (loc_network_overlaps(subnet, subnet_to_check)) {
add5bb65
MT
518 passed = 0;
519
4fc034e2 520 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
add5bb65
MT
521
522 loc_network_unref(subnet);
523 break;
524 }
525
058e7800
MT
526 // If the subnet is strictly greater, we do not need to continue the search
527 r = loc_network_cmp(subnet, subnet_to_check);
528 if (r > 0) {
529 loc_network_unref(subnet);
530 break;
531
532 // If it is strictly smaller, we can continue the search from here next
533 // time because all networks that are to be checked can only be larger
534 // than this one.
535 } else if (r < 0) {
536 smallest_subnet = i;
537 }
538
add5bb65
MT
539 loc_network_unref(subnet);
540 }
541
542 if (passed) {
543 r = loc_network_list_push(subnets, subnet_to_check);
544 }
545
546 loc_network_unref(subnet_to_check);
547 }
548
549 loc_network_list_unref(to_check);
550
551 return subnets;
552}
553
8298728e 554int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
f3e02bc5 555 // Add country code
a7a3d958 556 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
557
558 // Add ASN
71ff3e69 559 dbobj->asn = htobe32(network->asn);
f3e02bc5 560
a99e7c2b 561 // Flags
eee6346d 562 dbobj->flags = htobe16(network->flags);
a99e7c2b 563
f3e02bc5
MT
564 return 0;
565}
566
8298728e 567int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
b904896a 568 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
3dc8adfb 569 char country_code[3] = "\0\0";
a7a3d958 570
1fd09d0b
MT
571 // Adjust prefix for IPv4
572 if (IN6_IS_ADDR_V4MAPPED(address))
573 prefix -= 96;
574
39a55353 575 int r = loc_network_new(ctx, network, address, prefix);
94a3f329
MT
576 if (r) {
577 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
10778041 578 return r;
94a3f329 579 }
10778041
MT
580
581 // Import country code
a7a3d958 582 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
583
584 r = loc_network_set_country_code(*network, country_code);
94a3f329
MT
585 if (r) {
586 ERROR(ctx, "Could not set country code: %s\n", country_code);
10778041 587 return r;
94a3f329 588 }
10778041
MT
589
590 // Import ASN
94a3f329
MT
591 uint32_t asn = be32toh(dbobj->asn);
592 r = loc_network_set_asn(*network, asn);
593 if (r) {
594 ERROR(ctx, "Could not set ASN: %d\n", asn);
10778041 595 return r;
94a3f329 596 }
10778041 597
a99e7c2b 598 // Import flags
94a3f329
MT
599 int flags = be16toh(dbobj->flags);
600 r = loc_network_set_flag(*network, flags);
601 if (r) {
602 ERROR(ctx, "Could not set flags: %d\n", flags);
a99e7c2b 603 return r;
94a3f329 604 }
a99e7c2b 605
10778041
MT
606 return 0;
607}
608
3b5f4af2
MT
609struct loc_network_tree {
610 struct loc_ctx* ctx;
611 int refcount;
612
613 struct loc_network_tree_node* root;
614};
615
616struct loc_network_tree_node {
438db08c
MT
617 struct loc_ctx* ctx;
618 int refcount;
619
3b5f4af2
MT
620 struct loc_network_tree_node* zero;
621 struct loc_network_tree_node* one;
622
623 struct loc_network* network;
624};
625
7933f5bf 626int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
3b5f4af2
MT
627 struct loc_network_tree* t = calloc(1, sizeof(*t));
628 if (!t)
629 return -ENOMEM;
630
631 t->ctx = loc_ref(ctx);
632 t->refcount = 1;
633
634 // Create the root node
438db08c
MT
635 int r = loc_network_tree_node_new(ctx, &t->root);
636 if (r) {
637 loc_network_tree_unref(t);
638 return r;
639 }
3b5f4af2
MT
640
641 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
642 *tree = t;
643 return 0;
644}
645
7933f5bf 646struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
438db08c 647 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
648}
649
650static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
651 struct loc_network_tree_node** n;
652
2a30e4de 653 if (path == 0)
3b5f4af2 654 n = &node->zero;
3eda9cab
MT
655 else
656 n = &node->one;
3b5f4af2
MT
657
658 // If the desired node doesn't exist, yet, we will create it
659 if (*n == NULL) {
438db08c 660 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
661 if (r)
662 return NULL;
663 }
664
665 return *n;
666}
667
39a55353 668static 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
669 struct loc_network_tree_node* node = tree->root;
670
39a55353 671 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 672 // Check if the ith bit is one or zero
d698ca09 673 node = loc_network_tree_get_node(node, loc_address_get_bit(address, i));
3b5f4af2
MT
674 }
675
676 return node;
677}
678
679static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
680 int(*filter_callback)(struct loc_network* network, void* data),
681 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
682 int r;
683
684 // Finding a network ends the walk here
685 if (node->network) {
686 if (filter_callback) {
f3e02bc5 687 int f = filter_callback(node->network, data);
3b5f4af2
MT
688 if (f < 0)
689 return f;
690
691 // Skip network if filter function returns value greater than zero
692 if (f > 0)
693 return 0;
694 }
695
f3e02bc5 696 r = callback(node->network, data);
3b5f4af2
MT
697 if (r)
698 return r;
699 }
700
701 // Walk down on the left side of the tree first
702 if (node->zero) {
f3e02bc5 703 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
704 if (r)
705 return r;
706 }
707
708 // Then walk on the other side
709 if (node->one) {
f3e02bc5 710 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
711 if (r)
712 return r;
713 }
714
715 return 0;
716}
717
7933f5bf 718int loc_network_tree_walk(struct loc_network_tree* tree,
f3e02bc5
MT
719 int(*filter_callback)(struct loc_network* network, void* data),
720 int(*callback)(struct loc_network* network, void* data), void* data) {
721 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
722}
723
3b5f4af2
MT
724static void loc_network_tree_free(struct loc_network_tree* tree) {
725 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
726
438db08c 727 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
728
729 loc_unref(tree->ctx);
730 free(tree);
731}
732
7933f5bf 733struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
3b5f4af2
MT
734 if (--tree->refcount > 0)
735 return tree;
736
737 loc_network_tree_free(tree);
738 return NULL;
739}
740
e9b4e2a8 741static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
742 DEBUG(network->ctx, "Dumping network at %p\n", network);
743
0a0a289a 744 const char* s = loc_network_str(network);
3b5f4af2
MT
745 if (!s)
746 return 1;
747
748 INFO(network->ctx, "%s\n", s);
3b5f4af2
MT
749
750 return 0;
751}
752
7933f5bf 753int loc_network_tree_dump(struct loc_network_tree* tree) {
3b5f4af2
MT
754 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
755
f3e02bc5 756 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
757}
758
7933f5bf 759int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
3b5f4af2
MT
760 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
761
39a55353 762 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
ce4f5752 763 &network->first_address, network->prefix);
3b5f4af2
MT
764 if (!node) {
765 ERROR(tree->ctx, "Could not find a node\n");
766 return -ENOMEM;
767 }
768
769 // Check if node has not been set before
770 if (node->network) {
771 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 772 return -EBUSY;
3b5f4af2
MT
773 }
774
775 // Point node to the network
776 node->network = loc_network_ref(network);
777
778 return 0;
779}
f3e02bc5
MT
780
781static int __loc_network_tree_count(struct loc_network* network, void* data) {
782 size_t* counter = (size_t*)data;
783
784 // Increase the counter for each network
785 counter++;
786
787 return 0;
788}
789
7933f5bf 790size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
f3e02bc5
MT
791 size_t counter = 0;
792
793 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
794 if (r)
795 return r;
796
797 return counter;
798}
940f9c2b
MT
799
800static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
801 size_t counter = 1;
802
803 if (node->zero)
804 counter += __loc_network_tree_count_nodes(node->zero);
805
806 if (node->one)
807 counter += __loc_network_tree_count_nodes(node->one);
808
809 return counter;
810}
811
7933f5bf 812size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
940f9c2b
MT
813 return __loc_network_tree_count_nodes(tree->root);
814}
438db08c 815
7933f5bf 816int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
438db08c
MT
817 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
818 if (!n)
819 return -ENOMEM;
820
821 n->ctx = loc_ref(ctx);
822 n->refcount = 1;
823
824 n->zero = n->one = NULL;
825
826 DEBUG(n->ctx, "Network node allocated at %p\n", n);
827 *node = n;
828 return 0;
829}
830
7933f5bf 831struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
438db08c
MT
832 if (node)
833 node->refcount++;
834
835 return node;
836}
837
838static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
839 DEBUG(node->ctx, "Releasing network node at %p\n", node);
840
841 if (node->network)
842 loc_network_unref(node->network);
843
844 if (node->zero)
845 loc_network_tree_node_unref(node->zero);
846
847 if (node->one)
848 loc_network_tree_node_unref(node->one);
849
850 loc_unref(node->ctx);
851 free(node);
852}
853
7933f5bf 854struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
438db08c
MT
855 if (!node)
856 return NULL;
857
858 if (--node->refcount > 0)
859 return node;
860
861 loc_network_tree_node_free(node);
862 return NULL;
863}
864
7933f5bf 865struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
438db08c
MT
866 if (index == 0)
867 node = node->zero;
868 else
869 node = node->one;
870
871 if (!node)
872 return NULL;
873
874 return loc_network_tree_node_ref(node);
875}
876
7933f5bf 877int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
438db08c
MT
878 return (!!node->network);
879}
880
7933f5bf 881struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
438db08c
MT
882 return loc_network_ref(node->network);
883}