Skip to content

Commit

Permalink
bpf: test IPSec datapath on from-host
Browse files Browse the repository at this point in the history
These testcases cover IPSec datapath on `from-host` for both IPv4 and
IPv6.

The input skb should be ESP-encrypted with mark set to `node_id
<< 16 | key << 12 | 0xe00` and cb[4] set to sec_id. We will check the
following behaviors after execution of `from-host`:

1. skb->mark is cleared to 0
2. skb->cb[4] is cleared to 0
3. skb is redirected to cilium_vxlan
4. skb->data doesn't change
5. VxLAN VNI is set to sec_id

Signed-off-by: Zhichuan Liang <gray.liang@isovalent.com>

pktgen
  • Loading branch information
jschwinger233 authored and borkmann committed Jun 20, 2023
1 parent b760a99 commit dee0683
Show file tree
Hide file tree
Showing 2 changed files with 379 additions and 0 deletions.
317 changes: 317 additions & 0 deletions bpf/tests/ipsec_from_host_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright Authors of Cilium */

#include "common.h"
#include <bpf/ctx/skb.h>
#include "pktgen.h"
#define ROUTER_IP
#include "config_replacement.h"
#undef ROUTER_IP

#define NODE_ID 2333
#define ENCRYPT_KEY 3
#define ENABLE_IPV4
#define ENABLE_IPV6
#define ENABLE_IPSEC
#define TUNNEL_MODE
#define HAVE_ENCAP
#define ENCAP_IFINDEX 4
#define SECCTX_FROM_IPCACHE 1

#define skb_set_tunnel_key mock_skb_set_tunnel_key
#define ctx_redirect mock_ctx_redirect

int mock_skb_set_tunnel_key(__maybe_unused struct __sk_buff *skb,
const struct bpf_tunnel_key *from,
__maybe_unused __u32 size,
__maybe_unused __u32 flags)
{
/* 0xfffff is the default SECLABEL */
if (from->tunnel_id != 0xfffff)
return -1;
if (from->local_ipv4 != 0)
return -2;
if (from->remote_ipv4 != bpf_htonl(v4_node_two))
return -3;
return 0;
}

int mock_ctx_redirect(const struct __sk_buff *ctx __maybe_unused, int ifindex, __u32 flags)
{
if (ifindex != ENCAP_IFINDEX)
return -1;
if (flags != 0)
return -2;
return CTX_ACT_REDIRECT;
}

#include "bpf_host.c"

#define FROM_HOST 0
#define ESP_SEQUENCE 69865

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(key_size, sizeof(__u32));
__uint(max_entries, 1);
__array(values, int());
} entry_call_map __section(".maps") = {
.values = {
[FROM_HOST] = &cil_from_host,
},
};

PKTGEN("tc", "ipv4_ipsec_from_host")
int ipv4_ipsec_from_host_pktgen(struct __ctx_buff *ctx)
{
struct pktgen builder;
struct ethhdr *l2;
struct iphdr *l3;
struct ip_esp_hdr *l4;
void *data;

pktgen__init(&builder, ctx);

l2 = pktgen__push_ethhdr(&builder);
if (!l2)
return TEST_ERROR;
ethhdr__set_macs(l2, (__u8 *)mac_one, (__u8 *)mac_two);

l3 = pktgen__push_default_iphdr(&builder);
if (!l3)
return TEST_ERROR;
l3->saddr = v4_pod_one;
l3->daddr = v4_pod_two;

l4 = pktgen__push_default_esphdr(&builder);
if (!l4)
return TEST_ERROR;
l4->spi = ENCRYPT_KEY;
l4->seq_no = ESP_SEQUENCE;

data = pktgen__push_data(&builder, default_data, sizeof(default_data));
if (!data)
return TEST_ERROR;

pktgen__finish(&builder);
return 0;
}

