X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fnetwork.c;h=c112a41d3428b5235737f49f2bab32fd003bd129;hb=13ad6e695f9ffc7847b3afe3e9cbcea8fb3a443f;hp=866b377ba08f1bc6f9f50715edad49a238e89fd1;hpb=6183c0f2af55f1d2dade4d74cca0915aedce4a19;p=people%2Fms%2Flibloc.git diff --git a/src/network.c b/src/network.c index 866b377..c112a41 100644 --- a/src/network.c +++ b/src/network.c @@ -16,13 +16,18 @@ #include #include -#include #include #include #include #include +#ifdef HAVE_ENDIAN_H +# include +#endif + #include +#include +#include #include #include @@ -30,11 +35,14 @@ struct loc_network { struct loc_ctx* ctx; int refcount; - struct in6_addr start_address; + int family; + struct in6_addr first_address; + struct in6_addr last_address; unsigned int prefix; char country_code[3]; uint32_t asn; + enum loc_network_flags flags; }; static int valid_prefix(struct in6_addr* address, unsigned int prefix) { @@ -46,37 +54,77 @@ static int valid_prefix(struct in6_addr* address, unsigned int prefix) { 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_first_address(const struct in6_addr* address, const struct in6_addr* bitmask) { + struct in6_addr a; + + // 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, const struct in6_addr* bitmask) { + struct in6_addr a; + + // 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(&start_address, prefix) != 0) { + if (valid_prefix(address, prefix) != 0) { DEBUG(ctx, "Invalid prefix: %u\n", prefix); return -EINVAL; } @@ -88,58 +136,34 @@ 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 prefix n->prefix = prefix; - DEBUG(n->ctx, "Network allocated at %p\n", n); - *network = n; - 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; -} - -static int parse_address(struct loc_ctx* ctx, const char* string, struct in6_addr* address) { - DEBUG(ctx, "Paring IP address %s\n", string); - - // Try parsing this as an IPv6 address - int r = inet_pton(AF_INET6, string, address); - - // If inet_pton returns one it has been successful - if (r == 1) { - DEBUG(ctx, "%s is an IPv6 address\n", string); - return 0; - } - - // Try parsing this as an IPv4 address - struct in_addr ipv4_address; - r = inet_pton(AF_INET, string, &ipv4_address); - if (r == 1) { - DEBUG(ctx, "%s is an IPv4 address\n", string); + // Convert the prefix into a bitmask + struct in6_addr bitmask = prefix_to_bitmask(n->prefix); - // Convert to IPv6-mapped address - address->s6_addr32[0] = htonl(0x0000); - address->s6_addr32[1] = htonl(0x0000); - address->s6_addr32[2] = htonl(0xffff); - address->s6_addr32[3] = ipv4_address.s_addr; + // Store the first and last address in the network + n->first_address = make_first_address(address, &bitmask); + n->last_address = make_last_address(&n->first_address, &bitmask); - return 0; - } + // Set family + if (IN6_IS_ADDR_V4MAPPED(&n->first_address)) + n->family = AF_INET; + else + n->family = AF_INET6; - DEBUG(ctx, "%s is not an valid IP address\n", string); - return 1; + DEBUG(n->ctx, "Network allocated at %p\n", n); + *network = n; + return 0; } 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; + struct in6_addr first_address; char* prefix_string; - int r = 1; + int r = -EINVAL; + + DEBUG(ctx, "Attempting to parse network %s\n", address_string); // Make a copy of the string to work on it char* buffer = strdup(address_string); @@ -148,25 +172,46 @@ LOC_EXPORT int loc_network_new_from_string(struct loc_ctx* ctx, struct loc_netwo // Split address and prefix address_string = strsep(&prefix_string, "/"); - // Did we find a prefix? - if (prefix_string) { - // Convert prefix to integer - prefix = strtol(prefix_string, NULL, 10); + DEBUG(ctx, " Split into address = %s, prefix = %s\n", address_string, prefix_string); - if (prefix) { - // Parse the address - r = parse_address(ctx, address_string, &start_address); - } + // We need to have a prefix + if (!prefix_string) { + DEBUG(ctx, "No prefix set\n"); + goto FAIL; + } + + // Convert prefix to integer + unsigned int prefix = strtol(prefix_string, NULL, 10); + + // End if the prefix was invalid + if (!prefix) { + DEBUG(ctx, "The prefix is zero or not a number\n"); + goto FAIL; + } + + // Parse the address + r = loc_parse_address(ctx, address_string, &first_address); + if (r) { + DEBUG(ctx, "The address could not be parsed\n"); + goto FAIL; } + // Map the prefix to IPv6 if needed + if (IN6_IS_ADDR_V4MAPPED(&first_address)) + prefix += 96; + +FAIL: // Free temporary buffer free(buffer); - if (r == 0) { - r = loc_network_new(ctx, network, start_address, prefix); - } + // Exit if the parsing was unsuccessful + if (r) + return r; + + DEBUG(ctx, "GOT HERE\n"); - return r; + // Create a new network + return loc_network_new(ctx, network, &first_address, prefix); } LOC_EXPORT struct loc_network* loc_network_ref(struct loc_network* network) { @@ -183,6 +228,9 @@ static void loc_network_free(struct loc_network* network) { } LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) { + if (!network) + return NULL; + if (--network->refcount > 0) return network; @@ -190,17 +238,17 @@ LOC_EXPORT struct loc_network* loc_network_unref(struct loc_network* network) { return NULL; } -static int format_ipv6_address(struct loc_network* network, char* string, size_t length) { - const char* ret = inet_ntop(AF_INET6, &network->start_address, string, length); +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(struct loc_network* network, char* string, size_t length) { +static int format_ipv4_address(const struct in6_addr* address, char* string, size_t length) { struct in_addr ipv4_address; - ipv4_address.s_addr = network->start_address.s6_addr32[3]; + ipv4_address.s_addr = address->s6_addr32[3]; const char* ret = inet_ntop(AF_INET, &ipv4_address, string, length); if (!ret) @@ -217,14 +265,16 @@ LOC_EXPORT char* loc_network_str(struct loc_network* network) { if (!string) return NULL; - int family = loc_network_address_family(network); - switch (family) { + unsigned int prefix = network->prefix; + + switch (network->family) { case AF_INET6: - r = format_ipv6_address(network, string, length); + r = format_ipv6_address(&network->first_address, string, length); break; case AF_INET: - r = format_ipv4_address(network, string, length); + r = format_ipv4_address(&network->first_address, string, length); + prefix -= 96; break; default: @@ -240,27 +290,98 @@ LOC_EXPORT char* loc_network_str(struct loc_network* network) { } // Append prefix - sprintf(string + strlen(string), "/%u", network->prefix); + sprintf(string + strlen(string), "/%u", prefix); + + return string; +} + +LOC_EXPORT int loc_network_address_family(struct loc_network* network) { + return network->family; +} + +static char* loc_network_format_address(struct loc_network* network, const struct in6_addr* address) { + const size_t length = INET6_ADDRSTRLEN; + + char* string = malloc(length); + if (!string) + return NULL; + + int r = 0; + + switch (network->family) { + case AF_INET6: + r = format_ipv6_address(address, string, length); + break; + + case AF_INET: + r = format_ipv4_address(address, string, length); + break; + + default: + r = -1; + break; + } + + if (r) { + ERROR(network->ctx, "Could not format IP address to string: %s\n", strerror(errno)); + free(string); + + return NULL; + } return string; } +LOC_EXPORT char* loc_network_format_first_address(struct loc_network* network) { + return loc_network_format_address(network, &network->first_address); +} + +LOC_EXPORT char* loc_network_format_last_address(struct loc_network* network) { + return loc_network_format_address(network, &network->last_address); +} + +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->first_address, address) > 0) + return 1; + + // Address must be smaller than the last address + if (in6_addr_cmp(&network->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; } LOC_EXPORT int loc_network_set_country_code(struct loc_network* network, const char* country_code) { - // Country codes must be two characters - if (strlen(country_code) != 2) + // Set empty country code + if (!country_code || !*country_code) { + *network->country_code = '\0'; + return 0; + } + + // Check country code + if (!loc_country_code_is_valid(country_code)) return -EINVAL; - for (unsigned int i = 0; i < 3; i++) { - network->country_code[i] = country_code[i]; - } + loc_country_code_copy(network->country_code, country_code); return 0; } +LOC_EXPORT int loc_network_match_country_code(struct loc_network* network, const char* country_code) { + // Check country code + if (!loc_country_code_is_valid(country_code)) + return -EINVAL; + + return (network->country_code[0] == country_code[0]) + && (network->country_code[1] == country_code[1]); +} + LOC_EXPORT uint32_t loc_network_get_asn(struct loc_network* network) { return network->asn; } @@ -271,17 +392,86 @@ LOC_EXPORT int loc_network_set_asn(struct loc_network* network, uint32_t asn) { return 0; } -LOC_EXPORT int loc_network_to_database_v0(struct loc_network* network, struct loc_database_network_v0* dbobj) { - dbobj->prefix = htobe16(network->prefix); +LOC_EXPORT int loc_network_match_asn(struct loc_network* network, uint32_t asn) { + return network->asn == asn; +} + +LOC_EXPORT int loc_network_has_flag(struct loc_network* network, uint32_t flag) { + return network->flags & flag; +} + +LOC_EXPORT int loc_network_set_flag(struct loc_network* network, uint32_t flag) { + network->flags |= flag; + + return 0; +} + +LOC_EXPORT int loc_network_match_flag(struct loc_network* network, uint32_t flag) { + return loc_network_has_flag(network, flag); +} + +LOC_EXPORT int loc_network_is_subnet_of(struct loc_network* self, struct loc_network* other) { + // If the start address of the other network is smaller than this network, + // it cannot be a subnet. + if (in6_addr_cmp(&self->first_address, &other->first_address) < 0) + return 0; + // If the end address of the other network is greater than this network, + // it cannot be a subnet. + if (in6_addr_cmp(&self->last_address, &other->last_address) > 0) + return 0; + + return 1; +} + +LOC_EXPORT int loc_network_to_database_v1(struct loc_network* network, struct loc_database_network_v1* dbobj) { // Add country code - for (unsigned int i = 0; i < 2; i++) { - dbobj->country_code[i] = network->country_code ? network->country_code[i] : '\0'; - } + loc_country_code_copy(dbobj->country_code, network->country_code); // Add ASN dbobj->asn = htobe32(network->asn); + // Flags + dbobj->flags = htobe16(network->flags); + + return 0; +} + +LOC_EXPORT int loc_network_new_from_database_v1(struct loc_ctx* ctx, struct loc_network** network, + struct in6_addr* address, unsigned int prefix, const struct loc_database_network_v1* dbobj) { + char country_code[3] = "\0\0"; + + int r = loc_network_new(ctx, network, address, prefix); + if (r) { + ERROR(ctx, "Could not allocate a new network: %s", strerror(-r)); + return r; + } + + // Import country code + loc_country_code_copy(country_code, dbobj->country_code); + + r = loc_network_set_country_code(*network, country_code); + if (r) { + ERROR(ctx, "Could not set country code: %s\n", country_code); + return r; + } + + // Import ASN + uint32_t asn = be32toh(dbobj->asn); + r = loc_network_set_asn(*network, asn); + if (r) { + ERROR(ctx, "Could not set ASN: %d\n", asn); + return r; + } + + // Import flags + int flags = be16toh(dbobj->flags); + r = loc_network_set_flag(*network, flags); + if (r) { + ERROR(ctx, "Could not set flags: %d\n", flags); + return r; + } + return 0; } @@ -293,6 +483,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; @@ -308,35 +501,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; } @@ -344,12 +534,12 @@ static struct loc_network_tree_node* loc_network_tree_get_node(struct loc_networ return *n; } -static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address) { +static struct loc_network_tree_node* loc_network_tree_get_path(struct loc_network_tree* tree, const struct in6_addr* address, unsigned int prefix) { struct loc_network_tree_node* node = tree->root; - for (unsigned int i = 127; i > 0; i--) { + for (unsigned int i = 0; i < prefix; 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; @@ -400,23 +590,10 @@ LOC_EXPORT int loc_network_tree_walk(struct loc_network_tree* tree, return __loc_network_tree_walk(tree->ctx, tree->root, filter_callback, callback, data); } -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); -} - 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); @@ -430,7 +607,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, void* data) { +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); @@ -452,7 +629,8 @@ LOC_EXPORT int loc_network_tree_dump(struct loc_network_tree* tree) { LOC_EXPORT int loc_network_tree_add_network(struct loc_network_tree* tree, struct loc_network* network) { DEBUG(tree->ctx, "Adding network %p to tree %p\n", network, tree); - struct loc_network_tree_node* node = loc_network_tree_get_path(tree, &network->start_address); + struct loc_network_tree_node* node = loc_network_tree_get_path(tree, + &network->first_address, network->prefix); if (!node) { ERROR(tree->ctx, "Could not find a node\n"); return -ENOMEM; @@ -461,7 +639,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 @@ -488,3 +666,88 @@ LOC_EXPORT size_t loc_network_tree_count_networks(struct loc_network_tree* tree) 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); +}