]>
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 struct loc_network
* n
= NULL
;
56 // Validate the prefix
57 if (!loc_address_valid_prefix(address
, prefix
)) {
58 ERROR(ctx
, "Invalid prefix in %s: %u\n", loc_address_str(address
), prefix
);
63 // Allocate a new network
64 n
= calloc(1, sizeof(*n
));
68 n
->ctx
= loc_ref(ctx
);
72 if (IN6_IS_ADDR_V4MAPPED(address
))
73 n
->prefix
= prefix
+ 96;
77 // Convert the prefix into a bitmask
78 const struct in6_addr bitmask
= loc_prefix_to_bitmask(n
->prefix
);
80 // Store the first and last address in the network
81 n
->first_address
= loc_address_and(address
, &bitmask
);
82 n
->last_address
= loc_address_or(&n
->first_address
, &bitmask
);
85 n
->family
= loc_address_family(&n
->first_address
);
87 DEBUG(n
->ctx
, "Network allocated at %p\n", n
);
92 LOC_EXPORT
int loc_network_new_from_string(struct loc_ctx
* ctx
,
93 struct loc_network
** network
, const char* string
) {
94 struct in6_addr address
;
98 int r
= loc_address_parse(&address
, &prefix
, string
);
100 ERROR(ctx
, "Could not parse network %s: %m\n", string
);
104 // Create a new network
105 return loc_network_new(ctx
, network
, &address
, prefix
);
108 LOC_EXPORT
struct loc_network
* loc_network_ref(struct loc_network
* network
) {
114 static void loc_network_free(struct loc_network
* network
) {
115 DEBUG(network
->ctx
, "Releasing network at %p\n", network
);
117 loc_unref(network
->ctx
);
121 LOC_EXPORT
struct loc_network
* loc_network_unref(struct loc_network
* network
) {
122 if (--network
->refcount
> 0)
125 loc_network_free(network
);
129 LOC_EXPORT
const char* loc_network_str(struct loc_network
* network
) {
130 if (!*network
->string
) {
131 // Format the address
132 const char* address
= loc_address_str(&network
->first_address
);
137 unsigned int prefix
= loc_network_prefix(network
);
140 int r
= snprintf(network
->string
, sizeof(network
->string
) - 1,
141 "%s/%u", address
, prefix
);
143 ERROR(network
->ctx
, "Could not format network string: %m\n");
144 *network
->string
= '\0';
149 return network
->string
;
152 LOC_EXPORT
int loc_network_address_family(struct loc_network
* network
) {
153 return network
->family
;
156 LOC_EXPORT
unsigned int loc_network_prefix(struct loc_network
* network
) {
157 switch (network
->family
) {
159 return network
->prefix
;
162 return network
->prefix
- 96;
168 unsigned int loc_network_raw_prefix(struct loc_network
* network
) {
169 return network
->prefix
;
172 LOC_EXPORT
const struct in6_addr
* loc_network_get_first_address(struct loc_network
* network
) {
173 return &network
->first_address
;
176 LOC_EXPORT
const char* loc_network_format_first_address(struct loc_network
* network
) {
177 return loc_address_str(&network
->first_address
);
180 LOC_EXPORT
const struct in6_addr
* loc_network_get_last_address(struct loc_network
* network
) {
181 return &network
->last_address
;
184 LOC_EXPORT
const char* loc_network_format_last_address(struct loc_network
* network
) {
185 return loc_address_str(&network
->last_address
);
188 LOC_EXPORT
int loc_network_matches_address(struct loc_network
* network
, const struct in6_addr
* address
) {
189 // Address must be larger than the start address
190 if (loc_address_cmp(&network
->first_address
, address
) > 0)
193 // Address must be smaller than the last address
194 if (loc_address_cmp(&network
->last_address
, address
) < 0)
197 // The address is inside this network
201 LOC_EXPORT
const char* loc_network_get_country_code(struct loc_network
* network
) {
202 return network
->country_code
;
205 LOC_EXPORT
int loc_network_set_country_code(struct loc_network
* network
, const char* country_code
) {
206 // Set empty country code
207 if (!country_code
|| !*country_code
) {
208 *network
->country_code
= '\0';
212 // Check country code
213 if (!loc_country_code_is_valid(country_code
))
216 loc_country_code_copy(network
->country_code
, country_code
);
221 LOC_EXPORT
int loc_network_matches_country_code(struct loc_network
* network
, const char* country_code
) {
222 // Search for any special flags
223 const int flag
= loc_country_special_code_to_flag(country_code
);
225 // If we found a flag, we will return whether it is set or not
227 return loc_network_has_flag(network
, flag
);
229 // Check country code
230 if (!loc_country_code_is_valid(country_code
))
233 // Check for an exact match
234 return (network
->country_code
[0] == country_code
[0])
235 && (network
->country_code
[1] == country_code
[1]);
238 LOC_EXPORT
uint32_t loc_network_get_asn(struct loc_network
* network
) {
242 LOC_EXPORT
int loc_network_set_asn(struct loc_network
* network
, uint32_t asn
) {
248 LOC_EXPORT
int loc_network_has_flag(struct loc_network
* network
, uint32_t flag
) {
249 return network
->flags
& flag
;
252 LOC_EXPORT
int loc_network_set_flag(struct loc_network
* network
, uint32_t flag
) {
253 network
->flags
|= flag
;
258 LOC_EXPORT
int loc_network_cmp(struct loc_network
* self
, struct loc_network
* other
) {
260 int r
= loc_address_cmp(&self
->first_address
, &other
->first_address
);
265 if (self
->prefix
> other
->prefix
)
267 else if (self
->prefix
< other
->prefix
)
270 // Both networks are equal
274 int loc_network_properties_cmp(struct loc_network
* self
, struct loc_network
* other
) {
277 // Check country code
278 r
= loc_country_code_cmp(self
->country_code
, other
->country_code
);
283 if (self
->asn
> other
->asn
)
285 else if (self
->asn
< other
->asn
)
289 if (self
->flags
> other
->flags
)
291 else if (self
->flags
< other
->flags
)
297 LOC_EXPORT
int loc_network_overlaps(struct loc_network
* self
, struct loc_network
* other
) {
298 // Either of the start addresses must be in the other subnet
299 if (loc_network_matches_address(self
, &other
->first_address
))
302 if (loc_network_matches_address(other
, &self
->first_address
))
305 // Or either of the end addresses is in the other subnet
306 if (loc_network_matches_address(self
, &other
->last_address
))
309 if (loc_network_matches_address(other
, &self
->last_address
))
315 LOC_EXPORT
int loc_network_is_subnet(struct loc_network
* self
, struct loc_network
* other
) {
316 // The prefix must be smaller (this avoids the more complex comparisons later)
317 if (self
->prefix
> other
->prefix
)
320 // If the start address of the other network is smaller than this network,
321 // it cannot be a subnet.
322 if (loc_address_cmp(&self
->first_address
, &other
->first_address
) > 0)
325 // If the end address of the other network is greater than this network,
326 // it cannot be a subnet.
327 if (loc_address_cmp(&self
->last_address
, &other
->last_address
) < 0)
333 LOC_EXPORT
int loc_network_subnets(struct loc_network
* network
,
334 struct loc_network
** subnet1
, struct loc_network
** subnet2
) {
340 unsigned int prefix
= loc_network_prefix(network
) + 1;
342 // Check if the new prefix is valid
343 if (!loc_address_valid_prefix(&network
->first_address
, prefix
)) {
344 ERROR(network
->ctx
, "Invalid prefix: %d\n", prefix
);
349 // Create the first half of the network
350 r
= loc_network_new(network
->ctx
, subnet1
, &network
->first_address
, prefix
);
354 // The next subnet starts after the first one
355 struct in6_addr first_address
= (*subnet1
)->last_address
;
356 loc_address_increment(&first_address
);
358 // Create the second half of the network
359 r
= loc_network_new(network
->ctx
, subnet2
, &first_address
, prefix
);
364 const char* country_code
= loc_network_get_country_code(network
);
366 loc_network_set_country_code(*subnet1
, country_code
);
367 loc_network_set_country_code(*subnet2
, country_code
);
371 uint32_t asn
= loc_network_get_asn(network
);
373 loc_network_set_asn(*subnet1
, asn
);
374 loc_network_set_asn(*subnet2
, asn
);
378 loc_network_set_flag(*subnet1
, network
->flags
);
379 loc_network_set_flag(*subnet2
, network
->flags
);
384 static int __loc_network_exclude(struct loc_network
* network
,
385 struct loc_network
* other
, struct loc_network_list
* list
) {
386 struct loc_network
* subnet1
= NULL
;
387 struct loc_network
* subnet2
= NULL
;
389 int r
= loc_network_subnets(network
, &subnet1
, &subnet2
);
393 if (loc_network_cmp(other
, subnet1
) == 0) {
394 r
= loc_network_list_push(list
, subnet2
);
398 } else if (loc_network_cmp(other
, subnet2
) == 0) {
399 r
= loc_network_list_push(list
, subnet1
);
403 } else if (loc_network_is_subnet(subnet1
, other
)) {
404 r
= loc_network_list_push(list
, subnet2
);
408 r
= __loc_network_exclude(subnet1
, other
, list
);
412 } else if (loc_network_is_subnet(subnet2
, other
)) {
413 r
= loc_network_list_push(list
, subnet1
);
417 r
= __loc_network_exclude(subnet2
, other
, list
);
422 ERROR(network
->ctx
, "We should never get here\n");
429 loc_network_unref(subnet1
);
432 loc_network_unref(subnet2
);
435 DEBUG(network
->ctx
, "%s has failed with %d\n", __FUNCTION__
, r
);
440 static int __loc_network_exclude_to_list(struct loc_network
* self
,
441 struct loc_network
* other
, struct loc_network_list
* list
) {
442 // Other must be a subnet of self
443 if (!loc_network_is_subnet(self
, other
)) {
444 DEBUG(self
->ctx
, "Network %p is not contained in network %p\n", other
, self
);
450 // We cannot perform this operation if both networks equal
451 if (loc_network_cmp(self
, other
) == 0) {
452 DEBUG(self
->ctx
, "Networks %p and %p are equal\n", self
, other
);
458 return __loc_network_exclude(self
, other
, list
);
461 LOC_EXPORT
struct loc_network_list
* loc_network_exclude(
462 struct loc_network
* self
, struct loc_network
* other
) {
463 struct loc_network_list
* list
;
465 DEBUG(self
->ctx
, "Returning %s excluding %s...\n",
466 loc_network_str(self
), loc_network_str(other
));
468 // Create a new list with the result
469 int r
= loc_network_list_new(self
->ctx
, &list
);
471 ERROR(self
->ctx
, "Could not create network list: %d\n", r
);
476 r
= __loc_network_exclude_to_list(self
, other
, list
);
478 loc_network_list_unref(list
);
487 LOC_EXPORT
struct loc_network_list
* loc_network_exclude_list(
488 struct loc_network
* network
, struct loc_network_list
* list
) {
489 struct loc_network_list
* to_check
;
491 // Create a new list with all networks to look at
492 int r
= loc_network_list_new(network
->ctx
, &to_check
);
496 struct loc_network
* subnet
= NULL
;
497 struct loc_network_list
* subnets
= NULL
;
499 for (unsigned int i
= 0; i
< loc_network_list_size(list
); i
++) {
500 subnet
= loc_network_list_get(list
, i
);
502 // Find all excluded networks
503 if (!loc_network_list_contains(to_check
, subnet
)) {
504 r
= __loc_network_exclude_to_list(network
, subnet
, to_check
);
506 loc_network_list_unref(to_check
);
507 loc_network_unref(subnet
);
514 loc_network_unref(subnet
);
517 r
= loc_network_list_new(network
->ctx
, &subnets
);
519 loc_network_list_unref(to_check
);
523 off_t smallest_subnet
= 0;
525 while (!loc_network_list_empty(to_check
)) {
526 struct loc_network
* subnet_to_check
= loc_network_list_pop_first(to_check
);
528 // Check whether the subnet to check is part of the input list
529 if (loc_network_list_contains(list
, subnet_to_check
)) {
530 loc_network_unref(subnet_to_check
);
534 // Marks whether this subnet passed all checks
537 for (unsigned int i
= smallest_subnet
; i
< loc_network_list_size(list
); i
++) {
538 subnet
= loc_network_list_get(list
, i
);
540 // Drop this subnet if is a subnet of another subnet
541 if (loc_network_is_subnet(subnet
, subnet_to_check
)) {
543 loc_network_unref(subnet
);
547 // Break it down if it overlaps
548 if (loc_network_overlaps(subnet
, subnet_to_check
)) {
551 __loc_network_exclude_to_list(subnet_to_check
, subnet
, to_check
);
553 loc_network_unref(subnet
);
557 // If the subnet is strictly greater, we do not need to continue the search
558 r
= loc_network_cmp(subnet
, subnet_to_check
);
560 loc_network_unref(subnet
);
563 // If it is strictly smaller, we can continue the search from here next
564 // time because all networks that are to be checked can only be larger
570 loc_network_unref(subnet
);
574 r
= loc_network_list_push(subnets
, subnet_to_check
);
577 loc_network_unref(subnet_to_check
);
580 loc_network_list_unref(to_check
);
585 int loc_network_merge(struct loc_network
** n
,
586 struct loc_network
* n1
, struct loc_network
* n2
) {
587 struct loc_network
* network
= NULL
;
588 struct in6_addr address
;
594 DEBUG(n1
->ctx
, "Attempting to merge %s and %s\n", loc_network_str(n1
), loc_network_str(n2
));
597 if (n1
->family
!= n2
->family
)
600 // The prefix must match, too
601 if (n1
->prefix
!= n2
->prefix
)
604 // Cannot merge ::/0 or 0.0.0.0/0
605 if (!n1
->prefix
|| !n2
->prefix
)
608 const unsigned int prefix
= loc_network_prefix(n1
);
610 // How many bits do we need to represent this address?
611 const size_t bitlength
= loc_address_bit_length(&n1
->first_address
) - 1;
613 // We cannot shorten this any more
614 if (bitlength
< prefix
)
617 // Increment the last address of the first network
618 address
= n1
->last_address
;
619 loc_address_increment(&address
);
621 // If they don't match they are not neighbours
622 if (loc_address_cmp(&address
, &n2
->first_address
) != 0)
625 // All properties must match, too
626 if (loc_network_properties_cmp(n1
, n2
) != 0)
629 // Create a new network object
630 r
= loc_network_new(n1
->ctx
, &network
, &n1
->first_address
, prefix
- 1);
634 // Copy everything else
635 loc_country_code_copy(network
->country_code
, n1
->country_code
);
636 network
->asn
= n1
->asn
;
637 network
->flags
= n1
->flags
;
645 int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
647 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
650 dbobj
->asn
= htobe32(network
->asn
);
653 dbobj
->flags
= htobe16(network
->flags
);
658 int loc_network_new_from_database_v1(struct loc_ctx
* ctx
, struct loc_network
** network
,
659 struct in6_addr
* address
, unsigned int prefix
, const struct loc_database_network_v1
* dbobj
) {
660 char country_code
[3] = "\0\0";
662 // Adjust prefix for IPv4
663 if (IN6_IS_ADDR_V4MAPPED(address
))
666 int r
= loc_network_new(ctx
, network
, address
, prefix
);
668 ERROR(ctx
, "Could not allocate a new network: %m\n");
672 // Import country code
673 loc_country_code_copy(country_code
, dbobj
->country_code
);
675 r
= loc_network_set_country_code(*network
, country_code
);
677 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
682 uint32_t asn
= be32toh(dbobj
->asn
);
683 r
= loc_network_set_asn(*network
, asn
);
685 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
690 int flags
= be16toh(dbobj
->flags
);
691 r
= loc_network_set_flag(*network
, flags
);
693 ERROR(ctx
, "Could not set flags: %d\n", flags
);