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