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");