SETUP("tc", "ipv4_ipsec_from_host")
int ipv4_ipsec_from_host_setup(struct __ctx_buff *ctx)
{
struct ipcache_key cache_key = {};
struct remote_endpoint_info cache_value = {};

cache_key.lpm_key.prefixlen = IPCACHE_PREFIX_LEN(32);
cache_key.family = ENDPOINT_KEY_IPV4;
cache_key.ip4 = v4_pod_two;
cache_value.sec_identity = 233;
cache_value.tunnel_endpoint = v4_node_two;
cache_value.node_id = NODE_ID;
cache_value.key = ENCRYPT_KEY;
map_update_elem(&IPCACHE_MAP, &cache_key, &cache_value, BPF_ANY);

set_encrypt_key_mark(ctx, ENCRYPT_KEY, NODE_ID);
set_identity_meta(ctx, SECLABEL);
tail_call_static(ctx, &entry_call_map, FROM_HOST);
return TEST_ERROR;
}

CHECK("tc", "ipv4_ipsec_from_host")
int ipv4_ipsec_from_host_check(__maybe_unused const struct __ctx_buff *ctx)
{
void *data;
void *data_end;
__u32 *status_code;
struct ethhdr *l2;
struct iphdr *l3;
struct ip_esp_hdr *l4;
__u8 *payload;

test_init();

data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;

if (data + sizeof(*status_code) > data_end)
test_fatal("status code out of bounds");

status_code = data;
assert(*status_code == CTX_ACT_REDIRECT);

assert(ctx->mark == 0);
assert(ctx_load_meta(ctx, CB_ENCRYPT_IDENTITY) == 0);

l2 = data + sizeof(*status_code);

if ((void *)l2 + sizeof(struct ethhdr) > data_end)
test_fatal("l2 out of bounds");

if (l2->h_proto != bpf_htons(ETH_P_IP))
test_fatal("l2 proto hasn't been set to ETH_P_IP");

if (memcmp(l2->h_source, (__u8 *)mac_one, ETH_ALEN) != 0)
test_fatal("src mac hasn't been set to source ep's mac");

if (memcmp(l2->h_dest, (__u8 *)mac_two, ETH_ALEN) != 0)
test_fatal("dest mac hasn't been set to dest ep's mac");

l3 = (void *)l2 + sizeof(struct ethhdr);

if ((void *)l3 + sizeof(struct iphdr) > data_end)
test_fatal("l3 out of bounds");

if (l3->saddr != v4_pod_one)
test_fatal("src IP was changed");

if (l3->daddr != v4_pod_two)
test_fatal("dest IP was changed");

l4 = (void *)l3 + sizeof(struct iphdr);

if ((void *)l4 + sizeof(struct ip_esp_hdr) > data_end)
test_fatal("l4 out of bounds");

if (l4->spi != ENCRYPT_KEY)
test_fatal("ESP spi was changed");

if (l4->seq_no != ESP_SEQUENCE)
test_fatal("ESP seq was changed");

payload = (void *)l4 + sizeof(struct ip_esp_hdr);
if ((void *)payload + sizeof(default_data) > data_end)
test_fatal("paylaod out of bounds\n");

if (memcmp(payload, default_data, sizeof(default_data)) != 0)
test_fatal("tcp payload was changed");

test_finish();
}

PKTGEN("tc", "ipv6_ipsec_from_host")
int ipv6_ipsec_from_host_pktgen(struct __ctx_buff *ctx)
{
struct pktgen builder;
struct ethhdr *l2;
struct ipv6hdr *l3;
struct ip_esp_hdr *l4;
void *data;

pktgen__init(&builder, ctx);

l2 = pktgen__push_ethhdr(&builder);
if (!l2)
return TEST_ERROR;
ethhdr__set_macs(l2, (__u8 *)mac_one, (__u8 *)mac_two);

l3 = pktgen__push_default_ipv6hdr(&builder);
if (!l3)
return TEST_ERROR;
ipv6hdr__set_addrs(l3, (__u8 *)v6_pod_one, (__u8 *)v6_pod_two);

l4 = pktgen__push_default_esphdr(&builder);
if (!l4)
return TEST_ERROR;
l4->spi = ENCRYPT_KEY;
l4->seq_no = ESP_SEQUENCE;

data = pktgen__push_data(&builder, default_data, sizeof(default_data));
if (!data)
return TEST_ERROR;

pktgen__finish(&builder);
return 0;
}

