bpf,fou: Add bpf_skb_{set,get}_fou_encap kfuncs

Add two new kfuncs that allow a BPF tc-hook, installed on an ipip
device in collect-metadata mode, to control FOU encap parameters on a
per-packet level. The set of kfuncs is registered with the fou module.

The bpf_skb_set_fou_encap kfunc is supposed to be used in tandem and after
a successful call to the bpf_skb_set_tunnel_key bpf-helper. UDP source and
destination ports can be controlled by passing a struct bpf_fou_encap. A
source port of zero will auto-assign a source port. enum bpf_fou_encap_type
is used to specify if the egress path should FOU or GUE encap the packet.

On the ingress path bpf_skb_get_fou_encap can be used to read UDP source
and destination ports from the receiver's point of view and allows for
packet multiplexing across different destination ports within a single
BPF program and ipip device.

Signed-off-by: Christian Ehrig <cehrig@cloudflare.com>
Link: https://lore.kernel.org/r/e17c94a646b63e78ce0dbf3f04b2c33dc948a32d.1680874078.git.cehrig@cloudflare.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Christian Ehrig 2023-04-07 15:38:54 +02:00 committed by Alexei Starovoitov
parent ac931d4cde
commit c50e96099e
4 changed files with 127 additions and 1 deletions

View File

@ -17,4 +17,6 @@ int __fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
int __gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
u8 *protocol, __be16 *sport, int type);
int register_fou_bpf(void);
#endif

View File

@ -26,7 +26,7 @@ obj-$(CONFIG_IP_MROUTE) += ipmr.o
obj-$(CONFIG_IP_MROUTE_COMMON) += ipmr_base.o
obj-$(CONFIG_NET_IPIP) += ipip.o
gre-y := gre_demux.o
fou-y := fou_core.o fou_nl.o
fou-y := fou_core.o fou_nl.o fou_bpf.o
obj-$(CONFIG_NET_FOU) += fou.o
obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o

119
net/ipv4/fou_bpf.c Normal file
View File

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Unstable Fou Helpers for TC-BPF hook
*
* These are called from SCHED_CLS BPF programs. Note that it is
* allowed to break compatibility for these functions since the interface they
* are exposed through to BPF programs is explicitly unstable.
*/
#include <linux/bpf.h>
#include <linux/btf_ids.h>
#include <net/dst_metadata.h>
#include <net/fou.h>
struct bpf_fou_encap {
__be16 sport;
__be16 dport;
};
enum bpf_fou_encap_type {
FOU_BPF_ENCAP_FOU,
FOU_BPF_ENCAP_GUE,
};
__diag_push();
__diag_ignore_all("-Wmissing-prototypes",
"Global functions as their definitions will be in BTF");
/* bpf_skb_set_fou_encap - Set FOU encap parameters
*
* This function allows for using GUE or FOU encapsulation together with an
* ipip device in collect-metadata mode.
*
* It is meant to be used in BPF tc-hooks and after a call to the
* bpf_skb_set_tunnel_key helper, responsible for setting IP addresses.
*
* Parameters:
* @skb_ctx Pointer to ctx (__sk_buff) in TC program. Cannot be NULL
* @encap Pointer to a `struct bpf_fou_encap` storing UDP src and
* dst ports. If sport is set to 0 the kernel will auto-assign a
* port. This is similar to using `encap-sport auto`.
* Cannot be NULL
* @type Encapsulation type for the packet. Their definitions are
* specified in `enum bpf_fou_encap_type`
*/
__bpf_kfunc int bpf_skb_set_fou_encap(struct __sk_buff *skb_ctx,
struct bpf_fou_encap *encap, int type)
{
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
struct ip_tunnel_info *info = skb_tunnel_info(skb);
if (unlikely(!encap))
return -EINVAL;
if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX)))
return -EINVAL;
switch (type) {
case FOU_BPF_ENCAP_FOU:
info->encap.type = TUNNEL_ENCAP_FOU;
break;
case FOU_BPF_ENCAP_GUE:
info->encap.type = TUNNEL_ENCAP_GUE;
break;
default:
info->encap.type = TUNNEL_ENCAP_NONE;
}
if (info->key.tun_flags & TUNNEL_CSUM)
info->encap.flags |= TUNNEL_ENCAP_FLAG_CSUM;
info->encap.sport = encap->sport;
info->encap.dport = encap->dport;
return 0;
}
/* bpf_skb_get_fou_encap - Get FOU encap parameters
*
* This function allows for reading encap metadata from a packet received
* on an ipip device in collect-metadata mode.
*
* Parameters:
* @skb_ctx Pointer to ctx (__sk_buff) in TC program. Cannot be NULL
* @encap Pointer to a struct bpf_fou_encap storing UDP source and
* destination port. Cannot be NULL
*/
__bpf_kfunc int bpf_skb_get_fou_encap(struct __sk_buff *skb_ctx,
struct bpf_fou_encap *encap)
{
struct sk_buff *skb = (struct sk_buff *)skb_ctx;
struct ip_tunnel_info *info = skb_tunnel_info(skb);
if (unlikely(!info))
return -EINVAL;
encap->sport = info->encap.sport;
encap->dport = info->encap.dport;
return 0;
}
__diag_pop()
BTF_SET8_START(fou_kfunc_set)
BTF_ID_FLAGS(func, bpf_skb_set_fou_encap)
BTF_ID_FLAGS(func, bpf_skb_get_fou_encap)
BTF_SET8_END(fou_kfunc_set)
static const struct btf_kfunc_id_set fou_bpf_kfunc_set = {
.owner = THIS_MODULE,
.set = &fou_kfunc_set,
};
int register_fou_bpf(void)
{
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS,
&fou_bpf_kfunc_set);
}

View File

@ -1236,10 +1236,15 @@ static int __init fou_init(void)
if (ret < 0)
goto unregister;
ret = register_fou_bpf();
if (ret < 0)
goto kfunc_failed;
ret = ip_tunnel_encap_add_fou_ops();
if (ret == 0)
return 0;
kfunc_failed:
genl_unregister_family(&fou_nl_family);
unregister:
unregister_pernet_device(&fou_net_ops);