github.com/cilium/cilium@v1.16.2/bpf/tests/inter_cluster_snat_clusterip_client_overlay.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/compiler.h"
     7  #include <bpf/ctx/skb.h>
     8  #include "mock_skb_metadata.h"
     9  #include "pktgen.h"
    10  
    11  /*
    12   * Datapath configurations
    13   */
    14  
    15  /* Set dummy ifindex for tunnel device */
    16  #define ENCAP_IFINDEX 1
    17  
    18  /* Overlapping PodCIDR is only supported for IPv4 for now */
    19  #define ENABLE_IPV4
    20  
    21  /* Overlapping PodCIDR depends on tunnel */
    22  #define TUNNEL_MODE
    23  
    24  /* Fully enable KPR since kubeproxy doesn't understand cluster aware addressing */
    25  #define ENABLE_NODEPORT
    26  
    27  /* Cluster-aware addressing is mandatory for overlapping PodCIDR support */
    28  #define ENABLE_CLUSTER_AWARE_ADDRESSING
    29  
    30  /* Inter-cluster SNAT is mandatory for overlapping PodCIDR support for now */
    31  #define ENABLE_INTER_CLUSTER_SNAT
    32  
    33  /* Import map definitions and some default values */
    34  #include "node_config.h"
    35  
    36  /* Overwrite the default port range defined in node_config.h
    37   * to have deterministic source port selection.
    38   */
    39  #undef NODEPORT_PORT_MAX
    40  #undef NODEPORT_PORT_MIN_NAT
    41  #undef NODEPORT_PORT_MAX_NAT
    42  #define NODEPORT_PORT_MAX 32767
    43  #define NODEPORT_PORT_MIN_NAT (NODEPORT_PORT_MAX + 1)
    44  #define NODEPORT_PORT_MAX_NAT (NODEPORT_PORT_MIN_NAT + 1)
    45  
    46  /* Overwrite (local) CLUSTER_ID defined in node_config.h */
    47  #undef CLUSTER_ID
    48  #define CLUSTER_ID 1
    49  
    50  /*
    51   * Test configurations
    52   */
    53  #define CLIENT_IFINDEX		12345
    54  #define CLIENT_MAC		mac_one
    55  #define CLIENT_ROUTER_MAC	mac_two
    56  #define BACKEND_ROUTER_MAC	mac_three
    57  #define CLIENT_IP		v4_pod_one
    58  #define BACKEND_IP		v4_pod_two
    59  #define CLIENT_NODE_IP		v4_ext_one
    60  #define BACKEND_NODE_IP		v4_ext_two
    61  #define CLIENT_PORT		__bpf_htons(NODEPORT_PORT_MAX_NAT + 1)
    62  #define BACKEND_PORT		tcp_svc_one
    63  #define BACKEND_CLUSTER_ID	2
    64  #define BACKEND_IDENTITY	(0x00000000 | (BACKEND_CLUSTER_ID << 16) | 0xff01)
    65  
    66  #undef IPV4_INTER_CLUSTER_SNAT
    67  #define IPV4_INTER_CLUSTER_SNAT CLIENT_NODE_IP
    68  
    69  /* SNAT should always select NODEPORT_PORT_MIN_NAT as a source */
    70  #define CLIENT_INTER_CLUSTER_SNAT_PORT __bpf_htons(NODEPORT_PORT_MIN_NAT)
    71  
    72  /* Mock out get_tunnel_key to emulate input from tunnel device */
    73  #define skb_get_tunnel_key mock_skb_get_tunnel_key
    74  
    75  static __always_inline
    76  int mock_skb_get_tunnel_key(struct __ctx_buff *ctx __maybe_unused, struct bpf_tunnel_key *to,
    77  			    __u32 size __maybe_unused, __u32 flags __maybe_unused)
    78  {
    79  	to->remote_ipv4 = BACKEND_NODE_IP;
    80  	to->tunnel_id = BACKEND_IDENTITY;
    81  	return 0;
    82  }
    83  
    84  /*
    85   * Mock out send_drop_notify. This is because it uses ctx_store_meta internally
    86   * and breaks the skb->cb test.
    87   */
    88  
    89  #define DEBUG
    90  #include <lib/drop.h>
    91  
    92  #define _send_drop_notify mock_send_drop_notify
    93  
    94  static __always_inline
    95  int mock_send_drop_notify(__u8 file __maybe_unused, __u16 line __maybe_unused,
    96  			  struct __ctx_buff *ctx, __u32 src __maybe_unused,
    97  			  __u32 dst __maybe_unused, __u32 dst_id __maybe_unused,
    98  			  __u32 reason, __u32 exitcode, enum metric_dir direction)
    99  {
   100  	cilium_dbg3(ctx, DBG_GENERIC, reason, exitcode, direction);
   101  	return exitcode;
   102  }
   103  
   104  /* Include an actual datapath code */
   105  #include <bpf_overlay.c>
   106  
   107  #include "lib/endpoint.h"
   108  
   109  /*
   110   * Tests
   111   */
   112  
   113  #define TO_OVERLAY 0
   114  #define FROM_OVERLAY 1
   115  
   116  struct {
   117  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
   118  	__uint(key_size, sizeof(__u32));
   119  	__uint(max_entries, 2);
   120  	__array(values, int());
   121  } entry_call_map __section(".maps") = {
   122  	.values = {
   123  		[TO_OVERLAY] = &cil_to_overlay,
   124  		[FROM_OVERLAY] = &cil_from_overlay,
   125  	},
   126  };
   127  
   128  static __always_inline int
   129  pktgen_to_overlay(struct __ctx_buff *ctx, bool syn, bool ack)
   130  {
   131  	struct pktgen builder;
   132  	struct tcphdr *l4;
   133  	void *data;
   134  
   135  	pktgen__init(&builder, ctx);
   136  
   137  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   138  					  (__u8 *)CLIENT_MAC, (__u8 *)CLIENT_ROUTER_MAC,
   139  					  CLIENT_IP, BACKEND_IP,
   140  					  CLIENT_PORT, BACKEND_PORT);
   141  	if (!l4)
   142  		return TEST_ERROR;
   143  
   144  	l4->syn = syn ? 1 : 0;
   145  	l4->ack = ack ? 1 : 0;
   146  
   147  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   148  	if (!data)
   149  		return TEST_ERROR;
   150  
   151  	pktgen__finish(&builder);
   152  
   153  	return 0;
   154  }
   155  
   156  static __always_inline int
   157  pktgen_from_overlay(struct __ctx_buff *ctx, bool syn, bool ack)
   158  {
   159  	struct pktgen builder;
   160  	struct tcphdr *l4;
   161  	void *data;
   162  
   163  	pktgen__init(&builder, ctx);
   164  
   165  	l4 = pktgen__push_ipv4_tcp_packet(&builder,
   166  					  (__u8 *)BACKEND_ROUTER_MAC,
   167  					  (__u8 *)CLIENT_ROUTER_MAC,
   168  					  BACKEND_IP, IPV4_INTER_CLUSTER_SNAT,
   169  					  BACKEND_PORT, CLIENT_INTER_CLUSTER_SNAT_PORT);
   170  	if (!l4)
   171  		return TEST_ERROR;
   172  
   173  	l4->syn = syn ? 1 : 0;
   174  	l4->ack = ack ? 1 : 0;
   175  
   176  	data = pktgen__push_data(&builder, default_data, sizeof(default_data));
   177  	if (!data)
   178  		return TEST_ERROR;
   179  
   180  	pktgen__finish(&builder);
   181  
   182  	return 0;
   183  }
   184  
   185  PKTGEN("tc", "01_to_overlay_syn")
   186  int to_overlay_syn_pktgen(struct __ctx_buff *ctx)
   187  {
   188  	return pktgen_to_overlay(ctx, true, false);
   189  }
   190  
   191  SETUP("tc", "01_to_overlay_syn")
   192  int to_overlay_syn_setup(struct __ctx_buff *ctx)
   193  {
   194  	/* Emulate input from bpf_lxc */
   195  	ctx_set_cluster_id_mark(ctx, 2);
   196  
   197  	tail_call_static(ctx, entry_call_map, TO_OVERLAY);
   198  	return TEST_ERROR;
   199  }
   200  
   201  CHECK("tc", "01_to_overlay_syn")
   202  int to_overlay_syn_check(struct __ctx_buff *ctx)
   203  {
   204  	void *data, *data_end;
   205  	__s32 *status_code;
   206  	struct tcphdr *l4;
   207  	struct ethhdr *l2;
   208  	struct iphdr *l3;
   209  	struct ipv4_ct_tuple tuple;
   210  	struct ipv4_nat_entry *entry;
   211  
   212  	test_init();
   213  
   214  	data = (void *)(long)ctx_data(ctx);
   215  	data_end = (void *)(long)ctx->data_end;
   216  
   217  	if (data + sizeof(__u32) > data_end)
   218  		test_fatal("status code out of bounds");
   219  
   220  	status_code = data;
   221  
   222  	if (*status_code != CTX_ACT_OK)
   223  		test_fatal("unexpected status code %d, want %d", *status_code, CTX_ACT_OK);
   224  
   225  	l2 = data + sizeof(__u32);
   226  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   227  		test_fatal("l2 out of bounds");
   228  
   229  	l3 = (void *)l2 + sizeof(struct ethhdr);
   230  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   231  		test_fatal("l3 out of bounds");
   232  
   233  	l4 = (void *)l3 + sizeof(struct iphdr);
   234  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   235  		test_fatal("l4 out of bounds");
   236  
   237  	if (memcmp(l2->h_source, (__u8 *)CLIENT_MAC, ETH_ALEN) != 0)
   238  		test_fatal("src MAC has changed")
   239  
   240  	if (memcmp(l2->h_dest, (__u8 *)CLIENT_ROUTER_MAC, ETH_ALEN) != 0)
   241  		test_fatal("dst MAC has changed")
   242  
   243  	if (l3->saddr != IPV4_INTER_CLUSTER_SNAT)
   244  		test_fatal("src IP hasn't been SNATed for inter-cluster communication");
   245  
   246  	if (l3->daddr != BACKEND_IP)
   247  		test_fatal("dst IP has changed");
   248  
   249  	if (l3->check != bpf_htons(0x4111))
   250  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   251  
   252  	if (l4->source != CLIENT_INTER_CLUSTER_SNAT_PORT)
   253  		test_fatal("src port hasn't been SNATed for inter-cluster communication");
   254  
   255  	if (l4->dest != BACKEND_PORT)
   256  		test_fatal("dst port has changed");
   257  
   258  	tuple.daddr = BACKEND_IP;
   259  	tuple.saddr = CLIENT_IP;
   260  	tuple.dport = BACKEND_PORT;
   261  	tuple.sport = CLIENT_PORT;
   262  	tuple.nexthdr = IPPROTO_TCP;
   263  	tuple.flags = TUPLE_F_OUT;
   264  
   265  	entry = map_lookup_elem(&per_cluster_snat_mapping_ipv4_2, &tuple);
   266  	if (!entry)
   267  		test_fatal("couldn't find egress SNAT mapping");
   268  
   269  	tuple.daddr = IPV4_INTER_CLUSTER_SNAT;
   270  	tuple.saddr = BACKEND_IP;
   271  	tuple.dport = CLIENT_INTER_CLUSTER_SNAT_PORT;
   272  	tuple.sport = BACKEND_PORT;
   273  	tuple.nexthdr = IPPROTO_TCP;
   274  	tuple.flags = TUPLE_F_IN;
   275  
   276  	entry = map_lookup_elem(&per_cluster_snat_mapping_ipv4_2, &tuple);
   277  	if (!entry)
   278  		test_fatal("couldn't find ingress SNAT mapping");
   279  
   280  	test_finish();
   281  }
   282  
   283  PKTGEN("tc", "02_from_overlay_synack")
   284  int from_overlay_synack_pktgen(struct __ctx_buff *ctx)
   285  {
   286  	return pktgen_from_overlay(ctx, true, true);
   287  }
   288  
   289  SETUP("tc", "02_from_overlay_synack")
   290  int from_overlay_synack_setup(struct __ctx_buff *ctx)
   291  {
   292  	endpoint_v4_add_entry(CLIENT_IP, CLIENT_IFINDEX, 0, 0, 0,
   293  			      (__u8 *)CLIENT_MAC, (__u8 *)CLIENT_ROUTER_MAC);
   294  
   295  	tail_call_static(ctx, entry_call_map, FROM_OVERLAY);
   296  	return TEST_ERROR;
   297  }
   298  
   299  CHECK("tc", "02_from_overlay_synack")
   300  int from_overlay_synack_check(struct __ctx_buff *ctx)
   301  {
   302  	void *data, *data_end;
   303  	__s32 *status_code;
   304  	struct tcphdr *l4;
   305  	struct ethhdr *l2;
   306  	struct iphdr *l3;
   307  	__u32 meta;
   308  
   309  	test_init();
   310  
   311  	data = (void *)(long)ctx_data(ctx);
   312  	data_end = (void *)(long)ctx->data_end;
   313  
   314  	if (data + sizeof(__u32) > data_end)
   315  		test_fatal("status code out of bounds");
   316  
   317  	status_code = data;
   318  
   319  	/* The packet should go to ipv4_local_delivery and dropped with
   320  	 * missed tail call since the POLICY_CALL_MAP should be empty.
   321  	 */
   322  	if (*status_code != CTX_ACT_DROP)
   323  		test_fatal("unexpected status code %d, want %d", *status_code, CTX_ACT_DROP);
   324  
   325  	l2 = data + sizeof(__u32);
   326  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   327  		test_fatal("l2 out of bounds");
   328  
   329  	l3 = (void *)l2 + sizeof(struct ethhdr);
   330  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   331  		test_fatal("l3 out of bounds");
   332  
   333  	l4 = (void *)l3 + sizeof(struct iphdr);
   334  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   335  		test_fatal("l4 out of bounds");
   336  
   337  	if (memcmp(l2->h_source, (__u8 *)CLIENT_ROUTER_MAC, ETH_ALEN) != 0)
   338  		test_fatal("src MAC is not client router MAC");
   339  
   340  	if (memcmp(l2->h_dest, (__u8 *)CLIENT_MAC, ETH_ALEN) != 0)
   341  		test_fatal("dst MAC is not client MAC");
   342  
   343  	if (l3->saddr != BACKEND_IP)
   344  		test_fatal("src IP has changed");
   345  
   346  	if (l3->daddr != CLIENT_IP)
   347  		test_fatal("dst IP hasn't been RevSNATed to client IP");
   348  
   349  	if (l3->check != bpf_htons(0xfa68))
   350  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   351  
   352  	if (l4->source != BACKEND_PORT)
   353  		test_fatal("src port has changed");
   354  
   355  	if (l4->dest != CLIENT_PORT)
   356  		test_fatal("dst port hasn't been RevSNATed to client port");
   357  
   358  	meta = ctx_load_meta(ctx, CB_IFINDEX);
   359  	if (meta != CLIENT_IFINDEX)
   360  		test_fatal("skb->cb[CB_IFINDEX] should be %d, got %d", CLIENT_IFINDEX, meta);
   361  
   362  	meta = ctx_load_meta(ctx, CB_SRC_LABEL);
   363  	if (meta != BACKEND_IDENTITY)
   364  		test_fatal("skb->cb[CB_SRC_LABEL] should be %d, got %d", BACKEND_IDENTITY, meta);
   365  
   366  	meta = ctx_load_meta(ctx, CB_FROM_TUNNEL);
   367  	if (meta != 1)
   368  		test_fatal("skb->cb[CB_FROM_TUNNEL] should be 1, got %d", meta);
   369  
   370  	meta = ctx_load_meta(ctx, CB_FROM_HOST);
   371  	if (meta != 0)
   372  		test_fatal("skb->cb[CB_FROM_HOST] should be 0, got %d", meta);
   373  
   374  	meta = ctx_load_meta(ctx, CB_CLUSTER_ID_INGRESS);
   375  	if (meta != BACKEND_CLUSTER_ID)
   376  		test_fatal("skb->cb[CB_CLUSTER_ID_INGRESS] should be %u, got %d",
   377  			   BACKEND_CLUSTER_ID, meta);
   378  
   379  	test_finish();
   380  }
   381  
   382  PKTGEN("tc", "03_to_overlay_ack")
   383  int to_overlay_ack_pktgen(struct __ctx_buff *ctx)
   384  {
   385  	return pktgen_to_overlay(ctx, false, true);
   386  }
   387  
   388  SETUP("tc", "03_to_overlay_ack")
   389  int to_overlay_ack_setup(struct __ctx_buff *ctx)
   390  {
   391  	/* Emulate input from bpf_lxc */
   392  	ctx_set_cluster_id_mark(ctx, 2);
   393  
   394  	tail_call_static(ctx, entry_call_map, TO_OVERLAY);
   395  	return TEST_ERROR;
   396  }
   397  
   398  CHECK("tc", "03_to_overlay_ack")
   399  int to_overlay_ack_check(struct __ctx_buff *ctx)
   400  {
   401  	void *data, *data_end;
   402  	__s32 *status_code;
   403  	struct tcphdr *l4;
   404  	struct ethhdr *l2;
   405  	struct iphdr *l3;
   406  
   407  	test_init();
   408  
   409  	data = (void *)(long)ctx_data(ctx);
   410  	data_end = (void *)(long)ctx->data_end;
   411  
   412  	if (data + sizeof(__u32) > data_end)
   413  		test_fatal("status code out of bounds");
   414  
   415  	status_code = data;
   416  
   417  	if (*status_code != CTX_ACT_OK)
   418  		test_fatal("unexpected status code %d, want %d", *status_code, CTX_ACT_OK);
   419  
   420  	l2 = data + sizeof(__u32);
   421  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   422  		test_fatal("l2 out of bounds");
   423  
   424  	l3 = (void *)l2 + sizeof(struct ethhdr);
   425  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   426  		test_fatal("l3 out of bounds");
   427  
   428  	l4 = (void *)l3 + sizeof(struct iphdr);
   429  	if ((void *)l4 + sizeof(struct tcphdr) > data_end)
   430  		test_fatal("l4 out of bounds");
   431  
   432  	if (memcmp(l2->h_source, (__u8 *)CLIENT_MAC, ETH_ALEN) != 0)
   433  		test_fatal("src MAC has changed");
   434  
   435  	if (memcmp(l2->h_dest, (__u8 *)CLIENT_ROUTER_MAC, ETH_ALEN) != 0)
   436  		test_fatal("dst MAC has changed");
   437  
   438  	if (l3->saddr != IPV4_INTER_CLUSTER_SNAT)
   439  		test_fatal("src IP hasn't been SNATed for inter-cluster communication");
   440  
   441  	if (l3->daddr != BACKEND_IP)
   442  		test_fatal("dst IP has changed");
   443  
   444  	if (l3->check != bpf_htons(0x4111))
   445  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   446  
   447  	if (l4->source != CLIENT_INTER_CLUSTER_SNAT_PORT)
   448  		test_fatal("src port hasn't been SNATed for inter-cluster communication");
   449  
   450  	if (l4->dest != BACKEND_PORT)
   451  		test_fatal("dst port has changed");
   452  
   453  	test_finish();
   454  }