]>
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>
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
52 LOC_EXPORT
int loc_network_new(struct loc_ctx
* ctx
, struct loc_network
** network
,
53 struct in6_addr start_address
, unsigned int prefix
) {
54 // Address cannot be unspecified
55 if (IN6_IS_ADDR_UNSPECIFIED(&start_address
)) {
56 DEBUG(ctx
, "Start address is unspecified\n");
60 // Address cannot be loopback
61 if (IN6_IS_ADDR_LOOPBACK(&start_address
)) {
62 DEBUG(ctx
, "Start address is loopback address\n");
66 // Address cannot be link-local
67 if (IN6_IS_ADDR_LINKLOCAL(&start_address
)) {
68 DEBUG(ctx
, "Start address cannot be link-local\n");
72 // Address cannot be site-local
73 if (IN6_IS_ADDR_SITELOCAL(&start_address
)) {
74 DEBUG(ctx
, "Start address cannot be site-local\n");
78 // Validate the prefix
79 if (valid_prefix(&start_address
, prefix
) != 0) {
80 DEBUG(ctx
, "Invalid prefix: %u\n", prefix
);
84 struct loc_network
* n
= calloc(1, sizeof(*n
));
88 n
->ctx
= loc_ref(ctx
);
91 n
->start_address
= start_address
;
94 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
99 static int loc_network_address_family(struct loc_network
* network
) {
100 if (IN6_IS_ADDR_V4MAPPED(&network
->start_address
))
106 static int parse_address(struct loc_ctx
* ctx
, const char* string
, struct in6_addr
* address
) {
107 DEBUG(ctx
, "Paring IP address %s\n", string
);
109 // Try parsing this as an IPv6 address
110 int r
= inet_pton(AF_INET6
, string
, address
);
112 // If inet_pton returns one it has been successful
114 DEBUG(ctx
, "%s is an IPv6 address\n", string
);
118 // Try parsing this as an IPv4 address
119 struct in_addr ipv4_address
;
120 r
= inet_pton(AF_INET
, string
, &ipv4_address
);
122 DEBUG(ctx
, "%s is an IPv4 address\n", string
);
124 // Convert to IPv6-mapped address
125 address
->s6_addr32
[0] = htonl(0x0000);
126 address
->s6_addr32
[1] = htonl(0x0000);
127 address
->s6_addr32
[2] = htonl(0xffff);
128 address
->s6_addr32
[3] = ipv4_address
.s_addr
;
133 DEBUG(ctx
, "%s is not an valid IP address\n", string
);
137 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
, struct loc_network
** network
,
138 const char* address_string
) {
139 struct in6_addr start_address
;
140 unsigned int prefix
= 0;
144 // Make a copy of the string to work on it
145 char* buffer
= strdup(address_string
);
146 address_string
= prefix_string
= buffer
;
148 // Split address and prefix
149 address_string
= strsep(&prefix_string
, "/");
151 // Did we find a prefix?
153 // Convert prefix to integer
154 prefix
= strtol(prefix_string
, NULL
, 10);
158 r
= parse_address(ctx
, address_string
, &start_address
);
162 // Free temporary buffer
166 r
= loc_network_new(ctx
, network
, start_address
, prefix
);
172 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
178 static void loc_network_free(struct loc_network
* network
) {
179 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
181 loc_unref(network
->ctx
);
185 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
186 if (--network
->refcount
> 0)
189 loc_network_free(network
);
193 static int format_ipv6_address(struct loc_network
* network
, char* string
, size_t length
) {
194 const char* ret
= inet_ntop(AF_INET6
, &network
->start_address
, string
, length
);
201 static int format_ipv4_address(struct loc_network
* network
, char* string
, size_t length
) {
202 struct in_addr ipv4_address
;
203 ipv4_address
.s_addr
= network
->start_address
.s6_addr32
[3];
205 const char* ret
= inet_ntop(AF_INET
, &ipv4_address
, string
, length
);
212 LOC_EXPORT
char* loc_network_str(struct loc_network
* network
) {
214 const size_t length
= INET6_ADDRSTRLEN
+ 4;
216 char* string
= malloc(length
);
220 int family
= loc_network_address_family(network
);
223 r
= format_ipv6_address(network
, string
, length
);
227 r
= format_ipv4_address(network
, string
, length
);
236 ERROR(network
->ctx
, "Could not convert network to string: %s\n", strerror(errno
));
243 sprintf(string
+ strlen(string
), "/%u", network
->prefix
);
248 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
249 return network
->country_code
;
252 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
253 // Country codes must be two characters
254 if (strlen(country_code
) != 2)
257 for (unsigned int i
= 0; i
< 3; i
++) {
258 network
->country_code
[i
] = country_code
[i
];
264 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
268 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
274 LOC_EXPORT
int loc_network_to_database_v0(struct loc_network
* network
, struct loc_database_network_v0
* dbobj
) {
275 dbobj
->prefix
= htobe16(network
->prefix
);
278 for (unsigned int i
= 0; i
< 2; i
++) {
279 dbobj
->country_code
[i
] = network
->country_code
? network
->country_code
[i
] : '\0';
283 dbobj
->asn
= htobe32(network
->asn
);
288 struct loc_network_tree
{
292 struct loc_network_tree_node
* root
;
295 struct loc_network_tree_node
{
296 struct loc_network_tree_node
* zero
;
297 struct loc_network_tree_node
* one
;
299 struct loc_network
* network
;
302 LOC_EXPORT
int loc_network_tree_new(struct loc_ctx
* ctx
, struct loc_network_tree
** tree
) {
303 struct loc_network_tree
* t
= calloc(1, sizeof(*t
));
307 t
->ctx
= loc_ref(ctx
);
310 // Create the root node
311 t
->root
= calloc(1, sizeof(*t
->root
));
313 DEBUG(t
->ctx
, "Network tree allocated at %p\n", t
);
318 static int loc_network_tree_node_new(struct loc_network_tree_node
** node
) {
319 struct loc_network_tree_node
* n
= calloc(1, sizeof(*n
));
323 n
->zero
= n
->one
= NULL
;
329 static struct loc_network_tree_node
* loc_network_tree_get_node(struct loc_network_tree_node
* node
, int path
) {
330 struct loc_network_tree_node
** n
;
337 // If the desired node doesn't exist, yet, we will create it
339 int r
= loc_network_tree_node_new(n
);
347 static struct loc_network_tree_node
* loc_network_tree_get_path(struct loc_network_tree
* tree
, const struct in6_addr
* address
) {
348 struct loc_network_tree_node
* node
= tree
->root
;
350 for (unsigned int i
= 127; i
> 0; i
--) {
351 // Check if the ith bit is one or zero
352 node
= loc_network_tree_get_node(node
, ((address
->s6_addr32
[i
/ 32] & (1 << (i
% 32))) == 0));
358 static int __loc_network_tree_walk(struct loc_ctx
* ctx
, struct loc_network_tree_node
* node
,
359 int(*filter_callback
)(struct loc_network
* network
, void* data
),
360 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
363 // Finding a network ends the walk here
365 if (filter_callback
) {
366 int f
= filter_callback(node
->network
, data
);
370 // Skip network if filter function returns value greater than zero
375 r
= callback(node
->network
, data
);
380 // Walk down on the left side of the tree first
382 r
= __loc_network_tree_walk(ctx
, node
->zero
, filter_callback
, callback
, data
);
387 // Then walk on the other side
389 r
= __loc_network_tree_walk(ctx
, node
->one
, filter_callback
, callback
, data
);
397 LOC_EXPORT
int loc_network_tree_walk(struct loc_network_tree
* tree
,
398 int(*filter_callback
)(struct loc_network
* network
, void* data
),
399 int(*callback
)(struct loc_network
* network
, void* data
), void* data
) {
400 return __loc_network_tree_walk(tree
->ctx
, tree
->root
, filter_callback
, callback
, data
);
403 static void loc_network_tree_free_subtree(struct loc_network_tree_node
* node
) {
405 loc_network_unref(node
->network
);
408 loc_network_tree_free_subtree(node
->zero
);
411 loc_network_tree_free_subtree(node
->one
);
416 static void loc_network_tree_free(struct loc_network_tree
* tree
) {
417 DEBUG(tree
->ctx
, "Releasing network tree at %p\n", tree
);
419 loc_network_tree_free_subtree(tree
->root
);
421 loc_unref(tree
->ctx
);
425 LOC_EXPORT
struct loc_network_tree
* loc_network_tree_unref(struct loc_network_tree
* tree
) {
426 if (--tree
->refcount
> 0)
429 loc_network_tree_free(tree
);
433 int __loc_network_tree_dump(struct loc_network
* network
, void* data
) {
434 DEBUG(network
->ctx
, "Dumping network at %p\n", network
);
436 char* s
= loc_network_str(network
);
440 INFO(network
->ctx
, "%s\n", s
);
446 LOC_EXPORT
int loc_network_tree_dump(struct loc_network_tree
* tree
) {
447 DEBUG(tree
->ctx
, "Dumping network tree at %p\n", tree
);
449 return loc_network_tree_walk(tree
, NULL
, __loc_network_tree_dump
, NULL
);
452 LOC_EXPORT
int loc_network_tree_add_network(struct loc_network_tree
* tree
, struct loc_network
* network
) {
453 DEBUG(tree
->ctx
, "Adding network %p to tree %p\n", network
, tree
);
455 struct loc_network_tree_node
* node
= loc_network_tree_get_path(tree
, &network
->start_address
);
457 ERROR(tree
->ctx
, "Could not find a node\n");
461 // Check if node has not been set before
463 DEBUG(tree
->ctx
, "There is already a network at this path\n");
467 // Point node to the network
468 node
->network
= loc_network_ref(network
);
473 static int __loc_network_tree_count(struct loc_network
* network
, void* data
) {
474 size_t* counter
= (size_t*)data
;
476 // Increase the counter for each network
482 LOC_EXPORT
size_t loc_network_tree_count_networks(struct loc_network_tree
* tree
) {
485 int r
= loc_network_tree_walk(tree
, NULL
, __loc_network_tree_count
, &counter
);