Ejemplo n.º 1
0
/*
 * 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;
}
Ejemplo n.º 2
0
/**
 * "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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
/**
 * 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;
}
Ejemplo n.º 5
0
/**
 * 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;
}
Ejemplo n.º 6
0
/**
 * 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;
}
Ejemplo n.º 7
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;
}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
0
/**
 * 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;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
/**
 * 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;
}
Ejemplo n.º 13
0
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;
}