]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
be88d758da62aae1f336d4b6fe7f244bab667107
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 <loc/libloc.h>
29 #include <loc/compat.h>
30 #include <loc/country.h>
31 #include <loc/network.h>
32 #include <loc/private.h>
39 struct in6_addr first_address
;
40 struct in6_addr last_address
;
45 enum loc_network_flags flags
;
48 static int valid_prefix(struct in6_addr
* address
, unsigned int prefix
) {
49 // The prefix cannot be larger than 128 bits
53 // And the prefix cannot be zero
57 // For IPv4-mapped addresses the prefix has to be 96 or lager
58 if (IN6_IS_ADDR_V4MAPPED(address
) && prefix
<= 96)
64 static struct in6_addr
prefix_to_bitmask(unsigned int prefix
) {
65 struct in6_addr bitmask
;
67 for (unsigned int i
= 0; i
< 16; i
++)
68 bitmask
.s6_addr
[i
] = 0;
70 for (int i
= prefix
, j
= 0; i
> 0; i
-= 8, j
++) {
72 bitmask
.s6_addr
[j
] = 0xff;
74 bitmask
.s6_addr
[j
] = 0xff << (8 - i
);
80 static struct in6_addr
make_first_address(const struct in6_addr
* address
, const struct in6_addr
* bitmask
) {
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
];
90 static struct in6_addr
make_last_address(const struct in6_addr
* address
, const struct in6_addr
* bitmask
) {
94 for (unsigned int i
= 0; i
< 4; i
++)
95 a
.s6_addr32
[i
] = address
->s6_addr32
[i
] | ~bitmask
->s6_addr32
[i
];
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");
108 // Address cannot be loopback
109 if (IN6_IS_ADDR_LOOPBACK(address
)) {
110 DEBUG(ctx
, "Start address is loopback address\n");
114 // Address cannot be link-local
115 if (IN6_IS_ADDR_LINKLOCAL(address
)) {
116 DEBUG(ctx
, "Start address cannot be link-local\n");
120 // Address cannot be site-local
121 if (IN6_IS_ADDR_SITELOCAL(address
)) {
122 DEBUG(ctx
, "Start address cannot be site-local\n");
126 // Validate the prefix
127 if (valid_prefix(address
, prefix
) != 0) {
128 DEBUG(ctx
, "Invalid prefix: %u\n", prefix
);
132 struct loc_network
* n
= calloc(1, sizeof(*n
));
136 n
->ctx
= loc_ref(ctx
);
142 // Convert the prefix into a bitmask
143 struct in6_addr bitmask
= prefix_to_bitmask(n
->prefix
);
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
);
150 if (IN6_IS_ADDR_V4MAPPED(&n
->first_address
))
153 n
->family
= AF_INET6
;
155 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
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
;
166 DEBUG(ctx
, "Attempting to parse network %s\n", address_string
);
168 // Make a copy of the string to work on it
169 char* buffer
= strdup(address_string
);
170 address_string
= prefix_string
= buffer
;
172 // Split address and prefix
173 address_string
= strsep(&prefix_string
, "/");
175 DEBUG(ctx
, " Split into address = %s, prefix = %s\n", address_string
, prefix_string
);
177 // We need to have a prefix
178 if (!prefix_string
) {
179 DEBUG(ctx
, "No prefix set\n");
183 // Convert prefix to integer
184 unsigned int prefix
= strtol(prefix_string
, NULL
, 10);
186 // End if the prefix was invalid
188 DEBUG(ctx
, "The prefix is zero or not a number\n");
193 r
= loc_parse_address(ctx
, address_string
, &first_address
);
195 DEBUG(ctx
, "The address could not be parsed\n");
199 // Map the prefix to IPv6 if needed
200 if (IN6_IS_ADDR_V4MAPPED(&first_address
))
204 // Free temporary buffer
207 // Exit if the parsing was unsuccessful
211 // Create a new network
212 return loc_network_new(ctx
, network
, &first_address
, prefix
);
215 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
221 static void loc_network_free(struct loc_network
* network
) {
222 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
224 loc_unref(network
->ctx
);
228 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
232 if (--network
->refcount
> 0)
235 loc_network_free(network
);
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
);
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];
251 const char* ret
= inet_ntop(AF_INET
, &ipv4_address
, string
, length
);
258 LOC_EXPORT
char* loc_network_str(struct loc_network
* network
) {
260 const size_t length
= INET6_ADDRSTRLEN
+ 4;
262 char* string
= malloc(length
);
266 unsigned int prefix
= network
->prefix
;
268 switch (network
->family
) {
270 r
= format_ipv6_address(&network
->first_address
, string
, length
);
274 r
= format_ipv4_address(&network
->first_address
, string
, length
);
284 ERROR(network
->ctx
, "Could not convert network to string: %s\n", strerror(errno
));
291 sprintf(string
+ strlen(string
), "/%u", prefix
);
296 LOC_EXPORT
int loc_network_address_family(struct loc_network
* network
) {
297 return network
->family
;
300 static char* loc_network_format_address(struct loc_network
* network
, const struct in6_addr
* address
) {
301 const size_t length
= INET6_ADDRSTRLEN
;
303 char* string
= malloc(length
);
309 switch (network
->family
) {
311 r
= format_ipv6_address(address
, string
, length
);
315 r
= format_ipv4_address(address
, string
, length
);
324 ERROR(network
->ctx
, "Could not format IP address to string: %s\n", strerror(errno
));
333 LOC_EXPORT
char* loc_network_format_first_address(struct loc_network
* network
) {
334 return loc_network_format_address(network
, &network
->first_address
);
337 LOC_EXPORT
char* loc_network_format_last_address(struct loc_network
* network
) {
338 return loc_network_format_address(network
, &network
->last_address
);
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)
346 // Address must be smaller than the last address
347 if (in6_addr_cmp(&network
->last_address
, address
) < 0)
350 // The address is inside this network
354 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
355 return network
->country_code
;
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';
365 // Check country code
366 if (!loc_country_code_is_valid(country_code
))
369 loc_country_code_copy(network
->country_code
, country_code
);
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
))
379 return (network
->country_code
[0] == country_code
[0])
380 && (network
->country_code
[1] == country_code
[1]);
383 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
387 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
393 LOC_EXPORT
int loc_network_match_asn(struct loc_network
* network
, uint32_t asn
) {
394 return network
->asn
== asn
;
397 LOC_EXPORT
int loc_network_has_flag(struct loc_network
* network
, uint32_t flag
) {
398 return network
->flags
& flag
;
401 LOC_EXPORT
int loc_network_set_flag(struct loc_network
* network
, uint32_t flag
) {
402 network
->flags
|= flag
;
407 LOC_EXPORT
int loc_network_match_flag(struct loc_network
* network
, uint32_t flag
) {
408 return loc_network_has_flag(network
, flag
);
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)
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)
425 LOC_EXPORT
int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
427 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
430 dbobj
->asn
= htobe32(network
->asn
);
433 dbobj
->flags
= htobe16(network
->flags
);
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";
442 int r
= loc_network_new(ctx
, network
, address
, prefix
);
444 ERROR(ctx
, "Could not allocate a new network: %s", strerror(-r
));
448 // Import country code
449 loc_country_code_copy(country_code
, dbobj
->country_code
);
451 r
= loc_network_set_country_code(*network
, country_code
);
453 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
458 uint32_t asn
= be32toh(dbobj
->asn
);
459 r
= loc_network_set_asn(*network
, asn
);
461 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
466 int flags
= be16toh(dbobj
->flags
);
467 r
= loc_network_set_flag(*network
, flags
);
469 ERROR(ctx
, "Could not set flags: %d\n", flags
);
476 struct loc_network_tree
{
480 struct loc_network_tree_node
* root
;
483 struct loc_network_tree_node
{
487 struct loc_network_tree_node
* zero
;
488 struct loc_network_tree_node
* one
;
490 struct loc_network
* network
;
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
));
498 t
->ctx
= loc_ref(ctx
);
501 // Create the root node
502 int r
= loc_network_tree_node_new(ctx
, &t
->root
);
504 loc_network_tree_unref(t
);
508 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
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
);
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
;
525 // If the desired node doesn't exist, yet, we will create it
527 int r
= loc_network_tree_node_new(node
->ctx
, n
);
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
;
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
));
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
) {
551 // Finding a network ends the walk here
553 if (filter_callback
) {
554 int f
= filter_callback(node
->network
, data
);
558 // Skip network if filter function returns value greater than zero
563 r
= callback(node
->network
, data
);
568 // Walk down on the left side of the tree first
570 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
575 // Then walk on the other side
577 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
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
);
591 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
592 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
594 loc_network_tree_node_unref(tree
->root
);
596 loc_unref(tree
->ctx
);
600 LOC_EXPORT
struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
601 if (--tree
->refcount
> 0)
604 loc_network_tree_free(tree
);
608 static int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
609 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
611 char* s
= loc_network_str(network
);
615 INFO(network
->ctx
, "%s\n", s
);
621 LOC_EXPORT
int loc_network_tree_dump(struct loc_network_tree
* tree
) {
622 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
624 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
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
);
630 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
,
631 &network
->first_address
, network
->prefix
);
633 ERROR(tree
->ctx
, "Could not find a node\n");
637 // Check if node has not been set before
639 DEBUG(tree
->ctx
, "There is already a network at this path\n");
643 // Point node to the network
644 node
->network
= loc_network_ref(network
);
649 static int __loc_network_tree_count(struct loc_network
* network
, void* data
) {
650 size_t* counter
= (size_t*)data
;
652 // Increase the counter for each network
658 LOC_EXPORT
size_t loc_network_tree_count_networks(struct loc_network_tree
* tree
) {
661 int r
= loc_network_tree_walk(tree
, NULL
, __loc_network_tree_count
, &counter
);
668 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node
* node
) {
672 counter
+= __loc_network_tree_count_nodes(node
->zero
);
675 counter
+= __loc_network_tree_count_nodes(node
->one
);
680 LOC_EXPORT
size_t loc_network_tree_count_nodes(struct loc_network_tree
* tree
) {
681 return __loc_network_tree_count_nodes(tree
->root
);
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
));
689 n
->ctx
= loc_ref(ctx
);
692 n
->zero
= n
->one
= NULL
;
694 DEBUG(n
->ctx
, "Network node allocated at %p\n", n
);
699 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_ref(struct loc_network_tree_node
* node
) {
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
);
710 loc_network_unref(node
->network
);
713 loc_network_tree_node_unref(node
->zero
);
716 loc_network_tree_node_unref(node
->one
);
718 loc_unref(node
->ctx
);
722 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_unref(struct loc_network_tree_node
* node
) {
726 if (--node
->refcount
> 0)
729 loc_network_tree_node_free(node
);
733 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_get(struct loc_network_tree_node
* node
, unsigned int index
) {
742 return loc_network_tree_node_ref(node
);
745 LOC_EXPORT
int loc_network_tree_node_is_leaf(struct loc_network_tree_node
* node
) {
746 return (!!node
->network
);
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
);