github.com/cilium/cilium@v1.16.2/bpf/tests/tc_nodeport_l3_dev_to_tunnel.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  #include "pktgen.h"
     8  
     9  #define ETH_HLEN		0
    10  #define SECCTX_FROM_IPCACHE	1
    11  #define ENABLE_IPV4
    12  #define ENABLE_IPV6
    13  #define TUNNEL_MODE	1
    14  #define ENCAP_IFINDEX	42
    15  #define ENABLE_NODEPORT	1
    16  
    17  #define TEST_IP_LOCAL		v4_pod_one
    18  #define TEST_IP_REMOTE		v4_pod_two
    19  #define TEST_IPV6_LOCAL		v6_pod_one
    20  
    21  #define CLIENT_IP		v4_ext_one
    22  #define CLIENT_IPV6		{ .addr = { 0x1, 0x0, 0x0, 0x0, 0x0, 0x0 } }
    23  #define CLIENT_PORT		__bpf_htons(111)
    24  
    25  #define FRONTEND_IP		v4_svc_one
    26  #define FRONTEND_IPV6		{ .addr = { 0x5, 0x0, 0x0, 0x0, 0x0, 0x0 } }
    27  #define FRONTEND_PORT		tcp_svc_one
    28  
    29  #define BACKEND_IP		v4_pod_one
    30  #define BACKEND_IPV6		{ .addr = { 0x3, 0x0, 0x0, 0x0, 0x0, 0x0 } }
    31  #define BACKEND_PORT		__bpf_htons(8080)
    32  
    33  #include "bpf_host.c"
    34  #include "lib/ipcache.h"
    35  #include "lib/lb.h"
    36  
    37  static volatile const __u8 *node_mac = mac_two;
    38  
    39  struct {
    40  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    41  	__uint(key_size, sizeof(__u32));
    42  	__uint(max_entries, 2);
    43  	__array(values, int());
    44  } entry_call_map __section(".maps") = {
    45  	.values = {
    46  		[0] = &cil_from_netdev,
    47  	},
    48  };
    49  
    50  PKTGEN("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel")
    51  int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel(struct __ctx_buff *ctx)
    52  {
    53  	struct pktgen builder;
    54  	struct tcphdr *l4;
    55  	void *data;
    56  
    57  	/* Init packet builder */
    58  	pktgen__init(&builder, ctx);
    59  
    60  	/* We are building an L3 skb which doesn't have L2 header, so in theory
    61  	 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP),
    62  	 * but bpf verifier doesn't allow us to do so, and kernel also doesn't
    63  	 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156).
    64  	 * Therefore we workaround the issue by pushing L2 header in the PKTGEN
    65  	 * and stripping it in the SETUP.
    66  	 */
    67  
    68  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
    69  					  (__u8 *)node_mac, (__u8 *)node_mac,
    70  					  CLIENT_IP, FRONTEND_IP,
    71  					  CLIENT_PORT, FRONTEND_PORT);
    72  	if (!l4)
    73  		return TEST_ERROR;
    74  
    75  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
    76  
    77  	if (!data)
    78  		return TEST_ERROR;
    79  
    80  	pktgen__finish(&builder);
    81  
    82  	return 0;
    83  }
    84  
    85  SETUP("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel")
    86  int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel_setup(struct __ctx_buff *ctx)
    87  {
    88  	void *data = (void *)(long)ctx->data;
    89  	void *data_end = (void *)(long)ctx->data_end;
    90  	__u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO;
    91  
    92  	lb_v4_add_service(FRONTEND_IP, FRONTEND_PORT, 1, 1);
    93  	lb_v4_add_backend(FRONTEND_IP, FRONTEND_PORT, 1, 124,
    94  			  BACKEND_IP, BACKEND_PORT, IPPROTO_TCP, 0);
    95  
    96  	ipcache_v4_add_entry(BACKEND_IP, 0, 112233, TEST_IP_REMOTE, 0);
    97  
    98  	/* As commented in PKTGEN, now we strip the L2 header. Bpf helper
    99  	 * skb_adjust_room will use L2 header to overwrite L3 header, so we play
   100  	 * a trick to memcpy(ethhdr, iphdr, ETH_HLEN) ahead of skb_adjust_room
   101  	 * so as to guarantee L3 header keeps intact.
   102  	 */
   103  	if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end)
   104  		memcpy(data, data + __ETH_HLEN, __ETH_HLEN);
   105  
   106  	skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags);
   107  
   108  	tail_call_static(ctx, entry_call_map, 0);
   109  	return TEST_ERROR;
   110  }
   111  
   112  CHECK("tc", "ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel")
   113  int ipv4_tc_nodeport_l3_to_remote_backend_via_tunnel_check(__maybe_unused
   114  							   const struct __ctx_buff *ctx)
   115  {
   116  	void *data;
   117  	void *data_end;
   118  	__u32 *status_code;
   119  	struct ethhdr *l2;
   120  
   121  	test_init();
   122  
   123  	data = (void *)(long)ctx->data;
   124  	data_end = (void *)(long)ctx->data_end;
   125  
   126  	if (data + sizeof(__u32) > data_end)
   127  		test_fatal("status code out of bounds");
   128  
   129  	status_code = data;
   130  
   131  	/* Check that LB request got redirected (to a tunnel iface) */
   132  	assert(*status_code == TC_ACT_REDIRECT);
   133  
   134  	/* Check that L2 hdr was added */
   135  	l2 = data + sizeof(__u32);
   136  
   137  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   138  		test_fatal("l2 out of bounds");
   139  
   140  	if (l2->h_proto != bpf_htons(ETH_P_IP))
   141  		test_fatal("l2 proto hasn't been set to ETH_P_IP");
   142  
   143  	test_finish();
   144  }
   145  
   146  PKTGEN("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel")
   147  int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel(struct __ctx_buff *ctx)
   148  {
   149  	union v6addr frontend_ip = FRONTEND_IPV6;
   150  	union v6addr client_ip = CLIENT_IPV6;
   151  	struct pktgen builder;
   152  	struct tcphdr *l4;
   153  	void *data;
   154  
   155  	/* Init packet builder */
   156  	pktgen__init(&builder, ctx);
   157  
   158  	/* We are building an L3 skb which doesn't have L2 header, so in theory
   159  	 * we need to skip L2 header and set ctx->protocol = bpf_ntohs(ETH_P_IP),
   160  	 * but bpf verifier doesn't allow us to do so, and kernel also doesn't
   161  	 * handle an L3 skb properly (see https://elixir.bootlin.com/linux/v6.2.1/source/net/bpf/test_run.c#L1156).
   162  	 * Therefore we workaround the issue by pushing L2 header in the PKTGEN
   163  	 * and stripping it in the SETUP.
   164  	 */
   165  
   166  	l4 = pktgen__push_ipv6_tcp_packet(&builder,
   167  					  (__u8 *)node_mac, (__u8 *)node_mac,
   168  					  (__u8 *)&client_ip, (__u8 *)&frontend_ip,
   169  					  CLIENT_PORT, FRONTEND_PORT);
   170  	if (!l4)
   171  		return TEST_ERROR;
   172  
   173  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   174  
   175  	if (!data)
   176  		return TEST_ERROR;
   177  
   178  	pktgen__finish(&builder);
   179  
   180  	return 0;
   181  }
   182  
   183  SETUP("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel")
   184  int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel_setup(struct __ctx_buff *ctx)
   185  {
   186  	union v6addr frontend_ip = FRONTEND_IPV6;
   187  	union v6addr backend_ip = BACKEND_IPV6;
   188  	void *data = (void *)(long)ctx->data;
   189  	void *data_end = (void *)(long)ctx->data_end;
   190  	__u64 flags = BPF_F_ADJ_ROOM_FIXED_GSO;
   191  
   192  	lb_v6_add_service(&frontend_ip, FRONTEND_PORT, 1, 1);
   193  	lb_v6_add_backend(&frontend_ip, FRONTEND_PORT, 1, 124,
   194  			  &backend_ip, BACKEND_PORT, IPPROTO_TCP, 0);
   195  
   196  	ipcache_v6_add_entry(&backend_ip, 0, 112233, TEST_IP_REMOTE, 0);
   197  
   198  	/* As commented in PKTGEN, now we strip the L2 header. Bpf helper
   199  	 * skb_adjust_room will use L2 header to overwrite L3 header, so we play
   200  	 * a trick to memcpy(ethhdr, iphdr, ETH_HLEN) ahead of skb_adjust_room
   201  	 * so as to guarantee L3 header keeps intact.
   202  	 */
   203  	if ((void *)data + __ETH_HLEN + __ETH_HLEN <= data_end)
   204  		memcpy(data, data + __ETH_HLEN, __ETH_HLEN);
   205  
   206  	skb_adjust_room(ctx, -__ETH_HLEN, BPF_ADJ_ROOM_MAC, flags);
   207  
   208  	tail_call_static(ctx, entry_call_map, 0);
   209  	return TEST_ERROR;
   210  }
   211  
   212  CHECK("tc", "ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel")
   213  int ipv6_tc_nodeport_l3_to_remote_backend_via_tunnel_check(__maybe_unused
   214  							   const struct __ctx_buff *ctx)
   215  {
   216  	void *data;
   217  	void *data_end;
   218  	__u32 *status_code;
   219  	struct ethhdr *l2;
   220  
   221  	test_init();
   222  
   223  	data = (void *)(long)ctx->data;
   224  	data_end = (void *)(long)ctx->data_end;
   225  
   226  	if (data + sizeof(__u32) > data_end)
   227  		test_fatal("status code out of bounds");
   228  
   229  	status_code = data;
   230  
   231  	/* Check that LB request got redirected (to a tunnel iface) */
   232  	assert(*status_code == TC_ACT_REDIRECT);
   233  
   234  	/* Check that L2 hdr was added */
   235  	l2 = data + sizeof(__u32);
   236  
   237  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   238  		test_fatal("l2 out of bounds");
   239  
   240  	if (l2->h_proto != bpf_htons(ETH_P_IPV6))
   241  		test_fatal("l2 proto hasn't been set to ETH_P_IPV6");
   242  
   243  	test_finish();
   244  }