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