github.com/cilium/cilium@v1.16.2/bpf/tests/conntrack_test.c (about)

     1  // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
     2  /* Copyright Authors of Cilium */
     3  
     4  #include "common.h"
     5  
     6  #include "bpf/ctx/skb.h"
     7  
     8  #define ENABLE_IPV4
     9  #define ENABLE_NODEPORT
    10  
    11  #include "node_config.h"
    12  #include "lib/common.h"
    13  
    14  static __u64 __now;
    15  
    16  #define ktime_get_ns()	(__now * NSEC_PER_SEC)
    17  #define jiffies64()	(__now)
    18  
    19  /* Is not part of these tests, and is causing issues in the CI */
    20  #undef CONNTRACK_ACCOUNTING
    21  
    22  #include "lib/conntrack.h"
    23  #include "lib/conntrack_map.h"
    24  
    25  #define REPORT_ALL_FLAGS 0xFF
    26  #define REPORT_NO_FLAGS 0x0
    27  
    28  /* Advance global (fake) time by one unit. */
    29  void advance_time(void)
    30  {
    31  	__now = __now + 1;
    32  }
    33  
    34  /* Return true IFF 'entry' will expire in 'seconds'. */
    35  bool timeout_in(const struct ct_entry *entry, int seconds)
    36  {
    37  	return entry->lifetime == __now + seconds;
    38  }
    39  
    40  CHECK("tc", "conntrack")
    41  int bpf_test(__maybe_unused struct __sk_buff *sctx)
    42  {
    43  	test_init();
    44  
    45  	TEST("ct_update_timeout", {
    46  		struct ct_entry entry = {};
    47  		union tcp_flags flags = {};
    48  		__u32 then;
    49  		int monitor;
    50  
    51  		/* No update initially; mostly just because __now is less than the
    52  		 * default report interval.
    53  		 */
    54  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
    55  		assert(!monitor);
    56  
    57  		/* When a full report interval has passed, report. */
    58  		__now += 1 + CT_REPORT_INTERVAL;
    59  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
    60  		assert(monitor);
    61  		assert(entry.last_rx_report == __now);
    62  		assert(entry.last_tx_report == 0);
    63  		assert(entry.rx_flags_seen == 0);
    64  		/* If <= a full report interval passes, don't report. */
    65  		then = __now;
    66  		__now += CT_REPORT_INTERVAL;
    67  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
    68  		assert(!monitor);
    69  		assert(entry.last_rx_report == then);
    70  
    71  		/* When flags change, report. */
    72  		flags.value |= TCP_FLAG_SYN;
    73  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
    74  		assert(monitor);
    75  		assert(entry.last_rx_report == __now);
    76  		assert(entry.rx_flags_seen == tcp_flags_to_u8(TCP_FLAG_SYN));
    77  		assert(entry.last_tx_report == 0);
    78  		assert(entry.tx_flags_seen == 0);
    79  		/* Same call; no report. */
    80  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_ALL_FLAGS);
    81  		assert(!monitor);
    82  
    83  		/* If flags change but flag reporting is disabled, skip it. */
    84  		flags.value |= TCP_FLAG_FIN;
    85  		monitor = __ct_update_timeout(&entry, 1000, CT_INGRESS, flags, REPORT_NO_FLAGS);
    86  		assert(!monitor);
    87  		assert(entry.rx_flags_seen == tcp_flags_to_u8(TCP_FLAG_SYN));
    88  		assert(entry.tx_flags_seen == 0);
    89  	});
    90  
    91  	TEST("ct_lookup", {
    92  		#if __clang_major__ < 11
    93  			test_log("Skipping ct_lookup test on Clang < 11 due to compiler bug");
    94  			test_skip_now();
    95  		#endif
    96  
    97  		struct __ctx_buff ctx = {};
    98  		int res;
    99  		struct ipv4_ct_tuple tuple = {
   100  			.nexthdr = IPPROTO_TCP
   101  		};
   102  
   103  		struct ct_entry ct_entry_new = {};
   104  
   105  		res = map_update_elem(get_ct_map4(&tuple), &tuple, &ct_entry_new, BPF_ANY);
   106  		if (IS_ERR(res))
   107  			test_fatal("map_update_elem: %lld", res);
   108  
   109  		struct ct_entry *entry = map_lookup_elem(get_ct_map4(&tuple), &tuple);
   110  
   111  		if (!entry)
   112  			test_fatal("ct entry lookup failed");
   113  
   114  		union tcp_flags seen_flags = {0};
   115  		__u32 monitor;
   116  
   117  		seen_flags.value |= TCP_FLAG_SYN;
   118  
   119  		/* First packet is monitored */
   120  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   121  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   122  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   123  		assert(res == CT_ESTABLISHED);
   124  		assert(monitor == TRACE_PAYLOAD_LEN);
   125  		assert(timeout_in(entry, CT_SYN_TIMEOUT));
   126  
   127  		/* Second packet with the same flags is not monitored; it does reset
   128  		 * lifetime back to CT_SYN_TIMEOUT.
   129  		 */
   130  		advance_time();
   131  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   132  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   133  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   134  		assert(res == CT_ESTABLISHED);
   135  		assert(monitor == 0);
   136  		assert(timeout_in(entry, CT_SYN_TIMEOUT));
   137  
   138  		/* Subsequent non-SYN packets result in a default TCP lifetime */
   139  		advance_time();
   140  		seen_flags.value &= ~TCP_FLAG_SYN;
   141  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   142  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   143  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   144  		assert(res == CT_ESTABLISHED);
   145  		assert(monitor == 0);
   146  		assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));
   147  
   148  		/* Monitor if the connection is closing on one side */
   149  		advance_time();
   150  		seen_flags.value |= TCP_FLAG_FIN;
   151  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   152  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   153  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   154  		assert(res == CT_ESTABLISHED);
   155  		assert(monitor == TRACE_PAYLOAD_LEN);
   156  		assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));
   157  
   158  		/* This doesn't automatically trigger monitor for subsequent packets */
   159  		advance_time();
   160  		seen_flags.value &= ~TCP_FLAG_FIN;
   161  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   162  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   163  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   164  		assert(res == CT_ESTABLISHED);
   165  		assert(monitor == 0);
   166  		assert(timeout_in(entry, CT_CONNECTION_LIFETIME_TCP));
   167  
   168  		/* Monitor if the connection is closing on the other side. This
   169  		 * second FIN on the other side will reset lifetime to
   170  		 * CT_CLOSE_TIMEOUT.
   171  		 */
   172  		advance_time();
   173  		seen_flags.value |= TCP_FLAG_FIN;
   174  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   175  				  ct_tcp_select_action(seen_flags), CT_EGRESS,
   176  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   177  		assert(res == CT_ESTABLISHED);
   178  		assert(monitor == TRACE_PAYLOAD_LEN);
   179  		assert(timeout_in(entry, CT_CLOSE_TIMEOUT));
   180  
   181  		/* This doesn't automatically trigger monitor for subsequent packets */
   182  		advance_time();
   183  		monitor = 0;
   184  		seen_flags.value &= ~TCP_FLAG_FIN;
   185  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   186  				  ct_tcp_select_action(seen_flags), CT_EGRESS,
   187  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   188  		assert(res == CT_ESTABLISHED);
   189  		assert(monitor == 0);
   190  		assert(timeout_in(entry, CT_CLOSE_TIMEOUT - 1));
   191  
   192  		/* A connection is reopened due to a newly seen SYN.*/
   193  		advance_time();
   194  		monitor = 0;
   195  		seen_flags.value = TCP_FLAG_SYN;
   196  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   197  				  ct_tcp_select_action(seen_flags), CT_EGRESS,
   198  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   199  		assert(res == CT_NEW);
   200  		assert(monitor == TRACE_PAYLOAD_LEN);
   201  		assert(timeout_in(entry, CT_SYN_TIMEOUT));
   202  
   203  		/* Label connection as new if the tuple wasn't previously tracked */
   204  		tuple.saddr = 123;
   205  		seen_flags.value = TCP_FLAG_SYN;
   206  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   207  				  ct_tcp_select_action(seen_flags), CT_INGRESS,
   208  				  CT_ENTRY_ANY, NULL, true, seen_flags, &monitor);
   209  		assert(res == CT_NEW);
   210  		assert(monitor == TRACE_PAYLOAD_LEN);
   211  	});
   212  
   213  	test_finish();
   214  }
   215  
   216  CHECK("tc", "conntrack_svc")
   217  int svc_test(__maybe_unused struct __sk_buff *sctx)
   218  {
   219  	test_init();
   220  
   221  	TEST("ct_lookup_svc", {
   222  		struct __ctx_buff ctx = {};
   223  		int res;
   224  		struct ipv4_ct_tuple tuple = {};
   225  		struct ct_state ct_state = {};
   226  		union tcp_flags seen_flags = {0};
   227  		__u32 monitor;
   228  
   229  		tuple.nexthdr = IPPROTO_TCP;
   230  		tuple.flags = CT_SERVICE;
   231  
   232  		struct ct_entry ct_entry_new = {};
   233  
   234  		res = map_update_elem(get_ct_map4(&tuple), &tuple, &ct_entry_new, BPF_ANY);
   235  		if (IS_ERR(res))
   236  			test_fatal("map_update_elem: %lld", res);
   237  
   238  		struct ct_entry *entry = map_lookup_elem(get_ct_map4(&tuple), &tuple);
   239  
   240  		if (!entry)
   241  			test_fatal("ct entry lookup failed");
   242  
   243  		seen_flags.value |= TCP_FLAG_SYN;
   244  
   245  		/* First packet is monitored */
   246  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   247  				  ct_tcp_select_action(seen_flags), CT_SERVICE,
   248  				  CT_ENTRY_SVC, &ct_state, true, seen_flags, &monitor);
   249  		assert(res == CT_ESTABLISHED);
   250  		assert(monitor == TRACE_PAYLOAD_LEN);
   251  		assert(timeout_in(entry, CT_SYN_TIMEOUT));
   252  
   253  		/* Second packet with the same flags is not monitored; it does reset
   254  		 * lifetime back to CT_SYN_TIMEOUT.
   255  		 */
   256  		advance_time();
   257  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   258  				  ct_tcp_select_action(seen_flags), CT_SERVICE,
   259  				  CT_ENTRY_SVC, &ct_state, true, seen_flags, &monitor);
   260  		assert(res == CT_ESTABLISHED);
   261  		assert(monitor == 0);
   262  		assert(timeout_in(entry, CT_SYN_TIMEOUT));
   263  
   264  		/* Subsequent non-SYN packets result in a default SVC TCP lifetime */
   265  		advance_time();
   266  		seen_flags.value &= ~TCP_FLAG_SYN;
   267  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   268  				  ct_tcp_select_action(seen_flags), CT_SERVICE,
   269  				  CT_ENTRY_SVC, &ct_state, true, seen_flags, &monitor);
   270  		assert(res == CT_ESTABLISHED);
   271  		assert(monitor == 0);
   272  		assert(timeout_in(entry, CT_SERVICE_LIFETIME_TCP));
   273  
   274  		/* Monitor & lower lifetime if the connection is closing on just one side */
   275  		advance_time();
   276  		seen_flags.value |= TCP_FLAG_FIN;
   277  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   278  				  ct_tcp_select_action(seen_flags), CT_SERVICE,
   279  				  CT_ENTRY_SVC, &ct_state, true, seen_flags, &monitor);
   280  		assert(res == CT_ESTABLISHED);
   281  		assert(monitor == TRACE_PAYLOAD_LEN);
   282  		assert(timeout_in(entry, CT_CLOSE_TIMEOUT));
   283  
   284  		/* Label connection as new if the tuple wasn't previously tracked */
   285  		tuple.saddr = 456;
   286  		seen_flags.value = TCP_FLAG_SYN;
   287  		res = __ct_lookup(get_ct_map4(&tuple), &ctx, &tuple,
   288  				  ct_tcp_select_action(seen_flags), CT_SERVICE,
   289  				  CT_ENTRY_SVC, &ct_state, true, seen_flags, &monitor);
   290  		assert(res == CT_NEW);
   291  		assert(monitor == TRACE_PAYLOAD_LEN);
   292  	});
   293  
   294  	test_finish();
   295  }
   296  
   297  BPF_LICENSE("Dual BSD/GPL");