]> git.ipfire.org Git - location/libloc.git/blame - src/network.c
Merge branch 'apple'
[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>
9fc7f001 32#include <loc/private.h>
3b5f4af2
MT
33
34struct loc_network {
35 struct loc_ctx* ctx;
36 int refcount;
37
38 struct in6_addr start_address;
39 unsigned int prefix;
40
41 char country_code[3];
71ff3e69 42 uint32_t asn;
a293f829 43 enum loc_network_flags flags;
3b5f4af2
MT
44};
45
6183c0f2
MT
46static int valid_prefix(struct in6_addr* address, unsigned int prefix) {
47 // The prefix cannot be larger than 128 bits
48 if (prefix > 128)
49 return 1;
50
51 // And the prefix cannot be zero
52 if (prefix == 0)
53 return 1;
54
aa37232a
MT
55 // For IPv4-mapped addresses the prefix has to be 96 or lager
56 if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96)
57 return 1;
58
6183c0f2
MT
59 return 0;
60}
61
a82ce8bc
MT
62static struct in6_addr prefix_to_bitmask(unsigned int prefix) {
63 struct in6_addr bitmask;
64
65 for (unsigned int i = 0; i < 16; i++)
66 bitmask.s6_addr[i] = 0;
67
364a2a37 68 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
a82ce8bc
MT
69 if (i >= 8)
70 bitmask.s6_addr[j] = 0xff;
71 else
72 bitmask.s6_addr[j] = 0xff << (8 - i);
73 }
74
75 return bitmask;
76}
77
2a30e4de 78static struct in6_addr make_start_address(const struct in6_addr* address, unsigned int prefix) {
a82ce8bc
MT
79 struct in6_addr a;
80 struct in6_addr bitmask = prefix_to_bitmask(prefix);
81
82 // Perform bitwise AND
83 for (unsigned int i = 0; i < 4; i++)
84 a.s6_addr32[i] = address->s6_addr32[i] & bitmask.s6_addr32[i];
85
86 return a;
87}
88
2a30e4de
MT
89static struct in6_addr make_last_address(const struct in6_addr* address, unsigned int prefix) {
90 struct in6_addr a;
91 struct in6_addr bitmask = prefix_to_bitmask(prefix);
92
93 // Perform bitwise OR
94 for (unsigned int i = 0; i < 4; i++)
c7096aef 95 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask.s6_addr32[i];
2a30e4de
MT
96
97 return a;
98}
99
3b5f4af2 100LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 101 struct in6_addr* address, unsigned int prefix) {
3b5f4af2 102 // Address cannot be unspecified
bca87342 103 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
3b5f4af2
MT
104 DEBUG(ctx, "Start address is unspecified\n");
105 return -EINVAL;
106 }
107
108 // Address cannot be loopback
bca87342 109 if (IN6_IS_ADDR_LOOPBACK(address)) {
3b5f4af2
MT
110 DEBUG(ctx, "Start address is loopback address\n");
111 return -EINVAL;
112 }
113
114 // Address cannot be link-local
bca87342 115 if (IN6_IS_ADDR_LINKLOCAL(address)) {
3b5f4af2
MT
116 DEBUG(ctx, "Start address cannot be link-local\n");
117 return -EINVAL;
118 }
119
120 // Address cannot be site-local
bca87342 121 if (IN6_IS_ADDR_SITELOCAL(address)) {
3b5f4af2
MT
122 DEBUG(ctx, "Start address cannot be site-local\n");
123 return -EINVAL;
124 }
125
6183c0f2 126 // Validate the prefix
10778041 127 if (valid_prefix(address, prefix) != 0) {
6183c0f2
MT
128 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
129 return -EINVAL;
130 }
131
3b5f4af2
MT
132 struct loc_network* n = calloc(1, sizeof(*n));
133 if (!n)
134 return -ENOMEM;
135
136 n->ctx = loc_ref(ctx);
137 n->refcount = 1;
138
a82ce8bc 139 // Store the first address in the network
10778041 140 n->start_address = make_start_address(address, prefix);
3b5f4af2
MT
141 n->prefix = prefix;
142
143 DEBUG(n->ctx, "Network allocated at %p\n", n);
144 *network = n;
145 return 0;
146}
147
d3409788
MT
148static int loc_network_address_family(struct loc_network* network) {
149 if (IN6_IS_ADDR_V4MAPPED(&network->start_address))
150 return AF_INET;
151
152 return AF_INET6;
153}
154
3b5f4af2
MT
155LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
156 const char* address_string) {
157 struct in6_addr start_address;
b61f14fc 158 unsigned int prefix = 0;
3b5f4af2 159 char* prefix_string;
b61f14fc 160 int r = 1;
3b5f4af2
MT
161
162 // Make a copy of the string to work on it
163 char* buffer = strdup(address_string);
164 address_string = prefix_string = buffer;
165
166 // Split address and prefix
167 address_string = strsep(&prefix_string, "/");
168
b61f14fc
MT
169 // Did we find a prefix?
170 if (prefix_string) {
171 // Convert prefix to integer
172 prefix = strtol(prefix_string, NULL, 10);
3b5f4af2 173
b61f14fc
MT
174 if (prefix) {
175 // Parse the address
2a30e4de 176 r = loc_parse_address(ctx, address_string, &start_address);
aa37232a
MT
177
178 // Map the prefix to IPv6 if needed
179 if (IN6_IS_ADDR_V4MAPPED(&start_address))
180 prefix += 96;
b61f14fc
MT
181 }
182 }
3b5f4af2
MT
183
184 // Free temporary buffer
185 free(buffer);
186
d3409788 187 if (r == 0) {
10778041 188 r = loc_network_new(ctx, network, &start_address, prefix);
3b5f4af2
MT
189 }
190
191 return r;
192}
193
194LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
195 network->refcount++;
196
197 return network;
198}
199
200static void loc_network_free(struct loc_network* network) {
201 DEBUG(network->ctx, "Releasing network at %p\n", network);
202
3b5f4af2
MT
203 loc_unref(network->ctx);
204 free(network);
205}
206
207LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
2a30e4de
MT
208 if (!network)
209 return NULL;
210
3b5f4af2
MT
211 if (--network->refcount > 0)
212 return network;
213
214 loc_network_free(network);
215 return NULL;
216}
217
8fb874e9
MT
218static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) {
219 const char* ret = inet_ntop(AF_INET6, address, string, length);
d3409788
MT
220 if (!ret)
221 return -1;
222
223 return 0;
224}
225
8fb874e9 226static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
d3409788 227 struct in_addr ipv4_address;
8fb874e9 228 ipv4_address.s_addr = address->s6_addr32[3];
d3409788
MT
229
230 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
231 if (!ret)
232 return -1;
233
234 return 0;
235}
236
3b5f4af2 237LOC_EXPORT char* loc_network_str(struct loc_network* network) {
d3409788
MT
238 int r;
239 const size_t length = INET6_ADDRSTRLEN + 4;
3b5f4af2 240
d3409788 241 char* string = malloc(length);
3b5f4af2
MT
242 if (!string)
243 return NULL;
244
aa37232a
MT
245 unsigned int prefix = network->prefix;
246
d3409788
MT
247 int family = loc_network_address_family(network);
248 switch (family) {
249 case AF_INET6:
8fb874e9 250 r = format_ipv6_address(&network->start_address, string, length);
d3409788 251 break;
3b5f4af2 252
d3409788 253 case AF_INET:
8fb874e9 254 r = format_ipv4_address(&network->start_address, string, length);
aa37232a 255 prefix -= 96;
d3409788
MT
256 break;
257
258 default:
259 r = -1;
260 break;
261 }
262
263 if (r) {
264 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
3b5f4af2 265 free(string);
d3409788 266
3b5f4af2
MT
267 return NULL;
268 }
269
270 // Append prefix
aa37232a 271 sprintf(string + strlen(string), "/%u", prefix);
3b5f4af2
MT
272
273 return string;
274}
275
2a30e4de 276LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 277 // Address must be larger than the start address
2a30e4de
MT
278 if (in6_addr_cmp(&network->start_address, address) > 0)
279 return 1;
280
281 // Determine the last address in this network
282 struct in6_addr last_address = make_last_address(&network->start_address, network->prefix);
283
284 // Address must be smaller than the last address
0d33b671 285 if (in6_addr_cmp(address, &last_address) > 0)
2a30e4de
MT
286 return 1;
287
288 // The address is inside this network
289 return 0;
290}
291
3b5f4af2
MT
292LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
293 return network->country_code;
294}
295
296LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
297 // Set empty country code
298 if (!country_code || !*country_code) {
299 *network->country_code = '\0';
300 return 0;
301 }
302
57146963
MT
303 // Check country code
304 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
305 return -EINVAL;
306
a7a3d958 307 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
308
309 return 0;
310}
311
e3f696c1 312LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) {
57146963
MT
313 // Check country code
314 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
315 return -EINVAL;
316
317 return (network->country_code[0] == country_code[0])
318 && (network->country_code[1] == country_code[1]);
319}
320
71ff3e69
MT
321LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
322 return network->asn;
323}
324
325LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
326 network->asn = asn;
327
328 return 0;
329}
330
82910b95
MT
331LOC_EXPORT int loc_network_match_asn(struct loc_network* network, uint32_t asn) {
332 return network->asn == asn;
333}
334
a99e7c2b
MT
335LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
336 return network->flags & flag;
337}
338
339LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
340 network->flags |= flag;
341
342 return 0;
343}
344
345LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag) {
346 return loc_network_has_flag(network, flag);
347}
348
f3e02bc5 349LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) {
f3e02bc5 350 // Add country code
a7a3d958 351 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
352
353 // Add ASN
71ff3e69 354 dbobj->asn = htobe32(network->asn);
f3e02bc5 355
a99e7c2b 356 // Flags
eee6346d 357 dbobj->flags = htobe16(network->flags);
a99e7c2b 358
f3e02bc5
MT
359 return 0;
360}
361
10778041 362LOC_EXPORT int loc_network_new_from_database_v0(struct loc_ctx* ctx, struct loc_network** network,
39a55353 363 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v0* dbobj) {
a7a3d958
MT
364 char country_code[3];
365
39a55353 366 int r = loc_network_new(ctx, network, address, prefix);
10778041
MT
367 if (r)
368 return r;
369
370 // Import country code
a7a3d958 371 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
372
373 r = loc_network_set_country_code(*network, country_code);
374 if (r)
375 return r;
376
377 // Import ASN
378 r = loc_network_set_asn(*network, be32toh(dbobj->asn));
379 if (r)
380 return r;
381
a99e7c2b 382 // Import flags
eee6346d 383 r = loc_network_set_flag(*network, be16toh(dbobj->flags));
a99e7c2b
MT
384 if (r)
385 return r;
386
10778041
MT
387 return 0;
388}
389
3b5f4af2
MT
390struct loc_network_tree {
391 struct loc_ctx* ctx;
392 int refcount;
393
394 struct loc_network_tree_node* root;
395};
396
397struct loc_network_tree_node {
438db08c
MT
398 struct loc_ctx* ctx;
399 int refcount;
400
3b5f4af2
MT
401 struct loc_network_tree_node* zero;
402 struct loc_network_tree_node* one;
403
404 struct loc_network* network;
405};
406
407LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
408 struct loc_network_tree* t = calloc(1, sizeof(*t));
409 if (!t)
410 return -ENOMEM;
411
412 t->ctx = loc_ref(ctx);
413 t->refcount = 1;
414
415 // Create the root node
438db08c
MT
416 int r = loc_network_tree_node_new(ctx, &t->root);
417 if (r) {
418 loc_network_tree_unref(t);
419 return r;
420 }
3b5f4af2
MT
421
422 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
423 *tree = t;
424 return 0;
425}
426
438db08c
MT
427LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
428 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
429}
430
431static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
432 struct loc_network_tree_node** n;
433
2a30e4de 434 if (path == 0)
3b5f4af2 435 n = &node->zero;
3eda9cab
MT
436 else
437 n = &node->one;
3b5f4af2
MT
438
439 // If the desired node doesn't exist, yet, we will create it
440 if (*n == NULL) {
438db08c 441 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
442 if (r)
443 return NULL;
444 }
445
446 return *n;
447}
448
39a55353 449static 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
450 struct loc_network_tree_node* node = tree->root;
451
39a55353 452 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 453 // Check if the ith bit is one or zero
2a30e4de 454 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
3b5f4af2
MT
455 }
456
457 return node;
458}
459
460static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
461 int(*filter_callback)(struct loc_network* network, void* data),
462 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
463 int r;
464
465 // Finding a network ends the walk here
466 if (node->network) {
467 if (filter_callback) {
f3e02bc5 468 int f = filter_callback(node->network, data);
3b5f4af2
MT
469 if (f < 0)
470 return f;
471
472 // Skip network if filter function returns value greater than zero
473 if (f > 0)
474 return 0;
475 }
476
f3e02bc5 477 r = callback(node->network, data);
3b5f4af2
MT
478 if (r)
479 return r;
480 }
481
482 // Walk down on the left side of the tree first
483 if (node->zero) {
f3e02bc5 484 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
485 if (r)
486 return r;
487 }
488
489 // Then walk on the other side
490 if (node->one) {
f3e02bc5 491 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
492 if (r)
493 return r;
494 }
495
496 return 0;
497}
498
f3e02bc5
MT
499LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
500 int(*filter_callback)(struct loc_network* network, void* data),
501 int(*callback)(struct loc_network* network, void* data), void* data) {
502 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
503}
504
3b5f4af2
MT
505static void loc_network_tree_free(struct loc_network_tree* tree) {
506 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
507
438db08c 508 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
509
510 loc_unref(tree->ctx);
511 free(tree);
512}
513
514LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
515 if (--tree->refcount > 0)
516 return tree;
517
518 loc_network_tree_free(tree);
519 return NULL;
520}
521
e9b4e2a8 522static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
523 DEBUG(network->ctx, "Dumping network at %p\n", network);
524
525 char* s = loc_network_str(network);
526 if (!s)
527 return 1;
528
529 INFO(network->ctx, "%s\n", s);
530 free(s);
531
532 return 0;
533}
534
535LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
536 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
537
f3e02bc5 538 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
539}
540
541LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
542 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
543
39a55353
MT
544 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
545 &network->start_address, network->prefix);
3b5f4af2
MT
546 if (!node) {
547 ERROR(tree->ctx, "Could not find a node\n");
548 return -ENOMEM;
549 }
550
551 // Check if node has not been set before
552 if (node->network) {
553 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 554 return -EBUSY;
3b5f4af2
MT
555 }
556
557 // Point node to the network
558 node->network = loc_network_ref(network);
559
560 return 0;
561}
f3e02bc5
MT
562
563static int __loc_network_tree_count(struct loc_network* network, void* data) {
564 size_t* counter = (size_t*)data;
565
566 // Increase the counter for each network
567 counter++;
568
569 return 0;
570}
571
572LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
573 size_t counter = 0;
574
575 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
576 if (r)
577 return r;
578
579 return counter;
580}
940f9c2b
MT
581
582static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
583 size_t counter = 1;
584
585 if (node->zero)
586 counter += __loc_network_tree_count_nodes(node->zero);
587
588 if (node->one)
589 counter += __loc_network_tree_count_nodes(node->one);
590
591 return counter;
592}
593
594LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
595 return __loc_network_tree_count_nodes(tree->root);
596}
438db08c
MT
597
598LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
599 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
600 if (!n)
601 return -ENOMEM;
602
603 n->ctx = loc_ref(ctx);
604 n->refcount = 1;
605
606 n->zero = n->one = NULL;
607
608 DEBUG(n->ctx, "Network node allocated at %p\n", n);
609 *node = n;
610 return 0;
611}
612
613LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
614 if (node)
615 node->refcount++;
616
617 return node;
618}
619
620static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
621 DEBUG(node->ctx, "Releasing network node at %p\n", node);
622
623 if (node->network)
624 loc_network_unref(node->network);
625
626 if (node->zero)
627 loc_network_tree_node_unref(node->zero);
628
629 if (node->one)
630 loc_network_tree_node_unref(node->one);
631
632 loc_unref(node->ctx);
633 free(node);
634}
635
636LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
637 if (!node)
638 return NULL;
639
640 if (--node->refcount > 0)
641 return node;
642
643 loc_network_tree_node_free(node);
644 return NULL;
645}
646
647LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
648 if (index == 0)
649 node = node->zero;
650 else
651 node = node->one;
652
653 if (!node)
654 return NULL;
655
656 return loc_network_tree_node_ref(node);
657}
658
659LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
660 return (!!node->network);
661}
662
663LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
664 return loc_network_ref(node->network);
665}