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