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