]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
Pass right variable to IN6_IN_ADDR_* functions
[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
71ff3e69
MT
308LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
309 return network->asn;
310}
311
312LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
313 network->asn = asn;
314
315 return 0;
316}
317
f3e02bc5 318LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) {
f3e02bc5
MT
319 // Add country code
320 for (unsigned int i = 0; i < 2; i++) {
04cdff09 321 dbobj->country_code[i] = network->country_code[i] ? network->country_code[i] : '\0';
f3e02bc5
MT
322 }
323
324 // Add ASN
71ff3e69 325 dbobj->asn = htobe32(network->asn);
f3e02bc5
MT
326
327 return 0;
328}
329
10778041 330LOC_EXPORT int loc_network_new_from_database_v0(struct loc_ctx* ctx, struct loc_network** network,
39a55353
MT
331 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v0* dbobj) {
332 int r = loc_network_new(ctx, network, address, prefix);
10778041
MT
333 if (r)
334 return r;
335
336 // Import country code
337 char country_code[3];
338 for (unsigned int i = 0; i < 2; i++) {
339 country_code[i] = dbobj->country_code[i];
340 }
341 country_code[2] = '\0';
342
343 r = loc_network_set_country_code(*network, country_code);
344 if (r)
345 return r;
346
347 // Import ASN
348 r = loc_network_set_asn(*network, be32toh(dbobj->asn));
349 if (r)
350 return r;
351
352 return 0;
353}
354
3b5f4af2
MT
355struct loc_network_tree {
356 struct loc_ctx* ctx;
357 int refcount;
358
359 struct loc_network_tree_node* root;
360};
361
362struct loc_network_tree_node {
438db08c
MT
363 struct loc_ctx* ctx;
364 int refcount;
365
3b5f4af2
MT
366 struct loc_network_tree_node* zero;
367 struct loc_network_tree_node* one;
368
369 struct loc_network* network;
370};
371
372LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree** tree) {
373 struct loc_network_tree* t = calloc(1, sizeof(*t));
374 if (!t)
375 return -ENOMEM;
376
377 t->ctx = loc_ref(ctx);
378 t->refcount = 1;
379
380 // Create the root node
438db08c
MT
381 int r = loc_network_tree_node_new(ctx, &t->root);
382 if (r) {
383 loc_network_tree_unref(t);
384 return r;
385 }
3b5f4af2
MT
386
387 DEBUG(t->ctx, "Network tree allocated at %p\n", t);
388 *tree = t;
389 return 0;
390}
391
438db08c
MT
392LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) {
393 return loc_network_tree_node_ref(tree->root);
3b5f4af2
MT
394}
395
396static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) {
397 struct loc_network_tree_node** n;
398
2a30e4de 399 if (path == 0)
3b5f4af2 400 n = &node->zero;
3eda9cab
MT
401 else
402 n = &node->one;
3b5f4af2
MT
403
404 // If the desired node doesn't exist, yet, we will create it
405 if (*n == NULL) {
438db08c 406 int r = loc_network_tree_node_new(node->ctx, n);
3b5f4af2
MT
407 if (r)
408 return NULL;
409 }
410
411 return *n;
412}
413
39a55353 414static 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
415 struct loc_network_tree_node* node = tree->root;
416
39a55353 417 for (unsigned int i = 0; i < prefix; i++) {
3b5f4af2 418 // Check if the ith bit is one or zero
2a30e4de 419 node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i));
3b5f4af2
MT
420 }
421
422 return node;
423}
424
425static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node,
f3e02bc5
MT
426 int(*filter_callback)(struct loc_network* network, void* data),
427 int(*callback)(struct loc_network* network, void* data), void* data) {
3b5f4af2
MT
428 int r;
429
430 // Finding a network ends the walk here
431 if (node->network) {
432 if (filter_callback) {
f3e02bc5 433 int f = filter_callback(node->network, data);
3b5f4af2
MT
434 if (f < 0)
435 return f;
436
437 // Skip network if filter function returns value greater than zero
438 if (f > 0)
439 return 0;
440 }
441
f3e02bc5 442 r = callback(node->network, data);
3b5f4af2
MT
443 if (r)
444 return r;
445 }
446
447 // Walk down on the left side of the tree first
448 if (node->zero) {
f3e02bc5 449 r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data);
3b5f4af2
MT
450 if (r)
451 return r;
452 }
453
454 // Then walk on the other side
455 if (node->one) {
f3e02bc5 456 r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data);
3b5f4af2
MT
457 if (r)
458 return r;
459 }
460
461 return 0;
462}
463
f3e02bc5
MT
464LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree,
465 int(*filter_callback)(struct loc_network* network, void* data),
466 int(*callback)(struct loc_network* network, void* data), void* data) {
467 return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data);
468}
469
3b5f4af2
MT
470static void loc_network_tree_free(struct loc_network_tree* tree) {
471 DEBUG(tree->ctx, "Releasing network tree at %p\n", tree);
472
438db08c 473 loc_network_tree_node_unref(tree->root);
3b5f4af2
MT
474
475 loc_unref(tree->ctx);
476 free(tree);
477}
478
479LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tree* tree) {
480 if (--tree->refcount > 0)
481 return tree;
482
483 loc_network_tree_free(tree);
484 return NULL;
485}
486
e9b4e2a8 487static int __loc_network_tree_dump(struct loc_network* network, void* data) {
3b5f4af2
MT
488 DEBUG(network->ctx, "Dumping network at %p\n", network);
489
490 char* s = loc_network_str(network);
491 if (!s)
492 return 1;
493
494 INFO(network->ctx, "%s\n", s);
495 free(s);
496
497 return 0;
498}
499
500LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) {
501 DEBUG(tree->ctx, "Dumping network tree at %p\n", tree);
502
f3e02bc5 503 return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL);
3b5f4af2
MT
504}
505
506LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) {
507 DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree);
508
39a55353
MT
509 struct loc_network_tree_node* node = loc_network_tree_get_path(tree,
510 &network->start_address, network->prefix);
3b5f4af2
MT
511 if (!node) {
512 ERROR(tree->ctx, "Could not find a node\n");
513 return -ENOMEM;
514 }
515
516 // Check if node has not been set before
517 if (node->network) {
518 DEBUG(tree->ctx, "There is already a network at this path\n");
c04005bb 519 return -EBUSY;
3b5f4af2
MT
520 }
521
522 // Point node to the network
523 node->network = loc_network_ref(network);
524
525 return 0;
526}
f3e02bc5
MT
527
528static int __loc_network_tree_count(struct loc_network* network, void* data) {
529 size_t* counter = (size_t*)data;
530
531 // Increase the counter for each network
532 counter++;
533
534 return 0;
535}
536
537LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) {
538 size_t counter = 0;
539
540 int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter);
541 if (r)
542 return r;
543
544 return counter;
545}
940f9c2b
MT
546
547static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) {
548 size_t counter = 1;
549
550 if (node->zero)
551 counter += __loc_network_tree_count_nodes(node->zero);
552
553 if (node->one)
554 counter += __loc_network_tree_count_nodes(node->one);
555
556 return counter;
557}
558
559LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) {
560 return __loc_network_tree_count_nodes(tree->root);
561}
438db08c
MT
562
563LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) {
564 struct loc_network_tree_node* n = calloc(1, sizeof(*n));
565 if (!n)
566 return -ENOMEM;
567
568 n->ctx = loc_ref(ctx);
569 n->refcount = 1;
570
571 n->zero = n->one = NULL;
572
573 DEBUG(n->ctx, "Network node allocated at %p\n", n);
574 *node = n;
575 return 0;
576}
577
578LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) {
579 if (node)
580 node->refcount++;
581
582 return node;
583}
584
585static void loc_network_tree_node_free(struct loc_network_tree_node* node) {
586 DEBUG(node->ctx, "Releasing network node at %p\n", node);
587
588 if (node->network)
589 loc_network_unref(node->network);
590
591 if (node->zero)
592 loc_network_tree_node_unref(node->zero);
593
594 if (node->one)
595 loc_network_tree_node_unref(node->one);
596
597 loc_unref(node->ctx);
598 free(node);
599}
600
601LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) {
602 if (!node)
603 return NULL;
604
605 if (--node->refcount > 0)
606 return node;
607
608 loc_network_tree_node_free(node);
609 return NULL;
610}
611
612LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) {
613 if (index == 0)
614 node = node->zero;
615 else
616 node = node->one;
617
618 if (!node)
619 return NULL;
620
621 return loc_network_tree_node_ref(node);
622}
623
624LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) {
625 return (!!node->network);
626}
627
628LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) {
629 return loc_network_ref(node->network);
630}