bool pool4_range_touches(const struct pool4_range *r1, const struct pool4_range *r2) { return addr4_equals(&r1->addr, &r2->addr) && port_range_touches(&r1->ports, &r2->ports); }
static bool test_allocate_ipv4_transport_address(void) { struct tuple client1tuple, client2tuple, client3tuple; struct in_addr client1addr4, client2addr4, client3addr4; struct tuple *sharing_client_tuple; struct in_addr *non_sharing_addr; struct ipv4_transport_addr result; struct host6_node *host6; unsigned int i = 0; bool success = true; if (is_error(init_ipv6_tuple(&client1tuple, "1::1", 60000, "64:ff9b::1", 60000, L4PROTO_UDP))) goto fail; if (is_error(init_ipv6_tuple(&client2tuple, "1::2", 60000, "64:ff9b::2", 60000, L4PROTO_UDP))) goto fail; if (is_error(init_ipv6_tuple(&client3tuple, "1::3", 60000, "64:ff9b::3", 60000, L4PROTO_UDP))) goto fail; log_debug("IPv6 client 1 arrives and makes 25 connections."); /* * Because it's the same IPv6 client, all of those connections should be masked with the same * IPv4 address. This minimizes confusion from remote IPv4 hosts. */ client1tuple.src.addr6.l4 = 65511; if (!test_allocate_aux(&client1tuple, NULL, &client1addr4, true)) goto fail; for (i = 65512; i < 65536; i++) { client1tuple.src.addr6.l4 = i; if (!test_allocate_aux(&client1tuple, &client1addr4, NULL, true)) goto fail; } log_debug("Client 2 arrives and make 50 connections."); /* * All of them should share the same IPv4 address, * which should be different from client1's (again, to minimize confusion). */ client2tuple.src.addr6.l4 = 65486; success &= test_allocate_aux(&client2tuple, NULL, &client2addr4, true); success &= assert_true(client1addr4.s_addr != client2addr4.s_addr, "the nodes are being masked with different addresses"); if (!success) goto fail; for (i = 65487; i < 65536; i++) { client2tuple.src.addr6.l4 = i; if (!test_allocate_aux(&client2tuple, &client2addr4, NULL, true)) goto fail; } log_debug("Client 1 makes another 25 connections."); /* * Because there are still ports available, he should still get the same IPv4 address. * Essentially, this proves that client2's intervention doesn't affect client 1's connections. */ for (i = 65486; i < 65511; i++) { client1tuple.src.addr6.l4 = i; if (!test_allocate_aux(&client1tuple, &client1addr4, NULL, true)) goto fail; } log_debug("Client 3 arrives and hogs up all of its address's low ports."); /* * At this point, both IPv4 addresses have 50 high ports taken. * Because both IPv4 addresses are taken, client 3 will share its IPv4 address with someone. */ client3tuple.src.addr6.l4 = 0; if (!test_allocate_aux(&client3tuple, NULL, &client3addr4, true)) goto fail; for (i = 1; i < 1024; i++) { client3tuple.src.addr6.l4 = i; if (!test_allocate_aux(&client3tuple, &client3addr4, NULL, true)) goto fail; } log_debug("The client that shares an address with client 3 requests a low port."); /* * Because all of them are taken, he gets the same address but a runner-up high port instead. */ if (addr4_equals(&client1addr4, &client3addr4)) { sharing_client_tuple = &client1tuple; non_sharing_addr = &client2addr4; } else if (addr4_equals(&client2addr4, &client3addr4)) { sharing_client_tuple = &client2tuple; non_sharing_addr = &client1addr4; } else { log_err("Client 3 doesn't share its IPv4 address with anyone, despite validations."); goto fail; } if (is_error(host6_node_get_or_create(&sharing_client_tuple->src.addr6.l3, &host6))) goto fail; sharing_client_tuple->src.addr6.l4 = 0; success &= assert_equals_int(0, allocate_transport_address(host6, sharing_client_tuple, &result), "result 3"); success &= assert_equals_ipv4(&client3addr4, &result.l3, "runnerup still gets his addr"); success &= assert_true(is_high(result.l4), "runnerup gets a high port"); success &= bib_inject(&sharing_client_tuple->src.addr6.l3, sharing_client_tuple->src.addr6.l4, &result.l3, result.l4, L4PROTO_UDP) != NULL; host6_node_return(host6); if (!success) goto fail; log_debug("Client 3 now hogs up all of its address's remaining ports."); /* 51 high ports were already taken, so this will stop early. */ for (i = 1024; i < 65485; i++) { client3tuple.src.addr6.l4 = i; if (!test_allocate_aux(&client3tuple, &client3addr4, NULL, i != 65484)) goto fail; } /* * At this point, client's address has 50 + 1024 + 1 + 64461 = 65536 ports taken. * ie. It no longer has any ports. */ log_debug("Then, the function will fall back to use the other address."); client3tuple.src.addr6.l4 = i; if (is_error(host6_node_get_or_create(&client3tuple.src.addr6.l3, &host6))) goto fail; success &= assert_equals_int(0, allocate_transport_address(host6, &client3tuple, &result), "function result"); success &= assert_true(client3addr4.s_addr != result.l3.s_addr, "node gets a runnerup address"); success &= bib_inject(&client3tuple.src.addr6.l3, client3tuple.src.addr6.l4, &result.l3, result.l4, L4PROTO_UDP) != NULL; host6_node_return(host6); if (!success) goto fail; log_debug("It will also fall back to use the other address as the other sharing node now hogs " "all remaining ports."); /* * 51 high ports ports were already taken, so this will stop early. * Also, the sharing client already requested port 0, so we have to start at 1. */ for (i = 1; i < 65486; i++) { sharing_client_tuple->src.addr6.l4 = i; if (!test_allocate_aux(sharing_client_tuple, non_sharing_addr, NULL, i != 65485)) goto fail; } log_debug("Now the pool is completely exhausted, so further requests cannot fall back."); if (is_error(host6_node_get_or_create(&client1tuple.src.addr6.l3, &host6))) goto fail; success &= assert_equals_int(-ESRCH, allocate_transport_address(host6, &client1tuple, &result), "client 1's request is denied"); host6_node_return(host6); if (is_error(host6_node_get_or_create(&client2tuple.src.addr6.l3, &host6))) goto fail; success &= assert_equals_int(-ESRCH, allocate_transport_address(host6, &client2tuple, &result), "client 2's request is denied"); host6_node_return(host6); if (is_error(host6_node_get_or_create(&client3tuple.src.addr6.l3, &host6))) goto fail; success &= assert_equals_int(-ESRCH, allocate_transport_address(host6, &client3tuple, &result), "client 3's request is denied"); host6_node_return(host6); return success; fail: log_debug("i was %u.", i); return false; }
bool pool4_range_equals(struct pool4_range *r1, struct pool4_range *r2) { return addr4_equals(&r1->addr, &r2->addr) && port_range_equals(&r1->ports, &r2->ports); }