]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
73a299fe29a604d9cc7d672bbb53d37da1e0226a
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>
25 #include <loc/libloc.h>
26 #include <loc/network.h>
27 #include <loc/private.h>
33 struct in6_addr start_address
;
40 static int valid_prefix(struct in6_addr
* address
, unsigned int prefix
) {
41 // The prefix cannot be larger than 128 bits
45 // And the prefix cannot be zero
49 // For IPv4-mapped addresses the prefix has to be 96 or lager
50 if (IN6_IS_ADDR_V4MAPPED(address
) && prefix
<= 96)
56 static struct in6_addr
prefix_to_bitmask(unsigned int prefix
) {
57 struct in6_addr bitmask
;
59 for (unsigned int i
= 0; i
< 16; i
++)
60 bitmask
.s6_addr
[i
] = 0;
62 for (unsigned int i
= prefix
, j
= 0; i
> 0; i
-= 8, j
++) {
64 bitmask
.s6_addr
[j
] = 0xff;
66 bitmask
.s6_addr
[j
] = 0xff << (8 - i
);
72 static struct in6_addr
make_start_address(const struct in6_addr
* address
, unsigned int prefix
) {
74 struct in6_addr bitmask
= prefix_to_bitmask(prefix
);
76 // Perform bitwise AND
77 for (unsigned int i
= 0; i
< 4; i
++)
78 a
.s6_addr32
[i
] = address
->s6_addr32
[i
] & bitmask
.s6_addr32
[i
];
83 static struct in6_addr
make_last_address(const struct in6_addr
* address
, unsigned int prefix
) {
85 struct in6_addr bitmask
= prefix_to_bitmask(prefix
);
88 for (unsigned int i
= 0; i
< 4; i
++)
89 a
.s6_addr32
[i
] = address
->s6_addr
[i
] | ~bitmask
.s6_addr32
[i
];
94 LOC_EXPORT
int loc_network_new(struct loc_ctx
* ctx
, struct loc_network
** network
,
95 struct in6_addr
* address
, unsigned int prefix
) {
96 // Address cannot be unspecified
97 if (IN6_IS_ADDR_UNSPECIFIED(&address
)) {
98 DEBUG(ctx
, "Start address is unspecified\n");
102 // Address cannot be loopback
103 if (IN6_IS_ADDR_LOOPBACK(&address
)) {
104 DEBUG(ctx
, "Start address is loopback address\n");
108 // Address cannot be link-local
109 if (IN6_IS_ADDR_LINKLOCAL(&address
)) {
110 DEBUG(ctx
, "Start address cannot be link-local\n");
114 // Address cannot be site-local
115 if (IN6_IS_ADDR_SITELOCAL(&address
)) {
116 DEBUG(ctx
, "Start address cannot be site-local\n");
120 // Validate the prefix
121 if (valid_prefix(address
, prefix
) != 0) {
122 DEBUG(ctx
, "Invalid prefix: %u\n", prefix
);
126 struct loc_network
* n
= calloc(1, sizeof(*n
));
130 n
->ctx
= loc_ref(ctx
);
133 // Store the first address in the network
134 n
->start_address
= make_start_address(address
, prefix
);
137 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
142 static int loc_network_address_family(struct loc_network
* network
) {
143 if (IN6_IS_ADDR_V4MAPPED(&network
->start_address
))
149 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
, struct loc_network
** network
,
150 const char* address_string
) {
151 struct in6_addr start_address
;
152 unsigned int prefix
= 0;
156 // Make a copy of the string to work on it
157 char* buffer
= strdup(address_string
);
158 address_string
= prefix_string
= buffer
;
160 // Split address and prefix
161 address_string
= strsep(&prefix_string
, "/");
163 // Did we find a prefix?
165 // Convert prefix to integer
166 prefix
= strtol(prefix_string
, NULL
, 10);
170 r
= loc_parse_address(ctx
, address_string
, &start_address
);
172 // Map the prefix to IPv6 if needed
173 if (IN6_IS_ADDR_V4MAPPED(&start_address
))
178 // Free temporary buffer
182 r
= loc_network_new(ctx
, network
, &start_address
, prefix
);
188 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
194 static void loc_network_free(struct loc_network
* network
) {
195 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
197 loc_unref(network
->ctx
);
201 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
205 if (--network
->refcount
> 0)
208 loc_network_free(network
);
212 static int format_ipv6_address(struct loc_network
* network
, char* string
, size_t length
) {
213 const char* ret
= inet_ntop(AF_INET6
, &network
->start_address
, string
, length
);
220 static int format_ipv4_address(struct loc_network
* network
, char* string
, size_t length
) {
221 struct in_addr ipv4_address
;
222 ipv4_address
.s_addr
= network
->start_address
.s6_addr32
[3];
224 const char* ret
= inet_ntop(AF_INET
, &ipv4_address
, string
, length
);
231 LOC_EXPORT
char* loc_network_str(struct loc_network
* network
) {
233 const size_t length
= INET6_ADDRSTRLEN
+ 4;
235 char* string
= malloc(length
);
239 unsigned int prefix
= network
->prefix
;
241 int family
= loc_network_address_family(network
);
244 r
= format_ipv6_address(network
, string
, length
);
248 r
= format_ipv4_address(network
, string
, length
);
258 ERROR(network
->ctx
, "Could not convert network to string: %s\n", strerror(errno
));
265 sprintf(string
+ strlen(string
), "/%u", prefix
);
270 LOC_EXPORT
int loc_network_match_address(struct loc_network
* network
, const struct in6_addr
* address
) {
271 // Address must be larger then the start address
272 if (in6_addr_cmp(&network
->start_address
, address
) > 0)
275 // Determine the last address in this network
276 struct in6_addr last_address
= make_last_address(&network
->start_address
, network
->prefix
);
278 // Address must be smaller than the last address
279 if (in6_addr_cmp(&last_address
, address
) > 0)
282 // The address is inside this network
286 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
287 return network
->country_code
;
290 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
291 // Country codes must be two characters
292 if (strlen(country_code
) != 2)
295 for (unsigned int i
= 0; i
< 3; i
++) {
296 network
->country_code
[i
] = country_code
[i
];
302 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
306 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
312 LOC_EXPORT
int loc_network_to_database_v0(struct loc_network
* network
, struct loc_database_network_v0
* dbobj
) {
313 dbobj
->prefix
= network
->prefix
;
316 for (unsigned int i
= 0; i
< 2; i
++) {
317 dbobj
->country_code
[i
] = network
->country_code
? network
->country_code
[i
] : '\0';
321 dbobj
->asn
= htobe32(network
->asn
);
326 LOC_EXPORT
int loc_network_new_from_database_v0(struct loc_ctx
* ctx
, struct loc_network
** network
,
327 struct in6_addr
* address
, const struct loc_database_network_v0
* dbobj
) {
328 int r
= loc_network_new(ctx
, network
, address
, dbobj
->prefix
);
332 // Import country code
333 char country_code
[3];
334 for (unsigned int i
= 0; i
< 2; i
++) {
335 country_code
[i
] = dbobj
->country_code
[i
];
337 country_code
[2] = '\0';
339 r
= loc_network_set_country_code(*network
, country_code
);
344 r
= loc_network_set_asn(*network
, be32toh(dbobj
->asn
));
351 struct loc_network_tree
{
355 struct loc_network_tree_node
* root
;
358 struct loc_network_tree_node
{
362 struct loc_network_tree_node
* zero
;
363 struct loc_network_tree_node
* one
;
365 struct loc_network
* network
;
368 LOC_EXPORT
int loc_network_tree_new(struct loc_ctx
* ctx
, struct loc_network_tree
** tree
) {
369 struct loc_network_tree
* t
= calloc(1, sizeof(*t
));
373 t
->ctx
= loc_ref(ctx
);
376 // Create the root node
377 int r
= loc_network_tree_node_new(ctx
, &t
->root
);
379 loc_network_tree_unref(t
);
383 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
388 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_get_root(struct loc_network_tree
* tree
) {
389 return loc_network_tree_node_ref(tree
->root
);
392 static struct loc_network_tree_node
* loc_network_tree_get_node(struct loc_network_tree_node
* node
, int path
) {
393 struct loc_network_tree_node
** n
;
400 // If the desired node doesn't exist, yet, we will create it
402 int r
= loc_network_tree_node_new(node
->ctx
, n
);
410 static struct loc_network_tree_node
* loc_network_tree_get_path(struct loc_network_tree
* tree
, const struct in6_addr
* address
) {
411 struct loc_network_tree_node
* node
= tree
->root
;
413 for (unsigned int i
= 0; i
< 128; i
++) {
414 // Check if the ith bit is one or zero
415 node
= loc_network_tree_get_node(node
, in6_addr_get_bit(address
, i
));
421 static int __loc_network_tree_walk(struct loc_ctx
* ctx
, struct loc_network_tree_node
* node
,
422 int(*filter_callback
)(struct loc_network
* network
, void* data
),
423 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
426 // Finding a network ends the walk here
428 if (filter_callback
) {
429 int f
= filter_callback(node
->network
, data
);
433 // Skip network if filter function returns value greater than zero
438 r
= callback(node
->network
, data
);
443 // Walk down on the left side of the tree first
445 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
450 // Then walk on the other side
452 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
460 LOC_EXPORT
int loc_network_tree_walk(struct loc_network_tree
* tree
,
461 int(*filter_callback
)(struct loc_network
* network
, void* data
),
462 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
463 return __loc_network_tree_walk(tree
->ctx
, tree
->root
, filter_callback
, callback
, data
);
466 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
467 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
469 loc_network_tree_node_unref(tree
->root
);
471 loc_unref(tree
->ctx
);
475 LOC_EXPORT
struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
476 if (--tree
->refcount
> 0)
479 loc_network_tree_free(tree
);
483 static int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
484 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
486 char* s
= loc_network_str(network
);
490 INFO(network
->ctx
, "%s\n", s
);
496 LOC_EXPORT
int loc_network_tree_dump(struct loc_network_tree
* tree
) {
497 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
499 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
502 LOC_EXPORT
int loc_network_tree_add_network(struct loc_network_tree
* tree
, struct loc_network
* network
) {
503 DEBUG(tree
->ctx
, "Adding network %p to tree %p\n", network
, tree
);
505 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
, &network
->start_address
);
507 ERROR(tree
->ctx
, "Could not find a node\n");
511 // Check if node has not been set before
513 DEBUG(tree
->ctx
, "There is already a network at this path\n");
517 // Point node to the network
518 node
->network
= loc_network_ref(network
);
523 static int __loc_network_tree_count(struct loc_network
* network
, void* data
) {
524 size_t* counter
= (size_t*)data
;
526 // Increase the counter for each network
532 LOC_EXPORT
size_t loc_network_tree_count_networks(struct loc_network_tree
* tree
) {
535 int r
= loc_network_tree_walk(tree
, NULL
, __loc_network_tree_count
, &counter
);
542 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node
* node
) {
546 counter
+= __loc_network_tree_count_nodes(node
->zero
);
549 counter
+= __loc_network_tree_count_nodes(node
->one
);
554 LOC_EXPORT
size_t loc_network_tree_count_nodes(struct loc_network_tree
* tree
) {
555 return __loc_network_tree_count_nodes(tree
->root
);
558 LOC_EXPORT
int loc_network_tree_node_new(struct loc_ctx
* ctx
, struct loc_network_tree_node
** node
) {
559 struct loc_network_tree_node
* n
= calloc(1, sizeof(*n
));
563 n
->ctx
= loc_ref(ctx
);
566 n
->zero
= n
->one
= NULL
;
568 DEBUG(n
->ctx
, "Network node allocated at %p\n", n
);
573 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_ref(struct loc_network_tree_node
* node
) {
580 static void loc_network_tree_node_free(struct loc_network_tree_node
* node
) {
581 DEBUG(node
->ctx
, "Releasing network node at %p\n", node
);
584 loc_network_unref(node
->network
);
587 loc_network_tree_node_unref(node
->zero
);
590 loc_network_tree_node_unref(node
->one
);
592 loc_unref(node
->ctx
);
596 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_unref(struct loc_network_tree_node
* node
) {
600 if (--node
->refcount
> 0)
603 loc_network_tree_node_free(node
);
607 LOC_EXPORT
struct loc_network_tree_node
* loc_network_tree_node_get(struct loc_network_tree_node
* node
, unsigned int index
) {
616 return loc_network_tree_node_ref(node
);
619 LOC_EXPORT
int loc_network_tree_node_is_leaf(struct loc_network_tree_node
* node
) {
620 return (!!node
->network
);
623 LOC_EXPORT
struct loc_network
* loc_network_tree_node_get_network(struct loc_network_tree_node
* node
) {
624 return loc_network_ref(node
->network
);