Revision fc7655ab9292bc496fe95ccf92b75f4faa12c834 authored by André Martins on 16 June 2024, 14:50:48 UTC, committed by André Martins on 20 June 2024, 18:58:31 UTC
If we enable auto merge for trusted dependencies we will be able to
reduce the load on reviewers by skipping the reviews of certain
trusted libraries.

Signed-off-by: André Martins <andre@cilium.io>
1 parent 8e6d288
Raw File
overloadable_xdp.h
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright Authors of Cilium */

#pragma once

#include <linux/udp.h>
#include <linux/ip.h>

static __always_inline __maybe_unused void
bpf_clear_meta(struct xdp_md *ctx __maybe_unused)
{
}

static __always_inline __maybe_unused int
get_identity(struct xdp_md *ctx __maybe_unused)
{
	return 0;
}

static __always_inline __maybe_unused void
set_identity_mark(struct xdp_md *ctx __maybe_unused, __u32 identity __maybe_unused,
		  __u32 magic __maybe_unused)
{
}

static __always_inline __maybe_unused void
set_identity_meta(struct xdp_md *ctx __maybe_unused,
		__u32 identity __maybe_unused)
{
}

static __always_inline __maybe_unused void
set_encrypt_key_mark(struct xdp_md *ctx __maybe_unused, __u8 key __maybe_unused,
		     __u32 node_id __maybe_unused)
{
}

static __always_inline __maybe_unused void
set_encrypt_key_meta(struct __sk_buff *ctx __maybe_unused, __u8 key __maybe_unused,
		     __u32 node_id __maybe_unused)
{
}

static __always_inline __maybe_unused void
ctx_set_cluster_id_mark(struct xdp_md *ctx __maybe_unused, __u32 cluster_id __maybe_unused)
{
}

static __always_inline __maybe_unused __u32
ctx_get_cluster_id_mark(struct __sk_buff *ctx __maybe_unused)
{
	return 0;
}

static __always_inline __maybe_unused int
redirect_self(struct xdp_md *ctx __maybe_unused)
{
	return XDP_TX;
}

static __always_inline __maybe_unused int
redirect_neigh(int ifindex __maybe_unused,
	       struct bpf_redir_neigh *params __maybe_unused,
	       int plen __maybe_unused,
	       __u32 flags __maybe_unused)
{
	return XDP_DROP;
}

static __always_inline __maybe_unused bool
neigh_resolver_available(void)
{
	return false;
}

#define RECIRC_MARKER	5 /* tail call recirculation */
#define XFER_MARKER	6 /* xdp -> skb meta transfer */

static __always_inline __maybe_unused void
ctx_skip_nodeport_clear(struct xdp_md *ctx __maybe_unused)
{
#ifdef ENABLE_NODEPORT
	ctx_store_meta(ctx, RECIRC_MARKER, 0);
#endif
}

static __always_inline __maybe_unused void
ctx_skip_nodeport_set(struct xdp_md *ctx __maybe_unused)
{
#ifdef ENABLE_NODEPORT
	ctx_store_meta(ctx, RECIRC_MARKER, 1);
#endif
}

static __always_inline __maybe_unused bool
ctx_skip_nodeport(struct xdp_md *ctx __maybe_unused)
{
#ifdef ENABLE_NODEPORT
	return ctx_load_meta(ctx, RECIRC_MARKER);
#else
	return true;
#endif
}

static __always_inline __maybe_unused __u32
ctx_get_xfer(struct xdp_md *ctx __maybe_unused, __u32 off __maybe_unused)
{
	return 0; /* Only intended for SKB context. */
}

static __always_inline __maybe_unused void ctx_set_xfer(struct xdp_md *ctx,
							__u32 meta)
{
	__u32 val = ctx_load_meta(ctx, XFER_MARKER);

	val |= meta;
	ctx_store_meta(ctx, XFER_MARKER, val);
}

static __always_inline __maybe_unused void ctx_move_xfer(struct xdp_md *ctx)
{
	__u32 meta_xfer = ctx_load_meta(ctx, XFER_MARKER);
	/* We transfer data from XFER_MARKER. This specifically
	 * does not break packet trains in GRO.
	 */

	if (meta_xfer) {
		if (!ctx_adjust_meta(ctx, -(int)sizeof(meta_xfer))) {
			__u32 *data_meta = ctx_data_meta(ctx);
			__u32 *data = ctx_data(ctx);

			if (!ctx_no_room(data_meta + 1, data))
				data_meta[XFER_FLAGS] = meta_xfer;
		}
	}
}

static __always_inline __maybe_unused int
ctx_change_head(struct xdp_md *ctx __maybe_unused,
		__u32 head_room __maybe_unused,
		__u64 flags __maybe_unused)
{
	return 0; /* Only intended for SKB context. */
}

static __always_inline void ctx_snat_done_set(struct xdp_md *ctx)
{
	ctx_set_xfer(ctx, XFER_PKT_SNAT_DONE);
}

