]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
Implement search through the network tree
[people/ms/libloc.git] / src / network.c
CommitLineData
3b5f4af2
MT
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>
f3e02bc5 19#include <endian.h>
3b5f4af2 20#include <errno.h>
71ff3e69 21#include <stdio.h>
3b5f4af2
MT
22#include <stdlib.h>
23#include <string.h>
24
25#include <loc/libloc.h>
26#include <loc/network.h>
9fc7f001 27#include <loc/private.h>
3b5f4af2
MT
28
29struct 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];
71ff3e69 37 uint32_t asn;
3b5f4af2
MT
38};
39
6183c0f2
MT
40static 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
aa37232a
MT
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
6183c0f2
MT
53 return 0;
54}
55
a82ce8bc
MT
56static 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
364a2a37 62 for (int i = prefix, j = 0; i > 0; i -= 8, j++) {
a82ce8bc
MT
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
2a30e4de 72static struct in6_addr make_start_address(const struct in6_addr* address, unsigned int prefix) {
a82ce8bc
MT
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
2a30e4de
MT
83static 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++)
c7096aef 89 a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask.s6_addr32[i];
2a30e4de
MT
90
91 return a;
92}
93
3b5f4af2 94LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 95 struct in6_addr* address, unsigned int prefix) {
3b5f4af2 96 // Address cannot be unspecified
bca87342 97 if (IN6_IS_ADDR_UNSPECIFIED(address)) {
3b5f4af2
MT
98 DEBUG(ctx, "Start address is unspecified\n");
99 return -EINVAL;
100 }
101
102 // Address cannot be loopback
bca87342 103 if (IN6_IS_ADDR_LOOPBACK(address)) {
3b5f4af2
MT
104 DEBUG(ctx, "Start address is loopback address\n");
105 return -EINVAL;
106 }
107
108 // Address cannot be link-local
bca87342 109 if (IN6_IS_ADDR_LINKLOCAL(address)) {
3b5f4af2
MT
110 DEBUG(ctx, "Start address cannot be link-local\n");
111 return -EINVAL;
112 }
113
114 // Address cannot be site-local
bca87342 115 if (IN6_IS_ADDR_SITELOCAL(address)) {
3b5f4af2
MT
116 DEBUG(ctx, "Start address cannot be site-local\n");
117 return -EINVAL;
118 }
119
6183c0f2 120 // Validate the prefix
10778041 121 if (valid_prefix(address, prefix) != 0) {
6183c0f2
MT
122 DEBUG(ctx, "Invalid prefix: %u\n", prefix);
123 return -EINVAL;
124 }
125
3b5f4af2
MT
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
a82ce8bc 133 // Store the first address in the network
10778041 134 n->start_address = make_start_address(address, prefix);
3b5f4af2
MT
135 n->prefix = prefix;
136
137 DEBUG(n->ctx, "Network allocated at %p\n", n);
138 *network = n;
139 return 0;
140}
141
d3409788
MT
142static 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
3b5f4af2
MT
149LOC_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;
b61f14fc 152 unsigned int prefix = 0;
3b5f4af2 153 char* prefix_string;
b61f14fc 154 int r = 1;
3b5f4af2
MT
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
b61f14fc
MT
163 // Did we find a prefix?
164 if (prefix_string) {
165 // Convert prefix to integer
166 prefix = strtol(prefix_string, NULL, 10);
3b5f4af2 167
b61f14fc
MT
168 if (prefix) {
169 // Parse the address
2a30e4de 170 r = loc_parse_address(ctx, address_string, &start_address);
aa37232a
MT
171
172 // Map the prefix to IPv6 if needed
173 if (IN6_IS_ADDR_V4MAPPED(&start_address))
174 prefix += 96;
b61f14fc
MT
175 }
176 }
3b5f4af2
MT
177
178 // Free temporary buffer
179 free(buffer);
180
d3409788 181 if (r == 0) {
10778041 182 r = loc_network_new(ctx, network, &start_address, prefix);
3b5f4af2
MT
183 }
184
185 return r;
186}
187
188LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
189 network->refcount++;
190
191 return network;
192}
193
194static void loc_network_free(struct loc_network* network) {
195 DEBUG(network->ctx, "Releasing network at %p\n", network);
196
3b5f4af2
MT
197 loc_unref(network->ctx);
198 free(network);
199}
200
201LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
2a30e4de
MT
202 if (!network)
203 return NULL;
204
3b5f4af2
MT
205 if (--network->refcount > 0)
206 return network;
207
208 loc_network_free(network);
209 return NULL;
210}
211
8fb874e9
MT
212static 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);
d3409788
MT
214 if (!ret)
215 return -1;
216
217 return 0;
218}
219
8fb874e9 220static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) {
d3409788 221 struct in_addr ipv4_address;
8fb874e9 222 ipv4_address.s_addr = address->s6_addr32[3];
d3409788
MT
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
3b5f4af2 231LOC_EXPORT char* loc_network_str(struct loc_network* network) {
d3409788
MT
232 int r;
233 const size_t length = INET6_ADDRSTRLEN + 4;
3b5f4af2 234
d3409788 235 char* string = malloc(length);
3b5f4af2
MT
236 if (!string)
237 return NULL;
238
aa37232a
MT
239 unsigned int prefix = network->prefix;
240
d3409788
MT
241 int family = loc_network_address_family(network);
242 switch (family) {
243 case AF_INET6:
8fb874e9 244 r = format_ipv6_address(&network->start_address, string, length);
d3409788 245 break;
3b5f4af2 246
d3409788 247 case AF_INET:
8fb874e9 248 r = format_ipv4_address(&network->start_address, string, length);
aa37232a 249 prefix -= 96;
d3409788
MT
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));
3b5f4af2 259 free(string);
d3409788 260
3b5f4af2
MT
261 return NULL;
262 }
263
264 // Append prefix
aa37232a 265 sprintf(string + strlen(string), "/%u", prefix);
3b5f4af2
MT
266
267 return string;
268}
269
2a30e4de 270LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 271 // Address must be larger than the start address
2a30e4de
MT
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
0d33b671 279 if (in6_addr_cmp(address, &last_address) > 0)
2a30e4de
MT
280 return 1;
281
282 // The address is inside this network
283 return 0;
284}
285
3b5f4af2
MT
286LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
287 return network->country_code;
288}
289
290LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
291 // Set empty country code
292 if (!country_code || !*country_code) {
293 *network->country_code = '\0';
294 return 0;
295 }
296
3b5f4af2
MT
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
e3f696c1
MT
308LOC_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
71ff3e69
MT
317LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
318 return network->asn;
319}
320
321LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
322 network->asn = asn;
323
324 return 0;
325}
326
f3e02bc5 327LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) {
f3e02bc5
MT
328 // Add country code
329 for (unsigned int i = 0; i < 2; i++) {
04cdff09 330 dbobj->country_code[i] = network->country_code[i] ? network->country_code[i] : '\0';
f3e02bc5
MT
331 }
332
333 // Add ASN
71ff3e69 334 dbobj->asn = htobe32(network->asn);
f3e02bc5
MT
335
336 return 0;
337}
338
10778041 339LOC_EXPORT int loc_network_new_from_database_v0(struct loc_ctx* ctx, struct loc_network** network,
39a55353
MT
340 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v0* dbobj) {
341 int r = loc_network_new(ctx, network, address, prefix);
10778041
MT
342 if (r)
343 return r;
344
345 // Import country code
346 char country_code[3];
347 for (unsigned int i = 0; i < 2; i++) {
348 country_code[i] = dbobj->country_code[i];
349 }
350 country_code[2] = '\0';
351
352 r = loc_network_set_country_code(*network, country_code);
353 if (r)
354 return r;
355
356 // Import ASN
357 r = loc_network_set_asn(*network, be32toh(dbobj->asn));
358 if (r)
359 return r;
360
361 return 0;
362}
363
3b5f4af2
MT
364struct loc_network_tree {
365 struct loc_ctx* ctx;
366 int refcount;
367
368 struct loc_network_tree_node* root;
369};
370
371struct loc_network_tree_node {
438db08c
MT
372 struct loc_ctx* ctx;
373 int refcount;
374
3b5f4af2
MT
375 struct loc_network_tree_node* zero;
376 struct loc_network_tree_node* one;
377
378 struct loc_network* network;
379};
380
381LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
382 struct loc_network_tree* t = calloc(1, sizeof(*t));
383 if (!t)
384 return -ENOMEM;
385
386 t->ctx = loc_ref(ctx);
387 t->refcount = 1;
388
389 // Create the root node
438db08c
MT
390 int r = loc_network_tree_node_new(ctx, &t->root);
391 if (r) {
392 loc_network_tree_unref(t);
393 return r;
394 }
3b5f4af2
MT
395
396 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
397 *tree = t;
398 return 0;
399}
400
438db08c
MT
401LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
402 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
403}
404
405static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
406 struct loc_network_tree_node** n;
407
2a30e4de 408 if (path == 0)
3b5f4af2 409 n = &node->zero;
3eda9cab
MT
410 else
411 n = &node->one;
3b5f4af2
MT
412
413 // If the desired node doesn't exist, yet, we will create it
414 if (*n == NULL) {
438db08c 415 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
416 if (r)
417 return NULL;
418 }
419
420 return *n;
421}
422
39a55353 423static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) {
3b5f4af2
MT
424 struct loc_network_tree_node* node = tree->root;
425
39a55353 426 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 427 // Check if the ith bit is one or zero
2a30e4de 428 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
3b5f4af2
MT
429 }
430
431 return node;
432}
433
434static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
435 int(*filter_callback)(struct loc_network* network, void* data),
436 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
437 int r;
438
439 // Finding a network ends the walk here
440 if (node->network) {
441 if (filter_callback) {
f3e02bc5 442 int f = filter_callback(node->network, data);
3b5f4af2
MT
443 if (f < 0)
444 return f;
445
446 // Skip network if filter function returns value greater than zero
447 if (f > 0)
448 return 0;
449 }
450
f3e02bc5 451 r = callback(node->network, data);
3b5f4af2
MT
452 if (r)
453 return r;
454 }
455
456 // Walk down on the left side of the tree first
457 if (node->zero) {
f3e02bc5 458 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
459 if (r)
460 return r;
461 }
462
463 // Then walk on the other side
464 if (node->one) {
f3e02bc5 465 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
466 if (r)
467 return r;
468 }
469
470 return 0;
471}
472
f3e02bc5
MT
473LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
474 int(*filter_callback)(struct loc_network* network, void* data),
475 int(*callback)(struct loc_network* network, void* data), void* data) {
476 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
477}
478
3b5f4af2
MT
479static void loc_network_tree_free(struct loc_network_tree* tree) {
480 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
481
438db08c 482 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
483
484 loc_unref(tree->ctx);
485 free(tree);
486}
487
488LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
489 if (--tree->refcount > 0)
490 return tree;
491
492 loc_network_tree_free(tree);
493 return NULL;
494}
495
e9b4e2a8 496static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
497 DEBUG(network->ctx, "Dumping network at %p\n", network);
498
499 char* s = loc_network_str(network);
500 if (!s)
501 return 1;
502
503 INFO(network->ctx, "%s\n", s);
504 free(s);
505
506 return 0;
507}
508
509LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
510 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
511
f3e02bc5 512 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
513}
514
515LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
516 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
517
39a55353
MT
518 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
519 &network->start_address, network->prefix);
3b5f4af2
MT
520 if (!node) {
521 ERROR(tree->ctx, "Could not find a node\n");
522 return -ENOMEM;
523 }
524
525 // Check if node has not been set before
526 if (node->network) {
527 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 528 return -EBUSY;
3b5f4af2
MT
529 }
530
531 // Point node to the network
532 node->network = loc_network_ref(network);
533
534 return 0;
535}
f3e02bc5
MT
536
537static int __loc_network_tree_count(struct loc_network* network, void* data) {
538 size_t* counter = (size_t*)data;
539
540 // Increase the counter for each network
541 counter++;
542
543 return 0;
544}
545
546LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
547 size_t counter = 0;
548
549 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
550 if (r)
551 return r;
552
553 return counter;
554}
940f9c2b
MT
555
556static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
557 size_t counter = 1;
558
559 if (node->zero)
560 counter += __loc_network_tree_count_nodes(node->zero);
561
562 if (node->one)
563 counter += __loc_network_tree_count_nodes(node->one);
564
565 return counter;
566}
567
568LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
569 return __loc_network_tree_count_nodes(tree->root);
570}
438db08c
MT
571
572LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
573 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
574 if (!n)
575 return -ENOMEM;
576
577 n->ctx = loc_ref(ctx);
578 n->refcount = 1;
579
580 n->zero = n->one = NULL;
581
582 DEBUG(n->ctx, "Network node allocated at %p\n", n);
583 *node = n;
584 return 0;
585}
586
587LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
588 if (node)
589 node->refcount++;
590
591 return node;
592}
593
594static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
595 DEBUG(node->ctx, "Releasing network node at %p\n", node);
596
597 if (node->network)
598 loc_network_unref(node->network);
599
600 if (node->zero)
601 loc_network_tree_node_unref(node->zero);
602
603 if (node->one)
604 loc_network_tree_node_unref(node->one);
605
606 loc_unref(node->ctx);
607 free(node);
608}
609
610LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
611 if (!node)
612 return NULL;
613
614 if (--node->refcount > 0)
615 return node;
616
617 loc_network_tree_node_free(node);
618 return NULL;
619}
620
621LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
622 if (index == 0)
623 node = node->zero;
624 else
625 node = node->one;
626
627 if (!node)
628 return NULL;
629
630 return loc_network_tree_node_ref(node);
631}
632
633LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
634 return (!!node->network);
635}
636
637LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
638 return loc_network_ref(node->network);
639}