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

     1  // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
     2  /* Copyright Authors of Cilium */
     3  
     4  #include "bpf/ctx/xdp.h"
     5  #include "common.h"
     6  #include "pktgen.h"
     7  
     8  #define ENABLE_IPV4
     9  #define ENABLE_NODEPORT
    10  #define ENABLE_NODEPORT_ACCELERATION
    11  #define ENABLE_SESSION_AFFINITY
    12  
    13  /* Make sure we always pick backend slot 1 if we end up in backend selection. */
    14  #define LB_SELECTION LB_SELECTION_FIRST
    15  
    16  #define fib_lookup mock_fib_lookup
    17  
    18  static const char fib_smac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02};
    19  static const char fib_dmac[6] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37};
    20  
    21  long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
    22  		     __maybe_unused int plen, __maybe_unused __u32 flags)
    23  {
    24  	__bpf_memcpy_builtin(params->smac, fib_smac, ETH_ALEN);
    25  	__bpf_memcpy_builtin(params->dmac, fib_dmac, ETH_ALEN);
    26  	return 0;
    27  }
    28  
    29  #include "bpf_xdp.c"
    30  #include "lib/nodeport.h"
    31  
    32  struct {
    33  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    34  	__uint(key_size, sizeof(__u32));
    35  	__uint(max_entries, 2);
    36  	__array(values, int());
    37  } entry_call_map __section(".maps") = {
    38  	.values = {
    39  		[0] = &cil_xdp_entry,
    40  	},
    41  };
    42  
    43  #define CLIENT_IP IPV4(10, 0, 0, 1)
    44  #define FRONTEND_IP IPV4(10, 0, 1, 1)
    45  #define BACKEND_IP1 IPV4(10, 0, 2, 1)
    46  #define BACKEND_IP2 IPV4(10, 0, 3, 1)
    47  #define FRONTEND_PORT bpf_htons(80)
    48  #define BACKEND_PORT bpf_htons(8080)
    49  #define REV_NAT_INDEX 123
    50  #define BACKEND_ID1 7
    51  #define BACKEND_ID2 42
    52  
    53  static __always_inline int craft_packet(struct __ctx_buff *ctx)
    54  {
    55  	struct pktgen builder;
    56  	struct ethhdr *eh;
    57  	struct iphdr *iph;
    58  	struct tcphdr *tcph;
    59  
    60  	pktgen__init(&builder, ctx);
    61  
    62  	eh = pktgen__push_ethhdr(&builder);
    63  	if (!eh)
    64  		return TEST_ERROR;
    65  	*eh = (struct ethhdr){.h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
    66  			      .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67},
    67  			      .h_proto = bpf_htons(ETH_P_IP)};
    68  
    69  	iph = pktgen__push_default_iphdr(&builder);
    70  
    71  	if (!iph)
    72  		return TEST_ERROR;
    73  
    74  	iph->saddr = CLIENT_IP;
    75  	iph->daddr = FRONTEND_IP;
    76  
    77  	tcph = pktgen__push_default_tcphdr(&builder);
    78  	if (!tcph)
    79  		return TEST_ERROR;
    80  
    81  	tcph->source = bpf_htons(23445);
    82  	tcph->dest = FRONTEND_PORT;
    83  
    84  	if (!pktgen__push_data(&builder, default_data, sizeof(default_data)))
    85  		return TEST_ERROR;
    86  
    87  	pktgen__finish(&builder);
    88  
    89  	return 0;
    90  }
    91  
    92  #define SVC_KEY_VALUE(_beslot, _beid, _scope)				\
    93  	{								\
    94  		.key = {.address = FRONTEND_IP,				\
    95  			.dport = FRONTEND_PORT,				\
    96  			.scope = (_scope),				\
    97  			.backend_slot = (_beslot)},			\
    98  		.value = {						\
    99  			.flags = SVC_FLAG_ROUTABLE | SVC_FLAG_AFFINITY, \
   100  			.count = 2,					\
   101  			.rev_nat_index = REV_NAT_INDEX,			\
   102  			.backend_id = (_beid)				\
   103  		}							\
   104  	}
   105  
   106  #define BE_KEY_VALUE(_beid, _beip)		\
   107  	{					\
   108  		.key = (_beid),			\
   109  		.value = {.address = (_beip),	\
   110  			  .port = BACKEND_PORT, \
   111  			  .proto = IPPROTO_TCP},\
   112  	}
   113  
   114  SETUP("xdp", "session_affinity")
   115  int test1_setup(struct __ctx_buff *ctx)
   116  {
   117  	struct {
   118  		struct lb4_key key;
   119  		struct lb4_service value;
   120  	} services[] = {
   121  		SVC_KEY_VALUE(0, 100 /* affinity timeout */, LB_LOOKUP_SCOPE_INT),
   122  		SVC_KEY_VALUE(0, 100 /* affinity timeout */, LB_LOOKUP_SCOPE_EXT),
   123  
   124  		SVC_KEY_VALUE(1, BACKEND_ID1, LB_LOOKUP_SCOPE_EXT),
   125  		SVC_KEY_VALUE(2, BACKEND_ID2, LB_LOOKUP_SCOPE_EXT),
   126  	};
   127  	struct {
   128  		__u32 key;
   129  		struct lb4_backend value;
   130  	} backends[] = {
   131  		BE_KEY_VALUE(BACKEND_ID1, BACKEND_IP1),
   132  		BE_KEY_VALUE(BACKEND_ID2, BACKEND_IP2),
   133  	};
   134  	struct lb4_affinity_key aff_key = {
   135  		.client_id = {.client_ip = CLIENT_IP},
   136  		.rev_nat_id = REV_NAT_INDEX,
   137  		.netns_cookie = 0x0,
   138  	};
   139  	struct lb_affinity_val aff_value = {
   140  		.last_used = bpf_mono_now(),
   141  		.backend_id = BACKEND_ID2,
   142  	};
   143  	struct lb_affinity_match match_key = {
   144  		.backend_id = BACKEND_ID2,
   145  		.rev_nat_id = REV_NAT_INDEX,
   146  	};
   147  	int ret;
   148  	int zero = 0;
   149  
   150  	/* Insert the service and backend map values */
   151  	for (unsigned long i = 0; i < ARRAY_SIZE(services); i++) {
   152  		map_update_elem(&LB4_SERVICES_MAP_V2, &services[i].key,
   153  				&services[i].value, BPF_ANY);
   154  	}
   155  
   156  	for (unsigned long i = 0; i < ARRAY_SIZE(backends); i++) {
   157  		map_update_elem(&LB4_BACKEND_MAP, &backends[i].key,
   158  				&backends[i].value, BPF_ANY);
   159  	}
   160  
   161  	/* Create the session affinity entry for the client */
   162  	map_update_elem(&LB4_AFFINITY_MAP, &aff_key, &aff_value, BPF_ANY);
   163  
   164  	/* Add the affinity match entry to mark the backend as alive */
   165  	map_update_elem(&LB_AFFINITY_MATCH_MAP, &match_key, &zero, BPF_ANY);
   166  
   167  	ret = craft_packet(ctx);
   168  	if (ret)
   169  		return ret;
   170  
   171  	/* Jump into the entrypoint */
   172  	tail_call_static(ctx, entry_call_map, 0);
   173  	/* Fail if we didn't jump */
   174  	return TEST_ERROR;
   175  }
   176  
   177  CHECK("xdp", "session_affinity")
   178  int test1_check(__maybe_unused const struct __ctx_buff *ctx)
   179  {
   180  	test_init();
   181  
   182  	void *data = (void *)(long)ctx->data;
   183  	void *data_end = (void *)(long)ctx->data_end;
   184  
   185  	if (data + sizeof(__u32) > data_end)
   186  		test_fatal("status code out of bounds");
   187  
   188  	__u32 *status_code = data;
   189  
   190  	if (*status_code != XDP_TX) test_fatal("status code != XDP_TX %u", *status_code);
   191  
   192  	data += sizeof(__u32);
   193  
   194  	if (data + sizeof(struct ethhdr) > data_end)
   195  		test_fatal("ctx doesn't fit ethhdr");
   196  
   197  	struct ethhdr *l2 = data;
   198  
   199  	data += sizeof(struct ethhdr);
   200  
   201  	if (memcmp(l2->h_source, fib_smac, sizeof(fib_smac)) != 0)
   202  		test_fatal("l2->h_source != fib_smac");
   203  
   204  	if (memcmp(l2->h_dest, fib_dmac, sizeof(fib_dmac)) != 0)
   205  		test_fatal("l2->h_dest != fib_dmac");
   206  
   207  	if (data + sizeof(struct iphdr) > data_end)
   208  		test_fatal("ctx doesn't fit iphdr");
   209  
   210  	struct iphdr *l3 = data;
   211  
   212  	data += sizeof(struct iphdr);
   213  
   214  	if (l3->daddr != BACKEND_IP2) test_fatal("dst ip != backend IP");
   215  
   216  	if (data + sizeof(struct tcphdr) > data_end)
   217  		test_fatal("ctx doesn't fit tcphdr");
   218  
   219  	struct tcphdr *l4 = data;
   220  
   221  	data += sizeof(struct tcphdr);
   222  
   223  	if (l4->dest != BACKEND_PORT)
   224  		test_fatal("dst port changed");
   225  
   226  	test_finish();
   227  }