static bool test_compare_addr4(void) { struct bib_entry *bib; struct in_addr addr4; bool success = true; bib = bib_create_str("1::1", 1111, "1.1.1.1", 1111, L4PROTO_TCP); if (!bib) return false; if (str_to_addr4("2.2.2.2", &addr4) != 0) goto fail; success &= assert_true(compare_addr4(bib, &addr4) < 0, "< 0"); if (str_to_addr4("1.1.1.1", &addr4) != 0) goto fail; success &= assert_true(compare_addr4(bib, &addr4) == 0, "= 0"); if (str_to_addr4("0.0.0.0", &addr4) != 0) goto fail; success &= assert_true(compare_addr4(bib, &addr4) > 0, "> 0"); bib_kfree(bib); return success; fail: bib_kfree(bib); return false; }
static bool test_compare_full6(void) { struct bib_entry *bib; struct ipv6_transport_addr taddr6; bool success = true; bib = bib_create_str("1::1", 1111, "1.1.1.1", 1111, L4PROTO_TCP); if (!bib) return false; if (str_to_addr6("2::2", &taddr6.l3) != 0) goto fail; taddr6.l4 = 1111; success &= assert_true(compare_full6(bib, &taddr6) < 0, "<< 0"); if (str_to_addr6("1::1", &taddr6.l3) != 0) goto fail; taddr6.l4 = 1112; success &= assert_true(compare_full6(bib, &taddr6) < 0, "< 0"); taddr6.l4 = 1111; success &= assert_true(compare_full6(bib, &taddr6) == 0, "= 0"); taddr6.l4 = 1110; success &= assert_true(compare_full6(bib, &taddr6) > 0, "> 0"); if (str_to_addr6("0::0", &taddr6.l3) != 0) goto fail; taddr6.l4 = 1111; success &= assert_true(compare_full6(bib, &taddr6) > 0, ">> 0"); bib_kfree(bib); return success; fail: bib_kfree(bib); return false; }
/** * Inserts a single entry, validates it, removes it, validates again. * Does not touch the session tables. */ static bool simple_bib(void) { struct ipv4_transport_addr addr = addr4[0]; struct bib_entry *bib; bool success = true; if (is_error(pool4_get_any_port(L4PROTO_TCP, &addr.l3, &addr.l4))) return false; bib = bib_create(&addr, &addr6[0], false, L4PROTO_TCP); if (!assert_not_null(bib, "Allocation of test BIB entry")) return false; success &= assert_equals_int(0, bibdb_add(bib), "BIB insertion call"); success &= assert_bib("BIB insertion state", bib, false, true, false); if (!success) return false; success &= assert_equals_int(0, bibdb_remove(bib, false), "BIB removal call"); success &= assert_bib("BIB removal state", bib, false, false, false); if (!success) return false; bib_kfree(bib); return success; }
static bool add_bib(struct in_addr *ip4_addr, __u16 ip4_port, struct in6_addr *ip6_addr, __u16 ip6_port, l4_protocol l4_proto) { struct bib_entry *bib; struct ipv6_tuple_address addr6; struct ipv4_tuple_address addr4; /* Generate the BIB. */ addr4.address = *ip4_addr; addr4.l4_id = ip4_port; addr6.address = *ip6_addr; addr6.l4_id = ip6_port; bib = bib_create(&addr4, &addr6, false); if (!bib) { log_warning("Can't allocate a BIB entry!"); return false; } /* log_debug("BIB [%pI4#%u, %pI6c#%u]", &bib->ipv4.address, bib->ipv4.l4_id, &bib->ipv6.address, bib->ipv6.l4_id); */ /* Add it to the table. */ if (is_error(bib_add(bib, l4_proto))) { log_warning("Can't add the dummy BIB to the table."); bib_kfree(bib); return false; } return true; }
int add_static_route(struct request_bib *req) { struct bib_entry *bib = NULL; int error; error = pool4_get(req->l4_proto, &req->add.addr4); if (error) { log_err("The IPv4 address and port could not be reserved from the pool. " "Maybe the IPv4 address you provided does not belong to the pool. " "Or maybe they're being used by some other BIB entry?"); return error; } bib = bib_create(&req->add.addr4, &req->add.addr6, true, req->l4_proto); if (!bib) { log_err("Could not allocate the BIB entry."); error = -ENOMEM; goto bib_error; } error = bibdb_add(bib); if (error) { log_err("The BIB entry could not be added to the database. Maybe an entry with the " "same IPv4 and/or IPv6 transport address already exists?"); bib_kfree(bib); goto bib_error; } /* * We do not call bib_return(bib) here, because we want the entry to hold a fake user so the * timer doesn't delete it. */ return 0; bib_error: pool4_return(req->l4_proto, &req->add.addr4); return error; }
static bool test_hairpin(l4_protocol l4_proto, skb_creator create_skb_fn) { struct sk_buff *skb_in = NULL; struct sk_buff *skb_out = NULL; struct sk_buff *skb_tmp = NULL; struct bib_entry *static_bib = NULL; struct bib_entry *dynamic_bib = NULL; struct session_entry *static_session = NULL; struct session_entry *dynamic_session = NULL; struct tuple tuple6; bool success = true; static_bib = bib_create_str(SERVER_ADDR6, SERVER_PORT6, NAT64_POOL4, SERVER_PORT6, l4_proto); dynamic_bib = bib_create_str(CLIENT_ADDR, CLIENT_PORT, NAT64_POOL4, DYNAMIC_BIB_IPV4_PORT, l4_proto); static_session = session_create_str( SERVER_ADDR6, SERVER_PORT6, SERVER_HAIRPIN_ADDR, DYNAMIC_BIB_IPV4_PORT, NAT64_POOL4, SERVER_PORT6, NAT64_POOL4, DYNAMIC_BIB_IPV4_PORT, l4_proto); dynamic_session = session_create_str(CLIENT_ADDR, CLIENT_PORT, SERVER_HAIRPIN_ADDR, SERVER_PORT6, NAT64_POOL4, DYNAMIC_BIB_IPV4_PORT, NAT64_POOL4, SERVER_PORT6, l4_proto); if (!static_bib || !dynamic_bib || !static_session || !dynamic_session) goto fail; /* Send the request. */ if (is_error(init_ipv6_tuple(&tuple6, CLIENT_ADDR, CLIENT_PORT, SERVER_HAIRPIN_ADDR, SERVER_PORT6, l4_proto))) goto fail; if (is_error(create_skb_fn(&tuple6, &skb_in, 40, 32))) goto fail; success &= send(skb_in); success &= BIB_ASSERT(l4_proto, static_bib, dynamic_bib); success &= SESSION_ASSERT(l4_proto, static_session, dynamic_session); skb_out = skb_tmp = get_sent_skb(); success &= assert_not_null(skb_out, "Request packet"); if (!success) goto fail; do { success &= assert_equals_ipv6_str(SERVER_HAIRPIN_ADDR, &ipv6_hdr(skb_tmp)->saddr, "out src"); success &= assert_equals_ipv6_str(SERVER_ADDR6, &ipv6_hdr(skb_tmp)->daddr, "out dst"); skb_tmp = skb_tmp->next; } while (skb_tmp); switch (l4_proto) { case L4PROTO_UDP: success &= assert_equals_u16(DYNAMIC_BIB_IPV4_PORT, be16_to_cpu(udp_hdr(skb_out)->source), "out's src port"); success &= assert_equals_u16(SERVER_PORT6, be16_to_cpu(udp_hdr(skb_out)->dest), "out's dst port"); break; case L4PROTO_TCP: success &= assert_equals_u16(DYNAMIC_BIB_IPV4_PORT, be16_to_cpu(tcp_hdr(skb_out)->source), "out's src port"); success &= assert_equals_u16(SERVER_PORT6, be16_to_cpu(tcp_hdr(skb_out)->dest), "out's dst port"); break; case L4PROTO_ICMP: case L4PROTO_OTHER: log_err("Test is not designed for protocol %d.", l4_proto); success = false; break; } if (!success) goto fail; kfree_skb(skb_out); /* Send the response. */ if (is_error(init_ipv6_tuple(&tuple6, SERVER_ADDR6, SERVER_PORT6, SERVER_HAIRPIN_ADDR, DYNAMIC_BIB_IPV4_PORT, l4_proto))) goto fail; if (is_error(create_skb_fn(&tuple6, &skb_in, 100, 32))) goto fail; success &= send(skb_in); /* The module should have reused the entries, so the database shouldn't have changed. */ success &= BIB_ASSERT(l4_proto, static_bib, dynamic_bib); success &= SESSION_ASSERT(l4_proto, static_session, dynamic_session); skb_out = skb_tmp = get_sent_skb(); success &= assert_not_null(skb_out, "Response packet"); if (!success) goto fail; do { success &= assert_equals_ipv6_str(SERVER_HAIRPIN_ADDR, &ipv6_hdr(skb_out)->saddr, "out src"); success &= assert_equals_ipv6_str(CLIENT_ADDR, &ipv6_hdr(skb_out)->daddr, "out dst"); skb_tmp = skb_tmp->next; } while (skb_tmp); switch (l4_proto) { case L4PROTO_UDP: success &= assert_equals_u16(SERVER_PORT6, be16_to_cpu(udp_hdr(skb_out)->source), "out's src port"); success &= assert_equals_u16(CLIENT_PORT, be16_to_cpu(udp_hdr(skb_out)->dest), "out's dst port"); break; case L4PROTO_TCP: success &= assert_equals_u16(SERVER_PORT6, be16_to_cpu(tcp_hdr(skb_out)->source), "out's src port"); success &= assert_equals_u16(CLIENT_PORT, be16_to_cpu(tcp_hdr(skb_out)->dest), "out's dst port"); break; case L4PROTO_ICMP: case L4PROTO_OTHER: log_err("Test is not designed for protocol %d.", l4_proto); success = false; break; } kfree_skb(skb_out); session_return(dynamic_session); session_return(static_session); bib_kfree(dynamic_bib); bib_kfree(static_bib); return success; fail: kfree_skb(skb_out); if (dynamic_session) session_return(dynamic_session); if (static_session) session_return(static_session); if (dynamic_bib) bib_kfree(dynamic_bib); if (static_bib) bib_kfree(static_bib); return false; }
int add_static_route(struct request_bib *req) { struct bib_entry *bib_by_ipv6, *bib_by_ipv4; struct bib_entry *bib = NULL; int error; if (!pool4_contains(&req->add.ipv4.address)) { log_err(ERR_POOL6_NOT_FOUND, "The address '%pI4' does not belong to the IPv4 pool.", &req->add.ipv4.address); return -EINVAL; } spin_lock_bh(&bib_session_lock); /* Check if the BIB entry exists. */ error = bib_get_by_ipv6(&req->add.ipv6, req->l4_proto, &bib_by_ipv6); if (!error) { bib = bib_by_ipv6; goto already_mapped; } if (error != -ENOENT) goto generic_error; error = bib_get_by_ipv4(&req->add.ipv4, req->l4_proto, &bib_by_ipv4); if (!error) { bib = bib_by_ipv4; goto already_mapped; } if (error != -ENOENT) goto generic_error; /* Borrow the address and port from the IPv4 pool. */ if (is_error(pool4_get(req->l4_proto, &req->add.ipv4))) { /* * This might happen if Filtering just reserved the address#port, but hasn't yet inserted * the BIB entry to the table. This is because bib_session_lock doesn't cover the IPv4 * pool. * Otherwise something's not returning borrowed address#ports to the pool, which is an * error. */ log_err(ERR_BIB_REINSERT, "Port number %u from address %pI4 is taken from the IPv4 pool, " "but it wasn't found in the BIB. Please try again; if the problem persists, " "please report.", req->add.ipv4.l4_id, &req->add.ipv4.address); error = -EEXIST; goto failure; } /* Create and insert the entry. */ bib = bib_create(&req->add.ipv4, &req->add.ipv6, true); if (!bib) { log_err(ERR_ALLOC_FAILED, "Could NOT allocate a BIB entry."); error = -ENOMEM; goto failure; } error = bib_add(bib, req->l4_proto); if (error) { log_err(ERR_UNKNOWN_ERROR, "Could NOT add the BIB entry to the table."); goto failure; } spin_unlock_bh(&bib_session_lock); return 0; already_mapped: log_err(ERR_BIB_REINSERT, "%pI6c#%u is already mapped to %pI4#%u.", &bib->ipv6.address, bib->ipv6.l4_id, &bib->ipv4.address, bib->ipv4.l4_id); error = -EEXIST; bib = NULL; goto failure; generic_error: log_err(ERR_UNKNOWN_ERROR, "Error code %u while trying to interact with the BIB.", error); /* Fall through. */ failure: if (bib) bib_kfree(bib); spin_unlock_bh(&bib_session_lock); return error; }
int delete_static_route(struct request_bib *req) { struct bib_entry *bib; struct session_entry *session; int error = 0; spin_lock_bh(&bib_session_lock); switch (req->remove.l3_proto) { case L3PROTO_IPV6: error = bib_get_by_ipv6(&req->remove.ipv6, req->l4_proto, &bib); if (error) goto end; break; case L3PROTO_IPV4: error = bib_get_by_ipv4(&req->remove.ipv4, req->l4_proto, &bib); if (error) goto end; break; default: log_err(ERR_L3PROTO, "Unsupported network protocol: %u.", req->remove.l3_proto); error = -EINVAL; goto end; } if (!bib) { log_err(ERR_BIB_NOT_FOUND, "Could not find the BIB entry requested by the user."); error = -ENOENT; goto end; } /* * I'm tempted to assert that the entry is static here. Would that serve a purpose? * Nah. */ while (!list_empty(&bib->sessions)) { session = container_of(bib->sessions.next, struct session_entry, bib_list_hook); error = session_remove(session); if (error) { log_err(ERR_UNKNOWN_ERROR, "Session [%pI6c#%u, %pI6c#%u, %pI4#%u, %pI4#%u] refused to die.", &session->ipv6.remote.address, session->ipv6.remote.l4_id, &session->ipv6.local.address, session->ipv6.local.l4_id, &session->ipv4.local.address, session->ipv4.local.l4_id, &session->ipv4.remote.address, session->ipv4.remote.l4_id); goto end; } list_del(&session->bib_list_hook); list_del(&session->expire_list_hook); session_kfree(session); } error = bib_remove(bib, req->l4_proto); if (error) { log_err(ERR_UNKNOWN_ERROR, "Remove bib entry call ended with error code %d, " "despite validations.", error); goto end; } pool4_return(req->l4_proto, &bib->ipv4); bib_kfree(bib); /* Fall through. */ end: spin_unlock_bh(&bib_session_lock); return error; }