]>
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
;
50 static int valid_prefix(struct in6_addr
* address
, unsigned int prefix
) {
51 // The prefix cannot be larger than 128 bits
55 // For IPv4-mapped addresses the prefix has to be 96 or lager
56 if (IN6_IS_ADDR_V4MAPPED(address
) && prefix
<= 96)
62 LOC_EXPORT
int loc_network_new(struct loc_ctx
* ctx
, struct loc_network
** network
,
63 struct in6_addr
* address
, unsigned int prefix
) {
64 // Validate the prefix
65 if (valid_prefix(address
, prefix
) != 0) {
66 ERROR(ctx
, "Invalid prefix: %u\n", prefix
);
71 struct loc_network
* n
= calloc(1, sizeof(*n
));
77 n
->ctx
= loc_ref(ctx
);
83 // Convert the prefix into a bitmask
84 struct in6_addr bitmask
= loc_prefix_to_bitmask(n
->prefix
);
86 // Store the first and last address in the network
87 n
->first_address
= loc_address_and(address
, &bitmask
);
88 n
->last_address
= loc_address_or(&n
->first_address
, &bitmask
);
91 n
->family
= loc_address_family(&n
->first_address
);
93 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
98 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
, struct loc_network
** network
,
99 const char* address_string
) {
100 struct in6_addr first_address
;
102 unsigned int prefix
= 128;
105 DEBUG(ctx
, "Attempting to parse network %s\n", address_string
);
107 // Make a copy of the string to work on it
108 char* buffer
= strdup(address_string
);
109 address_string
= prefix_string
= buffer
;
111 // Split address and prefix
112 address_string
= strsep(&prefix_string
, "/");
114 DEBUG(ctx
, " Split into address = %s, prefix = %s\n", address_string
, prefix_string
);
117 r
= loc_parse_address(ctx
, address_string
, &first_address
);
119 DEBUG(ctx
, "The address could not be parsed\n");
123 // If a prefix was given, we will try to parse it
125 // Convert prefix to integer
126 prefix
= strtol(prefix_string
, NULL
, 10);
129 DEBUG(ctx
, "The prefix was not parsable: %s\n", prefix_string
);
133 // Map the prefix to IPv6 if needed
134 if (IN6_IS_ADDR_V4MAPPED(&first_address
))
139 // Free temporary buffer
142 // Exit if the parsing was unsuccessful
146 // Create a new network
147 return loc_network_new(ctx
, network
, &first_address
, prefix
);
150 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
156 static void loc_network_free(struct loc_network
* network
) {
157 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
159 loc_unref(network
->ctx
);
163 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
167 if (--network
->refcount
> 0)
170 loc_network_free(network
);
174 static int format_ipv6_address(const struct in6_addr
* address
, char* string
, size_t length
) {
175 const char* ret
= inet_ntop(AF_INET6
, address
, string
, length
);
182 static int format_ipv4_address(const struct in6_addr
* address
, char* string
, size_t length
) {
183 struct in_addr ipv4_address
;
184 ipv4_address
.s_addr
= address
->s6_addr32
[3];
186 const char* ret
= inet_ntop(AF_INET
, &ipv4_address
, string
, length
);
193 LOC_EXPORT
char* loc_network_str(struct loc_network
* network
) {
195 const size_t length
= INET6_ADDRSTRLEN
+ 4;
197 char* string
= malloc(length
);
201 unsigned int prefix
= network
->prefix
;
203 switch (network
->family
) {
205 r
= format_ipv6_address(&network
->first_address
, string
, length
);
209 r
= format_ipv4_address(&network
->first_address
, string
, length
);
219 ERROR(network
->ctx
, "Could not convert network to string: %s\n", strerror(errno
));
226 sprintf(string
+ strlen(string
), "/%u", prefix
);
231 LOC_EXPORT
int loc_network_address_family(struct loc_network
* network
) {
232 return network
->family
;
235 LOC_EXPORT
unsigned int loc_network_prefix(struct loc_network
* network
) {
236 switch (network
->family
) {
238 return network
->prefix
;
241 return network
->prefix
- 96;
247 static char* loc_network_format_address(struct loc_network
* network
, const struct in6_addr
* address
) {
248 const size_t length
= INET6_ADDRSTRLEN
;
250 char* string
= malloc(length
);
256 switch (network
->family
) {
258 r
= format_ipv6_address(address
, string
, length
);
262 r
= format_ipv4_address(address
, string
, length
);
271 ERROR(network
->ctx
, "Could not format IP address to string: %s\n", strerror(errno
));
280 LOC_EXPORT
const struct in6_addr
* loc_network_get_first_address(struct loc_network
* network
) {
281 return &network
->first_address
;
284 LOC_EXPORT
char* loc_network_format_first_address(struct loc_network
* network
) {
285 return loc_network_format_address(network
, &network
->first_address
);
288 LOC_EXPORT
const struct in6_addr
* loc_network_get_last_address(struct loc_network
* network
) {
289 return &network
->last_address
;
292 LOC_EXPORT
char* loc_network_format_last_address(struct loc_network
* network
) {
293 return loc_network_format_address(network
, &network
->last_address
);
296 LOC_EXPORT
int loc_network_matches_address(struct loc_network
* network
, const struct in6_addr
* address
) {
297 // Address must be larger than the start address
298 if (loc_address_cmp(&network
->first_address
, address
) > 0)
301 // Address must be smaller than the last address
302 if (loc_address_cmp(&network
->last_address
, address
) < 0)
305 // The address is inside this network
309 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
310 return network
->country_code
;
313 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
314 // Set empty country code
315 if (!country_code
|| !*country_code
) {
316 *network
->country_code
= '\0';
320 // Check country code
321 if (!loc_country_code_is_valid(country_code
))
324 loc_country_code_copy(network
->country_code
, country_code
);
329 LOC_EXPORT
int loc_network_matches_country_code(struct loc_network
* network
, const char* country_code
) {
330 // Search for any special flags
331 const int flag
= loc_country_special_code_to_flag(country_code
);
333 // If we found a flag, we will return whether it is set or not
335 return loc_network_has_flag(network
, flag
);
337 // Check country code
338 if (!loc_country_code_is_valid(country_code
))
341 // Check for an exact match
342 return (network
->country_code
[0] == country_code
[0])
343 && (network
->country_code
[1] == country_code
[1]);
346 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
350 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
356 LOC_EXPORT
int loc_network_has_flag(struct loc_network
* network
, uint32_t flag
) {
357 return network
->flags
& flag
;
360 LOC_EXPORT
int loc_network_set_flag(struct loc_network
* network
, uint32_t flag
) {
361 network
->flags
|= flag
;
366 LOC_EXPORT
int loc_network_cmp(struct loc_network
* self
, struct loc_network
* other
) {
368 int r
= loc_address_cmp(&self
->first_address
, &other
->first_address
);
373 if (self
->prefix
> other
->prefix
)
375 else if (self
->prefix
< other
->prefix
)
378 // Both networks are equal
382 LOC_EXPORT
int loc_network_overlaps(struct loc_network
* self
, struct loc_network
* other
) {
383 // Either of the start addresses must be in the other subnet
384 if (loc_network_matches_address(self
, &other
->first_address
))
387 if (loc_network_matches_address(other
, &self
->first_address
))
390 // Or either of the end addresses is in the other subnet
391 if (loc_network_matches_address(self
, &other
->last_address
))
394 if (loc_network_matches_address(other
, &self
->last_address
))
400 LOC_EXPORT
int loc_network_is_subnet(struct loc_network
* self
, struct loc_network
* other
) {
401 // The prefix must be smaller (this avoids the more complex comparisons later)
402 if (self
->prefix
> other
->prefix
)
405 // If the start address of the other network is smaller than this network,
406 // it cannot be a subnet.
407 if (loc_address_cmp(&self
->first_address
, &other
->first_address
) > 0)
410 // If the end address of the other network is greater than this network,
411 // it cannot be a subnet.
412 if (loc_address_cmp(&self
->last_address
, &other
->last_address
) < 0)
418 LOC_EXPORT
int loc_network_subnets(struct loc_network
* network
,
419 struct loc_network
** subnet1
, struct loc_network
** subnet2
) {
425 unsigned int prefix
= network
->prefix
+ 1;
427 // Check if the new prefix is valid
428 if (valid_prefix(&network
->first_address
, prefix
))
431 // Create the first half of the network
432 r
= loc_network_new(network
->ctx
, subnet1
, &network
->first_address
, prefix
);
436 // The next subnet starts after the first one
437 struct in6_addr first_address
= (*subnet1
)->last_address
;
438 loc_address_increment(&first_address
);
440 // Create the second half of the network
441 r
= loc_network_new(network
->ctx
, subnet2
, &first_address
, prefix
);
446 const char* country_code
= loc_network_get_country_code(network
);
448 loc_network_set_country_code(*subnet1
, country_code
);
449 loc_network_set_country_code(*subnet2
, country_code
);
453 uint32_t asn
= loc_network_get_asn(network
);
455 loc_network_set_asn(*subnet1
, asn
);
456 loc_network_set_asn(*subnet2
, asn
);
460 loc_network_set_flag(*subnet1
, network
->flags
);
461 loc_network_set_flag(*subnet2
, network
->flags
);
466 static int __loc_network_exclude(struct loc_network
* network
,
467 struct loc_network
* other
, struct loc_network_list
* list
) {
468 struct loc_network
* subnet1
= NULL
;
469 struct loc_network
* subnet2
= NULL
;
471 int r
= loc_network_subnets(network
, &subnet1
, &subnet2
);
475 if (loc_network_cmp(other
, subnet1
) == 0) {
476 r
= loc_network_list_push(list
, subnet2
);
480 } else if (loc_network_cmp(other
, subnet2
) == 0) {
481 r
= loc_network_list_push(list
, subnet1
);
485 } else if (loc_network_is_subnet(subnet1
, other
)) {
486 r
= loc_network_list_push(list
, subnet2
);
490 r
= __loc_network_exclude(subnet1
, other
, list
);
494 } else if (loc_network_is_subnet(subnet2
, other
)) {
495 r
= loc_network_list_push(list
, subnet1
);
499 r
= __loc_network_exclude(subnet2
, other
, list
);
504 ERROR(network
->ctx
, "We should never get here\n");
511 loc_network_unref(subnet1
);
514 loc_network_unref(subnet2
);
519 static int __loc_network_exclude_to_list(struct loc_network
* self
,
520 struct loc_network
* other
, struct loc_network_list
* list
) {
521 // Other must be a subnet of self
522 if (!loc_network_is_subnet(self
, other
)) {
523 DEBUG(self
->ctx
, "Network %p is not contained in network %p\n", other
, self
);
529 // We cannot perform this operation if both networks equal
530 if (loc_network_cmp(self
, other
) == 0) {
531 DEBUG(self
->ctx
, "Networks %p and %p are equal\n", self
, other
);
537 return __loc_network_exclude(self
, other
, list
);
540 LOC_EXPORT
struct loc_network_list
* loc_network_exclude(
541 struct loc_network
* self
, struct loc_network
* other
) {
542 struct loc_network_list
* list
;
545 char* n1
= loc_network_str(self
);
546 char* n2
= loc_network_str(other
);
548 DEBUG(self
->ctx
, "Returning %s excluding %s...\n", n1
, n2
);
554 // Create a new list with the result
555 int r
= loc_network_list_new(self
->ctx
, &list
);
557 ERROR(self
->ctx
, "Could not create network list: %d\n", r
);
562 r
= __loc_network_exclude_to_list(self
, other
, list
);
564 loc_network_list_unref(list
);
573 LOC_EXPORT
struct loc_network_list
* loc_network_exclude_list(
574 struct loc_network
* network
, struct loc_network_list
* list
) {
575 struct loc_network_list
* to_check
;
577 // Create a new list with all networks to look at
578 int r
= loc_network_list_new(network
->ctx
, &to_check
);
582 struct loc_network
* subnet
= NULL
;
583 struct loc_network_list
* subnets
= NULL
;
585 for (unsigned int i
= 0; i
< loc_network_list_size(list
); i
++) {
586 subnet
= loc_network_list_get(list
, i
);
588 // Find all excluded networks
589 if (!loc_network_list_contains(to_check
, subnet
)) {
590 r
= __loc_network_exclude_to_list(network
, subnet
, to_check
);
592 loc_network_list_unref(to_check
);
593 loc_network_unref(subnet
);
600 loc_network_unref(subnet
);
603 r
= loc_network_list_new(network
->ctx
, &subnets
);
605 loc_network_list_unref(to_check
);
609 off_t smallest_subnet
= 0;
611 while (!loc_network_list_empty(to_check
)) {
612 struct loc_network
* subnet_to_check
= loc_network_list_pop_first(to_check
);
614 // Check whether the subnet to check is part of the input list
615 if (loc_network_list_contains(list
, subnet_to_check
)) {
616 loc_network_unref(subnet_to_check
);
620 // Marks whether this subnet passed all checks
623 for (unsigned int i
= smallest_subnet
; i
< loc_network_list_size(list
); i
++) {
624 subnet
= loc_network_list_get(list
, i
);
626 // Drop this subnet if is a subnet of another subnet
627 if (loc_network_is_subnet(subnet
, subnet_to_check
)) {
629 loc_network_unref(subnet
);
633 // Break it down if it overlaps
634 if (loc_network_overlaps(subnet
, subnet_to_check
)) {
637 __loc_network_exclude_to_list(subnet_to_check
, subnet
, to_check
);
639 loc_network_unref(subnet
);
643 // If the subnet is strictly greater, we do not need to continue the search
644 r
= loc_network_cmp(subnet
, subnet_to_check
);
646 loc_network_unref(subnet
);
649 // If it is strictly smaller, we can continue the search from here next
650 // time because all networks that are to be checked can only be larger
656 loc_network_unref(subnet
);
660 r
= loc_network_list_push(subnets
, subnet_to_check
);
663 loc_network_unref(subnet_to_check
);
666 loc_network_list_unref(to_check
);
671 int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
673 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
676 dbobj
->asn
= htobe32(network
->asn
);
679 dbobj
->flags
= htobe16(network
->flags
);
684 int loc_network_new_from_database_v1(struct loc_ctx
* ctx
, struct loc_network
** network
,
685 struct in6_addr
* address
, unsigned int prefix
, const struct loc_database_network_v1
* dbobj
) {
686 char country_code
[3] = "\0\0";
688 int r
= loc_network_new(ctx
, network
, address
, prefix
);
690 ERROR(ctx
, "Could not allocate a new network: %s", strerror(-r
));
694 // Import country code
695 loc_country_code_copy(country_code
, dbobj
->country_code
);
697 r
= loc_network_set_country_code(*network
, country_code
);
699 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
704 uint32_t asn
= be32toh(dbobj
->asn
);
705 r
= loc_network_set_asn(*network
, asn
);
707 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
712 int flags
= be16toh(dbobj
->flags
);
713 r
= loc_network_set_flag(*network
, flags
);
715 ERROR(ctx
, "Could not set flags: %d\n", flags
);
722 struct loc_network_tree
{
726 struct loc_network_tree_node
* root
;
729 struct loc_network_tree_node
{
733 struct loc_network_tree_node
* zero
;
734 struct loc_network_tree_node
* one
;
736 struct loc_network
* network
;
739 int loc_network_tree_new(struct loc_ctx
* ctx
, struct loc_network_tree
** tree
) {
740 struct loc_network_tree
* t
= calloc(1, sizeof(*t
));
744 t
->ctx
= loc_ref(ctx
);
747 // Create the root node
748 int r
= loc_network_tree_node_new(ctx
, &t
->root
);
750 loc_network_tree_unref(t
);
754 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
759 struct loc_network_tree_node
* loc_network_tree_get_root(struct loc_network_tree
* tree
) {
760 return loc_network_tree_node_ref(tree
->root
);
763 static struct loc_network_tree_node
* loc_network_tree_get_node(struct loc_network_tree_node
* node
, int path
) {
764 struct loc_network_tree_node
** n
;
771 // If the desired node doesn't exist, yet, we will create it
773 int r
= loc_network_tree_node_new(node
->ctx
, n
);
781 static struct loc_network_tree_node
* loc_network_tree_get_path(struct loc_network_tree
* tree
, const struct in6_addr
* address
, unsigned int prefix
) {
782 struct loc_network_tree_node
* node
= tree
->root
;
784 for (unsigned int i
= 0; i
< prefix
; i
++) {
785 // Check if the ith bit is one or zero
786 node
= loc_network_tree_get_node(node
, loc_address_get_bit(address
, i
));
792 static int __loc_network_tree_walk(struct loc_ctx
* ctx
, struct loc_network_tree_node
* node
,
793 int(*filter_callback
)(struct loc_network
* network
, void* data
),
794 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
797 // Finding a network ends the walk here
799 if (filter_callback
) {
800 int f
= filter_callback(node
->network
, data
);
804 // Skip network if filter function returns value greater than zero
809 r
= callback(node
->network
, data
);
814 // Walk down on the left side of the tree first
816 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
821 // Then walk on the other side
823 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
831 int loc_network_tree_walk(struct loc_network_tree
* tree
,
832 int(*filter_callback
)(struct loc_network
* network
, void* data
),
833 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
834 return __loc_network_tree_walk(tree
->ctx
, tree
->root
, filter_callback
, callback
, data
);
837 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
838 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
840 loc_network_tree_node_unref(tree
->root
);
842 loc_unref(tree
->ctx
);
846 struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
847 if (--tree
->refcount
> 0)
850 loc_network_tree_free(tree
);
854 static int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
855 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
857 char* s
= loc_network_str(network
);
861 INFO(network
->ctx
, "%s\n", s
);
867 int loc_network_tree_dump(struct loc_network_tree
* tree
) {
868 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
870 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
873 int loc_network_tree_add_network(struct loc_network_tree
* tree
, struct loc_network
* network
) {
874 DEBUG(tree
->ctx
, "Adding network %p to tree %p\n", network
, tree
);
876 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
,
877 &network
->first_address
, network
->prefix
);
879 ERROR(tree
->ctx
, "Could not find a node\n");
883 // Check if node has not been set before
885 DEBUG(tree
->ctx
, "There is already a network at this path\n");
889 // Point node to the network
890 node
->network
= loc_network_ref(network
);
895 static int __loc_network_tree_count(struct loc_network
* network
, void* data
) {
896 size_t* counter
= (size_t*)data
;
898 // Increase the counter for each network
904 size_t loc_network_tree_count_networks(struct loc_network_tree
* tree
) {
907 int r
= loc_network_tree_walk(tree
, NULL
, __loc_network_tree_count
, &counter
);
914 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node
* node
) {
918 counter
+= __loc_network_tree_count_nodes(node
->zero
);
921 counter
+= __loc_network_tree_count_nodes(node
->one
);
926 size_t loc_network_tree_count_nodes(struct loc_network_tree
* tree
) {
927 return __loc_network_tree_count_nodes(tree
->root
);
930 int loc_network_tree_node_new(struct loc_ctx
* ctx
, struct loc_network_tree_node
** node
) {
931 struct loc_network_tree_node
* n
= calloc(1, sizeof(*n
));
935 n
->ctx
= loc_ref(ctx
);
938 n
->zero
= n
->one
= NULL
;
940 DEBUG(n
->ctx
, "Network node allocated at %p\n", n
);
945 struct loc_network_tree_node
* loc_network_tree_node_ref(struct loc_network_tree_node
* node
) {
952 static void loc_network_tree_node_free(struct loc_network_tree_node
* node
) {
953 DEBUG(node
->ctx
, "Releasing network node at %p\n", node
);
956 loc_network_unref(node
->network
);
959 loc_network_tree_node_unref(node
->zero
);
962 loc_network_tree_node_unref(node
->one
);
964 loc_unref(node
->ctx
);
968 struct loc_network_tree_node
* loc_network_tree_node_unref(struct loc_network_tree_node
* node
) {
972 if (--node
->refcount
> 0)
975 loc_network_tree_node_free(node
);
979 struct loc_network_tree_node
* loc_network_tree_node_get(struct loc_network_tree_node
* node
, unsigned int index
) {
988 return loc_network_tree_node_ref(node
);
991 int loc_network_tree_node_is_leaf(struct loc_network_tree_node
* node
) {
992 return (!!node
->network
);
995 struct loc_network
* loc_network_tree_node_get_network(struct loc_network_tree_node
* node
) {
996 return loc_network_ref(node
->network
);