static __always_inline bool ctx_snat_done(struct xdp_md *ctx)
{
	/* shouldn't be needed, there's no relevant Egress hook in XDP */
	return ctx_load_meta(ctx, XFER_MARKER) & XFER_PKT_SNAT_DONE;
}

#ifdef HAVE_ENCAP
static __always_inline __maybe_unused int
ctx_set_encap_info(struct xdp_md *ctx, __u32 src_ip, __be16 src_port,
		   __u32 daddr, __u32 seclabel __maybe_unused,
		   __u32 vni __maybe_unused, void *opt, __u32 opt_len)
{
	__u32 inner_len = ctx_full_len(ctx);
	__u32 tunnel_hdr_len = 8; /* geneve / vxlan */
	void *data, *data_end;
	struct ethhdr *eth;
	struct udphdr *udp;
	struct iphdr *ip4;
	__u32 outer_len;

	/* Add space in front (50 bytes + options) */
	outer_len = sizeof(*eth) + sizeof(*ip4) + sizeof(*udp) + tunnel_hdr_len + opt_len;

	if (ctx_adjust_hroom(ctx, outer_len, BPF_ADJ_ROOM_NET, ctx_adjust_hroom_flags()))
		return DROP_INVALID;

	/* validate access to outer headers: */
	data = ctx_data(ctx);
	data_end = ctx_data_end(ctx);

	if (data + outer_len > data_end)
		return DROP_INVALID;

	eth = data;
	ip4 = (void *)eth + sizeof(*eth);
	udp = (void *)ip4 + sizeof(*ip4);

	memset(data, 0, sizeof(*eth) + sizeof(*ip4) + sizeof(*udp) + tunnel_hdr_len);

	switch (TUNNEL_PROTOCOL) {
	case TUNNEL_PROTOCOL_GENEVE:
		{
			struct genevehdr *geneve = (void *)udp + sizeof(*udp);

			if (opt_len > 0)
				memcpy((void *)geneve + sizeof(*geneve), opt, opt_len);

			geneve->opt_len = (__u8)(opt_len >> 2);
			geneve->protocol_type = bpf_htons(ETH_P_TEB);

			seclabel = bpf_htonl(get_tunnel_id(seclabel) << 8);
			memcpy(&geneve->vni, &seclabel, sizeof(__u32));
		}
		break;
	case TUNNEL_PROTOCOL_VXLAN:
		if (opt_len > 0)
			return DROP_INVALID;

		{
			struct vxlanhdr *vxlan = (void *)udp + sizeof(*udp);

			vxlan->vx_flags = bpf_htonl(1U << 27);

			seclabel = bpf_htonl(get_tunnel_id(seclabel) << 8);
			memcpy(&vxlan->vx_vni, &seclabel, sizeof(__u32));
		}
		break;
	default:
		__throw_build_bug();
	}

	udp->source = src_port;
	udp->dest = bpf_htons(TUNNEL_PORT);
	udp->len = bpf_htons((__u16)(sizeof(*udp) + tunnel_hdr_len + opt_len + inner_len));
	udp->check = 0; /* we use BPF_F_ZERO_CSUM_TX */

	ip4->ihl = 5;
	ip4->version = IPVERSION;
	ip4->tot_len = bpf_htons((__u16)(sizeof(*ip4) + bpf_ntohs(udp->len)));
	ip4->ttl = IPDEFTTL;
	ip4->protocol = IPPROTO_UDP;
	ip4->saddr = src_ip;
	ip4->daddr = bpf_htonl(daddr);
	ip4->check = csum_fold(csum_diff(NULL, 0, ip4, sizeof(*ip4), 0));

	eth->h_proto = bpf_htons(ETH_P_IP);

	return CTX_ACT_REDIRECT;
}

static __always_inline __maybe_unused int
ctx_set_tunnel_opt(struct xdp_md *ctx, void *opt, __u32 opt_len)
{
	const __u32 geneve_off = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr);
	struct genevehdr geneve;

	/* add free space after GENEVE header: */
	if (ctx_adjust_hroom(ctx, opt_len, BPF_ADJ_ROOM_MAC, ctx_adjust_hroom_flags()) < 0)
		return DROP_INVALID;

	/* write the options */
	if (ctx_store_bytes(ctx, geneve_off + sizeof(geneve), opt, opt_len, 0) < 0)
		return DROP_WRITE_ERROR;

	/* update the options length in the GENEVE header: */
	if (ctx_load_bytes(ctx, geneve_off, &geneve, sizeof(geneve)) < 0)
		return DROP_INVALID;

	geneve.opt_len += (__u8)(opt_len >> 2);

	if (ctx_store_bytes(ctx, geneve_off, &geneve, sizeof(geneve), 0) < 0)
		return DROP_WRITE_ERROR;

	return 0;
}
#endif /* HAVE_ENCAP */
back to top