https://github.com/cilium/cilium
Raw File
Tip revision: 419999c3da2bf48ffec28900b9e02f91366cd0fa authored by Nate Sweet on 25 May 2021, 19:47:13 UTC
api: Add Packet Size to Flow Structure
Tip revision: 419999c
conntrack_test.h
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2020 Authors of Cilium */

enum {
	__TUPLE_EXIST,
	__TUPLE_NOEXIST,
};

static struct ct_entry __ipv4_map[] = {
	{},
};

static void *__map_lookup_elem(const void *map, const void *tuple)
{
	if (map == __ipv4_map) {
		__u64 idx = (__u64)tuple;

		if (idx == __TUPLE_EXIST)
			return &__ipv4_map[idx];
	}
	return NULL;
}
#define map_lookup_elem(map, tuple) __map_lookup_elem(map, tuple)
#include "lib/conntrack.h"
#undef map_lookup_elem

#define REPORT_ALL_FLAGS 0xFF
#define REPORT_NO_FLAGS 0x0

/* Advance global (fake) time by one unit. */
void advance_time(void)
{
	__now = __now + 1;
}

/* Return true IFF 'entry' will expire in 'seconds'. */
bool timeout_in(const struct ct_entry *entry, int seconds)
{
	return entry->lifetime == __now + seconds;
}

static void test___ct_update_timeout(void)
{
	struct ct_entry entry = {};
	union tcp_flags flags = {};
	__u32 then;
	int monitor;

	/* No update initially; mostly just because __now is less than the
	 * default report interval.
	 */
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
	assert(!monitor);

	/* When a full report interval has passed, report. */
	__now += 1+CT_REPORT_INTERVAL;
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
	assert(monitor);
	assert(entry.last_rx_report == __now);
	assert(entry.last_tx_report == 0);
	assert(entry.rx_flags_seen == 0);
	/* If <= a full report interval passes, don't report. */
	then = __now;
	__now += CT_REPORT_INTERVAL;
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
	assert(!monitor);
	assert(entry.last_rx_report == then);

	/* When flags change, report. */
	flags.value |= TCP_FLAG_SYN;
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
	assert(monitor);
	assert(entry.last_rx_report == __now);
	assert(entry.rx_flags_seen == bpf_ntohs(TCP_FLAG_SYN));
	assert(entry.last_tx_report == 0);
	assert(entry.tx_flags_seen == 0);
	/* Same call; no report. */
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
	assert(!monitor);

	/* If flags change but flag reporting is disabled, skip it. */
	flags.value |= TCP_FLAG_FIN;
	monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_NO_FLAGS);
	assert(!monitor);
	assert(entry.rx_flags_seen == bpf_ntohs(TCP_FLAG_SYN));
	assert(entry.tx_flags_seen == 0);
}

static void test___ct_lookup(void)
{
	void *map = __ipv4_map;
	struct ct_entry *entry = &__ipv4_map[0];
	struct __ctx_buff ctx = {};
	void *tuple = (void *)__TUPLE_EXIST;

	struct ct_state ct_state;
	union tcp_flags seen_flags = {0};
	__u32 monitor;
	int res;

	seen_flags.value |= TCP_FLAG_SYN;

	/* First packet is monitored */
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == TRACE_PAYLOAD_LEN);
	assert(timeout_in(entry, CT_SYN_TIMEOUT));

	/* Second packet with the same flags is not monitored; it does reset
	 * lifetime back to CT_SYN_TIMEOUT.
	 */
	advance_time();
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == 0);
	assert(timeout_in(entry, CT_SYN_TIMEOUT));

	/* Subsequent non-SYN packets result in a default TCP lifetime */
	advance_time();
	seen_flags.value &= ~TCP_FLAG_SYN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == 0);
	assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));

	/* Monitor if the connection is closing on one side */
	advance_time();
	seen_flags.value |= TCP_FLAG_FIN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CLOSE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == TRACE_PAYLOAD_LEN);
	assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));

	/* This doesn't automatically trigger monitor for subsequent packets */
	advance_time();
	seen_flags.value &= ~TCP_FLAG_FIN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == 0);
	assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));

	/* Monitor if the connection is closing on the other side. This
	 * second FIN on the other side will reset lifetime to
	 * CT_CLOSE_TIMEOUT.
	 */
	advance_time();
	seen_flags.value |= TCP_FLAG_FIN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CLOSE, CT_EGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == TRACE_PAYLOAD_LEN);
	assert(timeout_in(entry, CT_CLOSE_TIMEOUT));

	/* This doesn't automatically trigger monitor for subsequent packets */
	advance_time();
	monitor = 0;
	seen_flags.value &= ~TCP_FLAG_FIN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_EGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_ESTABLISHED);
	assert(monitor == 0);
	assert(timeout_in(entry, CT_CLOSE_TIMEOUT - 1));

	/* A connection is reopened due to a newly seen SYN.*/
	advance_time();
	monitor = 0;
	seen_flags.value = TCP_FLAG_SYN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_EGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_REOPENED);
	assert(monitor == TRACE_PAYLOAD_LEN);
	assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));

	/* Label connection as new if the tuple wasn't previously tracked */
	tuple = (void *)__TUPLE_NOEXIST;
	seen_flags.value = TCP_FLAG_SYN;
	res = __ct_lookup(map, &ctx, tuple, ACTION_CREATE, CT_INGRESS,
			  &ct_state, true, seen_flags, &monitor);
	assert(res == CT_NEW);
	assert(monitor == TRACE_PAYLOAD_LEN);
}
back to top