]> git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
importer: Drop EDROP as it has been merged into DROP
[people/ms/libloc.git] / src / network.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
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.
10
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.
15 */
16
17 #include <arpa/inet.h>
18 #include <assert.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #ifdef HAVE_ENDIAN_H
25 # include <endian.h>
26 #endif
27
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>
35
36 struct loc_network {
37 struct loc_ctx* ctx;
38 int refcount;
39
40 int family;
41 struct in6_addr first_address;
42 struct in6_addr last_address;
43 unsigned int prefix;
44
45 char country_code[3];
46 uint32_t asn;
47 enum loc_network_flags flags;
48
49 char string[INET6_ADDRSTRLEN + 4];
50 };
51
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;
55
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);
59 errno = EINVAL;
60 return 1;
61 }
62
63 // Allocate a new network
64 n = calloc(1, sizeof(*n));
65 if (!n)
66 return 1;
67
68 n->ctx = loc_ref(ctx);
69 n->refcount = 1;
70
71 // Store the prefix
72 if (IN6_IS_ADDR_V4MAPPED(address))
73 n->prefix = prefix + 96;
74 else
75 n->prefix = prefix;
76
77 // Convert the prefix into a bitmask
78 const struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
79
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);
83
84 // Set family
85 n->family = loc_address_family(&n->first_address);
86
87 DEBUG(n->ctx, "Network allocated at %p\n", n);
88 *network = n;
89 return 0;
90 }
91
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;
95 unsigned int prefix;
96
97 // Parse the input
98 int r = loc_address_parse(&address, &prefix, string);
99 if (r) {
100 ERROR(ctx, "Could not parse network %s: %m\n", string);
101 return r;
102 }
103
104 // Create a new network
105 return loc_network_new(ctx, network, &address, prefix);
106 }
107
108 LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
109 network->refcount++;
110
111 return network;
112 }
113
114 static void loc_network_free(struct loc_network* network) {
115 DEBUG(network->ctx, "Releasing network at %p\n", network);
116
117 loc_unref(network->ctx);
118 free(network);
119 }
120
121 LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
122 if (--network->refcount > 0)
123 return network;
124
125 loc_network_free(network);
126 return NULL;
127 }
128
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);
133 if (!address)
134 return NULL;
135
136 // Fetch the prefix
137 unsigned int prefix = loc_network_prefix(network);
138
139 // Format the string
140 int r = snprintf(network->string, sizeof(network->string) - 1,
141 "%s/%u", address, prefix);
142 if (r < 0) {
143 ERROR(network->ctx, "Could not format network string: %m\n");
144 *network->string = '\0';
145 return NULL;
146 }
147 }
148
149 return network->string;
150 }
151
152 LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
153 return network->family;
154 }
155
156 LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
157 switch (network->family) {
158 case AF_INET6:
159 return network->prefix;
160
161 case AF_INET:
162 return network->prefix - 96;
163 }
164
165 return 0;
166 }
167
168 unsigned int loc_network_raw_prefix(struct loc_network* network) {
169 return network->prefix;
170 }
171
172 LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
173 return &network->first_address;
174 }
175
176 LOC_EXPORT const char* loc_network_format_first_address(struct loc_network* network) {
177 return loc_address_str(&network->first_address);
178 }
179
180 LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
181 return &network->last_address;
182 }
183
184 LOC_EXPORT const char* loc_network_format_last_address(struct loc_network* network) {
185 return loc_address_str(&network->last_address);
186 }
187
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)
191 return 0;
192
193 // Address must be smaller than the last address
194 if (loc_address_cmp(&network->last_address, address) < 0)
195 return 0;
196
197 // The address is inside this network
198 return 1;
199 }
200
201 LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
202 return network->country_code;
203 }
204
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';
209 return 0;
210 }
211
212 // Check country code
213 if (!loc_country_code_is_valid(country_code))
214 return -EINVAL;
215
216 loc_country_code_copy(network->country_code, country_code);
217
218 return 0;
219 }
220
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);
224
225 // If we found a flag, we will return whether it is set or not
226 if (flag)
227 return loc_network_has_flag(network, flag);
228
229 // Check country code
230 if (!loc_country_code_is_valid(country_code))
231 return -EINVAL;
232
233 // Check for an exact match
234 return (network->country_code[0] == country_code[0])
235 && (network->country_code[1] == country_code[1]);
236 }
237
238 LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
239 return network->asn;
240 }
241
242 LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
243 network->asn = asn;
244
245 return 0;
246 }
247
248 LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
249 return network->flags & flag;
250 }
251
252 LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
253 network->flags |= flag;
254
255 return 0;
256 }
257
258 LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
259 // Compare address
260 int r = loc_address_cmp(&self->first_address, &other->first_address);
261 if (r)
262 return r;
263
264 // Compare prefix
265 if (self->prefix > other->prefix)
266 return 1;
267 else if (self->prefix < other->prefix)
268 return -1;
269
270 // Both networks are equal
271 return 0;
272 }
273
274 int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
275 int r;
276
277 // Check country code
278 r = loc_country_code_cmp(self->country_code, other->country_code);
279 if (r)
280 return r;
281
282 // Check ASN
283 if (self->asn > other->asn)
284 return 1;
285 else if (self->asn < other->asn)
286 return -1;
287
288 // Check flags
289 if (self->flags > other->flags)
290 return 1;
291 else if (self->flags < other->flags)
292 return -1;
293
294 return 0;
295 }
296
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))
300 return 1;
301
302 if (loc_network_matches_address(other, &self->first_address))
303 return 1;
304
305 // Or either of the end addresses is in the other subnet
306 if (loc_network_matches_address(self, &other->last_address))
307 return 1;
308
309 if (loc_network_matches_address(other, &self->last_address))
310 return 1;
311
312 return 0;
313 }
314
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)
318 return 0;
319
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)
323 return 0;
324
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)
328 return 0;
329
330 return 1;
331 }
332
333 LOC_EXPORT int loc_network_subnets(struct loc_network* network,
334 struct loc_network** subnet1, struct loc_network** subnet2) {
335 int r;
336 *subnet1 = NULL;
337 *subnet2 = NULL;
338
339 // New prefix length
340 unsigned int prefix = loc_network_prefix(network) + 1;
341
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);
345 errno = EINVAL;
346 return 1;
347 }
348
349 // Create the first half of the network
350 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
351 if (r)
352 return r;
353
354 // The next subnet starts after the first one
355 struct in6_addr first_address = (*subnet1)->last_address;
356 loc_address_increment(&first_address);
357
358 // Create the second half of the network
359 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
360 if (r)
361 return r;
362
363 // Copy country code
364 const char* country_code = loc_network_get_country_code(network);
365 if (country_code) {
366 loc_network_set_country_code(*subnet1, country_code);
367 loc_network_set_country_code(*subnet2, country_code);
368 }
369
370 // Copy ASN
371 uint32_t asn = loc_network_get_asn(network);
372 if (asn) {
373 loc_network_set_asn(*subnet1, asn);
374 loc_network_set_asn(*subnet2, asn);
375 }
376
377 // Copy flags
378 loc_network_set_flag(*subnet1, network->flags);
379 loc_network_set_flag(*subnet2, network->flags);
380
381 return 0;
382 }
383
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;
388
389 int r = loc_network_subnets(network, &subnet1, &subnet2);
390 if (r)
391 goto ERROR;
392
393 if (loc_network_cmp(other, subnet1) == 0) {
394 r = loc_network_list_push(list, subnet2);
395 if (r)
396 goto ERROR;
397
398 } else if (loc_network_cmp(other, subnet2) == 0) {
399 r = loc_network_list_push(list, subnet1);
400 if (r)
401 goto ERROR;
402
403 } else if (loc_network_is_subnet(subnet1, other)) {
404 r = loc_network_list_push(list, subnet2);
405 if (r)
406 goto ERROR;
407
408 r = __loc_network_exclude(subnet1, other, list);
409 if (r)
410 goto ERROR;
411
412 } else if (loc_network_is_subnet(subnet2, other)) {
413 r = loc_network_list_push(list, subnet1);
414 if (r)
415 goto ERROR;
416
417 r = __loc_network_exclude(subnet2, other, list);
418 if (r)
419 goto ERROR;
420
421 } else {
422 ERROR(network->ctx, "We should never get here\n");
423 r = 1;
424 goto ERROR;
425 }
426
427 ERROR:
428 if (subnet1)
429 loc_network_unref(subnet1);
430
431 if (subnet2)
432 loc_network_unref(subnet2);
433
434 if (r)
435 DEBUG(network->ctx, "%s has failed with %d\n", __FUNCTION__, r);
436
437 return r;
438 }
439
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);
445
446 // Exit silently
447 return 0;
448 }
449
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);
453
454 // Exit silently
455 return 0;
456 }
457
458 return __loc_network_exclude(self, other, list);
459 }
460
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;
464
465 DEBUG(self->ctx, "Returning %s excluding %s...\n",
466 loc_network_str(self), loc_network_str(other));
467
468 // Create a new list with the result
469 int r = loc_network_list_new(self->ctx, &list);
470 if (r) {
471 ERROR(self->ctx, "Could not create network list: %d\n", r);
472
473 return NULL;
474 }
475
476 r = __loc_network_exclude_to_list(self, other, list);
477 if (r) {
478 loc_network_list_unref(list);
479
480 return NULL;
481 }
482
483 // Return the result
484 return list;
485 }
486
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;
490
491 // Create a new list with all networks to look at
492 int r = loc_network_list_new(network->ctx, &to_check);
493 if (r)
494 return NULL;
495
496 struct loc_network* subnet = NULL;
497 struct loc_network_list* subnets = NULL;
498
499 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
500 subnet = loc_network_list_get(list, i);
501
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);
505 if (r) {
506 loc_network_list_unref(to_check);
507 loc_network_unref(subnet);
508
509 return NULL;
510 }
511 }
512
513 // Cleanup
514 loc_network_unref(subnet);
515 }
516
517 r = loc_network_list_new(network->ctx, &subnets);
518 if (r) {
519 loc_network_list_unref(to_check);
520 return NULL;
521 }
522
523 off_t smallest_subnet = 0;
524
525 while (!loc_network_list_empty(to_check)) {
526 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
527
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);
531 continue;
532 }
533
534 // Marks whether this subnet passed all checks
535 int passed = 1;
536
537 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
538 subnet = loc_network_list_get(list, i);
539
540 // Drop this subnet if is a subnet of another subnet
541 if (loc_network_is_subnet(subnet, subnet_to_check)) {
542 passed = 0;
543 loc_network_unref(subnet);
544 break;
545 }
546
547 // Break it down if it overlaps
548 if (loc_network_overlaps(subnet, subnet_to_check)) {
549 passed = 0;
550
551 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
552
553 loc_network_unref(subnet);
554 break;
555 }
556
557 // If the subnet is strictly greater, we do not need to continue the search
558 r = loc_network_cmp(subnet, subnet_to_check);
559 if (r > 0) {
560 loc_network_unref(subnet);
561 break;
562
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
565 // than this one.
566 } else if (r < 0) {
567 smallest_subnet = i;
568 }
569
570 loc_network_unref(subnet);
571 }
572
573 if (passed) {
574 r = loc_network_list_push(subnets, subnet_to_check);
575 }
576
577 loc_network_unref(subnet_to_check);
578 }
579
580 loc_network_list_unref(to_check);
581
582 return subnets;
583 }
584
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;
589 int r;
590
591 // Reset pointer
592 *n = NULL;
593
594 DEBUG(n1->ctx, "Attempting to merge %s and %s\n", loc_network_str(n1), loc_network_str(n2));
595
596 // Family must match
597 if (n1->family != n2->family)
598 return 0;
599
600 // The prefix must match, too
601 if (n1->prefix != n2->prefix)
602 return 0;
603
604 // Cannot merge ::/0 or 0.0.0.0/0
605 if (!n1->prefix || !n2->prefix)
606 return 0;
607
608 const unsigned int prefix = loc_network_prefix(n1);
609
610 // How many bits do we need to represent this address?
611 const size_t bitlength = loc_address_bit_length(&n1->first_address);
612
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);
617
618 return 0;
619 }
620
621 // Increment the last address of the first network
622 address = n1->last_address;
623 loc_address_increment(&address);
624
625 // If they don't match they are not neighbours
626 if (loc_address_cmp(&address, &n2->first_address) != 0)
627 return 0;
628
629 // All properties must match, too
630 if (loc_network_properties_cmp(n1, n2) != 0)
631 return 0;
632
633 // Create a new network object
634 r = loc_network_new(n1->ctx, &network, &n1->first_address, prefix - 1);
635 if (r)
636 return r;
637
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;
642
643 // Return pointer
644 *n = network;
645
646 return 0;
647 }
648
649 int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
650 // Add country code
651 loc_country_code_copy(dbobj->country_code, network->country_code);
652
653 // Add ASN
654 dbobj->asn = htobe32(network->asn);
655
656 // Flags
657 dbobj->flags = htobe16(network->flags);
658
659 return 0;
660 }
661
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";
665
666 // Adjust prefix for IPv4
667 if (IN6_IS_ADDR_V4MAPPED(address))
668 prefix -= 96;
669
670 int r = loc_network_new(ctx, network, address, prefix);
671 if (r) {
672 ERROR(ctx, "Could not allocate a new network: %m\n");
673 return r;
674 }
675
676 // Import country code
677 loc_country_code_copy(country_code, dbobj->country_code);
678
679 r = loc_network_set_country_code(*network, country_code);
680 if (r) {
681 ERROR(ctx, "Could not set country code: %s\n", country_code);
682 return r;
683 }
684
685 // Import ASN
686 uint32_t asn = be32toh(dbobj->asn);
687 r = loc_network_set_asn(*network, asn);
688 if (r) {
689 ERROR(ctx, "Could not set ASN: %d\n", asn);
690 return r;
691 }
692
693 // Import flags
694 int flags = be16toh(dbobj->flags);
695 r = loc_network_set_flag(*network, flags);
696 if (r) {
697 ERROR(ctx, "Could not set flags: %d\n", flags);
698 return r;
699 }
700
701 return 0;
702 }
703
704 static char* loc_network_reverse_pointer6(struct loc_network* network, const char* suffix) {
705 char* buffer = NULL;
706 int r;
707
708 unsigned int prefix = loc_network_prefix(network);
709
710 // Must border on a nibble
711 if (prefix % 4) {
712 errno = ENOTSUP;
713 return NULL;
714 }
715
716 if (!suffix)
717 suffix = "ip6.arpa.";
718
719 // Initialize the buffer
720 r = asprintf(&buffer, "%s", suffix);
721 if (r < 0)
722 goto ERROR;
723
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);
726 if (r < 0)
727 goto ERROR;
728 }
729
730 // Add the asterisk
731 if (prefix < 128) {
732 r = asprintf(&buffer, "*.%s", buffer);
733 if (r < 0)
734 goto ERROR;
735 }
736
737 return buffer;
738
739 ERROR:
740 if (buffer)
741 free(buffer);
742
743 return NULL;
744 }
745
746 static char* loc_network_reverse_pointer4(struct loc_network* network, const char* suffix) {
747 char* buffer = NULL;
748 int r;
749
750 unsigned int prefix = loc_network_prefix(network);
751
752 // Must border on an octet
753 if (prefix % 8) {
754 errno = ENOTSUP;
755 return NULL;
756 }
757
758 if (!suffix)
759 suffix = "in-addr.arpa.";
760
761 switch (prefix) {
762 case 32:
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),
768 suffix);
769 break;
770
771 case 24:
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),
776 suffix);
777 break;
778
779 case 16:
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),
783 suffix);
784 break;
785
786 case 8:
787 r = asprintf(&buffer, "*.%d.%s",
788 loc_address_get_octet(&network->first_address, 0),
789 suffix);
790 break;
791
792 case 0:
793 r = asprintf(&buffer, "*.%s", suffix);
794 break;
795
796 // To make the compiler happy
797 default:
798 return NULL;
799 }
800
801 if (r < 0)
802 return NULL;
803
804 return buffer;
805 }
806
807 LOC_EXPORT char* loc_network_reverse_pointer(struct loc_network* network, const char* suffix) {
808 switch (network->family) {
809 case AF_INET6:
810 return loc_network_reverse_pointer6(network, suffix);
811
812 case AF_INET:
813 return loc_network_reverse_pointer4(network, suffix);
814
815 default:
816 break;
817 }
818
819 return NULL;
820 }