]> git.ipfire.org Git - people/ms/libloc.git/blob - src/network.c
network: Store the first and last address of each network
[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 <loc/libloc.h>
29 #include <loc/compat.h>
30 #include <loc/country.h>
31 #include <loc/network.h>
32 #include <loc/private.h>
33
34 struct loc_network {
35 struct loc_ctx* ctx;
36 int refcount;
37
38 struct in6_addr first_address;
39 struct in6_addr last_address;
40 unsigned int prefix;
41
42 char country_code[3];
43 uint32_t asn;
44 enum loc_network_flags flags;
45 };
46
47 static int valid_prefix(struct in6_addr* address, unsigned int prefix) {
48 // The prefix cannot be larger than 128 bits
49 if (prefix > 128)
50 return 1;
51
52 // And the prefix cannot be zero
53 if (prefix == 0)
54 return 1;
55
56 // For IPv4-mapped addresses the prefix has to be 96 or lager
57 if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96)
58 return 1;
59
60 return 0;
61 }
62
63 static struct in6_addr prefix_to_bitmask(unsigned int prefix) {
64 struct in6_addr bitmask;
65
66 for (unsigned int i = 0; i < 16; i++)
67 bitmask.s6_addr[i] = 0;
68
69 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
70 if (i >= 8)
71 bitmask.s6_addr[j] = 0xff;
72 else
73 bitmask.s6_addr[j] = 0xff << (8 - i);
74 }
75
76 return bitmask;
77 }
78
79 static struct in6_addr make_first_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
80 struct in6_addr a;
81
82 // Perform bitwise AND
83 for (unsigned int i = 0; i < 4; i++)
84 a.s6_addr32[i] = address->s6_addr32[i] & bitmask->s6_addr32[i];
85
86 return a;
87 }
88
89 static struct in6_addr make_last_address(const struct in6_addr* address, const struct in6_addr* bitmask) {
90 struct in6_addr a;
91
92 // Perform bitwise OR
93 for (unsigned int i = 0; i < 4; i++)
94 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask->s6_addr32[i];
95
96 return a;
97 }
98
99 LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
100 struct in6_addr* address, unsigned int prefix) {
101 // Address cannot be unspecified
102 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
103 DEBUG(ctx, "Start address is unspecified\n");
104 return -EINVAL;
105 }
106
107 // Address cannot be loopback
108 if (IN6_IS_ADDR_LOOPBACK(address)) {
109 DEBUG(ctx, "Start address is loopback address\n");
110 return -EINVAL;
111 }
112
113 // Address cannot be link-local
114 if (IN6_IS_ADDR_LINKLOCAL(address)) {
115 DEBUG(ctx, "Start address cannot be link-local\n");
116 return -EINVAL;
117 }
118
119 // Address cannot be site-local
120 if (IN6_IS_ADDR_SITELOCAL(address)) {
121 DEBUG(ctx, "Start address cannot be site-local\n");
122 return -EINVAL;
123 }
124
125 // Validate the prefix
126 if (valid_prefix(address, prefix) != 0) {
127 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
128 return -EINVAL;
129 }
130
131 struct loc_network* n = calloc(1, sizeof(*n));
132 if (!n)
133 return -ENOMEM;
134
135 n->ctx = loc_ref(ctx);
136 n->refcount = 1;
137
138 // Store the prefix
139 n->prefix = prefix;
140
141 // Convert the prefix into a bitmask
142 struct in6_addr bitmask = prefix_to_bitmask(n->prefix);
143
144 // Store the first and last address in the network
145 n->first_address = make_first_address(address, &bitmask);
146 n->last_address = make_last_address(&n->first_address, &bitmask);
147
148 DEBUG(n->ctx, "Network allocated at %p\n", n);
149 *network = n;
150 return 0;
151 }
152
153 LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network,
154 const char* address_string) {
155 struct in6_addr first_address;
156 unsigned int prefix = 0;
157 char* prefix_string;
158 int r = 1;
159
160 // Make a copy of the string to work on it
161 char* buffer = strdup(address_string);
162 address_string = prefix_string = buffer;
163
164 // Split address and prefix
165 address_string = strsep(&prefix_string, "/");
166
167 // Did we find a prefix?
168 if (prefix_string) {
169 // Convert prefix to integer
170 prefix = strtol(prefix_string, NULL, 10);
171
172 if (prefix) {
173 // Parse the address
174 r = loc_parse_address(ctx, address_string, &first_address);
175
176 // Map the prefix to IPv6 if needed
177 if (IN6_IS_ADDR_V4MAPPED(&first_address))
178 prefix += 96;
179 }
180 }
181
182 // Free temporary buffer
183 free(buffer);
184
185 if (r == 0) {
186 r = loc_network_new(ctx, network, &first_address, prefix);
187 }
188
189 return r;
190 }
191
192 LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
193 network->refcount++;
194
195 return network;
196 }
197
198 static void loc_network_free(struct loc_network* network) {
199 DEBUG(network->ctx, "Releasing network at %p\n", network);
200
201 loc_unref(network->ctx);
202 free(network);
203 }
204
205 LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
206 if (!network)
207 return NULL;
208
209 if (--network->refcount > 0)
210 return network;
211
212 loc_network_free(network);
213 return NULL;
214 }
215
216 static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) {
217 const char* ret = inet_ntop(AF_INET6, address, string, length);
218 if (!ret)
219 return -1;
220
221 return 0;
222 }
223
224 static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
225 struct in_addr ipv4_address;
226 ipv4_address.s_addr = address->s6_addr32[3];
227
228 const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length);
229 if (!ret)
230 return -1;
231
232 return 0;
233 }
234
235 LOC_EXPORT char* loc_network_str(struct loc_network* network) {
236 int r;
237 const size_t length = INET6_ADDRSTRLEN + 4;
238
239 char* string = malloc(length);
240 if (!string)
241 return NULL;
242
243 unsigned int prefix = network->prefix;
244
245 int family = loc_network_address_family(network);
246 switch (family) {
247 case AF_INET6:
248 r = format_ipv6_address(&network->first_address, string, length);
249 break;
250
251 case AF_INET:
252 r = format_ipv4_address(&network->first_address, string, length);
253 prefix -= 96;
254 break;
255
256 default:
257 r = -1;
258 break;
259 }
260
261 if (r) {
262 ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno));
263 free(string);
264
265 return NULL;
266 }
267
268 // Append prefix
269 sprintf(string + strlen(string), "/%u", prefix);
270
271 return string;
272 }
273
274 LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
275 if (IN6_IS_ADDR_V4MAPPED(&network->first_address))
276 return AF_INET;
277
278 return AF_INET6;
279 }
280
281 LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
282 // Address must be larger than the start address
283 if (in6_addr_cmp(&network->first_address, address) > 0)
284 return 1;
285
286 // Address must be smaller than the last address
287 if (in6_addr_cmp(&network->last_address, address) < 0)
288 return 1;
289
290 // The address is inside this network
291 return 0;
292 }
293
294 LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
295 return network->country_code;
296 }
297
298 LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
299 // Set empty country code
300 if (!country_code || !*country_code) {
301 *network->country_code = '\0';
302 return 0;
303 }
304
305 // Check country code
306 if (!loc_country_code_is_valid(country_code))
307 return -EINVAL;
308
309 loc_country_code_copy(network->country_code, country_code);
310
311 return 0;
312 }
313
314 LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) {
315 // Check country code
316 if (!loc_country_code_is_valid(country_code))
317 return -EINVAL;
318
319 return (network->country_code[0] == country_code[0])
320 && (network->country_code[1] == country_code[1]);
321 }
322
323 LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
324 return network->asn;
325 }
326
327 LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
328 network->asn = asn;
329
330 return 0;
331 }
332
333 LOC_EXPORT int loc_network_match_asn(struct loc_network* network, uint32_t asn) {
334 return network->asn == asn;
335 }
336
337 LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
338 return network->flags & flag;
339 }
340
341 LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
342 network->flags |= flag;
343
344 return 0;
345 }
346
347 LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag) {
348 return loc_network_has_flag(network, flag);
349 }
350
351 LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other) {
352 // If the start address of the other network is smaller than this network,
353 // it cannot be a subnet.
354 if (in6_addr_cmp(&self->first_address, &other->first_address) < 0)
355 return 0;
356
357 // If the end address of the other network is greater than this network,
358 // it cannot be a subnet.
359 if (in6_addr_cmp(&self->last_address, &other->last_address) > 0)
360 return 0;
361
362 return 1;
363 }
364
365 LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
366 // Add country code
367 loc_country_code_copy(dbobj->country_code, network->country_code);
368
369 // Add ASN
370 dbobj->asn = htobe32(network->asn);
371
372 // Flags
373 dbobj->flags = htobe16(network->flags);
374
375 return 0;
376 }
377
378 LOC_EXPORT int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
379 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
380 char country_code[3] = "\0\0";
381
382 int r = loc_network_new(ctx, network, address, prefix);
383 if (r) {
384 ERROR(ctx, "Could not allocate a new network: %s", strerror(-r));
385 return r;
386 }
387
388 // Import country code
389 loc_country_code_copy(country_code, dbobj->country_code);
390
391 r = loc_network_set_country_code(*network, country_code);
392 if (r) {
393 ERROR(ctx, "Could not set country code: %s\n", country_code);
394 return r;
395 }
396
397 // Import ASN
398 uint32_t asn = be32toh(dbobj->asn);
399 r = loc_network_set_asn(*network, asn);
400 if (r) {
401 ERROR(ctx, "Could not set ASN: %d\n", asn);
402 return r;
403 }
404
405 // Import flags
406 int flags = be16toh(dbobj->flags);
407 r = loc_network_set_flag(*network, flags);
408 if (r) {
409 ERROR(ctx, "Could not set flags: %d\n", flags);
410 return r;
411 }
412
413 return 0;
414 }
415
416 struct loc_network_tree {
417 struct loc_ctx* ctx;
418 int refcount;
419
420 struct loc_network_tree_node* root;
421 };
422
423 struct loc_network_tree_node {
424 struct loc_ctx* ctx;
425 int refcount;
426
427 struct loc_network_tree_node* zero;
428 struct loc_network_tree_node* one;
429
430 struct loc_network* network;
431 };
432
433 LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
434 struct loc_network_tree* t = calloc(1, sizeof(*t));
435 if (!t)
436 return -ENOMEM;
437
438 t->ctx = loc_ref(ctx);
439 t->refcount = 1;
440
441 // Create the root node
442 int r = loc_network_tree_node_new(ctx, &t->root);
443 if (r) {
444 loc_network_tree_unref(t);
445 return r;
446 }
447
448 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
449 *tree = t;
450 return 0;
451 }
452
453 LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
454 return loc_network_tree_node_ref(tree->root);
455 }
456
457 static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
458 struct loc_network_tree_node** n;
459
460 if (path == 0)
461 n = &node->zero;
462 else
463 n = &node->one;
464
465 // If the desired node doesn't exist, yet, we will create it
466 if (*n == NULL) {
467 int r = loc_network_tree_node_new(node->ctx, n);
468 if (r)
469 return NULL;
470 }
471
472 return *n;
473 }
474
475 static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
476 struct loc_network_tree_node* node = tree->root;
477
478 for (unsigned int i = 0; i < prefix; i++) {
479 // Check if the ith bit is one or zero
480 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
481 }
482
483 return node;
484 }
485
486 static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
487 int(*filter_callback)(struct loc_network* network, void* data),
488 int(*callback)(struct loc_network* network, void* data), void* data) {
489 int r;
490
491 // Finding a network ends the walk here
492 if (node->network) {
493 if (filter_callback) {
494 int f = filter_callback(node->network, data);
495 if (f < 0)
496 return f;
497
498 // Skip network if filter function returns value greater than zero
499 if (f > 0)
500 return 0;
501 }
502
503 r = callback(node->network, data);
504 if (r)
505 return r;
506 }
507
508 // Walk down on the left side of the tree first
509 if (node->zero) {
510 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
511 if (r)
512 return r;
513 }
514
515 // Then walk on the other side
516 if (node->one) {
517 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
518 if (r)
519 return r;
520 }
521
522 return 0;
523 }
524
525 LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
526 int(*filter_callback)(struct loc_network* network, void* data),
527 int(*callback)(struct loc_network* network, void* data), void* data) {
528 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
529 }
530
531 static void loc_network_tree_free(struct loc_network_tree* tree) {
532 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
533
534 loc_network_tree_node_unref(tree->root);
535
536 loc_unref(tree->ctx);
537 free(tree);
538 }
539
540 LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
541 if (--tree->refcount > 0)
542 return tree;
543
544 loc_network_tree_free(tree);
545 return NULL;
546 }
547
548 static int __loc_network_tree_dump(struct loc_network* network, void* data) {
549 DEBUG(network->ctx, "Dumping network at %p\n", network);
550
551 char* s = loc_network_str(network);
552 if (!s)
553 return 1;
554
555 INFO(network->ctx, "%s\n", s);
556 free(s);
557
558 return 0;
559 }
560
561 LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
562 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
563
564 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
565 }
566
567 LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
568 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
569
570 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
571 &network->first_address, network->prefix);
572 if (!node) {
573 ERROR(tree->ctx, "Could not find a node\n");
574 return -ENOMEM;
575 }
576
577 // Check if node has not been set before
578 if (node->network) {
579 DEBUG(tree->ctx, "There is already a network at this path\n");
580 return -EBUSY;
581 }
582
583 // Point node to the network
584 node->network = loc_network_ref(network);
585
586 return 0;
587 }
588
589 static int __loc_network_tree_count(struct loc_network* network, void* data) {
590 size_t* counter = (size_t*)data;
591
592 // Increase the counter for each network
593 counter++;
594
595 return 0;
596 }
597
598 LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
599 size_t counter = 0;
600
601 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
602 if (r)
603 return r;
604
605 return counter;
606 }
607
608 static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
609 size_t counter = 1;
610
611 if (node->zero)
612 counter += __loc_network_tree_count_nodes(node->zero);
613
614 if (node->one)
615 counter += __loc_network_tree_count_nodes(node->one);
616
617 return counter;
618 }
619
620 LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
621 return __loc_network_tree_count_nodes(tree->root);
622 }
623
624 LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
625 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
626 if (!n)
627 return -ENOMEM;
628
629 n->ctx = loc_ref(ctx);
630 n->refcount = 1;
631
632 n->zero = n->one = NULL;
633
634 DEBUG(n->ctx, "Network node allocated at %p\n", n);
635 *node = n;
636 return 0;
637 }
638
639 LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
640 if (node)
641 node->refcount++;
642
643 return node;
644 }
645
646 static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
647 DEBUG(node->ctx, "Releasing network node at %p\n", node);
648
649 if (node->network)
650 loc_network_unref(node->network);
651
652 if (node->zero)
653 loc_network_tree_node_unref(node->zero);
654
655 if (node->one)
656 loc_network_tree_node_unref(node->one);
657
658 loc_unref(node->ctx);
659 free(node);
660 }
661
662 LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
663 if (!node)
664 return NULL;
665
666 if (--node->refcount > 0)
667 return node;
668
669 loc_network_tree_node_free(node);
670 return NULL;
671 }
672
673 LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
674 if (index == 0)
675 node = node->zero;
676 else
677 node = node->one;
678
679 if (!node)
680 return NULL;
681
682 return loc_network_tree_node_ref(node);
683 }
684
685 LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
686 return (!!node->network);
687 }
688
689 LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
690 return loc_network_ref(node->network);
691 }