/* * A V6 SYN packet arrives. */ static bool test_tcp_v4_init_state_handle_v6syn(void) { struct session_entry *session; struct expire_timer *expirer; struct sk_buff *skb; unsigned long timeout; bool success = true; /* Prepare */ session = session_create_str_tcp("1::2", 1212, "3::4", 3434, "5.6.7.8", 5678, "8.7.6.5", 8765, V4_INIT); if (!session) return false; if (is_error(create_tcp_packet(&skb, L3PROTO_IPV6, true, false, false))) { session_return(session); return false; } /* Evaluate */ success &= assert_equals_int(0, tcp_v4_init_state_handle(skb, session, &expirer), "V6 syn-result"); success &= assert_equals_u8(ESTABLISHED, session->state, "V6 syn-state"); success &= assert_equals_int(0, sessiondb_get_timeout(session, &timeout), "V6 syn-toresult"); success &= assert_equals_ulong(TCPEST_TIMEOUT, timeout, "V6 syn-lifetime"); kfree_skb(skb); session_return(session); return success; }
/** * "ars" means add, remove, send */ static bool test_pkt_queue_ars(void) { struct session_entry *session; struct sk_buff *skb; struct tuple tuple4; struct tcphdr *hdr_tcp; bool success = true; /* Prepare */ if (is_error(init_ipv4_tuple(&tuple4, "5.6.7.8", 5678, "192.168.2.1", 8765, L4PROTO_TCP))) return false; session = session_create_str_tcp("1::2", 1212, "3::4", 3434, "192.168.2.1", 8765, "5.6.7.8", 5678, V4_INIT); /* The session entry that is supposed to be created in "tcp_close_state_handle". */ if (!session) return false; if (is_error(create_skb4_tcp(&tuple4, &skb, 100, 32))) { session_return(session); return false; } hdr_tcp = tcp_hdr(skb); hdr_tcp->syn = true; hdr_tcp->rst = false; hdr_tcp->fin = false; success &= assert_equals_int(0, pktqueue_add(session, skb), "pktqueue_add 1"); success &= assert_equals_int(0, pktqueue_remove(session), "pktqueue_remove 1"); success &= assert_equals_int(-ENOENT, pktqueue_send(session), "pktqueue_send 1"); success &= assert_equals_int(0, icmp64_pop(), "pktqueue not sent an icmp error"); session_return(session); /* kfree_skb(skb); "skb" kfreed when pktqueue_remove is executed */ return success; }
static bool test_address_filtering(void) { struct session_entry *session; bool success = true; /* Init. */ session = create_and_insert_session(0, 0, 0, 0); if (!session) return false; /* Test the packet is allowed when the tuple and session match perfectly. */ success &= assert_true(test_address_filtering_aux(0, 0, 0, 0), "lol1"); /* Test a tuple that completely mismatches the session. */ success &= assert_false(test_address_filtering_aux(1, 1, 1, 1), "lol2"); /* Now test tuples that nearly match the session. */ success &= assert_false(test_address_filtering_aux(0, 0, 0, 1), "lol3"); success &= assert_false(test_address_filtering_aux(0, 0, 1, 0), "lol4"); /* The remote port is the only one that doesn't matter. */ success &= assert_true(test_address_filtering_aux(0, 1, 0, 0), "lol5"); success &= assert_false(test_address_filtering_aux(1, 0, 0, 0), "lol6"); /* Now we erase the session entry */ remove(session, &session_table_udp); session_return(session); session = NULL; /* Repeat the "lol5" test but now the assert must be false */ success &= assert_false(test_address_filtering_aux(0, 1, 0, 0), "lol7"); return success; }
/** * Assumes that "tuple" represents a IPv4-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, second halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv4_simple(struct packet *pkt, struct tuple *tuple4) { int error; struct bib_entry *bib; struct session_entry *session; error = get_bib_ipv4(pkt, tuple4, &bib); if (error == -ESRCH) return VERDICT_ACCEPT; else if (error) return VERDICT_DROP; log_bib(bib); error = sessiondb_get_or_create_ipv4(tuple4, bib, &session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VERDICT_DROP; } log_session(session); session_return(session); bib_return(bib); return VERDICT_CONTINUE; }
/** * Assumes that "tuple" represents a IPv6-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, first halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv6_simple(struct packet *pkt, struct tuple *tuple6) { struct bib_entry *bib; struct session_entry *session; int error; error = bibdb_get_or_create_ipv6(pkt, tuple6, &bib); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } log_bib(bib); error = sessiondb_get_or_create_ipv6(tuple6, bib, &session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VERDICT_DROP; } log_session(session); session_return(session); bib_return(bib); return VERDICT_CONTINUE; }
/** * Assumes that "tuple" and "bib"'s session doesn't exist, and creates it. Returns the resulting * entry in "session". * Assumes that "tuple" represents a IPv6 packet. */ static int create_session_ipv6(struct tuple *tuple6, struct bib_entry *bib, struct session_entry **session, enum session_timer_type timer_type, enum tcp_state state) { struct ipv6_prefix prefix; struct in_addr ipv4_dst; struct ipv4_transport_addr addr4; int error; /* Translate address from IPv6 to IPv4 */ error = pool6_get(&tuple6->dst.addr6.l3, &prefix); if (error) { log_debug("Errcode %d while obtaining %pI6c's prefix.", error, &tuple6->dst.addr6.l3); return error; } error = addr_6to4(&tuple6->dst.addr6.l3, &prefix, &ipv4_dst); if (error) { log_debug("Error code %d while translating the packet's address.", error); return error; } /* * Create the session entry. * * Fortunately, ICMP errors cannot reach this code because of the requirements in the header * of section 3.5, so we can use the tuple as shortcuts for the packet's fields. */ addr4.l3 = ipv4_dst; addr4.l4 = (tuple6->l4_proto != L4PROTO_ICMP) ? tuple6->dst.addr6.l4 : bib->ipv4.l4; *session = session_create(&tuple6->src.addr6, &tuple6->dst.addr6, &bib->ipv4, &addr4, tuple6->l4_proto, bib); if (!(*session)) { log_debug("Failed to allocate a session entry."); return -ENOMEM; } (*session)->state = state; apply_policies(); /* Add it to the table. */ error = sessiondb_add(*session, timer_type); if (error) { session_return(*session); log_debug("Error code %d while adding the session to the DB.", error); return error; } return 0; }
/** * First half of the filtering and updating done during the CLOSED state of the TCP state machine. * Processes IPv6 SYN packets when there's no state. * Part of RFC 6146 section 3.5.2.2. */ static int tcp_closed_v6_syn(struct packet *pkt, struct tuple *tuple6) { struct bib_entry *bib; struct session_entry *session; int error; error = bibdb_get_or_create_ipv6(pkt, tuple6, &bib); if (error) return error; log_bib(bib); error = create_session_ipv6(tuple6, bib, &session, SESSIONTIMER_TRANS, V6_INIT); if (error) { bib_return(bib); return error; } log_session(session); session_return(session); bib_return(bib); return 0; }
/** * Assumes that "tuple" represents a TCP packet, and filters and updates based on it. * Encapsulates the TCP state machine. * * This is RFC 6146 section 3.5.2. */ static verdict tcp(struct packet *pkt, struct tuple *tuple) { struct session_entry *session; int error; error = sessiondb_get(tuple, &session); if (error != 0 && error != -ESRCH) { log_debug("Error code %d while trying to find a TCP session.", error); inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } if (error == -ESRCH) return tcp_closed_state_handle(pkt, tuple); log_session(session); error = sessiondb_tcp_state_machine(pkt, session); session_return(session); if (error) { inc_stats(pkt, IPSTATS_MIB_INDISCARDS); return VERDICT_DROP; } return VERDICT_CONTINUE; }
/** * Assumes that "tuple" represents a IPv4-UDP or ICMP packet, and filters and updates based on it. * * This is RFC 6146, second halves of both sections 3.5.1 and 3.5.3. * * @param[in] skb tuple's packet. This is actually only used for error reporting. * @param[in] tuple summary of the packet Jool is currently translating. * @return VER_CONTINUE if everything went OK, VER_DROP otherwise. */ static verdict ipv4_simple(struct sk_buff *skb, struct tuple *tuple4) { int error; struct bib_entry *bib; struct session_entry *session; error = get_bib_ipv4(skb, tuple4, &bib); if (error) return VER_DROP; log_bib(bib); error = sessiondb_get_or_create_ipv4(tuple4, bib, &session); if (error) { inc_stats(skb, IPSTATS_MIB_INDISCARDS); bib_return(bib); return VER_DROP; } log_session(session); session_return(session); bib_return(bib); return VER_CONTINUE; }
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_compare_session4(void) { struct session_entry *s1, *s2; bool success = true; { s1 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.4", 44, L4PROTO_UDP); if (!s1) return false; s2 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 34, "4.4.4.4", 44, L4PROTO_UDP); if (!s2) return false; success &= assert_true(compare_session4(s1, s2) < 0, "< 0 remote"); success &= assert_true(compare_session4(s2, s1) > 0, "> 0 remote"); session_return(s1); session_return(s2); } { s1 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.4", 44, L4PROTO_UDP); if (!s1) return false; s2 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.4", 33, "4.4.4.4", 44, L4PROTO_UDP); if (!s2) return false; success &= assert_true(compare_session4(s1, s2) < 0, "< 0 remote"); success &= assert_true(compare_session4(s2, s1) > 0, "> 0 remote"); session_return(s1); session_return(s2); } { s1 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.4", 44, L4PROTO_UDP); if (!s1) return false; s2 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.4", 45, L4PROTO_UDP); if (!s2) return false; success &= assert_true(compare_session4(s1, s2) < 0, "< 0 remote"); success &= assert_true(compare_session4(s2, s1) > 0, "> 0 remote"); session_return(s1); session_return(s2); } { s1 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.4", 44, L4PROTO_UDP); if (!s1) return false; s2 = session_create_str("1::1", 11, "2::2", 22, "3.3.3.3", 33, "4.4.4.5", 44, L4PROTO_UDP); if (!s2) return false; success &= assert_true(compare_session4(s1, s2) < 0, "<< 0 remote"); success &= assert_true(compare_session4(s2, s1) > 0, ">> 0 remote"); session_return(s1); session_return(s2); } return success; }
/** * Second half of the filtering and updating done during the CLOSED state of the TCP state machine. * Processes IPv4 SYN packets when there's no state. * Part of RFC 6146 section 3.5.2.2. */ static verdict tcp_closed_v4_syn(struct packet *pkt, struct tuple *tuple4) { struct bib_entry *bib; struct session_entry *session; int error; verdict result = VERDICT_DROP; if (config_get_drop_external_connections()) { log_debug("Applying policy: Dropping externally initiated TCP connections."); return VERDICT_DROP; } error = bibdb_get(tuple4, &bib); if (error) { if (error != -ESRCH) return VERDICT_DROP; bib = NULL; } log_bib(bib); error = create_session_ipv4(tuple4, bib, &session); if (error) goto end_bib; log_session(session); session->state = V4_INIT; if (!bib || config_get_addr_dependent_filtering()) { error = pktqueue_add(session, pkt); if (error) { if (error == -E2BIG) { /* Fall back to assume there's no Simultaneous Open. */ icmp64_send(pkt, ICMPERR_PORT_UNREACHABLE, 0); } goto end_session; } /* At this point, skb's original skb completely belongs to pktqueue. */ result = VERDICT_STOLEN; error = sessiondb_add(session, SESSIONTIMER_SYN); if (error) { log_debug("Error code %d while adding the session to the DB.", error); pktqueue_remove(session); goto end_session; } } else { error = sessiondb_add(session, SESSIONTIMER_TRANS); if (error) { log_debug("Error code %d while adding the session to the DB.", error); goto end_session; } result = VERDICT_CONTINUE; } /* Fall through. */ end_session: session_return(session); /* Fall through. */ end_bib: if (bib) bib_return(bib); return result; }
verdict compute_out_tuple(struct tuple *in, struct tuple *out, struct packet *pkt_in) { struct session_entry *session; int error; log_debug("Step 3: Computing the Outgoing Tuple"); error = sessiondb_get(in, &session); if (error) { /* * Bogus ICMP errors might cause this because Filtering never cares for them, * so it's not critical. */ log_debug("Error code %d while trying to find the packet's session entry.", error); inc_stats(pkt_in, IPSTATS_MIB_INNOROUTES); return VERDICT_DROP; } /* * Though the end result is the same, the following section of code collides with the RFC * in a superfluous sense. * * If the IPv6 pool has multiple prefixes, algorithmically generating addresses at this point * is pointless because, in order to do that, we'd need to know which prefix was used when the * session was created. This bit of information would have to be extracted from the session. * However, the address already algorithmically generated also belongs to the session. * So why bother generating it again? Just copy it. * * Additionally, the RFC wants some information extracted from the BIB entry. * We *also* extract that information from the session because it's the same, by definition. * * And finally, the RFC wants some information extracted from the tuple. * Same deal. If you draw all the scenarios (weirdass ICMP errors included), it's always the * same as the session. * * Given all of that, I really don't understand why the RFC bothers with any of this, including * making a distinction between 3-tuples and 5-tuples. The outgoing tuple is always a copy of * the other side of the session, plain and simple. When you think about it, that last claim * makes sense even in a general sense. */ switch (in->l3_proto) { case L3PROTO_IPV6: out->l3_proto = L3PROTO_IPV4; out->l4_proto = in->l4_proto; out->src.addr4 = session->local4; out->dst.addr4 = session->remote4; break; case L3PROTO_IPV4: out->l3_proto = L3PROTO_IPV6; out->l4_proto = in->l4_proto; out->src.addr6 = session->local6; out->dst.addr6 = session->remote6; break; } session_return(session); log_tuple(out); log_debug("Done step 3."); return VERDICT_CONTINUE; }