]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network.c
network-tree: Use the raw prefix to place networks onto the 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>
19#include <errno.h>
71ff3e69 20#include <stdio.h>
3b5f4af2
MT
21#include <stdlib.h>
22#include <string.h>
23
42f3ccd7
MT
24#ifdef HAVE_ENDIAN_H
25# include <endian.h>
26#endif
27
c12a1385 28#include <libloc/libloc.h>
0b1fef38 29#include <libloc/address.h>
c12a1385
MT
30#include <libloc/compat.h>
31#include <libloc/country.h>
32#include <libloc/network.h>
33#include <libloc/network-list.h>
34#include <libloc/private.h>
3b5f4af2
MT
35
36struct loc_network {
37 struct loc_ctx* ctx;
38 int refcount;
39
c3068be1 40 int family;
ce4f5752 41 struct in6_addr first_address;
b43edb61 42 struct in6_addr last_address;
3b5f4af2
MT
43 unsigned int prefix;
44
45 char country_code[3];
71ff3e69 46 uint32_t asn;
a293f829 47 enum loc_network_flags flags;
0a0a289a
MT
48
49 char string[INET6_ADDRSTRLEN + 4];
3b5f4af2
MT
50};
51
52LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network,
10778041 53 struct in6_addr* address, unsigned int prefix) {
6183c0f2 54 // Validate the prefix
1fd09d0b 55 if (!loc_address_valid_prefix(address, prefix)) {
95b6a8e4 56 ERROR(ctx, "Invalid prefix in %s: %u\n", loc_address_str(address), prefix);
5559e086
MT
57 errno = EINVAL;
58 return 1;
6183c0f2
MT
59 }
60
3b5f4af2 61 struct loc_network* n = calloc(1, sizeof(*n));
198e382c 62 if (!n)
5559e086 63 return 1;
3b5f4af2
MT
64
65 n->ctx = loc_ref(ctx);
66 n->refcount = 1;
67
b43edb61 68 // Store the prefix
1fd09d0b
MT
69 if (IN6_IS_ADDR_V4MAPPED(address))
70 n->prefix = prefix + 96;
71 else
72 n->prefix = prefix;
3b5f4af2 73
b43edb61 74 // Convert the prefix into a bitmask
30f0f0ed 75 struct in6_addr bitmask = loc_prefix_to_bitmask(n->prefix);
b43edb61
MT
76
77 // Store the first and last address in the network
30f0f0ed
MT
78 n->first_address = loc_address_and(address, &bitmask);
79 n->last_address = loc_address_or(&n->first_address, &bitmask);
b43edb61 80
c3068be1 81 // Set family
71877f5f 82 n->family = loc_address_family(&n->first_address);
c3068be1 83
3b5f4af2
MT
84 DEBUG(n->ctx, "Network allocated at %p\n", n);
85 *network = n;
86 return 0;
87}
88
95b6a8e4
MT
89LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx,
90 struct loc_network** network, const char* string) {
91 struct in6_addr address;
92 unsigned int prefix;
13ad6e69 93
95b6a8e4
MT
94 // Parse the input
95 int r = loc_address_parse(&address, &prefix, string);
13ad6e69 96 if (r) {
95b6a8e4 97 ERROR(ctx, "Could not parse network %s: %m\n", string);
13ad6e69 98 return r;
95b6a8e4 99 }
13ad6e69 100
13ad6e69 101 // Create a new network
95b6a8e4 102 return loc_network_new(ctx, network, &address, prefix);
3b5f4af2
MT
103}
104
105LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) {
106 network->refcount++;
107
108 return network;
109}
110
111static void loc_network_free(struct loc_network* network) {
112 DEBUG(network->ctx, "Releasing network at %p\n", network);
113
3b5f4af2
MT
114 loc_unref(network->ctx);
115 free(network);
116}
117
118LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) {
119 if (--network->refcount > 0)
120 return network;
121
122 loc_network_free(network);
123 return NULL;
124}
125
0a0a289a
MT
126LOC_EXPORT const char* loc_network_str(struct loc_network* network) {
127 if (!*network->string) {
128 // Format the address
129 const char* address = loc_address_str(&network->first_address);
130 if (!address)
131 return NULL;
d3409788 132
0a0a289a
MT
133 // Fetch the prefix
134 unsigned int prefix = loc_network_prefix(network);
d3409788 135
0a0a289a
MT
136 // Format the string
137 int r = snprintf(network->string, sizeof(network->string) - 1,
138 "%s/%u", address, prefix);
139 if (r < 0) {
140 ERROR(network->ctx, "Could not format network string: %m\n");
141 *network->string = '\0';
142 return NULL;
143 }
3b5f4af2
MT
144 }
145
0a0a289a 146 return network->string;
3b5f4af2
MT
147}
148
44e5ef71 149LOC_EXPORT int loc_network_address_family(struct loc_network* network) {
c3068be1 150 return network->family;
44e5ef71
MT
151}
152
7fe6a218
MT
153LOC_EXPORT unsigned int loc_network_prefix(struct loc_network* network) {
154 switch (network->family) {
155 case AF_INET6:
156 return network->prefix;
157
158 case AF_INET:
159 return network->prefix - 96;
160 }
161
162 return 0;
163}
164
a92139f1
MT
165unsigned int loc_network_raw_prefix(struct loc_network* network) {
166 return network->prefix;
167}
168
a1a00053
MT
169LOC_EXPORT const struct in6_addr* loc_network_get_first_address(struct loc_network* network) {
170 return &network->first_address;
171}
172
0a0a289a
MT
173LOC_EXPORT const char* loc_network_format_first_address(struct loc_network* network) {
174 return loc_address_str(&network->first_address);
2b9338ea
MT
175}
176
a1a00053
MT
177LOC_EXPORT const struct in6_addr* loc_network_get_last_address(struct loc_network* network) {
178 return &network->last_address;
179}
180
0a0a289a
MT
181LOC_EXPORT const char* loc_network_format_last_address(struct loc_network* network) {
182 return loc_address_str(&network->last_address);
2b9338ea
MT
183}
184
0258d3c9 185LOC_EXPORT int loc_network_matches_address(struct loc_network* network, const struct in6_addr* address) {
f50adb09 186 // Address must be larger than the start address
9c1e2943 187 if (loc_address_cmp(&network->first_address, address) > 0)
fc692a58 188 return 0;
2a30e4de 189
2a30e4de 190 // Address must be smaller than the last address
9c1e2943 191 if (loc_address_cmp(&network->last_address, address) < 0)
fc692a58 192 return 0;
2a30e4de
MT
193
194 // The address is inside this network
fc692a58 195 return 1;
2a30e4de
MT
196}
197
3b5f4af2
MT
198LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) {
199 return network->country_code;
200}
201
202LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) {
901ef882
MT
203 // Set empty country code
204 if (!country_code || !*country_code) {
205 *network->country_code = '\0';
206 return 0;
207 }
208
57146963
MT
209 // Check country code
210 if (!loc_country_code_is_valid(country_code))
3b5f4af2
MT
211 return -EINVAL;
212
a7a3d958 213 loc_country_code_copy(network->country_code, country_code);
3b5f4af2
MT
214
215 return 0;
216}
217
0258d3c9 218LOC_EXPORT int loc_network_matches_country_code(struct loc_network* network, const char* country_code) {
0b27a567
MT
219 // Search for any special flags
220 const int flag = loc_country_special_code_to_flag(country_code);
221
222 // If we found a flag, we will return whether it is set or not
223 if (flag)
224 return loc_network_has_flag(network, flag);
225
57146963
MT
226 // Check country code
227 if (!loc_country_code_is_valid(country_code))
e3f696c1
MT
228 return -EINVAL;
229
0b27a567 230 // Check for an exact match
e3f696c1
MT
231 return (network->country_code[0] == country_code[0])
232 && (network->country_code[1] == country_code[1]);
233}
234
71ff3e69
MT
235LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) {
236 return network->asn;
237}
238
239LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) {
240 network->asn = asn;
241
242 return 0;
243}
244
a99e7c2b
MT
245LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) {
246 return network->flags & flag;
247}
248
249LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) {
250 network->flags |= flag;
251
252 return 0;
253}
254
af4689bf 255LOC_EXPORT int loc_network_cmp(struct loc_network* self, struct loc_network* other) {
af4689bf 256 // Compare address
9c1e2943 257 int r = loc_address_cmp(&self->first_address, &other->first_address);
af4689bf
MT
258 if (r)
259 return r;
260
261 // Compare prefix
262 if (self->prefix > other->prefix)
263 return 1;
264 else if (self->prefix < other->prefix)
265 return -1;
266
267 // Both networks are equal
268 return 0;
269}
270
fe8277ed 271int loc_network_properties_cmp(struct loc_network* self, struct loc_network* other) {
f6c6b8d6
MT
272 int r;
273
274 // Check country code
275 r = loc_country_code_cmp(self->country_code, other->country_code);
276 if (r)
277 return r;
278
279 // Check ASN
280 if (self->asn > other->asn)
281 return 1;
282 else if (self->asn < other->asn)
283 return -1;
284
285 // Check flags
286 if (self->flags > other->flags)
287 return 1;
288 else if (self->flags < other->flags)
289 return -1;
290
291 return 0;
292}
293
6159d384 294LOC_EXPORT int loc_network_overlaps(struct loc_network* self, struct loc_network* other) {
fc692a58 295 // Either of the start addresses must be in the other subnet
0258d3c9 296 if (loc_network_matches_address(self, &other->first_address))
6159d384
MT
297 return 1;
298
0258d3c9 299 if (loc_network_matches_address(other, &self->first_address))
6159d384
MT
300 return 1;
301
fc692a58 302 // Or either of the end addresses is in the other subnet
0258d3c9 303 if (loc_network_matches_address(self, &other->last_address))
6159d384
MT
304 return 1;
305
0258d3c9 306 if (loc_network_matches_address(other, &self->last_address))
6159d384
MT
307 return 1;
308
309 return 0;
310}
311
33a051e0 312LOC_EXPORT int loc_network_is_subnet(struct loc_network* self, struct loc_network* other) {
cf8d3c64
MT
313 // The prefix must be smaller (this avoids the more complex comparisons later)
314 if (self->prefix > other->prefix)
315 return 0;
316
33a051e0
MT
317 // If the start address of the other network is smaller than this network,
318 // it cannot be a subnet.
9c1e2943 319 if (loc_address_cmp(&self->first_address, &other->first_address) > 0)
33a051e0
MT
320 return 0;
321
322 // If the end address of the other network is greater than this network,
323 // it cannot be a subnet.
9c1e2943 324 if (loc_address_cmp(&self->last_address, &other->last_address) < 0)
33a051e0
MT
325 return 0;
326
327 return 1;
328}
329
a5967330
MT
330LOC_EXPORT int loc_network_subnets(struct loc_network* network,
331 struct loc_network** subnet1, struct loc_network** subnet2) {
332 int r;
333 *subnet1 = NULL;
334 *subnet2 = NULL;
850e7516
MT
335
336 // New prefix length
47b55e70 337 unsigned int prefix = loc_network_prefix(network) + 1;
850e7516
MT
338
339 // Check if the new prefix is valid
92ba094c
MT
340 if (!loc_address_valid_prefix(&network->first_address, prefix)) {
341 ERROR(network->ctx, "Invalid prefix: %d\n", prefix);
342 errno = EINVAL;
343 return 1;
344 }
850e7516
MT
345
346 // Create the first half of the network
a5967330 347 r = loc_network_new(network->ctx, subnet1, &network->first_address, prefix);
850e7516 348 if (r)
a5967330 349 return r;
850e7516
MT
350
351 // The next subnet starts after the first one
5b72642c
MT
352 struct in6_addr first_address = (*subnet1)->last_address;
353 loc_address_increment(&first_address);
850e7516
MT
354
355 // Create the second half of the network
a5967330 356 r = loc_network_new(network->ctx, subnet2, &first_address, prefix);
850e7516 357 if (r)
a5967330 358 return r;
850e7516 359
594ca328
MT
360 // Copy country code
361 const char* country_code = loc_network_get_country_code(network);
362 if (country_code) {
a5967330
MT
363 loc_network_set_country_code(*subnet1, country_code);
364 loc_network_set_country_code(*subnet2, country_code);
594ca328
MT
365 }
366
367 // Copy ASN
368 uint32_t asn = loc_network_get_asn(network);
369 if (asn) {
a5967330
MT
370 loc_network_set_asn(*subnet1, asn);
371 loc_network_set_asn(*subnet2, asn);
594ca328
MT
372 }
373
3e8d86ab
MT
374 // Copy flags
375 loc_network_set_flag(*subnet1, network->flags);
376 loc_network_set_flag(*subnet2, network->flags);
377
a5967330
MT
378 return 0;
379}
850e7516 380
a5967330
MT
381static int __loc_network_exclude(struct loc_network* network,
382 struct loc_network* other, struct loc_network_list* list) {
383 struct loc_network* subnet1 = NULL;
384 struct loc_network* subnet2 = NULL;
385
386 int r = loc_network_subnets(network, &subnet1, &subnet2);
387 if (r)
388 goto ERROR;
389
da101d55 390 if (loc_network_cmp(other, subnet1) == 0) {
a5967330
MT
391 r = loc_network_list_push(list, subnet2);
392 if (r)
393 goto ERROR;
394
da101d55 395 } else if (loc_network_cmp(other, subnet2) == 0) {
a5967330
MT
396 r = loc_network_list_push(list, subnet1);
397 if (r)
398 goto ERROR;
399
d6a5092f 400 } else if (loc_network_is_subnet(subnet1, other)) {
a5967330
MT
401 r = loc_network_list_push(list, subnet2);
402 if (r)
403 goto ERROR;
404
405 r = __loc_network_exclude(subnet1, other, list);
406 if (r)
407 goto ERROR;
408
d6a5092f 409 } else if (loc_network_is_subnet(subnet2, other)) {
a5967330
MT
410 r = loc_network_list_push(list, subnet1);
411 if (r)
412 goto ERROR;
413
414 r = __loc_network_exclude(subnet2, other, list);
415 if (r)
416 goto ERROR;
417
418 } else {
419 ERROR(network->ctx, "We should never get here\n");
420 r = 1;
421 goto ERROR;
422 }
850e7516
MT
423
424ERROR:
425 if (subnet1)
426 loc_network_unref(subnet1);
427
428 if (subnet2)
429 loc_network_unref(subnet2);
430
725718e1
MT
431 if (r)
432 DEBUG(network->ctx, "%s has failed with %d\n", __FUNCTION__, r);
433
a5967330 434 return r;
850e7516
MT
435}
436
c650008e
MT
437static int __loc_network_exclude_to_list(struct loc_network* self,
438 struct loc_network* other, struct loc_network_list* list) {
850e7516 439 // Other must be a subnet of self
d6a5092f 440 if (!loc_network_is_subnet(self, other)) {
850e7516
MT
441 DEBUG(self->ctx, "Network %p is not contained in network %p\n", other, self);
442
abf55926
MT
443 // Exit silently
444 return 0;
850e7516
MT
445 }
446
447 // We cannot perform this operation if both networks equal
da101d55 448 if (loc_network_cmp(self, other) == 0) {
850e7516
MT
449 DEBUG(self->ctx, "Networks %p and %p are equal\n", self, other);
450
abf55926
MT
451 // Exit silently
452 return 0;
850e7516
MT
453 }
454
c650008e
MT
455 return __loc_network_exclude(self, other, list);
456}
457
458LOC_EXPORT struct loc_network_list* loc_network_exclude(
459 struct loc_network* self, struct loc_network* other) {
460 struct loc_network_list* list;
461
0a0a289a
MT
462 DEBUG(self->ctx, "Returning %s excluding %s...\n",
463 loc_network_str(self), loc_network_str(other));
c650008e 464
850e7516
MT
465 // Create a new list with the result
466 int r = loc_network_list_new(self->ctx, &list);
467 if (r) {
468 ERROR(self->ctx, "Could not create network list: %d\n", r);
c650008e 469
850e7516
MT
470 return NULL;
471 }
472
c650008e 473 r = __loc_network_exclude_to_list(self, other, list);
a5967330
MT
474 if (r) {
475 loc_network_list_unref(list);
850e7516 476
a5967330 477 return NULL;
850e7516
MT
478 }
479
850e7516
MT
480 // Return the result
481 return list;
850e7516
MT
482}
483
add5bb65
MT
484LOC_EXPORT struct loc_network_list* loc_network_exclude_list(
485 struct loc_network* network, struct loc_network_list* list) {
486 struct loc_network_list* to_check;
487
488 // Create a new list with all networks to look at
489 int r = loc_network_list_new(network->ctx, &to_check);
490 if (r)
491 return NULL;
492
493 struct loc_network* subnet = NULL;
494 struct loc_network_list* subnets = NULL;
495
496 for (unsigned int i = 0; i < loc_network_list_size(list); i++) {
497 subnet = loc_network_list_get(list, i);
498
499 // Find all excluded networks
c650008e
MT
500 if (!loc_network_list_contains(to_check, subnet)) {
501 r = __loc_network_exclude_to_list(network, subnet, to_check);
502 if (r) {
503 loc_network_list_unref(to_check);
504 loc_network_unref(subnet);
505
506 return NULL;
507 }
add5bb65
MT
508 }
509
510 // Cleanup
511 loc_network_unref(subnet);
512 }
513
514 r = loc_network_list_new(network->ctx, &subnets);
515 if (r) {
516 loc_network_list_unref(to_check);
517 return NULL;
518 }
519
058e7800
MT
520 off_t smallest_subnet = 0;
521
add5bb65 522 while (!loc_network_list_empty(to_check)) {
058e7800 523 struct loc_network* subnet_to_check = loc_network_list_pop_first(to_check);
add5bb65 524
06177d8c
MT
525 // Check whether the subnet to check is part of the input list
526 if (loc_network_list_contains(list, subnet_to_check)) {
527 loc_network_unref(subnet_to_check);
528 continue;
529 }
530
add5bb65
MT
531 // Marks whether this subnet passed all checks
532 int passed = 1;
533
058e7800 534 for (unsigned int i = smallest_subnet; i < loc_network_list_size(list); i++) {
add5bb65
MT
535 subnet = loc_network_list_get(list, i);
536
add5bb65 537 // Drop this subnet if is a subnet of another subnet
4de8ff8e 538 if (loc_network_is_subnet(subnet, subnet_to_check)) {
add5bb65
MT
539 passed = 0;
540 loc_network_unref(subnet);
541 break;
542 }
543
544 // Break it down if it overlaps
4de8ff8e 545 if (loc_network_overlaps(subnet, subnet_to_check)) {
add5bb65
MT
546 passed = 0;
547
4fc034e2 548 __loc_network_exclude_to_list(subnet_to_check, subnet, to_check);
add5bb65
MT
549
550 loc_network_unref(subnet);
551 break;
552 }
553
058e7800
MT
554 // If the subnet is strictly greater, we do not need to continue the search
555 r = loc_network_cmp(subnet, subnet_to_check);
556 if (r > 0) {
557 loc_network_unref(subnet);
558 break;
559
560 // If it is strictly smaller, we can continue the search from here next
561 // time because all networks that are to be checked can only be larger
562 // than this one.
563 } else if (r < 0) {
564 smallest_subnet = i;
565 }
566
add5bb65
MT
567 loc_network_unref(subnet);
568 }
569
570 if (passed) {
571 r = loc_network_list_push(subnets, subnet_to_check);
572 }
573
574 loc_network_unref(subnet_to_check);
575 }
576
577 loc_network_list_unref(to_check);
578
579 return subnets;
580}
581
fe8277ed 582int loc_network_merge(struct loc_network** n,
c1ce4349
MT
583 struct loc_network* n1, struct loc_network* n2) {
584 struct loc_network* network = NULL;
585 struct in6_addr address;
586 int r;
587
588 // Reset pointer
589 *n = NULL;
590
fe8277ed
MT
591 DEBUG(n1->ctx, "Attempting to merge %s and %s\n", loc_network_str(n1), loc_network_str(n2));
592
c1ce4349
MT
593 // Family must match
594 if (n1->family != n2->family)
595 return 0;
596
597 // The prefix must match, too
598 if (n1->prefix != n2->prefix)
599 return 0;
600
601 // Cannot merge ::/0 or 0.0.0.0/0
602 if (!n1->prefix || !n2->prefix)
603 return 0;
604
605 const unsigned int prefix = loc_network_prefix(n1);
606
607 // How many bits do we need to represent this address?
608 const size_t bitlength = loc_address_bit_length(&n1->first_address) - 1;
609
610 // We cannot shorten this any more
f6d762e0 611 if (bitlength < prefix)
c1ce4349
MT
612 return 0;
613
614 // Increment the last address of the first network
615 address = n1->last_address;
616 loc_address_increment(&address);
617
618 // If they don't match they are not neighbours
619 if (loc_address_cmp(&address, &n2->first_address) != 0)
620 return 0;
621
622 // All properties must match, too
623 if (loc_network_properties_cmp(n1, n2) != 0)
624 return 0;
625
626 // Create a new network object
627 r = loc_network_new(n1->ctx, &network, &n1->first_address, prefix - 1);
628 if (r)
629 return r;
630
631 // Copy everything else
632 loc_country_code_copy(network->country_code, n1->country_code);
633 network->asn = n1->asn;
634 network->flags = n1->flags;
635
636 // Return pointer
637 *n = network;
638
639 return 0;
640}
641
8298728e 642int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) {
f3e02bc5 643 // Add country code
a7a3d958 644 loc_country_code_copy(dbobj->country_code, network->country_code);
f3e02bc5
MT
645
646 // Add ASN
71ff3e69 647 dbobj->asn = htobe32(network->asn);
f3e02bc5 648
a99e7c2b 649 // Flags
eee6346d 650 dbobj->flags = htobe16(network->flags);
a99e7c2b 651
f3e02bc5
MT
652 return 0;
653}
654
8298728e 655int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network,
b904896a 656 struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) {
3dc8adfb 657 char country_code[3] = "\0\0";
a7a3d958 658
1fd09d0b
MT
659 // Adjust prefix for IPv4
660 if (IN6_IS_ADDR_V4MAPPED(address))
661 prefix -= 96;
662
39a55353 663 int r = loc_network_new(ctx, network, address, prefix);
94a3f329 664 if (r) {
198e382c 665 ERROR(ctx, "Could not allocate a new network: %m\n");
10778041 666 return r;
94a3f329 667 }
10778041
MT
668
669 // Import country code
a7a3d958 670 loc_country_code_copy(country_code, dbobj->country_code);
10778041
MT
671
672 r = loc_network_set_country_code(*network, country_code);
94a3f329
MT
673 if (r) {
674 ERROR(ctx, "Could not set country code: %s\n", country_code);
10778041 675 return r;
94a3f329 676 }
10778041
MT
677
678 // Import ASN
94a3f329
MT
679 uint32_t asn = be32toh(dbobj->asn);
680 r = loc_network_set_asn(*network, asn);
681 if (r) {
682 ERROR(ctx, "Could not set ASN: %d\n", asn);
10778041 683 return r;
94a3f329 684 }
10778041 685
a99e7c2b 686 // Import flags
94a3f329
MT
687 int flags = be16toh(dbobj->flags);
688 r = loc_network_set_flag(*network, flags);
689 if (r) {
690 ERROR(ctx, "Could not set flags: %d\n", flags);
a99e7c2b 691 return r;
94a3f329 692 }
a99e7c2b 693
10778041
MT
694 return 0;
695}