SETUP("tc", "ipv6_ipsec_from_host")
int ipv6_ipsec_from_host_setup(struct __ctx_buff *ctx)
{
struct ipcache_key cache_key = {};
struct remote_endpoint_info cache_value = {};

cache_key.lpm_key.prefixlen = IPCACHE_PREFIX_LEN(128);
cache_key.family = ENDPOINT_KEY_IPV6;
memcpy(&cache_key.ip6, (__u8 *)v6_pod_two, 16);
cache_value.sec_identity = 233;
cache_value.tunnel_endpoint = v4_node_two;
cache_value.node_id = NODE_ID;
cache_value.key = ENCRYPT_KEY;
map_update_elem(&IPCACHE_MAP, &cache_key, &cache_value, BPF_ANY);

set_encrypt_key_mark(ctx, ENCRYPT_KEY, NODE_ID);
set_identity_meta(ctx, SECLABEL);
tail_call_static(ctx, &entry_call_map, FROM_HOST);
return TEST_ERROR;
}

CHECK("tc", "ipv6_ipsec_from_host")
int ipv6_ipsec_from_host_check(__maybe_unused const struct __ctx_buff *ctx)
{
void *data;
void *data_end;
__u32 *status_code;
struct ethhdr *l2;
struct ipv6hdr *l3;
struct ip_esp_hdr *l4;
__u8 *payload;

test_init();

data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;

if (data + sizeof(*status_code) > data_end)
test_fatal("status code out of bounds");

status_code = data;
assert(*status_code == CTX_ACT_REDIRECT);

assert(ctx->mark == 0);
assert(ctx_load_meta(ctx, CB_ENCRYPT_IDENTITY) == 0);

l2 = data + sizeof(*status_code);

if ((void *)l2 + sizeof(struct ethhdr) > data_end)
test_fatal("l2 out of bounds");

if (l2->h_proto != bpf_htons(ETH_P_IPV6))
test_fatal("l2 proto hasn't been set to ETH_P_IP");

if (memcmp(l2->h_source, (__u8 *)mac_one, ETH_ALEN) != 0)
test_fatal("src mac hasn't been set to source ep's mac");

if (memcmp(l2->h_dest, (__u8 *)mac_two, ETH_ALEN) != 0)
test_fatal("dest mac hasn't been set to dest ep's mac");

l3 = (void *)l2 + sizeof(struct ethhdr);

if ((void *)l3 + sizeof(struct ipv6hdr) > data_end)
test_fatal("l3 out of bounds");

if (memcmp((__u8 *)&l3->saddr, (__u8 *)v6_pod_one, 16) != 0)
test_fatal("src IP was changed");

if (memcmp((__u8 *)&l3->daddr, (__u8 *)v6_pod_two, 16) != 0)
test_fatal("dest IP was changed");

l4 = (void *)l3 + sizeof(struct ipv6hdr);

if ((void *)l4 + sizeof(struct ip_esp_hdr) > data_end)
test_fatal("l4 out of bounds");

if (l4->spi != ENCRYPT_KEY)
test_fatal("ESP spi was changed");

if (l4->seq_no != ESP_SEQUENCE)
test_fatal("ESP seq was changed");

payload = (void *)l4 + sizeof(struct ip_esp_hdr);
if ((void *)payload + sizeof(default_data) > data_end)
test_fatal("paylaod out of bounds\n");

if (memcmp(payload, default_data, sizeof(default_data)) != 0)
test_fatal("tcp payload was changed");

test_finish();
}

0 comments on commit dee0683

Please sign in to comment.