github.com/cilium/cilium@v1.16.2/bpf/tests/xdp_egressgw_reply.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 ENABLE_EGRESS_GATEWAY
    18  #define ENABLE_MASQUERADE
    19  
    20  #define TUNNEL_PROTOCOL		TUNNEL_PROTOCOL_VXLAN
    21  #define ENCAP_IFINDEX		42
    22  
    23  #define DISABLE_LOOPBACK_LB
    24  
    25  /* Skip ingress policy checks, not needed to validate hairpin flow */
    26  #define USE_BPF_PROG_FOR_INGRESS_POLICY
    27  
    28  #define IPV4_DIRECT_ROUTING	v4_node_one /* gateway node */
    29  #define MASQ_PORT		__bpf_htons(NODEPORT_PORT_MIN_NAT + 1)
    30  #define DIRECT_ROUTING_IFINDEX	25
    31  
    32  #define ctx_redirect mock_ctx_redirect
    33  static __always_inline __maybe_unused int
    34  mock_ctx_redirect(const struct __ctx_buff *ctx __maybe_unused, int ifindex __maybe_unused,
    35  		  __u32 flags __maybe_unused);
    36  
    37  #define fib_lookup mock_fib_lookup
    38  static __always_inline __maybe_unused long
    39  mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
    40  		__maybe_unused int plen, __maybe_unused __u32 flags);
    41  
    42  #include <bpf_xdp.c>
    43  
    44  #include "lib/egressgw.h"
    45  #include "lib/ipcache.h"
    46  
    47  static __always_inline __maybe_unused int
    48  mock_ctx_redirect(const struct __ctx_buff *ctx __maybe_unused, int ifindex __maybe_unused,
    49  		  __u32 flags __maybe_unused)
    50  {
    51  	if (ifindex != DIRECT_ROUTING_IFINDEX)
    52  		return CTX_ACT_DROP;
    53  
    54  	return CTX_ACT_REDIRECT;
    55  }
    56  
    57  static __always_inline __maybe_unused long
    58  mock_fib_lookup(__maybe_unused void *ctx, struct bpf_fib_lookup *params,
    59  		__maybe_unused int plen, __maybe_unused __u32 flags)
    60  {
    61  	params->ifindex = DIRECT_ROUTING_IFINDEX;
    62  
    63  	if (params->ipv4_dst == CLIENT_NODE_IP) {
    64  		__bpf_memcpy_builtin(params->smac, (__u8 *)gateway_mac, ETH_ALEN);
    65  		__bpf_memcpy_builtin(params->dmac, (__u8 *)client_mac, ETH_ALEN);
    66  	} else {
    67  		return CTX_ACT_DROP;
    68  	}
    69  
    70  	return 0;
    71  }
    72  
    73  #define FROM_NETDEV	0
    74  
    75  struct {
    76  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    77  	__uint(key_size, sizeof(__u32));
    78  	__uint(max_entries, 1);
    79  	__array(values, int());
    80  } entry_call_map __section(".maps") = {
    81  	.values = {
    82  		[FROM_NETDEV] = &cil_xdp_entry,
    83  	},
    84  };
    85  
    86  /* Test that a EgressGW reply gets RevSNATed, and forwarded to the
    87   * worker node via tunnel.
    88   */
    89  PKTGEN("xdp", "xdp_egressgw_reply")
    90  int egressgw_reply_pktgen(struct __ctx_buff *ctx)
    91  {
    92  	/* Add a new NAT entry so that pktgen can figure out the correct destination port */
    93  	struct ipv4_ct_tuple tuple = {
    94  		.saddr   = CLIENT_IP,
    95  		.daddr   = EXTERNAL_SVC_IP,
    96  		.dport   = EXTERNAL_SVC_PORT,
    97  		.sport   = client_port(TEST_XDP_REPLY),
    98  		.nexthdr = IPPROTO_TCP,
    99  	};
   100  
   101  	struct ipv4_nat_entry nat_entry = {
   102  		.to_saddr = EGRESS_IP,
   103  		.to_sport = MASQ_PORT,
   104  	};
   105  
   106  	map_update_elem(&SNAT_MAPPING_IPV4, &tuple, &nat_entry, BPF_ANY);
   107  
   108  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
   109  			.test = TEST_XDP_REPLY,
   110  			.dir = CT_INGRESS,
   111  		});
   112  }
   113  
   114  SETUP("xdp", "xdp_egressgw_reply")
   115  int egressgw_reply_setup(struct __ctx_buff *ctx)
   116  {
   117  	/* install EgressGW policy for the connection: */
   118  	add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, GATEWAY_NODE_IP, 0);
   119  
   120  	/* install RevSNAT entry */
   121  	struct ipv4_ct_tuple snat_tuple = {
   122  		.daddr   = EGRESS_IP,
   123  		.saddr   = EXTERNAL_SVC_IP,
   124  		.dport   = MASQ_PORT,
   125  		.sport   = EXTERNAL_SVC_PORT,
   126  		.nexthdr = IPPROTO_TCP,
   127  		.flags   = NAT_DIR_INGRESS,
   128  	};
   129  
   130  	struct ipv4_nat_entry snat_entry = {
   131  		.to_daddr = CLIENT_IP,
   132  		.to_dport = client_port(TEST_XDP_REPLY),
   133  	};
   134  
   135  	map_update_elem(&SNAT_MAPPING_IPV4, &snat_tuple, &snat_entry, BPF_ANY);
   136  
   137  	/* install ipcache entry for the CLIENT_IP: */
   138  	ipcache_v4_add_entry(CLIENT_IP, 0, 0, CLIENT_NODE_IP, 0);
   139  
   140  	/* Jump into the entrypoint */
   141  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   142  	/* Fail if we didn't jump */
   143  	return TEST_ERROR;
   144  }
   145  
   146  CHECK("xdp", "xdp_egressgw_reply")
   147  int egressgw_reply_check(__maybe_unused const struct __ctx_buff *ctx)
   148  {
   149  	void *data, *data_end;
   150  	__u32 *status_code;
   151  	struct tcphdr *inner_l4;
   152  	struct udphdr *l4;
   153  	struct ethhdr *l2, *inner_l2;
   154  	struct iphdr *l3, *inner_l3;
   155  	struct vxlanhdr *vxlan;
   156  
   157  	test_init();
   158  
   159  	data = (void *)(long)ctx_data(ctx);
   160  	data_end = (void *)(long)ctx->data_end;
   161  
   162  	if (data + sizeof(__u32) > data_end)
   163  		test_fatal("status code out of bounds");
   164  
   165  	status_code = data;
   166  
   167  	assert(*status_code == CTX_ACT_REDIRECT);
   168  
   169  	l2 = data + sizeof(__u32);
   170  	if ((void *)l2 + sizeof(*l2) > data_end)
   171  		test_fatal("l2 out of bounds");
   172  
   173  	l3 = (void *)l2 + sizeof(*l2);
   174  	if ((void *)l3 + sizeof(*l3) > data_end)
   175  		test_fatal("l3 out of bounds");
   176  
   177  	l4 = (void *)l3 + sizeof(*l3);
   178  	if ((void *)l4 + sizeof(*l4) > data_end)
   179  		test_fatal("l4 out of bounds");
   180  
   181  	vxlan = (void *)l4 + sizeof(*l4);
   182  	if ((void *)vxlan + sizeof(*vxlan) > data_end)
   183  		test_fatal("vxlan out of bounds");
   184  
   185  	inner_l2 = (void *)vxlan + sizeof(*vxlan);
   186  	if ((void *)inner_l2 + sizeof(*inner_l2) > data_end)
   187  		test_fatal("inner l2 out of bounds");
   188  
   189  	inner_l3 = (void *)inner_l2 + sizeof(*inner_l2);
   190  	if ((void *)inner_l3 + sizeof(*inner_l3) > data_end)
   191  		test_fatal("inner l3 out of bounds");
   192  
   193  	inner_l4 = (void *)inner_l3 + sizeof(*inner_l3);
   194  	if ((void *)inner_l4 + sizeof(*inner_l4) > data_end)
   195  		test_fatal("inner l4 out of bounds");
   196  
   197  	if (memcmp(l2->h_source, (__u8 *)gateway_mac, ETH_ALEN) != 0)
   198  		test_fatal("src MAC is not the gateway MAC")
   199  	if (memcmp(l2->h_dest, (__u8 *)client_mac, ETH_ALEN) != 0)
   200  		test_fatal("dst MAC is not the client node MAC")
   201  
   202  	if (l2->h_proto != bpf_htons(ETH_P_IP))
   203  		test_fatal("l2 doesn't have correct proto type")
   204  
   205  	if (l3->protocol != IPPROTO_UDP)
   206  		test_fatal("outer IP doesn't have correct L4 protocol")
   207  
   208  	if (l3->check != bpf_htons(0x527e))
   209  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   210  
   211  	if (l3->saddr != IPV4_DIRECT_ROUTING)
   212  		test_fatal("outerSrcIP is not correct")
   213  
   214  	if (l3->daddr != CLIENT_NODE_IP)
   215  		test_fatal("outerDstIP is not correct")
   216  
   217  	if (l4->dest != bpf_htons(TUNNEL_PORT))
   218  		test_fatal("outerDstPort is not tunnel port")
   219  
   220  	if (inner_l2->h_proto != bpf_htons(ETH_P_IP))
   221  		test_fatal("inner L2 doesn't have correct ethertype")
   222  
   223  	if (inner_l3->protocol != IPPROTO_TCP)
   224  		test_fatal("inner IP doesn't have correct L4 protocol")
   225  
   226  	if (inner_l3->saddr != EXTERNAL_SVC_IP)
   227  		test_fatal("innerSrcIP is not the external SVC IP");
   228  
   229  	if (inner_l3->daddr != CLIENT_IP)
   230  		test_fatal("innerDstIP hasn't been revNATed to the client IP");
   231  
   232  	if (inner_l3->check != bpf_htons(0x4212))
   233  		test_fatal("inner L3 checksum is invalid: %d", bpf_htons(inner_l3->check));
   234  
   235  	if (inner_l4->source != EXTERNAL_SVC_PORT)
   236  		test_fatal("innerSrcPort is not the external SVC port");
   237  
   238  	if (inner_l4->dest != client_port(TEST_XDP_REPLY))
   239  		test_fatal("innerDstPort hasn't been revNATed to client port");
   240  
   241  	del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24);
   242  
   243  	test_finish();
   244  }