github.com/cilium/cilium@v1.16.2/bpf/tests/tc_egressgw_snat.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_EGRESS_GATEWAY
    16  #define ENABLE_MASQUERADE_IPV4
    17  #define ENCAP_IFINDEX		42
    18  #define SECONDARY_IFACE_IFINDEX	44
    19  
    20  #define SECCTX_FROM_IPCACHE 1
    21  
    22  #define ctx_redirect mock_ctx_redirect
    23  static __always_inline __maybe_unused int
    24  mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused,
    25  		  int ifindex __maybe_unused, __u32 flags __maybe_unused);
    26  
    27  #define fib_lookup mock_fib_lookup
    28  static __always_inline __maybe_unused long
    29  mock_fib_lookup(void *ctx __maybe_unused, struct bpf_fib_lookup *params __maybe_unused,
    30  		int plen __maybe_unused, __u32 flags __maybe_unused);
    31  
    32  #include "bpf_host.c"
    33  
    34  #include "lib/egressgw.h"
    35  #include "lib/ipcache.h"
    36  
    37  static __always_inline __maybe_unused int
    38  mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused,
    39  		  int ifindex __maybe_unused, __u32 flags __maybe_unused)
    40  {
    41  	if (ifindex == ENCAP_IFINDEX)
    42  		return CTX_ACT_REDIRECT;
    43  	if (ifindex == SECONDARY_IFACE_IFINDEX)
    44  		return CTX_ACT_REDIRECT;
    45  
    46  	return CTX_ACT_DROP;
    47  }
    48  
    49  static __always_inline __maybe_unused long
    50  mock_fib_lookup(void *ctx __maybe_unused, struct bpf_fib_lookup *params __maybe_unused,
    51  		int plen __maybe_unused, __u32 flags __maybe_unused)
    52  {
    53  	if (params && params->ipv4_src == EGRESS_IP2)
    54  		params->ifindex = SECONDARY_IFACE_IFINDEX;
    55  
    56  	return 0;
    57  }
    58  
    59  #define TO_NETDEV 0
    60  #define FROM_NETDEV 1
    61  
    62  struct {
    63  	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
    64  	__uint(key_size, sizeof(__u32));
    65  	__uint(max_entries, 2);
    66  	__array(values, int());
    67  } entry_call_map __section(".maps") = {
    68  	.values = {
    69  		[TO_NETDEV] = &cil_to_netdev,
    70  		[FROM_NETDEV] = &cil_from_netdev,
    71  	},
    72  };
    73  
    74  /* Test that a packet matching an egress gateway policy on the to-netdev program
    75   * gets correctly SNATed with the egress IP of the policy.
    76   */
    77  PKTGEN("tc", "tc_egressgw_snat1")
    78  int egressgw_snat1_pktgen(struct __ctx_buff *ctx)
    79  {
    80  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
    81  			.test = TEST_SNAT1,
    82  		});
    83  }
    84  
    85  SETUP("tc", "tc_egressgw_snat1")
    86  int egressgw_snat1_setup(struct __ctx_buff *ctx)
    87  {
    88  	add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24,
    89  				  GATEWAY_NODE_IP, EGRESS_IP);
    90  
    91  	/* Jump into the entrypoint */
    92  	ctx_egw_done_set(ctx);
    93  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
    94  	/* Fail if we didn't jump */
    95  	return TEST_ERROR;
    96  }
    97  
    98  CHECK("tc", "tc_egressgw_snat1")
    99  int egressgw_snat1_check(const struct __ctx_buff *ctx)
   100  {
   101  	return egressgw_snat_check(ctx, (struct egressgw_test_ctx) {
   102  			.test = TEST_SNAT1,
   103  			.packets = 1,
   104  			.status_code = CTX_ACT_OK
   105  		});
   106  }
   107  
   108  /* Test that a packet matching an egress gateway policy on the from-netdev program
   109   * gets correctly revSNATed and connection tracked.
   110   */
   111  PKTGEN("tc", "tc_egressgw_snat1_2_reply")
   112  int egressgw_snat1_2_reply_pktgen(struct __ctx_buff *ctx)
   113  {
   114  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
   115  			.test = TEST_SNAT1,
   116  			.dir = CT_INGRESS,
   117  		});
   118  }
   119  
   120  SETUP("tc", "tc_egressgw_snat1_2_reply")
   121  int egressgw_snat1_2_reply_setup(struct __ctx_buff *ctx)
   122  {
   123  	/* install ipcache entry for the CLIENT_IP: */
   124  	ipcache_v4_add_entry(CLIENT_IP, 0, 0, CLIENT_NODE_IP, 0);
   125  
   126  	/* Jump into the entrypoint */
   127  	tail_call_static(ctx, entry_call_map, FROM_NETDEV);
   128  	/* Fail if we didn't jump */
   129  	return TEST_ERROR;
   130  }
   131  
   132  CHECK("tc", "tc_egressgw_snat1_2_reply")
   133  int egressgw_snat1_2_reply_check(const struct __ctx_buff *ctx)
   134  {
   135  	return egressgw_snat_check(ctx, (struct egressgw_test_ctx) {
   136  			.test = TEST_SNAT1,
   137  			.dir = CT_INGRESS,
   138  			.packets = 2,
   139  			.status_code = CTX_ACT_REDIRECT,
   140  		});
   141  }
   142  
   143  PKTGEN("tc", "tc_egressgw_snat2")
   144  int egressgw_snat2_pktgen(struct __ctx_buff *ctx)
   145  {
   146  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
   147  			.test = TEST_SNAT2,
   148  		});
   149  }
   150  
   151  SETUP("tc", "tc_egressgw_snat2")
   152  int egressgw_snat2_setup(struct __ctx_buff *ctx)
   153  {
   154  	/* Jump into the entrypoint */
   155  	ctx_egw_done_set(ctx);
   156  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
   157  	/* Fail if we didn't jump */
   158  	return TEST_ERROR;
   159  }
   160  
   161  CHECK("tc", "tc_egressgw_snat2")
   162  int egressgw_snat2_check(struct __ctx_buff *ctx)
   163  {
   164  	int ret = egressgw_snat_check(ctx, (struct egressgw_test_ctx) {
   165  			.test = TEST_SNAT2,
   166  			.packets = 1,
   167  			.status_code = CTX_ACT_OK
   168  		});
   169  
   170  	del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0Xffffff, 24);
   171  
   172  	return ret;
   173  }
   174  
   175  /* Test that a packet matching an excluded CIDR egress gateway policy on the
   176   * to-netdev program does not get SNATed with the egress IP of the policy.
   177   */
   178  PKTGEN("tc", "tc_egressgw_skip_excluded_cidr_snat")
   179  int egressgw_skip_excluded_cidr_snat_pktgen(struct __ctx_buff *ctx)
   180  {
   181  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
   182  			.test = TEST_SNAT_EXCL_CIDR,
   183  		});
   184  }
   185  
   186  SETUP("tc", "tc_egressgw_skip_excluded_cidr_snat")
   187  int egressgw_skip_excluded_cidr_snat_setup(struct __ctx_buff *ctx)
   188  {
   189  
   190  	add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24, GATEWAY_NODE_IP, 0);
   191  	add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP, 32, EGRESS_GATEWAY_EXCLUDED_CIDR, 0);
   192  
   193  	/* Jump into the entrypoint */
   194  	ctx_egw_done_set(ctx);
   195  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
   196  	/* Fail if we didn't jump */
   197  	return TEST_ERROR;
   198  }
   199  
   200  CHECK("tc", "tc_egressgw_skip_excluded_cidr_snat")
   201  int egressgw_skip_excluded_cidr_snat_check(const struct __ctx_buff *ctx)
   202  {
   203  	void *data, *data_end;
   204  	__u32 *status_code;
   205  	struct tcphdr *l4;
   206  	struct ethhdr *l2;
   207  	struct iphdr *l3;
   208  
   209  	test_init();
   210  
   211  	del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP, 32);
   212  
   213  	data = (void *)(long)ctx_data(ctx);
   214  	data_end = (void *)(long)ctx->data_end;
   215  
   216  	if (data + sizeof(__u32) > data_end)
   217  		test_fatal("status code out of bounds");
   218  
   219  	status_code = data;
   220  	assert(*status_code == CTX_ACT_OK);
   221  
   222  	l2 = data + sizeof(__u32);
   223  	if ((void *)l2 + sizeof(struct ethhdr) > data_end)
   224  		test_fatal("l2 out of bounds");
   225  
   226  	l3 = (void *)l2 + sizeof(struct ethhdr);
   227  	if ((void *)l3 + sizeof(struct iphdr) > data_end)
   228  		test_fatal("l3 out of bounds");
   229  
   230  	if (l3->check != bpf_htons(0x4112))
   231  		test_fatal("L3 checksum is invalid: %d", bpf_htons(l3->check));
   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 is not the client MAC")
   239  
   240  	if (memcmp(l2->h_dest, (__u8 *)ext_svc_mac, ETH_ALEN) != 0)
   241  		test_fatal("dst MAC is not the external svc MAC")
   242  
   243  	if (l3->saddr != CLIENT_IP)
   244  		test_fatal("src IP has changed");
   245  
   246  	if (l3->daddr != EXTERNAL_SVC_IP)
   247  		test_fatal("dst IP has changed");
   248  
   249  	if (l4->source != client_port(TEST_SNAT_EXCL_CIDR))
   250  		test_fatal("src TCP port has changed");
   251  
   252  	if (l4->dest != EXTERNAL_SVC_PORT)
   253  		test_fatal("dst port has changed");
   254  
   255  	test_finish();
   256  }
   257  
   258  PKTGEN("tc", "tc_egressgw_fib_redirect")
   259  int egressgw_fib_redirect_pktgen(struct __ctx_buff *ctx)
   260  {
   261  	return egressgw_pktgen(ctx, (struct egressgw_test_ctx) {
   262  			.test = TEST_FIB,
   263  			.redirect = true,
   264  		});
   265  }
   266  
   267  SETUP("tc", "tc_egressgw_fib_redirect")
   268  int egressgw_fib_redirect_setup(struct __ctx_buff *ctx)
   269  {
   270  	add_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24,
   271  				  GATEWAY_NODE_IP, EGRESS_IP2);
   272  
   273  	/* Jump into the entrypoint */
   274  	ctx_egw_done_set(ctx);
   275  	tail_call_static(ctx, entry_call_map, TO_NETDEV);
   276  	/* Fail if we didn't jump */
   277  	return TEST_ERROR;
   278  }
   279  
   280  CHECK("tc", "tc_egressgw_fib_redirect")
   281  int egressgw_fib_redirect_check(const struct __ctx_buff *ctx __maybe_unused)
   282  {
   283  	int ret = egressgw_snat_check(ctx, (struct egressgw_test_ctx) {
   284  			.test = TEST_FIB,
   285  			.redirect = true,
   286  			.packets = 1,
   287  			.status_code = CTX_ACT_REDIRECT,
   288  		});
   289  
   290  	del_egressgw_policy_entry(CLIENT_IP, EXTERNAL_SVC_IP & 0xffffff, 24);
   291  
   292  	return ret;
   293  }
   294