github.com/cilium/cilium@v1.16.2/bpf/tests/tc_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/skb.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_HOST_ROUTING
    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  #define CLIENT_IP_2		v4_ext_two
    26  
    27  #define FRONTEND_IP_LOCAL	v4_svc_one
    28  #define FRONTEND_IP_REMOTE	v4_svc_two
    29  #define FRONTEND_PORT		tcp_svc_one
    30  
    31  #define LB_IP			v4_node_one
    32  #define IPV4_DIRECT_ROUTING	LB_IP
    33  
    34  #define BACKEND_IP_LOCAL	v4_pod_one
    35  #define BACKEND_IP_REMOTE	v4_pod_two
    36  #define BACKEND_PORT		__bpf_htons(8080)
    37  
    38  #define NATIVE_DEV_IFINDEX	24
    39  #define DEFAULT_IFACE		NATIVE_DEV_IFINDEX
    40  #define BACKEND_IFACE		25
    41  #define SVC_EGRESS_IFACE	26
    42  
    43  #define fib_lookup mock_fib_lookup
    44  
    45  static volatile const __u8 *client_mac = mac_one;
    46  /* this matches the default node_config.h: */
    47  static volatile const __u8 lb_mac[ETH_ALEN]	= { 0xce, 0x72, 0xa7, 0x03, 0x88, 0x56 };
    48  static volatile const __u8 *node_mac = mac_three;
    49  static volatile const __u8 *local_backend_mac = mac_four;
    50  static volatile const __u8 *remote_backend_mac = mac_five;
    51  
    52  struct mock_settings {
    53  	__be16 nat_source_port;
    54  	bool fail_fib;
    55  };
    56  
    57  struct {
    58  	__uint(type, BPF_MAP_TYPE_ARRAY);
    59  	__uint(key_size, sizeof(__u32));
    60  	__uint(value_size, sizeof(struct mock_settings));
    61  	__uint(max_entries, 1);
    62  } settings_map __section_maps_btf;
    63  
    64  #define fib_lookup mock_fib_lookup
    65  
    66  long mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
    67  		     __maybe_unused int plen, __maybe_unused __u32 flags)
    68  {
    69  	__u32 key = 0;
    70  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
    71  
    72  	if (settings && settings->fail_fib)
    73  		return BPF_FIB_LKUP_RET_NO_NEIGH;
    74  
    75  	params->ifindex = DEFAULT_IFACE;
    76  
    77  	if (params->ipv4_dst == BACKEND_IP_REMOTE) {
    78  		__bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN);
    79  		__bpf_memcpy_builtin(params->dmac, (__u8 *)remote_backend_mac, ETH_ALEN);
    80  	} else if (params->ipv4_src == FRONTEND_IP_LOCAL &&
    81  		   params->ipv4_dst == CLIENT_IP_2) {
    82  		__bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN);
    83  		__bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN);
    84  		params->ifindex = SVC_EGRESS_IFACE;
    85  	} else {
    86  		__bpf_memcpy_builtin(params->smac, (__u8 *)lb_mac, ETH_ALEN);
    87  		__bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN);
    88  	}
    89  
    90  	return 0;
    91  }
    92  
    93  #define ctx_redirect mock_ctx_redirect
    94  
    95  static __always_inline __maybe_unused int
    96  mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused,
    97  		  int ifindex __maybe_unused, __u32 flags __maybe_unused)
    98  {
    99  	void *data = (void *)(long)ctx_data(ctx);
   100  	void *data_end = (void *)(long)ctx->data_end;
   101  	struct iphdr *ip4;
   102  
   103  	ip4 = data + sizeof(struct ethhdr);
   104  	if ((void *)ip4 + sizeof(*ip4) > data_end)
   105  		return CTX_ACT_DROP;
   106  
   107  	/* Forward to backend: */
   108  	if (ip4->saddr == CLIENT_IP && ifindex == BACKEND_IFACE)
   109  		return CTX_ACT_REDIRECT;
   110  	if (ip4->saddr == CLIENT_IP_2 && ifindex == BACKEND_IFACE)
   111  		return CTX_ACT_REDIRECT;
   112  	if (ip4->saddr == LB_IP && ifindex == DEFAULT_IFACE)
   113  		return CTX_ACT_REDIRECT;
   114  
   115  	/* Redirected reply: */
   116  	if (ip4->daddr == CLIENT_IP_2 && ifindex == SVC_EGRESS_IFACE)
   117  		return CTX_ACT_REDIRECT;
   118  	if (ip4->saddr == FRONTEND_IP_REMOTE && ifindex == DEFAULT_IFACE)
   119  		return CTX_ACT_REDIRECT;
   120  
   121  	return CTX_ACT_DROP;
   122  }
   123  
   124  #define SECCTX_FROM_IPCACHE 1
   125  
   126  #include "bpf_host.c"
   127  
   128  #include "lib/endpoint.h"
   129  #include "lib/ipcache.h"
   130  #include "lib/lb.h"
   131  
   132  #define FROM_NETDEV	0
   133  #define TO_NETDEV	1
   134  
   135  struct {
   136  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
   137  	__uint(key_size, sizeof(__u32));
   138  	__uint(max_entries, 2);
   139  	__array(values, int());
   140  } entry_call_map __section(".maps") = {
   141  	.values = {
   142  		[FROM_NETDEV] = &cil_from_netdev,
   143  		[TO_NETDEV] = &cil_to_netdev,
   144  	},
   145  };
   146  
   147  /* Test that a SVC request to a local backend
   148   * - gets DNATed (but not SNATed)
   149   * - gets redirected by TC (as ENABLE_HOST_ROUTING is set)
   150   */
   151  PKTGEN("tc", "tc_nodeport_local_backend")
   152  int nodeport_local_backend_pktgen(struct __ctx_buff *ctx)
   153  {
   154  	struct pktgen builder;
   155  	struct tcphdr *l4;
   156  	void *data;
   157  
   158  	/* Init packet builder */
   159  	pktgen__init(&builder, ctx);
   160  
   161  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   162  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   163  					  CLIENT_IP, FRONTEND_IP_LOCAL,
   164  					  CLIENT_PORT, FRONTEND_PORT);
   165  	if (!l4)
   166  		return TEST_ERROR;
   167  
   168  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   169  	if (!data)
   170  		return TEST_ERROR;
   171  
   172  	/* Calc lengths, set protocol fields and calc checksums */
   173  	pktgen__finish(&builder);
   174  
   175  	return 0;
   176  }
   177  
   178  SETUP("tc", "tc_nodeport_local_backend")
   179  int nodeport_local_backend_setup(struct __ctx_buff *ctx)
   180  {
   181  	__u16 revnat_id = 1;
   182  
   183  	lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id);
   184  	lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 124,
   185  			  BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_TCP, 0);
   186  
   187  	/* add local backend */
   188  	endpoint_v4_add_entry(BACKEND_IP_LOCAL, BACKEND_IFACE, 0, 0, 0,
   189  			      (__u8 *)local_backend_mac, (__u8 *)node_mac);
   190  
   191  	ipcache_v4_add_entry(BACKEND_IP_LOCAL, 0, 112233, 0, 0);
   192  
   193  	/* Jump into the entrypoint */
   194  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   195  	/* Fail if we didn't jump */
   196  	return TEST_ERROR;
   197  }
   198  
   199  CHECK("tc", "tc_nodeport_local_backend")
   200  int nodeport_local_backend_check(const struct __ctx_buff *ctx)
   201  {
   202  	void *data, *data_end;
   203  	__u32 *status_code;
   204  	struct tcphdr *l4;
   205  	struct ethhdr *l2;
   206  	struct iphdr *l3;
   207  
   208  	test_init();
   209  
   210  	data = (void *)(long)ctx_data(ctx);
   211  	data_end = (void *)(long)ctx->data_end;
   212  
   213  	if (data + sizeof(__u32) > data_end)
   214  		test_fatal("status code out of bounds");
   215  
   216  	status_code = data;
   217  
   218  	assert(*status_code == CTX_ACT_REDIRECT);
   219  
   220  	l2 = data + sizeof(__u32);
   221  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   222  		test_fatal("l2 out of bounds");
   223  
   224  	l3 = (void *)l2 + sizeof(struct ethhdr);
   225  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   226  		test_fatal("l3 out of bounds");
   227  
   228  	l4 = (void *)l3 + sizeof(struct iphdr);
   229  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   230  		test_fatal("l4 out of bounds");
   231  
   232  	if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0)
   233  		test_fatal("src MAC is not the node MAC")
   234  	if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0)
   235  		test_fatal("dst MAC is not the endpoint MAC")
   236  
   237  	if (l3->saddr != CLIENT_IP)
   238  		test_fatal("src IP has changed");
   239  
   240  	if (l3->daddr != BACKEND_IP_LOCAL)
   241  		test_fatal("dst IP hasn't been NATed to local backend IP");
   242  
   243  	if (l3->check != bpf_htons(0x4212))
   244  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   245  
   246  	if (l4->source != CLIENT_PORT)
   247  		test_fatal("src port has changed");
   248  
   249  	if (l4->dest != BACKEND_PORT)
   250  		test_fatal("dst TCP port hasn't been NATed to backend port");
   251  
   252  	test_finish();
   253  }
   254  
   255  /* Test that a reply by the local backend gets revDNATed at to-netdev. */
   256  PKTGEN("tc", "tc_nodeport_local_backend_reply")
   257  int nodeport_local_backend_reply_pktgen(struct __ctx_buff *ctx)
   258  {
   259  	struct pktgen builder;
   260  	struct tcphdr *l4;
   261  	void *data;
   262  
   263  	/* Init packet builder */
   264  	pktgen__init(&builder, ctx);
   265  
   266  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   267  					  (__u8 *)lb_mac, (__u8 *)client_mac,
   268  					  BACKEND_IP_LOCAL, CLIENT_IP,
   269  					  BACKEND_PORT, CLIENT_PORT);
   270  	if (!l4)
   271  		return TEST_ERROR;
   272  
   273  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   274  	if (!data)
   275  		return TEST_ERROR;
   276  
   277  	/* Calc lengths, set protocol fields and calc checksums */
   278  	pktgen__finish(&builder);
   279  
   280  	return 0;
   281  }
   282  
   283  SETUP("tc", "tc_nodeport_local_backend_reply")
   284  int nodeport_local_backend_reply_setup(struct __ctx_buff *ctx)
   285  {
   286  	/* Jump into the entrypoint */
   287  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
   288  	/* Fail if we didn't jump */
   289  	return TEST_ERROR;
   290  }
   291  
   292  CHECK("tc", "tc_nodeport_local_backend_reply")
   293  int nodeport_local_backend_reply_check(const struct __ctx_buff *ctx)
   294  {
   295  	void *data, *data_end;
   296  	__u32 *status_code;
   297  	struct tcphdr *l4;
   298  	struct ethhdr *l2;
   299  	struct iphdr *l3;
   300  
   301  	test_init();
   302  
   303  	data = (void *)(long)ctx_data(ctx);
   304  	data_end = (void *)(long)ctx->data_end;
   305  
   306  	if (data + sizeof(__u32) > data_end)
   307  		test_fatal("status code out of bounds");
   308  
   309  	status_code = data;
   310  
   311  	assert(*status_code == CTX_ACT_OK);
   312  
   313  	l2 = data + sizeof(__u32);
   314  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   315  		test_fatal("l2 out of bounds");
   316  
   317  	l3 = (void *)l2 + sizeof(struct ethhdr);
   318  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   319  		test_fatal("l3 out of bounds");
   320  
   321  	l4 = (void *)l3 + sizeof(struct iphdr);
   322  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   323  		test_fatal("l4 out of bounds");
   324  
   325  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   326  		test_fatal("src MAC is not the LB MAC")
   327  	if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0)
   328  		test_fatal("dst MAC is not the client MAC")
   329  
   330  	if (l3->saddr != FRONTEND_IP_LOCAL)
   331  		test_fatal("src IP hasn't been revNATed to frontend IP");
   332  
   333  	if (l3->daddr != CLIENT_IP)
   334  		test_fatal("dst IP has changed");
   335  
   336  	if (l3->check != bpf_htons(0x4baa))
   337  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   338  
   339  	if (l4->source != FRONTEND_PORT)
   340  		test_fatal("src port hasn't been revNATed to frontend port");
   341  
   342  	if (l4->dest != CLIENT_PORT)
   343  		test_fatal("dst port has changed");
   344  
   345  	test_finish();
   346  }
   347  
   348  /* Same scenario as above, but for a different CLIENT_IP_2. Here replies
   349   * should leave via a non-default interface.
   350   */
   351  PKTGEN("tc", "tc_nodeport_local_backend_redirect")
   352  int nodeport_local_backend_redirect_pktgen(struct __ctx_buff *ctx)
   353  {
   354  	struct pktgen builder;
   355  	struct tcphdr *l4;
   356  	void *data;
   357  
   358  	/* Init packet builder */
   359  	pktgen__init(&builder, ctx);
   360  
   361  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   362  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   363  					  CLIENT_IP_2, FRONTEND_IP_LOCAL,
   364  					  CLIENT_PORT, FRONTEND_PORT);
   365  	if (!l4)
   366  		return TEST_ERROR;
   367  
   368  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   369  	if (!data)
   370  		return TEST_ERROR;
   371  
   372  	/* Calc lengths, set protocol fields and calc checksums */
   373  	pktgen__finish(&builder);
   374  
   375  	return 0;
   376  }
   377  
   378  SETUP("tc", "tc_nodeport_local_backend_redirect")
   379  int nodeport_local_backend_redirect_setup(struct __ctx_buff *ctx)
   380  {
   381  	/* Jump into the entrypoint */
   382  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   383  	/* Fail if we didn't jump */
   384  	return TEST_ERROR;
   385  }
   386  
   387  CHECK("tc", "tc_nodeport_local_backend_redirect")
   388  int nodeport_local_backend_redirect_check(const struct __ctx_buff *ctx)
   389  {
   390  	void *data, *data_end;
   391  	__u32 *status_code;
   392  	struct tcphdr *l4;
   393  	struct ethhdr *l2;
   394  	struct iphdr *l3;
   395  
   396  	test_init();
   397  
   398  	data = (void *)(long)ctx_data(ctx);
   399  	data_end = (void *)(long)ctx->data_end;
   400  
   401  	if (data + sizeof(__u32) > data_end)
   402  		test_fatal("status code out of bounds");
   403  
   404  	status_code = data;
   405  
   406  	assert(*status_code == CTX_ACT_REDIRECT);
   407  
   408  	l2 = data + sizeof(__u32);
   409  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   410  		test_fatal("l2 out of bounds");
   411  
   412  	l3 = (void *)l2 + sizeof(struct ethhdr);
   413  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   414  		test_fatal("l3 out of bounds");
   415  
   416  	l4 = (void *)l3 + sizeof(struct iphdr);
   417  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   418  		test_fatal("l4 out of bounds");
   419  
   420  	if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0)
   421  		test_fatal("src MAC is not the node MAC")
   422  	if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0)
   423  		test_fatal("dst MAC is not the endpoint MAC")
   424  
   425  	if (l3->saddr != CLIENT_IP_2)
   426  		test_fatal("src IP has changed");
   427  
   428  	if (l3->daddr != BACKEND_IP_LOCAL)
   429  		test_fatal("dst IP hasn't been NATed to local backend IP");
   430  
   431  	if (l3->check != bpf_htons(0x3711))
   432  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   433  
   434  	if (l4->source != CLIENT_PORT)
   435  		test_fatal("src port has changed");
   436  
   437  	if (l4->dest != BACKEND_PORT)
   438  		test_fatal("dst TCP port hasn't been NATed to backend port");
   439  
   440  	test_finish();
   441  }
   442  
   443  /* Test that to-netdev respects the routing needed for CLIENT_IP_2,
   444   * and redirects the packet to the correct egress interface.
   445   */
   446  PKTGEN("tc", "tc_nodeport_local_backend_redirect_reply")
   447  int nodeport_local_backend_redirect_reply_pktgen(struct __ctx_buff *ctx)
   448  {
   449  	struct pktgen builder;
   450  	struct tcphdr *l4;
   451  	void *data;
   452  
   453  	/* Init packet builder */
   454  	pktgen__init(&builder, ctx);
   455  
   456  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   457  					  (__u8 *)lb_mac, (__u8 *)client_mac,
   458  					  BACKEND_IP_LOCAL, CLIENT_IP_2,
   459  					  BACKEND_PORT, CLIENT_PORT);
   460  	if (!l4)
   461  		return TEST_ERROR;
   462  
   463  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   464  	if (!data)
   465  		return TEST_ERROR;
   466  
   467  	/* Calc lengths, set protocol fields and calc checksums */
   468  	pktgen__finish(&builder);
   469  
   470  	return 0;
   471  }
   472  
   473  SETUP("tc", "tc_nodeport_local_backend_redirect_reply")
   474  int nodeport_local_backend_redirect_reply_setup(struct __ctx_buff *ctx)
   475  {
   476  	/* Jump into the entrypoint */
   477  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
   478  	/* Fail if we didn't jump */
   479  	return TEST_ERROR;
   480  }
   481  
   482  CHECK("tc", "tc_nodeport_local_backend_redirect_reply")
   483  int nodeport_local_backend_redirect_reply_check(const struct __ctx_buff *ctx)
   484  {
   485  	void *data, *data_end;
   486  	__u32 *status_code;
   487  	struct tcphdr *l4;
   488  	struct ethhdr *l2;
   489  	struct iphdr *l3;
   490  
   491  	test_init();
   492  
   493  	data = (void *)(long)ctx_data(ctx);
   494  	data_end = (void *)(long)ctx->data_end;
   495  
   496  	if (data + sizeof(__u32) > data_end)
   497  		test_fatal("status code out of bounds");
   498  
   499  	status_code = data;
   500  
   501  	assert(*status_code == CTX_ACT_REDIRECT);
   502  
   503  	l2 = data + sizeof(__u32);
   504  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   505  		test_fatal("l2 out of bounds");
   506  
   507  	l3 = (void *)l2 + sizeof(struct ethhdr);
   508  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   509  		test_fatal("l3 out of bounds");
   510  
   511  	l4 = (void *)l3 + sizeof(struct iphdr);
   512  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   513  		test_fatal("l4 out of bounds");
   514  
   515  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   516  		test_fatal("src MAC is not the LB MAC")
   517  	if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0)
   518  		test_fatal("dst MAC is not the client MAC")
   519  
   520  	if (l3->saddr != BACKEND_IP_LOCAL)
   521  		test_fatal("src IP has changed");
   522  
   523  	if (l3->daddr != CLIENT_IP_2)
   524  		test_fatal("dst IP has changed");
   525  
   526  	if (l3->check != bpf_htons(0x3611))
   527  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   528  
   529  	if (l4->source != BACKEND_PORT)
   530  		test_fatal("src port has changed");
   531  
   532  	if (l4->dest != CLIENT_PORT)
   533  		test_fatal("dst port has changed");
   534  
   535  	test_finish();
   536  }
   537  
   538  /* Test that a SVC request (UDP) to a local backend
   539   * - gets DNATed (but not SNATed)
   540   * - gets redirected by TC (as ENABLE_HOST_ROUTING is set)
   541   */
   542  PKTGEN("tc", "tc_nodeport_udp_local_backend")
   543  int nodeport_udp_local_backend_pktgen(struct __ctx_buff *ctx)
   544  {
   545  	struct pktgen builder;
   546  	struct udphdr *l4;
   547  	void *data;
   548  
   549  	/* Init packet builder */
   550  	pktgen__init(&builder, ctx);
   551  
   552  	l4 = pktgen__push_ipv4_udp_packet(&builder,
   553  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   554  					  CLIENT_IP, FRONTEND_IP_LOCAL,
   555  					  CLIENT_PORT, FRONTEND_PORT);
   556  	if (!l4)
   557  		return TEST_ERROR;
   558  
   559  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   560  	if (!data)
   561  		return TEST_ERROR;
   562  
   563  	/* Calc lengths, set protocol fields and calc checksums */
   564  	pktgen__finish(&builder);
   565  
   566  	return 0;
   567  }
   568  
   569  SETUP("tc", "tc_nodeport_udp_local_backend")
   570  int nodeport_udp_local_backend_setup(struct __ctx_buff *ctx)
   571  {
   572  	__u16 revnat_id = 2;
   573  
   574  	lb_v4_add_service(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, revnat_id);
   575  	lb_v4_add_backend(FRONTEND_IP_LOCAL, FRONTEND_PORT, 1, 125,
   576  			  BACKEND_IP_LOCAL, BACKEND_PORT, IPPROTO_UDP, 0);
   577  
   578  	/* Jump into the entrypoint */
   579  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   580  	/* Fail if we didn't jump */
   581  	return TEST_ERROR;
   582  }
   583  
   584  CHECK("tc", "tc_nodeport_udp_local_backend")
   585  int nodeport_udp_local_backend_check(const struct __ctx_buff *ctx)
   586  {
   587  	void *data, *data_end;
   588  	__u32 *status_code;
   589  	struct udphdr *l4;
   590  	struct ethhdr *l2;
   591  	struct iphdr *l3;
   592  
   593  	test_init();
   594  
   595  	data = (void *)(long)ctx_data(ctx);
   596  	data_end = (void *)(long)ctx->data_end;
   597  
   598  	if (data + sizeof(__u32) > data_end)
   599  		test_fatal("status code out of bounds");
   600  
   601  	status_code = data;
   602  
   603  	assert(*status_code == CTX_ACT_REDIRECT);
   604  
   605  	l2 = data + sizeof(__u32);
   606  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   607  		test_fatal("l2 out of bounds");
   608  
   609  	l3 = (void *)l2 + sizeof(struct ethhdr);
   610  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   611  		test_fatal("l3 out of bounds");
   612  
   613  	l4 = (void *)l3 + sizeof(struct iphdr);
   614  	if ((void *)l4 + sizeof(*l4) > data_end)
   615  		test_fatal("l4 out of bounds");
   616  
   617  	if (memcmp(l2->h_source, (__u8 *)node_mac, ETH_ALEN) != 0)
   618  		test_fatal("src MAC is not the node MAC")
   619  	if (memcmp(l2->h_dest, (__u8 *)local_backend_mac, ETH_ALEN) != 0)
   620  		test_fatal("dst MAC is not the endpoint MAC")
   621  
   622  	if (l3->saddr != CLIENT_IP)
   623  		test_fatal("src IP has changed");
   624  
   625  	if (l3->daddr != BACKEND_IP_LOCAL)
   626  		test_fatal("dst IP hasn't been NATed to local backend IP");
   627  
   628  	if (l3->check != bpf_htons(0x4213))
   629  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   630  
   631  	if (l4->source != CLIENT_PORT)
   632  		test_fatal("src port has changed");
   633  
   634  	if (l4->dest != BACKEND_PORT)
   635  		test_fatal("dst port hasn't been NATed to backend port");
   636  
   637  	test_finish();
   638  }
   639  
   640  /* Test that a SVC request that is LBed to a NAT remote backend
   641   * - gets DNATed and SNATed,
   642   * - gets redirected back out by TC
   643   */
   644  PKTGEN("tc", "tc_nodeport_nat_fwd")
   645  int nodeport_nat_fwd_pktgen(struct __ctx_buff *ctx)
   646  {
   647  	struct pktgen builder;
   648  	struct tcphdr *l4;
   649  	void *data;
   650  
   651  	/* Init packet builder */
   652  	pktgen__init(&builder, ctx);
   653  
   654  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   655  					  (__u8 *)client_mac, (__u8 *)lb_mac,
   656  					  CLIENT_IP, FRONTEND_IP_REMOTE,
   657  					  CLIENT_PORT, FRONTEND_PORT);
   658  	if (!l4)
   659  		return TEST_ERROR;
   660  
   661  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   662  	if (!data)
   663  		return TEST_ERROR;
   664  
   665  	/* Calc lengths, set protocol fields and calc checksums */
   666  	pktgen__finish(&builder);
   667  
   668  	return 0;
   669  }
   670  
   671  SETUP("tc", "tc_nodeport_nat_fwd")
   672  int nodeport_nat_fwd_setup(struct __ctx_buff *ctx)
   673  {
   674  	__u16 revnat_id = 1;
   675  
   676  	lb_v4_add_service(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, revnat_id);
   677  	lb_v4_add_backend(FRONTEND_IP_REMOTE, FRONTEND_PORT, 1, 124,
   678  			  BACKEND_IP_REMOTE, BACKEND_PORT, IPPROTO_TCP, 0);
   679  
   680  	ipcache_v4_add_entry(BACKEND_IP_REMOTE, 0, 112233, 0, 0);
   681  
   682  	/* Jump into the entrypoint */
   683  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   684  	/* Fail if we didn't jump */
   685  	return TEST_ERROR;
   686  }
   687  
   688  CHECK("tc", "tc_nodeport_nat_fwd")
   689  int nodeport_nat_fwd_check(__maybe_unused const struct __ctx_buff *ctx)
   690  {
   691  	void *data, *data_end;
   692  	__u32 *status_code;
   693  	struct tcphdr *l4;
   694  	struct ethhdr *l2;
   695  	struct iphdr *l3;
   696  	__u32 key = 0;
   697  
   698  	test_init();
   699  
   700  	data = (void *)(long)ctx_data(ctx);
   701  	data_end = (void *)(long)ctx->data_end;
   702  
   703  	if (data + sizeof(__u32) > data_end)
   704  		test_fatal("status code out of bounds");
   705  
   706  	status_code = data;
   707  
   708  	assert(*status_code == CTX_ACT_REDIRECT);
   709  
   710  	l2 = data + sizeof(__u32);
   711  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   712  		test_fatal("l2 out of bounds");
   713  
   714  	l3 = (void *)l2 + sizeof(struct ethhdr);
   715  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   716  		test_fatal("l3 out of bounds");
   717  
   718  	l4 = (void *)l3 + sizeof(struct iphdr);
   719  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   720  		test_fatal("l4 out of bounds");
   721  
   722  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   723  		test_fatal("src MAC is not the LB MAC")
   724  	if (memcmp(l2->h_dest, (__u8 *)remote_backend_mac, ETH_ALEN) != 0)
   725  		test_fatal("dst MAC is not the remote backend MAC")
   726  
   727  	if (l3->saddr != LB_IP)
   728  		test_fatal("src IP hasn't been NATed to LB IP");
   729  
   730  	if (l3->daddr != BACKEND_IP_REMOTE)
   731  		test_fatal("dst IP hasn't been NATed to remote backend IP");
   732  
   733  	if (l3->check != bpf_htons(0xa711))
   734  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   735  
   736  	if (l4->source == CLIENT_PORT)
   737  		test_fatal("src port hasn't been NATed");
   738  
   739  	if (l4->dest != BACKEND_PORT)
   740  		test_fatal("dst port hasn't been NATed to backend port");
   741  
   742  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   743  
   744  	if (settings)
   745  		settings->nat_source_port = l4->source;
   746  
   747  	test_finish();
   748  }
   749  
   750  static __always_inline int build_reply(struct __ctx_buff *ctx)
   751  {
   752  	struct pktgen builder;
   753  	struct tcphdr *l4;
   754  	void *data;
   755  	__u16 nat_source_port = 0;
   756  	__u32 key = 0;
   757  
   758  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   759  
   760  	if (settings)
   761  		nat_source_port = settings->nat_source_port;
   762  
   763  	/* Init packet builder */
   764  	pktgen__init(&builder, ctx);
   765  
   766  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   767  					  (__u8 *)remote_backend_mac, (__u8 *)lb_mac,
   768  					  BACKEND_IP_REMOTE, LB_IP,
   769  					  BACKEND_PORT, nat_source_port);
   770  	if (!l4)
   771  		return TEST_ERROR;
   772  
   773  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   774  	if (!data)
   775  		return TEST_ERROR;
   776  
   777  	/* Calc lengths, set protocol fields and calc checksums */
   778  	pktgen__finish(&builder);
   779  
   780  	return 0;
   781  }
   782  
   783  static __always_inline int check_reply(const struct __ctx_buff *ctx)
   784  {
   785  	void *data, *data_end;
   786  	__u32 *status_code;
   787  	struct tcphdr *l4;
   788  	struct ethhdr *l2;
   789  	struct iphdr *l3;
   790  
   791  	test_init();
   792  
   793  	data = (void *)(long)ctx_data(ctx);
   794  	data_end = (void *)(long)ctx->data_end;
   795  
   796  	if (data + sizeof(__u32) > data_end)
   797  		test_fatal("status code out of bounds");
   798  
   799  	status_code = data;
   800  
   801  	assert(*status_code == CTX_ACT_REDIRECT);
   802  
   803  	l2 = data + sizeof(__u32);
   804  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   805  		test_fatal("l2 out of bounds");
   806  
   807  	l3 = (void *)l2 + sizeof(struct ethhdr);
   808  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   809  		test_fatal("l3 out of bounds");
   810  
   811  	l4 = (void *)l3 + sizeof(struct iphdr);
   812  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   813  		test_fatal("l4 out of bounds");
   814  
   815  	if (memcmp(l2->h_source, (__u8 *)lb_mac, ETH_ALEN) != 0)
   816  		test_fatal("src MAC is not the LB MAC")
   817  	if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0)
   818  		test_fatal("dst MAC is not the client MAC")
   819  
   820  	if (l3->saddr != FRONTEND_IP_REMOTE)
   821  		test_fatal("src IP hasn't been RevNATed to frontend IP");
   822  
   823  	if (l3->daddr != CLIENT_IP)
   824  		test_fatal("dst IP hasn't been RevNATed to client IP");
   825  
   826  	if (l4->source != FRONTEND_PORT)
   827  		test_fatal("src port hasn't been RevNATed to frontend port");
   828  
   829  	if (l4->dest != CLIENT_PORT)
   830  		test_fatal("dst port hasn't been RevNATed to client port");
   831  
   832  	test_finish();
   833  }
   834  
   835  /* Test that the LB RevDNATs and RevSNATs a reply from the
   836   * NAT remote backend, and sends it back to the client.
   837   */
   838  PKTGEN("tc", "tc_nodeport_nat_fwd_reply")
   839  int nodeport_nat_fwd_reply_pktgen(struct __ctx_buff *ctx)
   840  {
   841  	return build_reply(ctx);
   842  }
   843  
   844  SETUP("tc", "tc_nodeport_nat_fwd_reply")
   845  int nodeport_nat_fwd_reply_setup(struct __ctx_buff *ctx)
   846  {
   847  	/* Jump into the entrypoint */
   848  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   849  	/* Fail if we didn't jump */
   850  	return TEST_ERROR;
   851  }
   852  
   853  CHECK("tc", "tc_nodeport_nat_fwd_reply")
   854  int nodeport_nat_fwd_reply_check(const struct __ctx_buff *ctx)
   855  {
   856  	return check_reply(ctx);
   857  }
   858  
   859  /* Test that the LB RevDNATs and RevSNATs a reply from the
   860   * NAT remote backend, and sends it back to the client.
   861   * Even if the FIB lookup fails.
   862   */
   863  PKTGEN("tc", "tc_nodeport_nat_fwd_reply_no_fib")
   864  int nodepoirt_nat_fwd_reply_no_fib_pktgen(struct __ctx_buff *ctx)
   865  {
   866  	return build_reply(ctx);
   867  }
   868  
   869  SETUP("tc", "tc_nodeport_nat_fwd_reply_no_fib")
   870  int nodeport_nat_fwd_reply_no_fib_setup(struct __ctx_buff *ctx)
   871  {
   872  	__u32 key = 0;
   873  	struct mock_settings *settings = map_lookup_elem(&settings_map, &key);
   874  
   875  	if (settings)
   876  		settings->fail_fib = true;
   877  
   878  	/* Jump into the entrypoint */
   879  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   880  	/* Fail if we didn't jump */
   881  	return TEST_ERROR;
   882  }
   883  
   884  CHECK("tc", "tc_nodeport_nat_fwd_reply_no_fib")
   885  int nodeport_nat_fwd_reply_no_fib_check(__maybe_unused const struct __ctx_buff *ctx)
   886  {
   887  	return check_reply(ctx);
   888  }