X-Git-Url: http://git.ipfire.org/?p=people%2Fms%2Flibloc.git;a=blobdiff_plain;f=src%2Fnetwork.c;h=3d386126f103bd3c8716ea3f98ad353588cd4a80;hp=bf11f2a68b46c360ed1ed5caf4404309c086f20c;hb=c04005bbd5a1e2cbef630600cd229b8a6b49bf14;hpb=3b5f4af2cd45de45599774740290a081cd3d220d diff --git a/src/network.c b/src/network.c index bf11f2a..3d38612 100644 --- a/src/network.c +++ b/src/network.c @@ -16,15 +16,15 @@ #include #include +#include #include +#include #include #include #include #include - -#include "libloc-private.h" -#include "as.h" +#include struct loc_network { struct loc_ctx* ctx; @@ -34,36 +34,95 @@ struct loc_network { unsigned int prefix; char country_code[3]; - - struct loc_as* as; + uint32_t asn; }; +static int valid_prefix(struct in6_addr* address, unsigned int prefix) { + // The prefix cannot be larger than 128 bits + if (prefix > 128) + return 1; + + // And the prefix cannot be zero + if (prefix == 0) + return 1; + + // For IPv4-mapped addresses the prefix has to be 96 or lager + if (IN6_IS_ADDR_V4MAPPED(address) && prefix <= 96) + return 1; + + return 0; +} + +static struct in6_addr prefix_to_bitmask(unsigned int prefix) { + struct in6_addr bitmask; + + for (unsigned int i = 0; i < 16; i++) + bitmask.s6_addr[i] = 0; + + for (int i = prefix, j = 0; i > 0; i -= 8, j++) { + if (i >= 8) + bitmask.s6_addr[j] = 0xff; + else + bitmask.s6_addr[j] = 0xff << (8 - i); + } + + return bitmask; +} + +static struct in6_addr make_start_address(const struct in6_addr* address, unsigned int prefix) { + struct in6_addr a; + struct in6_addr bitmask = prefix_to_bitmask(prefix); + + // Perform bitwise AND + for (unsigned int i = 0; i < 4; i++) + a.s6_addr32[i] = address->s6_addr32[i] & bitmask.s6_addr32[i]; + + return a; +} + +static struct in6_addr make_last_address(const struct in6_addr* address, unsigned int prefix) { + struct in6_addr a; + struct in6_addr bitmask = prefix_to_bitmask(prefix); + + // Perform bitwise OR + for (unsigned int i = 0; i < 4; i++) + a.s6_addr32[i] = address->s6_addr32[i] | ~bitmask.s6_addr32[i]; + + return a; +} + LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network, - struct in6_addr start_address, unsigned int prefix) { + struct in6_addr* address, unsigned int prefix) { // Address cannot be unspecified - if (IN6_IS_ADDR_UNSPECIFIED(&start_address)) { + if (IN6_IS_ADDR_UNSPECIFIED(&address)) { DEBUG(ctx, "Start address is unspecified\n"); return -EINVAL; } // Address cannot be loopback - if (IN6_IS_ADDR_LOOPBACK(&start_address)) { + if (IN6_IS_ADDR_LOOPBACK(&address)) { DEBUG(ctx, "Start address is loopback address\n"); return -EINVAL; } // Address cannot be link-local - if (IN6_IS_ADDR_LINKLOCAL(&start_address)) { + if (IN6_IS_ADDR_LINKLOCAL(&address)) { DEBUG(ctx, "Start address cannot be link-local\n"); return -EINVAL; } // Address cannot be site-local - if (IN6_IS_ADDR_SITELOCAL(&start_address)) { + if (IN6_IS_ADDR_SITELOCAL(&address)) { DEBUG(ctx, "Start address cannot be site-local\n"); return -EINVAL; } + // Validate the prefix + if (valid_prefix(address, prefix) != 0) { + DEBUG(ctx, "Invalid prefix: %u\n", prefix); + return -EINVAL; + } + struct loc_network* n = calloc(1, sizeof(*n)); if (!n) return -ENOMEM; @@ -71,7 +130,8 @@ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network n->ctx = loc_ref(ctx); n->refcount = 1; - n->start_address = start_address; + // Store the first address in the network + n->start_address = make_start_address(address, prefix); n->prefix = prefix; DEBUG(n->ctx, "Network allocated at %p\n", n); @@ -79,10 +139,19 @@ LOC_EXPORT int loc_network_new(struct loc_ctx* ctx, struct loc_network** network return 0; } +static int loc_network_address_family(struct loc_network* network) { + if (IN6_IS_ADDR_V4MAPPED(&network->start_address)) + return AF_INET; + + return AF_INET6; +} + LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_network** network, const char* address_string) { struct in6_addr start_address; + unsigned int prefix = 0; char* prefix_string; + int r = 1; // Make a copy of the string to work on it char* buffer = strdup(address_string); @@ -91,17 +160,26 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo // Split address and prefix address_string = strsep(&prefix_string, "/"); - // Convert prefix to integer - unsigned int prefix = strtol(prefix_string, NULL, 10); + // Did we find a prefix? + if (prefix_string) { + // Convert prefix to integer + prefix = strtol(prefix_string, NULL, 10); - // Parse the address - int r = inet_pton(AF_INET6, address_string, &start_address); + if (prefix) { + // Parse the address + r = loc_parse_address(ctx, address_string, &start_address); + + // Map the prefix to IPv6 if needed + if (IN6_IS_ADDR_V4MAPPED(&start_address)) + prefix += 96; + } + } // Free temporary buffer free(buffer); - if (r == 1) { - r = loc_network_new(ctx, network, start_address, prefix); + if (r == 0) { + r = loc_network_new(ctx, network, &start_address, prefix); } return r; @@ -116,14 +194,14 @@ LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) { static void loc_network_free(struct loc_network* network) { DEBUG(network->ctx, "Releasing network at %p\n", network); - if (network->as) - loc_as_unref(network->as); - loc_unref(network->ctx); free(network); } LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) { + if (!network) + return NULL; + if (--network->refcount > 0) return network; @@ -131,27 +209,80 @@ LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) { return NULL; } +static int format_ipv6_address(const struct in6_addr* address, char* string, size_t length) { + const char* ret = inet_ntop(AF_INET6, address, string, length); + if (!ret) + return -1; + + return 0; +} + +static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) { + struct in_addr ipv4_address; + ipv4_address.s_addr = address->s6_addr32[3]; + + const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length); + if (!ret) + return -1; + + return 0; +} + LOC_EXPORT char* loc_network_str(struct loc_network* network) { - const size_t l = INET6_ADDRSTRLEN + 3; + int r; + const size_t length = INET6_ADDRSTRLEN + 4; - char* string = malloc(l); + char* string = malloc(length); if (!string) return NULL; - const char* ret = inet_ntop(AF_INET6, &network->start_address, string, l); - if (!ret) { - ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno)); + unsigned int prefix = network->prefix; + + int family = loc_network_address_family(network); + switch (family) { + case AF_INET6: + r = format_ipv6_address(&network->start_address, string, length); + break; + case AF_INET: + r = format_ipv4_address(&network->start_address, string, length); + prefix -= 96; + break; + + default: + r = -1; + break; + } + + if (r) { + ERROR(network->ctx, "Could not convert network to string: %s\n", strerror(errno)); free(string); + return NULL; } // Append prefix - sprintf(string + strlen(string), "/%u", network->prefix); + sprintf(string + strlen(string), "/%u", prefix); return string; } +LOC_EXPORT int loc_network_match_address(struct loc_network* network, const struct in6_addr* address) { + // Address must be larger than the start address + if (in6_addr_cmp(&network->start_address, address) > 0) + return 1; + + // Determine the last address in this network + struct in6_addr last_address = make_last_address(&network->start_address, network->prefix); + + // Address must be smaller than the last address + if (in6_addr_cmp(&last_address, address) > 0) + return 1; + + // The address is inside this network + return 0; +} + LOC_EXPORT const char* loc_network_get_country_code(struct loc_network* network) { return network->country_code; } @@ -168,6 +299,55 @@ LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const c return 0; } +LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) { + return network->asn; +} + +LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) { + network->asn = asn; + + return 0; +} + +LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) { + dbobj->prefix = network->prefix; + + // Add country code + for (unsigned int i = 0; i < 2; i++) { + dbobj->country_code[i] = network->country_code ? network->country_code[i] : '\0'; + } + + // Add ASN + dbobj->asn = htobe32(network->asn); + + return 0; +} + +LOC_EXPORT int loc_network_new_from_database_v0(struct loc_ctx* ctx, struct loc_network** network, + struct in6_addr* address, const struct loc_database_network_v0* dbobj) { + int r = loc_network_new(ctx, network, address, dbobj->prefix); + if (r) + return r; + + // Import country code + char country_code[3]; + for (unsigned int i = 0; i < 2; i++) { + country_code[i] = dbobj->country_code[i]; + } + country_code[2] = '\0'; + + r = loc_network_set_country_code(*network, country_code); + if (r) + return r; + + // Import ASN + r = loc_network_set_asn(*network, be32toh(dbobj->asn)); + if (r) + return r; + + return 0; +} + struct loc_network_tree { struct loc_ctx* ctx; int refcount; @@ -176,6 +356,9 @@ struct loc_network_tree { }; struct loc_network_tree_node { + struct loc_ctx* ctx; + int refcount; + struct loc_network_tree_node* zero; struct loc_network_tree_node* one; @@ -191,35 +374,32 @@ LOC_EXPORT int loc_network_tree_new(struct loc_ctx* ctx, struct loc_network_tree t->refcount = 1; // Create the root node - t->root = calloc(1, sizeof(*t->root)); + int r = loc_network_tree_node_new(ctx, &t->root); + if (r) { + loc_network_tree_unref(t); + return r; + } DEBUG(t->ctx, "Network tree allocated at %p\n", t); *tree = t; return 0; } -static int loc_network_tree_node_new(struct loc_network_tree_node** node) { - struct loc_network_tree_node* n = calloc(1, sizeof(*n)); - if (!n) - return -ENOMEM; - - n->zero = n->one = NULL; - - *node = n; - return 0; +LOC_EXPORT struct loc_network_tree_node* loc_network_tree_get_root(struct loc_network_tree* tree) { + return loc_network_tree_node_ref(tree->root); } static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_network_tree_node* node, int path) { struct loc_network_tree_node** n; - if (path) - n = &node->one; - else + if (path == 0) n = &node->zero; + else + n = &node->one; // If the desired node doesn't exist, yet, we will create it if (*n == NULL) { - int r = loc_network_tree_node_new(n); + int r = loc_network_tree_node_new(node->ctx, n); if (r) return NULL; } @@ -230,22 +410,23 @@ static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_networ static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address) { struct loc_network_tree_node* node = tree->root; - for (unsigned int i = 127; i > 0; i--) { + for (unsigned int i = 0; i < 128; i++) { // Check if the ith bit is one or zero - node = loc_network_tree_get_node(node, ((address->s6_addr32[i / 32] & (1 << (i % 32))) == 0)); + node = loc_network_tree_get_node(node, in6_addr_get_bit(address, i)); } return node; } static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_node* node, - int(*filter_callback)(struct loc_network* network), int(*callback)(struct loc_network* network)) { + int(*filter_callback)(struct loc_network* network, void* data), + int(*callback)(struct loc_network* network, void* data), void* data) { int r; // Finding a network ends the walk here if (node->network) { if (filter_callback) { - int f = filter_callback(node->network); + int f = filter_callback(node->network, data); if (f < 0) return f; @@ -254,21 +435,21 @@ static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_ return 0; } - r = callback(node->network); + r = callback(node->network, data); if (r) return r; } // Walk down on the left side of the tree first if (node->zero) { - r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback); + r = __loc_network_tree_walk(ctx, node->zero, filter_callback, callback, data); if (r) return r; } // Then walk on the other side if (node->one) { - r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback); + r = __loc_network_tree_walk(ctx, node->one, filter_callback, callback, data); if (r) return r; } @@ -276,23 +457,16 @@ static int __loc_network_tree_walk(struct loc_ctx* ctx, struct loc_network_tree_ return 0; } -static void loc_network_tree_free_subtree(struct loc_network_tree_node* node) { - if (node->network) - loc_network_unref(node->network); - - if (node->zero) - loc_network_tree_free_subtree(node->zero); - - if (node->one) - loc_network_tree_free_subtree(node->one); - - free(node); +LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree, + int(*filter_callback)(struct loc_network* network, void* data), + int(*callback)(struct loc_network* network, void* data), void* data) { + return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data); } static void loc_network_tree_free(struct loc_network_tree* tree) { DEBUG(tree->ctx, "Releasing network tree at %p\n", tree); - loc_network_tree_free_subtree(tree->root); + loc_network_tree_node_unref(tree->root); loc_unref(tree->ctx); free(tree); @@ -306,7 +480,7 @@ LOC_EXPORT struct loc_network_tree* loc_network_tree_unref(struct loc_network_tr return NULL; } -int __loc_network_tree_dump(struct loc_network* network) { +static int __loc_network_tree_dump(struct loc_network* network, void* data) { DEBUG(network->ctx, "Dumping network at %p\n", network); char* s = loc_network_str(network); @@ -322,7 +496,7 @@ int __loc_network_tree_dump(struct loc_network* network) { LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) { DEBUG(tree->ctx, "Dumping network tree at %p\n", tree); - return __loc_network_tree_walk(tree->ctx, tree->root, NULL, __loc_network_tree_dump); + return loc_network_tree_walk(tree, NULL, __loc_network_tree_dump, NULL); } LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) { @@ -337,7 +511,7 @@ LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struc // Check if node has not been set before if (node->network) { DEBUG(tree->ctx, "There is already a network at this path\n"); - return 1; + return -EBUSY; } // Point node to the network @@ -345,3 +519,107 @@ LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struc return 0; } + +static int __loc_network_tree_count(struct loc_network* network, void* data) { + size_t* counter = (size_t*)data; + + // Increase the counter for each network + counter++; + + return 0; +} + +LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) { + size_t counter = 0; + + int r = loc_network_tree_walk(tree, NULL, __loc_network_tree_count, &counter); + if (r) + return r; + + return counter; +} + +static size_t __loc_network_tree_count_nodes(struct loc_network_tree_node* node) { + size_t counter = 1; + + if (node->zero) + counter += __loc_network_tree_count_nodes(node->zero); + + if (node->one) + counter += __loc_network_tree_count_nodes(node->one); + + return counter; +} + +LOC_EXPORT size_t loc_network_tree_count_nodes(struct loc_network_tree* tree) { + return __loc_network_tree_count_nodes(tree->root); +} + +LOC_EXPORT int loc_network_tree_node_new(struct loc_ctx* ctx, struct loc_network_tree_node** node) { + struct loc_network_tree_node* n = calloc(1, sizeof(*n)); + if (!n) + return -ENOMEM; + + n->ctx = loc_ref(ctx); + n->refcount = 1; + + n->zero = n->one = NULL; + + DEBUG(n->ctx, "Network node allocated at %p\n", n); + *node = n; + return 0; +} + +LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_ref(struct loc_network_tree_node* node) { + if (node) + node->refcount++; + + return node; +} + +static void loc_network_tree_node_free(struct loc_network_tree_node* node) { + DEBUG(node->ctx, "Releasing network node at %p\n", node); + + if (node->network) + loc_network_unref(node->network); + + if (node->zero) + loc_network_tree_node_unref(node->zero); + + if (node->one) + loc_network_tree_node_unref(node->one); + + loc_unref(node->ctx); + free(node); +} + +LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_unref(struct loc_network_tree_node* node) { + if (!node) + return NULL; + + if (--node->refcount > 0) + return node; + + loc_network_tree_node_free(node); + return NULL; +} + +LOC_EXPORT struct loc_network_tree_node* loc_network_tree_node_get(struct loc_network_tree_node* node, unsigned int index) { + if (index == 0) + node = node->zero; + else + node = node->one; + + if (!node) + return NULL; + + return loc_network_tree_node_ref(node); +} + +LOC_EXPORT int loc_network_tree_node_is_leaf(struct loc_network_tree_node* node) { + return (!!node->network); +} + +LOC_EXPORT struct loc_network* loc_network_tree_node_get_network(struct loc_network_tree_node* node) { + return loc_network_ref(node->network); +}