static bool test_6to4(l4_protocol l4_proto, int (*create_skb6_fn)(struct tuple *, struct sk_buff **, u16, u8), int (*create_skb4_fn)(struct tuple *, struct sk_buff **, u16, u8), u16 expected_payload4_len) { struct sk_buff *skb6 = NULL, *skb4_expected = NULL, *skb4_actual = NULL; struct tuple tuple6, tuple4; bool result = false; if (init_ipv6_tuple(&tuple6, "1::1", 50080, "64::192.0.2.5", 51234, L4PROTO_UDP) != 0 || init_ipv4_tuple(&tuple4, "192.0.2.2", 80, "192.0.2.5", 1234, L4PROTO_UDP) != 0 || create_skb6_fn(&tuple6, &skb6, 100, 32) != 0 || create_skb4_fn(&tuple4, &skb4_expected, expected_payload4_len, 31) != 0) goto end; if (translating_the_packet(&tuple4, skb6, &skb4_actual) != VER_CONTINUE) goto end; result = compare_skbs(skb4_expected, skb4_actual); /* Fall through. */ end: kfree_skb(skb6); kfree_skb(skb4_expected); kfree_skb(skb4_actual); return result; }
int create_tcp_packet(struct sk_buff **skb, l3_protocol l3_proto, bool syn, bool rst, bool fin) { struct tcphdr *hdr_tcp; struct tuple tuple; int error; switch (l3_proto) { case L3PROTO_IPV4: error = init_ipv4_tuple(&tuple, "8.7.6.5", 8765, "5.6.7.8", 5678, L4PROTO_TCP); if (error) return error; error = create_skb4_tcp(&tuple, skb, 100, 32); if (error) return error; break; case L3PROTO_IPV6: error = init_ipv6_tuple(&tuple, "1::2", 1212, "3::4", 3434, L4PROTO_TCP); if (error) return error; error = create_skb6_tcp(&tuple, skb, 100, 32); if (error) return error; break; } hdr_tcp = tcp_hdr(*skb); hdr_tcp->syn = syn; hdr_tcp->rst = rst; hdr_tcp->fin = fin; return 0; }
static int init_payload_inner_ipv6(void *target, u16 payload_len) { struct ipv6hdr *hdr_ipv6; struct tcphdr *hdr_tcp; struct ipv6hdr tmp_hdr_ipv6; struct tcphdr tmp_hdr_tcp; unsigned char *inner_payload; struct tuple tuple6; int error; if (payload_len <= 0) return 0; /* Nothing to do here. */ error = init_ipv6_tuple(&tuple6, "1::1", 50080, "64::192.0.2.5", 51234, L4PROTO_TCP); if (error) return error; hdr_ipv6 = target; hdr_tcp = (struct tcphdr *) (hdr_ipv6 + 1); inner_payload = (unsigned char *) (hdr_tcp + 1); error = init_ipv6_hdr(&tmp_hdr_ipv6, 1300, NEXTHDR_TCP, &tuple6, true, false, 0, 32); if (error) return error; if (payload_len >= IPV6_HDR_LEN) { memcpy(hdr_ipv6, &tmp_hdr_ipv6, IPV6_HDR_LEN); payload_len -= IPV6_HDR_LEN; } else { memcpy(hdr_ipv6, &tmp_hdr_ipv6, payload_len); goto end; } error = init_tcp_hdr(&tmp_hdr_tcp, ETH_P_IPV6, 1300, &tuple6); if (error) return error; if (payload_len >= TCP_HDR_LEN) { memcpy(hdr_tcp, &tmp_hdr_tcp, TCP_HDR_LEN); payload_len -= TCP_HDR_LEN; } else { memcpy(hdr_tcp, &tmp_hdr_tcp, payload_len); goto end; } error = init_payload_normal(inner_payload, payload_len); if (error) return error; hdr_tcp->check = csum_ipv6_magic(&hdr_ipv6->saddr, &hdr_ipv6->daddr, sizeof(*hdr_tcp) + payload_len, NEXTHDR_TCP, csum_partial(hdr_tcp, sizeof(*hdr_tcp) + payload_len, 0)); /* Fall through. */ end: return 0; }
static bool divide_skb_test(l4_protocol l4_proto, int (*create_skb6_frag_fn)(struct tuple *, struct sk_buff **, u16, u16, bool, bool, u16, u8)) { struct sk_buff *skb6 = NULL; struct tuple tuple6; bool result = true; u16 total_payload = 3000; /* IPv6 Parameters. (To evaluate). */ bool is_first[] = {true, false, false}; bool is_last[] = {false, false, true}; u16 frag6_offset[] = {0, 1232, 2464}; u16 *payload6_len, *payload_offset; int total_frags6 = 3; u16 total_l4_len; u16 payload6_udp_icmp_len[] = {1224, 1232, 544}; u16 payload_udp_icmp_offset[] = {0, 1224, 2456}; u16 payload6_tcp_len[] = {1212, 1232, 556}; u16 payload_tcp_offset[] = {0, 1212, 2444}; if (l4_proto == L4PROTO_TCP) { payload6_len = payload6_tcp_len; payload_offset = payload_tcp_offset; total_l4_len = sizeof(struct tcphdr); } else { payload6_len = payload6_udp_icmp_len; payload_offset = payload_udp_icmp_offset; if (l4_proto == L4PROTO_UDP) total_l4_len = sizeof(struct udphdr) + total_payload; else total_l4_len = sizeof(struct icmphdr); } if (init_ipv6_tuple(&tuple6, "1::1", 6000, "64:ff9b::192.0.2.7", 4000, l4_proto) != 0) return false; if (create_skb6_frag_fn(&tuple6, &skb6, total_payload, total_l4_len ,false, false, 0, 32) != 0) return false; /* Just for precaution. */ skb6->next = NULL; skb6->prev = NULL; if (divide(skb6, 1280) != 0) { kfree_skb_queued(skb6); return false; } result = validate_frags6(skb6, &tuple6, total_frags6, is_first, is_last, frag6_offset, payload6_len, payload_offset, total_payload, l4_proto); kfree_skb_queued(skb6); return result; }
static bool test_icmp(void) { struct tuple tuple6; struct sk_buff *skb; bool success = true; if (is_error(init_ipv6_tuple(&tuple6, CLIENT_ADDR, CLIENT_PORT, SERVER_HAIRPIN_ADDR, SERVER_PORT6, L4PROTO_ICMP))) return false; if (is_error(create_skb6_icmp_info(&tuple6, &skb, 100, 32))) return false; set_sent_skb(NULL); success &= assert_equals_int(NF_DROP, core_6to4(skb), "Request result"); success &= assert_null(get_sent_skb(), "Sent SKB"); return success; }
static bool test_determine_in_tuple_ipv6(void) { struct packet pkt; struct sk_buff *skb; struct tuple actual, expected; bool success = true; if (is_error(init_ipv6_tuple(&expected, "1::2", 1212, "3::4", 3434, L4PROTO_TCP))) return false; if (is_error(create_skb6_tcp(&expected, &skb, 8, 32))) return false; if (is_error(pkt_init_ipv6(&pkt, skb))) return false; success &= assert_equals_int(VERDICT_CONTINUE, determine_in_tuple(&pkt, &actual), "verdict"); success &= assert_equals_tuple(&expected, &actual, "tuple"); kfree_skb(skb); return success; }
static bool test_6to4(l4_protocol l4_proto) { struct tuple in, out; int field = 0; bool success = true; if (is_error(init_ipv6_tuple(&in, remote6, 1234, local6, 80, l4_proto))) return false; success &= assert_equals_int(VER_CONTINUE, compute_out_tuple(&in, &out, NULL), "Call"); success &= assert_equals_int(L3PROTO_IPV4, out.l3_proto, "l3 proto"); success &= assert_equals_int(l4_proto, out.l4_proto, "l4 proto"); success &= assert_equals_ipv4_str(local4, &out.src.addr4.l3, "src addr"); if (l4_proto == L4PROTO_ICMP) success &= assert_equals_u16(80, out.src.addr4.l4, "src port (icmp id)"); else success &= assert_equals_u16(5678, out.src.addr4.l4, "src port"); success &= assert_equals_ipv4_str(remote4, &out.dst.addr4.l3, "dst addr"); success &= assert_equals_u16(80, out.dst.addr4.l4, "dst port (icmp id)"); success &= assert_equals_int(0, field, "unchanged field"); return success; }
bool validate_inner_pkt_ipv6(unsigned char *payload, u16 len) { struct ipv6hdr *hdr_ipv6; struct tcphdr *hdr_tcp; unsigned char *inner_payload; struct tuple tuple6; if (init_ipv6_tuple(&tuple6, "2::2", 4321, "1::1", 1234, L4PROTO_TCP) != 0) return false; hdr_ipv6 = (struct ipv6hdr *) payload; hdr_tcp = (struct tcphdr *) (hdr_ipv6 + 1); inner_payload = (unsigned char *) (hdr_tcp + 1); if (!validate_ipv6_hdr(hdr_ipv6, 1300, NEXTHDR_TCP, &tuple6)) return false; if (!validate_tcp_hdr(hdr_tcp, &tuple6)) return false; if (!validate_payload(inner_payload, len - sizeof(*hdr_ipv6) - sizeof(*hdr_tcp), 0)) return false; return true; }
static bool test_determine_in_tuple_ipv6(void) { struct sk_buff *skb; struct fragment *frag; struct tuple actual, expected; struct ipv6_pair pair6; bool success = true; if (is_error(init_ipv6_tuple(&expected, "1::2", 1212, "3::4", 3434, L4PROTO_TCP))) return false; if (is_error(init_pair6(&pair6, "1::2", 1212, "3::4", 3434))) return false; if (is_error(create_skb_ipv6_tcp_fragment_1(&pair6, &skb, 8))) return false; if (is_error(frag_create_from_skb(skb, &frag))) return false; success &= assert_equals_int(VER_CONTINUE, determine_in_tuple(frag, &actual), "verdict"); success &= assert_equals_tuple(&expected, &actual, "tuple"); frag_kfree(frag); return success; }
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; }
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; }