github.com/cilium/cilium@v1.16.2/bpf/tests/xdp_nodeport_lb4_nat_lb.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/xdp.h>
     7  #include "pktgen.h"
     8  
     9  /* Set ETH_HLEN to 14 to indicate that the packet has a 14 byte ethernet header */
    10  #define ETH_HLEN 14
    11  
    12  /* Enable code paths under test */
    13  #define ENABLE_IPV4
    14  #define ENABLE_NODEPORT
    15  #define ENABLE_NODEPORT_ACCELERATION
    16  
    17  #define DISABLE_LOOPBACK_LB
    18  
    19  /* Skip ingress policy checks, not needed to validate hairpin flow */
    20  #define USE_BPF_PROG_FOR_INGRESS_POLICY
    21  #undef FORCE_LOCAL_POLICY_EVAL_AT_SOURCE
    22  
    23  #define CLIENT_IP		v4_ext_one
    24  #define CLIENT_PORT		__bpf_htons(111)
    25  
    26  #define FRONTEND_IP_LOCAL	v4_svc_one
    27  #define FRONTEND_IP_REMOTE	v4_svc_two
    28  #define FRONTEND_PORT		tcp_svc_one
    29  
    30  #define LB_IP			v4_node_one
    31  #define IPV4_DIRECT_ROUTING	LB_IP
    32  
    33  #define BACKEND_IP_LOCAL	v4_pod_one
    34  #define BACKEND_IP_REMOTE	v4_pod_two
    35  #define BACKEND_PORT		__bpf_htons(8080)
    36  
    37  #define fib_lookup mock_fib_lookup
    38  
    39  static volatile const __u8 *client_mac = mac_one;
    40  /* this matches the default node_config.h: */
    41  static volatile const __u8 lb_mac[ETH_ALEN]	= { 0xce, 0x72, 0xa7, 0x03, 0x88, 0x56 };
    42  static volatile const __u8 *node_mac = mac_three;
    43  static volatile const __u8 *local_backend_mac = mac_four;
    44  static volatile const __u8 *remote_backend_mac = mac_five;
    45  
    46  #include <bpf/compiler.h>
    47  #include <bpf/helpers.h>
    48  #include <bpf/loader.h>
    49  #include <bpf/section.h>
    50  #include <linux/bpf.h>
    51  #include <linux/types.h>
    52  
    53  struct mock_settings {
    54  	__be16 nat_source_port;
    55  	bool fail_fib;
    56  };
    57  
    58  struct {
    59  	__uint(type, BPF_MAP_TYPE_ARRAY);
    60  	__uint(key_size, sizeof(__u32));
    61  	__uint(value_size, sizeof(struct mock_settings));
    62  	__uint(max_entries, 1);
    63  } settings_map __section_maps_btf;
    64  
    65  long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
    66  		     __maybe_unused int plen, __maybe_unused __u32 flags)
    67  {
    68  	__u32 key = 0;
    69  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
    70  
    71  	if (settings && settings->fail_fib)
    72  		return BPF_FIB_LKUP_RET_NO_NEIGH;
    73  
    74  	params->ifindex = 0;
    75  
    76  	if (params->ipv4_dst == BACKEND_IP_REMOTE) {
    77  		__bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN);
    78  		__bpf_memcpy_builtin(params->dmac, (__u8 *)remote_backend_mac, ETH_ALEN);
    79  	} else {
    80  		__bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN);
    81  		__bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN);
    82  	}
    83  
    84  	return 0;
    85  }
    86  
    87  #include <bpf_xdp.c>
    88  
    89  #include "lib/endpoint.h"
    90  #include "lib/ipcache.h"
    91  #include "lib/lb.h"
    92  
    93  #define FROM_NETDEV	0
    94  
    95  struct {
    96  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    97  	__uint(key_size, sizeof(__u32));
    98  	__uint(max_entries, 1);
    99  	__array(values, int());
   100  } entry_call_map __section(".maps") = {
   101  	.values = {
   102  		[FROM_NETDEV] = &cil_xdp_entry,
   103  	},
   104  };
   105  
   106  /* Test that a SVC request to a local backend
   107   * - gets DNATed (but not SNATed)
   108   * - gets passed up from XDP to TC
   109   */
   110  PKTGEN("xdp", "xdp_nodeport_local_backend")
   111  int nodeport_local_backend_pktgen(struct __ctx_buff *ctx)
   112  {
   113  	struct pktgen builder;
   114  	struct tcphdr *l4;
   115  	void *data;
   116  
   117  	/* Init packet builder */
   118  	pktgen__init(&builder, ctx);
   119  
   120  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   121  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   122  					  CLIENT_IP, FRONTEND_IP_LOCAL,
   123  					  CLIENT_PORT, FRONTEND_PORT);
   124  	if (!l4)
   125  		return TEST_ERROR;
   126  
   127  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   128  	if (!data)
   129  		return TEST_ERROR;
   130  
   131  	/* Calc lengths, set protocol fields and calc checksums */
   132  	pktgen__finish(&builder);
   133  
   134  	return 0;
   135  }
   136  
   137  SETUP("xdp", "xdp_nodeport_local_backend")
   138  int nodeport_local_backend_setup(struct __ctx_buff *ctx)
   139  {
   140  	__u16 revnat_id = 1;
   141  
   142  	lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id);
   143  	lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 124,
   144  			  BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_TCP, 0);
   145  
   146  	/* add local backend */
   147  	endpoint_v4_add_entry(BACKEND_IP_LOCAL, 0, 0, 0, 0,
   148  			      (__u8 *)local_backend_mac, (__u8 *)node_mac);
   149  	ipcache_v4_add_entry(BACKEND_IP_LOCAL, 0, 112233, 0, 0);
   150  
   151  	/* Jump into the entrypoint */
   152  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   153  	/* Fail if we didn't jump */
   154  	return TEST_ERROR;
   155  }
   156  
   157  CHECK("xdp", "xdp_nodeport_local_backend")
   158  int nodeport_local_backend_check(const struct __ctx_buff *ctx)
   159  {
   160  	void *data, *data_end;
   161  	__u32 *status_code;
   162  	struct tcphdr *l4;
   163  	struct ethhdr *l2;
   164  	struct iphdr *l3;
   165  	__u32 *meta;
   166  
   167  	test_init();
   168  
   169  	data = (void *)(long)ctx_data(ctx);
   170  	data_end = (void *)(long)ctx->data_end;
   171  
   172  	status_code = data;
   173  	if (data + sizeof(__u32) > data_end)
   174  		test_fatal("status code out of bounds");
   175  
   176  	meta = (void *)status_code + sizeof(__u32);
   177  	if ((void *)meta + sizeof(__u32) > data_end)
   178  		test_fatal("meta out of bounds");
   179  
   180  	l2 = (void *)meta + sizeof(__u32);
   181  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   182  		test_fatal("l2 out of bounds");
   183  
   184  	l3 = (void *)l2 + sizeof(struct ethhdr);
   185  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   186  		test_fatal("l3 out of bounds");
   187  
   188  	l4 = (void *)l3 + sizeof(struct iphdr);
   189  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   190  		test_fatal("l4 out of bounds");
   191  
   192  	assert(*status_code == CTX_ACT_OK);
   193  
   194  	assert((*meta & XFER_PKT_NO_SVC) == XFER_PKT_NO_SVC);
   195  
   196  	if (memcmp(l2->h_source, (__u8 *)client_mac, ETH_ALEN) != 0)
   197  		test_fatal("src MAC is not the client MAC")
   198  	if (memcmp(l2->h_dest, (__u8 *)lb_mac, ETH_ALEN) != 0)
   199  		test_fatal("dst MAC is not the LB MAC")
   200  
   201  	if (l3->saddr != CLIENT_IP)
   202  		test_fatal("src IP has changed");
   203  
   204  	if (l3->daddr != BACKEND_IP_LOCAL)
   205  		test_fatal("dst IP hasn't been NATed to local backend IP");
   206  
   207  	if (l3->check != bpf_htons(0x4112))
   208  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   209  
   210  	if (l4->source != CLIENT_PORT)
   211  		test_fatal("src port has changed");
   212  
   213  	if (l4->dest != BACKEND_PORT)
   214  		test_fatal("dst TCP port hasn't been NATed to backend port");
   215  
   216  	test_finish();
   217  }
   218  
   219  /* Test that a SVC request that is LBed to a NAT remote backend
   220   * - gets DNATed and SNATed,
   221   * - gets redirected back out by XDP
   222   */
   223  PKTGEN("xdp", "xdp_nodeport_nat_fwd")
   224  int nodeport_nat_fwd_pktgen(struct __ctx_buff *ctx)
   225  {
   226  	struct pktgen builder;
   227  	struct tcphdr *l4;
   228  	void *data;
   229  
   230  	/* Init packet builder */
   231  	pktgen__init(&builder, ctx);
   232  
   233  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   234  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   235  					  CLIENT_IP, FRONTEND_IP_REMOTE,
   236  					  CLIENT_PORT, FRONTEND_PORT);
   237  	if (!l4)
   238  		return TEST_ERROR;
   239  
   240  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   241  	if (!data)
   242  		return TEST_ERROR;
   243  
   244  	/* Calc lengths, set protocol fields and calc checksums */
   245  	pktgen__finish(&builder);
   246  
   247  	return 0;
   248  }
   249  
   250  SETUP("xdp", "xdp_nodeport_nat_fwd")
   251  int nodeport_nat_fwd_setup(struct __ctx_buff *ctx)
   252  {
   253  	__u16 revnat_id = 1;
   254  
   255  	lb_v4_add_service(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, revnat_id);
   256  	lb_v4_add_backend(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, 124,
   257  			  BACKEND_IP_REMOTE, BACKEND_PORT, IPPROTO_TCP, 0);
   258  
   259  	ipcache_v4_add_entry(BACKEND_IP_REMOTE, 0, 112233, 0, 0);
   260  
   261  	/* Jump into the entrypoint */
   262  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   263  	/* Fail if we didn't jump */
   264  	return TEST_ERROR;
   265  }
   266  
   267  CHECK("xdp", "xdp_nodeport_nat_fwd")
   268  int nodeport_nat_fwd_check(__maybe_unused const struct __ctx_buff *ctx)
   269  {
   270  	void *data, *data_end;
   271  	__u32 *status_code;
   272  	struct tcphdr *l4;
   273  	struct ethhdr *l2;
   274  	struct iphdr *l3;
   275  	__u32 key = 0;
   276  
   277  	test_init();
   278  
   279  	data = (void *)(long)ctx_data(ctx);
   280  	data_end = (void *)(long)ctx->data_end;
   281  
   282  	if (data + sizeof(__u32) > data_end)
   283  		test_fatal("status code out of bounds");
   284  
   285  	status_code = data;
   286  
   287  	assert(fib_ok(*status_code));
   288  
   289  	l2 = data + sizeof(__u32);
   290  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   291  		test_fatal("l2 out of bounds");
   292  
   293  	l3 = (void *)l2 + sizeof(struct ethhdr);
   294  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   295  		test_fatal("l3 out of bounds");
   296  
   297  	l4 = (void *)l3 + sizeof(struct iphdr);
   298  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   299  		test_fatal("l4 out of bounds");
   300  
   301  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   302  		test_fatal("src MAC is not the LB MAC")
   303  	if (memcmp(l2->h_dest, (__u8 *)remote_backend_mac, ETH_ALEN) != 0)
   304  		test_fatal("dst MAC is not the backend MAC")
   305  
   306  	if (l3->saddr != LB_IP)
   307  		test_fatal("src IP hasn't been NATed to LB IP");
   308  
   309  	if (l3->daddr != BACKEND_IP_REMOTE)
   310  		test_fatal("dst IP hasn't been NATed to remote backend IP");
   311  
   312  	if (l3->check != bpf_htons(0xa711))
   313  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   314  
   315  	if (l4->source == CLIENT_PORT)
   316  		test_fatal("src port hasn't been NATed");
   317  
   318  	if (l4->dest != BACKEND_PORT)
   319  		test_fatal("dst port hasn't been NATed to backend port");
   320  
   321  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   322  
   323  	if (settings)
   324  		settings->nat_source_port = l4->source;
   325  
   326  	test_finish();
   327  }
   328  
   329  static __always_inline int build_reply(struct __ctx_buff *ctx)
   330  {
   331  	struct pktgen builder;
   332  	struct tcphdr *l4;
   333  	void *data;
   334  	__u16 nat_source_port = 0;
   335  	__u32 key = 0;
   336  
   337  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   338  
   339  	if (settings && settings->nat_source_port)
   340  		nat_source_port = settings->nat_source_port;
   341  
   342  	/* Init packet builder */
   343  	pktgen__init(&builder, ctx);
   344  
   345  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   346  					  (__u8 *)remote_backend_mac, (__u8 *)lb_mac,
   347  					  BACKEND_IP_REMOTE, LB_IP,
   348  					  BACKEND_PORT, nat_source_port);
   349  	if (!l4)
   350  		return TEST_ERROR;
   351  
   352  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   353  	if (!data)
   354  		return TEST_ERROR;
   355  
   356  	/* Calc lengths, set protocol fields and calc checksums */
   357  	pktgen__finish(&builder);
   358  
   359  	return 0;
   360  }
   361  
   362  static __always_inline int check_reply(const struct __ctx_buff *ctx)
   363  {
   364  	void *data, *data_end;
   365  	__u32 *status_code;
   366  	struct tcphdr *l4;
   367  	struct ethhdr *l2;
   368  	struct iphdr *l3;
   369  
   370  	test_init();
   371  
   372  	data = (void *)(long)ctx_data(ctx);
   373  	data_end = (void *)(long)ctx->data_end;
   374  
   375  	if (data + sizeof(__u32) > data_end)
   376  		test_fatal("status code out of bounds");
   377  
   378  	status_code = data;
   379  
   380  	assert(fib_ok(*status_code));
   381  
   382  	l2 = data + sizeof(__u32);
   383  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   384  		test_fatal("l2 out of bounds");
   385  
   386  	l3 = (void *)l2 + sizeof(struct ethhdr);
   387  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   388  		test_fatal("l3 out of bounds");
   389  
   390  	l4 = (void *)l3 + sizeof(struct iphdr);
   391  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   392  		test_fatal("l4 out of bounds");
   393  
   394  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   395  		test_fatal("src MAC is not the LB MAC")
   396  	if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0)
   397  		test_fatal("dst MAC is not the client MAC")
   398  
   399  	if (l3->saddr != FRONTEND_IP_REMOTE)
   400  		test_fatal("src IP hasn't been RevNATed to frontend IP");
   401  
   402  	if (l3->daddr != CLIENT_IP)
   403  		test_fatal("dst IP hasn't been RevNATed to client IP");
   404  
   405  	if (l3->check != bpf_htons(0x4ca9))
   406  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   407  
   408  	if (l4->source != FRONTEND_PORT)
   409  		test_fatal("src port hasn't been RevNATed to frontend port");
   410  
   411  	if (l4->dest != CLIENT_PORT)
   412  		test_fatal("dst port hasn't been RevNATed to client port");
   413  
   414  	test_finish();
   415  }
   416  
   417  /* Test that the LB RevDNATs and RevSNATs a reply from the
   418   * NAT remote backend, and sends it back to the client.
   419   */
   420  PKTGEN("xdp", "xdp_nodeport_nat_fwd_reply")
   421  int nodeport_nat_fwd_reply_pktgen(struct __ctx_buff *ctx)
   422  {
   423  	return build_reply(ctx);
   424  }
   425  
   426  SETUP("xdp", "xdp_nodeport_nat_fwd_reply")
   427  int nodeport_nat_fwd_reply_setup(struct __ctx_buff *ctx)
   428  {
   429  	/* Jump into the entrypoint */
   430  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   431  	/* Fail if we didn't jump */
   432  	return TEST_ERROR;
   433  }
   434  
   435  CHECK("xdp", "xdp_nodeport_nat_fwd_reply")
   436  int nodeport_nat_fwd_reply_check(const struct __ctx_buff *ctx)
   437  {
   438  	return check_reply(ctx);
   439  }
   440  
   441  /* Test that the LB RevDNATs and RevSNATs a reply from the
   442   * NAT remote backend, and sends it back to the client.
   443   * Even if the FIB lookup fails.
   444   */
   445  PKTGEN("xdp", "xdp_nodeport_nat_fwd_reply_no_fib")
   446  int nodepoirt_nat_fwd_reply_no_fib_pktgen(struct __ctx_buff *ctx)
   447  {
   448  	return build_reply(ctx);
   449  }
   450  
   451  SETUP("xdp", "xdp_nodeport_nat_fwd_reply_no_fib")
   452  int nodeport_nat_fwd_reply_no_fib_setup(struct __ctx_buff *ctx)
   453  {
   454  	__u32 key = 0;
   455  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   456  
   457  	if (settings)
   458  		settings->fail_fib = true;
   459  
   460  	/* Jump into the entrypoint */
   461  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   462  	/* Fail if we didn't jump */
   463  	return TEST_ERROR;
   464  }
   465  
   466  CHECK("xdp", "xdp_nodeport_nat_fwd_reply_no_fib")
   467  int nodeport_nat_fwd_reply_no_fib_check(__maybe_unused const struct __ctx_buff *ctx)
   468  {
   469  	return check_reply(ctx);
   470  }