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