]>
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
);
613 // We cannot shorten this any more
614 if (bitlength
>= prefix
) {
615 DEBUG(n1
->ctx
, "Cannot shorten this any further because we need at least %jd bits,"
616 " but only have %d\n", bitlength
, prefix
);
621 // Increment the last address of the first network
622 address
= n1
->last_address
;
623 loc_address_increment(&address
);
625 // If they don't match they are not neighbours
626 if (loc_address_cmp(&address
, &n2
->first_address
) != 0)
629 // All properties must match, too
630 if (loc_network_properties_cmp(n1
, n2
) != 0)
633 // Create a new network object
634 r
= loc_network_new(n1
->ctx
, &network
, &n1
->first_address
, prefix
- 1);
638 // Copy everything else
639 loc_country_code_copy(network
->country_code
, n1
->country_code
);
640 network
->asn
= n1
->asn
;
641 network
->flags
= n1
->flags
;
649 int loc_network_to_database_v1(struct loc_network
* network
, struct loc_database_network_v1
* dbobj
) {
651 loc_country_code_copy(dbobj
->country_code
, network
->country_code
);
654 dbobj
->asn
= htobe32(network
->asn
);
657 dbobj
->flags
= htobe16(network
->flags
);
662 int loc_network_new_from_database_v1(struct loc_ctx
* ctx
, struct loc_network
** network
,
663 struct in6_addr
* address
, unsigned int prefix
, const struct loc_database_network_v1
* dbobj
) {
664 char country_code
[3] = "\0\0";
666 // Adjust prefix for IPv4
667 if (IN6_IS_ADDR_V4MAPPED(address
))
670 int r
= loc_network_new(ctx
, network
, address
, prefix
);
672 ERROR(ctx
, "Could not allocate a new network: %m\n");
676 // Import country code
677 loc_country_code_copy(country_code
, dbobj
->country_code
);
679 r
= loc_network_set_country_code(*network
, country_code
);
681 ERROR(ctx
, "Could not set country code: %s\n", country_code
);
686 uint32_t asn
= be32toh(dbobj
->asn
);
687 r
= loc_network_set_asn(*network
, asn
);
689 ERROR(ctx
, "Could not set ASN: %d\n", asn
);
694 int flags
= be16toh(dbobj
->flags
);
695 r
= loc_network_set_flag(*network
, flags
);
697 ERROR(ctx
, "Could not set flags: %d\n", flags
);
704 static char* loc_network_reverse_pointer6(struct loc_network
* network
, const char* suffix
) {
708 unsigned int prefix
= loc_network_prefix(network
);
710 // Must border on a nibble
717 suffix
= "ip6.arpa.";
719 // Initialize the buffer
720 r
= asprintf(&buffer
, "%s", suffix
);
724 for (unsigned int i
= 0; i
< (prefix
/ 4); i
++) {
725 r
= asprintf(&buffer
, "%x.%s", loc_address_get_nibble(&network
->first_address
, i
), buffer
);
732 r
= asprintf(&buffer
, "*.%s", buffer
);
746 static char* loc_network_reverse_pointer4(struct loc_network
* network
, const char* suffix
) {
750 unsigned int prefix
= loc_network_prefix(network
);
752 // Must border on an octet
759 suffix
= "in-addr.arpa.";
763 r
= asprintf(&buffer
, "%d.%d.%d.%d.%s",
764 loc_address_get_octet(&network
->first_address
, 3),
765 loc_address_get_octet(&network
->first_address
, 2),
766 loc_address_get_octet(&network
->first_address
, 1),
767 loc_address_get_octet(&network
->first_address
, 0),
772 r
= asprintf(&buffer
, "*.%d.%d.%d.%s",
773 loc_address_get_octet(&network
->first_address
, 2),
774 loc_address_get_octet(&network
->first_address
, 1),
775 loc_address_get_octet(&network
->first_address
, 0),
780 r
= asprintf(&buffer
, "*.%d.%d.%s",
781 loc_address_get_octet(&network
->first_address
, 1),
782 loc_address_get_octet(&network
->first_address
, 0),
787 r
= asprintf(&buffer
, "*.%d.%s",
788 loc_address_get_octet(&network
->first_address
, 0),
793 r
= asprintf(&buffer
, "*.%s", suffix
);
796 // To make the compiler happy
807 LOC_EXPORT
char* loc_network_reverse_pointer(struct loc_network
* network
, const char* suffix
) {
808 switch (network
->family
) {
810 return loc_network_reverse_pointer6(network
, suffix
);
813 return loc_network_reverse_pointer4(network
, suffix
);