]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
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.
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.
17 #include <arpa/inet.h>
28 #include <libloc/libloc.h>
29 #include <libloc/address.h>
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>
41 struct in6_addr first_address
;
42 struct in6_addr last_address
;
47 enum loc_network_flags flags
;
49 char string
[INET6_ADDRSTRLEN
+ 4];
52 LOC_EXPORT
int loc_network_new(struct loc_ctx
* ctx
, struct loc_network
** network
,
53 struct in6_addr
* address
, unsigned int prefix
) {
54 // Validate the prefix
55 if (!loc_address_valid_prefix(address
, prefix
)) {
56 ERROR(ctx
, "Invalid prefix in %s: %u\n", loc_address_str(address
), prefix
);
61 struct loc_network
* n
= calloc(1, sizeof(*n
));
67 n
->ctx
= loc_ref(ctx
);
71 if (IN6_IS_ADDR_V4MAPPED(address
))
72 n
->prefix
= prefix
+ 96;
76 // Convert the prefix into a bitmask
77 struct in6_addr bitmask
= loc_prefix_to_bitmask(n
->prefix
);
79 // Store the first and last address in the network
80 n
->first_address
= loc_address_and(address
, &bitmask
);
81 n
->last_address
= loc_address_or(&n
->first_address
, &bitmask
);
84 n
->family
= loc_address_family(&n
->first_address
);
86 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
91 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
,
92 struct loc_network
** network
, const char* string
) {
93 struct in6_addr address
;
97 int r
= loc_address_parse(&address
, &prefix
, string
);
99 ERROR(ctx
, "Could not parse network %s: %m\n", string
);
103 // Create a new network
104 return loc_network_new(ctx
, network
, &address
, prefix
);
107 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
113 static void loc_network_free(struct loc_network
* network
) {
114 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
116 loc_unref(network
->ctx
);
120 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
124 if (--network
->refcount
> 0)
127 loc_network_free(network
);
131 LOC_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
);
139 unsigned int prefix
= loc_network_prefix(network
);
142 int r
= snprintf(network
->string
, sizeof(network
->string
) - 1,
143 "%s/%u", address
, prefix
);
145 ERROR(network
->ctx
, "Could not format network string: %m\n");
146 *network
->string
= '\0';
151 return network
->string
;
154 LOC_EXPORT
int loc_network_address_family(struct loc_network
* network
) {
155 return network
->family
;
158 LOC_EXPORT
unsigned int loc_network_prefix(struct loc_network
* network
) {
159 switch (network
->family
) {
161 return network
->prefix
;
164 return network
->prefix
- 96;
170 LOC_EXPORT
const struct in6_addr
* loc_network_get_first_address(struct loc_network
* network
) {
171 return &network
->first_address
;
174 LOC_EXPORT
const char* loc_network_format_first_address(struct loc_network
* network
) {
175 return loc_address_str(&network
->first_address
);
178 LOC_EXPORT
const struct in6_addr
* loc_network_get_last_address(struct loc_network
* network
) {
179 return &network
->last_address
;
182 LOC_EXPORT
const char* loc_network_format_last_address(struct loc_network
* network
) {
183 return loc_address_str(&network
->last_address
);
186 LOC_EXPORT
int loc_network_matches_address(struct loc_network
* network
, const struct in6_addr
* address
) {
187 // Address must be larger than the start address
188 if (loc_address_cmp(&network
->first_address
, address
) > 0)
191 // Address must be smaller than the last address
192 if (loc_address_cmp(&network
->last_address
, address
) < 0)
195 // The address is inside this network
199 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
200 return network
->country_code
;
203 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
204 // Set empty country code
205 if (!country_code
|| !*country_code
) {
206 *network
->country_code
= '\0';
210 // Check country code
211 if (!loc_country_code_is_valid(country_code
))
214 loc_country_code_copy(network
->country_code
, country_code
);
219 LOC_EXPORT
int loc_network_matches_country_code(struct loc_network
* network
, const char* country_code
) {
220 // Search for any special flags
221 const int flag
= loc_country_special_code_to_flag(country_code
);
223 // If we found a flag, we will return whether it is set or not
225 return loc_network_has_flag(network
, flag
);
227 // Check country code
228 if (!loc_country_code_is_valid(country_code
))
231 // Check for an exact match
232 return (network
->country_code
[0] == country_code
[0])
233 && (network
->country_code
[1] == country_code
[1]);
236 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
240 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
246 LOC_EXPORT
int loc_network_has_flag(struct loc_network
* network
, uint32_t flag
) {
247 return network
->flags
& flag
;
250 LOC_EXPORT
int loc_network_set_flag(struct loc_network
* network
, uint32_t flag
) {
251 network
->flags
|= flag
;
256 LOC_EXPORT
int loc_network_cmp(struct loc_network
* self
, struct loc_network
* other
) {
258 int r
= loc_address_cmp(&self
->first_address
, &other
->first_address
);
263 if (self
->prefix
> other
->prefix
)
265 else if (self
->prefix
< other
->prefix
)
268 // Both networks are equal
272 LOC_EXPORT
int loc_network_overlaps(struct loc_network
* self
, struct loc_network
* other
) {
273 // Either of the start addresses must be in the other subnet
274 if (loc_network_matches_address(self
, &other
->first_address
))
277 if (loc_network_matches_address(other
, &self
->first_address
))
280 // Or either of the end addresses is in the other subnet
281 if (loc_network_matches_address(self
, &other
->last_address
))
284 if (loc_network_matches_address(other
, &self
->last_address
))
290 LOC_EXPORT
int loc_network_is_subnet(struct loc_network
* self
, struct loc_network
* other
) {
291 // The prefix must be smaller (this avoids the more complex comparisons later)
292 if (self
->prefix
> other
->prefix
)
295 // If the start address of the other network is smaller than this network,
296 // it cannot be a subnet.
297 if (loc_address_cmp(&self
->first_address
, &other
->first_address
) > 0)
300 // If the end address of the other network is greater than this network,
301 // it cannot be a subnet.
302 if (loc_address_cmp(&self
->last_address
, &other
->last_address
) < 0)
308 LOC_EXPORT
int loc_network_subnets(struct loc_network
* network
,
309 struct loc_network
** subnet1
, struct loc_network
** subnet2
) {
315 unsigned int prefix
= network
->prefix
+ 1;
317 // Check if the new prefix is valid
318 if (!loc_address_valid_prefix(&network
->first_address
, prefix
)) {
319 ERROR(network
->ctx
, "Invalid prefix: %d\n", prefix
);
324 // Create the first half of the network
325 r
= loc_network_new(network
->ctx
, subnet1
, &network
->first_address
, prefix
);
329 // The next subnet starts after the first one
330 struct in6_addr first_address
= (*subnet1
)->last_address
;
331 loc_address_increment(&first_address
);
333 // Create the second half of the network
334 r
= loc_network_new(network
->ctx
, subnet2
, &first_address
, prefix
);
339 const char* country_code
= loc_network_get_country_code(network
);
341 loc_network_set_country_code(*subnet1
, country_code
);
342 loc_network_set_country_code(*subnet2
, country_code
);
346 uint32_t asn
= loc_network_get_asn(network
);
348 loc_network_set_asn(*subnet1
, asn
);
349 loc_network_set_asn(*subnet2
, asn
);
353 loc_network_set_flag(*subnet1
, network
->flags
);
354 loc_network_set_flag(*subnet2
, network
->flags
);
359 static 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
;
364 int r
= loc_network_subnets(network
, &subnet1
, &subnet2
);
368 if (loc_network_cmp(other
, subnet1
) == 0) {
369 r
= loc_network_list_push(list
, subnet2
);
373 } else if (loc_network_cmp(other
, subnet2
) == 0) {
374 r
= loc_network_list_push(list
, subnet1
);
378 } else if (loc_network_is_subnet(subnet1
, other
)) {
379 r
= loc_network_list_push(list
, subnet2
);
383 r
= __loc_network_exclude(subnet1
, other
, list
);
387 } else if (loc_network_is_subnet(subnet2
, other
)) {
388 r
= loc_network_list_push(list
, subnet1
);
392 r
= __loc_network_exclude(subnet2
, other
, list
);
397 ERROR(network
->ctx
, "We should never get here\n");
404 loc_network_unref(subnet1
);
407 loc_network_unref(subnet2
);
410 DEBUG(network
->ctx
, "%s has failed with %d\n", __FUNCTION__
, r
);
415 static int __loc_network_exclude_to_list(struct loc_network
* self
,
416 struct loc_network
* other
, struct loc_network_list
* list
) {
417 // Other must be a subnet of self
418 if (!loc_network_is_subnet(self
, other
)) {
419 DEBUG(self
->ctx
, "Network %p is not contained in network %p\n", other
, self
);
425 // We cannot perform this operation if both networks equal
426 if (loc_network_cmp(self
, other
) == 0) {
427 DEBUG(self
->ctx
, "Networks %p and %p are equal\n", self
, other
);
433 return __loc_network_exclude(self
, other
, list
);
436 LOC_EXPORT
struct loc_network_list
* loc_network_exclude(
437 struct loc_network
* self
, struct loc_network
* other
) {
438 struct loc_network_list
* list
;
440 DEBUG(self
->ctx
, "Returning %s excluding %s...\n",
441 loc_network_str(self
), loc_network_str(other
));
443 // Create a new list with the result
444 int r
= loc_network_list_new(self
->ctx
, &list
);
446 ERROR(self
->ctx
, "Could not create network list: %d\n", r
);
451 r
= __loc_network_exclude_to_list(self
, other
, list
);
453 loc_network_list_unref(list
);
462 LOC_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
;
466 // Create a new list with all networks to look at
467 int r
= loc_network_list_new(network
->ctx
, &to_check
);
471 struct loc_network
* subnet
= NULL
;
472 struct loc_network_list
* subnets
= NULL
;
474 for (unsigned int i
= 0; i
< loc_network_list_size(list
); i
++) {
475 subnet
= loc_network_list_get(list
, i
);
477 // Find all excluded networks
478 if (!loc_network_list_contains(to_check
, subnet
)) {
479 r
= __loc_network_exclude_to_list(network
, subnet
, to_check
);
481 loc_network_list_unref(to_check
);
482 loc_network_unref(subnet
);
489 loc_network_unref(subnet
);
492 r
= loc_network_list_new(network
->ctx
, &subnets
);
494 loc_network_list_unref(to_check
);
498 off_t smallest_subnet
= 0;
500 while (!loc_network_list_empty(to_check
)) {
501 struct loc_network
* subnet_to_check
= loc_network_list_pop_first(to_check
);
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
);
509 // Marks whether this subnet passed all checks
512 for (unsigned int i
= smallest_subnet
; i
< loc_network_list_size(list
); i
++) {
513 subnet
= loc_network_list_get(list
, i
);
515 // Drop this subnet if is a subnet of another subnet
516 if (loc_network_is_subnet(subnet
, subnet_to_check
)) {
518 loc_network_unref(subnet
);
522 // Break it down if it overlaps
523 if (loc_network_overlaps(subnet
, subnet_to_check
)) {
526 __loc_network_exclude_to_list(subnet_to_check
, subnet
, to_check
);
528 loc_network_unref(subnet
);
532 // If the subnet is strictly greater, we do not need to continue the search
533 r
= loc_network_cmp(subnet
, subnet_to_check
);
535 loc_network_unref(subnet
);
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
545 loc_network_unref(subnet
);
549 r
= loc_network_list_push(subnets
, subnet_to_check
);
552 loc_network_unref(subnet_to_check
);
555 loc_network_list_unref(to_check
);
560 int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
562 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
565 dbobj
->asn
= htobe32(network
->asn
);
568 dbobj
->flags
= htobe16(network
->flags
);
573 int loc_network_new_from_database_v1(struct loc_ctx
* ctx
, struct loc_network
** network
,
574 struct in6_addr
* address
, unsigned int prefix
, const struct loc_database_network_v1
* dbobj
) {
575 char country_code
[3] = "\0\0";
577 // Adjust prefix for IPv4
578 if (IN6_IS_ADDR_V4MAPPED(address
))
581 int r
= loc_network_new(ctx
, network
, address
, prefix
);
583 ERROR(ctx
, "Could not allocate a new network: %s", strerror(-r
));
587 // Import country code
588 loc_country_code_copy(country_code
, dbobj
->country_code
);
590 r
= loc_network_set_country_code(*network
, country_code
);
592 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
597 uint32_t asn
= be32toh(dbobj
->asn
);
598 r
= loc_network_set_asn(*network
, asn
);
600 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
605 int flags
= be16toh(dbobj
->flags
);
606 r
= loc_network_set_flag(*network
, flags
);
608 ERROR(ctx
, "Could not set flags: %d\n", flags
);
615 struct loc_network_tree
{
619 struct loc_network_tree_node
* root
;
622 struct loc_network_tree_node
{
626 struct loc_network_tree_node
* zero
;
627 struct loc_network_tree_node
* one
;
629 struct loc_network
* network
;
632 int loc_network_tree_new(struct loc_ctx
* ctx
, struct loc_network_tree
** tree
) {
633 struct loc_network_tree
* t
= calloc(1, sizeof(*t
));
637 t
->ctx
= loc_ref(ctx
);
640 // Create the root node
641 int r
= loc_network_tree_node_new(ctx
, &t
->root
);
643 loc_network_tree_unref(t
);
647 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
652 struct loc_network_tree_node
* loc_network_tree_get_root(struct loc_network_tree
* tree
) {
653 return loc_network_tree_node_ref(tree
->root
);
656 static 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
;
664 // If the desired node doesn't exist, yet, we will create it
666 int r
= loc_network_tree_node_new(node
->ctx
, n
);
674 static struct loc_network_tree_node
* loc_network_tree_get_path(struct loc_network_tree
* tree
, const struct in6_addr
* address
, unsigned int prefix
) {
675 struct loc_network_tree_node
* node
= tree
->root
;
677 for (unsigned int i
= 0; i
< prefix
; i
++) {
678 // Check if the ith bit is one or zero
679 node
= loc_network_tree_get_node(node
, loc_address_get_bit(address
, i
));
685 static int __loc_network_tree_walk(struct loc_ctx
* ctx
, struct loc_network_tree_node
* node
,
686 int(*filter_callback
)(struct loc_network
* network
, void* data
),
687 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
690 // Finding a network ends the walk here
692 if (filter_callback
) {
693 int f
= filter_callback(node
->network
, data
);
697 // Skip network if filter function returns value greater than zero
702 r
= callback(node
->network
, data
);
707 // Walk down on the left side of the tree first
709 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
714 // Then walk on the other side
716 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
724 int loc_network_tree_walk(struct loc_network_tree
* tree
,
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
);
730 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
731 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
733 loc_network_tree_node_unref(tree
->root
);
735 loc_unref(tree
->ctx
);
739 struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
740 if (--tree
->refcount
> 0)
743 loc_network_tree_free(tree
);
747 static int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
748 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
750 const char* s
= loc_network_str(network
);
754 INFO(network
->ctx
, "%s\n", s
);
759 int loc_network_tree_dump(struct loc_network_tree
* tree
) {
760 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
762 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
765 int loc_network_tree_add_network(struct loc_network_tree
* tree
, struct loc_network
* network
) {
766 DEBUG(tree
->ctx
, "Adding network %p to tree %p\n", network
, tree
);
768 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
,
769 &network
->first_address
, network
->prefix
);
771 ERROR(tree
->ctx
, "Could not find a node\n");
775 // Check if node has not been set before
777 DEBUG(tree
->ctx
, "There is already a network at this path\n");
781 // Point node to the network
782 node
->network
= loc_network_ref(network
);
787 static int __loc_network_tree_count(struct loc_network
* network
, void* data
) {
788 size_t* counter
= (size_t*)data
;
790 // Increase the counter for each network
796 size_t loc_network_tree_count_networks(struct loc_network_tree
* tree
) {
799 int r
= loc_network_tree_walk(tree
, NULL
, __loc_network_tree_count
, &counter
);
806 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node
* node
) {
810 counter
+= __loc_network_tree_count_nodes(node
->zero
);
813 counter
+= __loc_network_tree_count_nodes(node
->one
);
818 size_t loc_network_tree_count_nodes(struct loc_network_tree
* tree
) {
819 return __loc_network_tree_count_nodes(tree
->root
);
822 int loc_network_tree_node_new(struct loc_ctx
* ctx
, struct loc_network_tree_node
** node
) {
823 struct loc_network_tree_node
* n
= calloc(1, sizeof(*n
));
827 n
->ctx
= loc_ref(ctx
);
830 n
->zero
= n
->one
= NULL
;
832 DEBUG(n
->ctx
, "Network node allocated at %p\n", n
);
837 struct loc_network_tree_node
* loc_network_tree_node_ref(struct loc_network_tree_node
* node
) {
844 static void loc_network_tree_node_free(struct loc_network_tree_node
* node
) {
845 DEBUG(node
->ctx
, "Releasing network node at %p\n", node
);
848 loc_network_unref(node
->network
);
851 loc_network_tree_node_unref(node
->zero
);
854 loc_network_tree_node_unref(node
->one
);
856 loc_unref(node
->ctx
);
860 struct loc_network_tree_node
* loc_network_tree_node_unref(struct loc_network_tree_node
* node
) {
864 if (--node
->refcount
> 0)
867 loc_network_tree_node_free(node
);
871 struct loc_network_tree_node
* loc_network_tree_node_get(struct loc_network_tree_node
* node
, unsigned int index
) {
880 return loc_network_tree_node_ref(node
);
883 int loc_network_tree_node_is_leaf(struct loc_network_tree_node
* node
) {
884 return (!!node
->network
);
887 struct loc_network
* loc_network_tree_node_get_network(struct loc_network_tree_node
* node
) {
888 return loc_network_ref(node
->network
);