Skip to content

Commit 004d4b2

Browse files
Zashasborkmann
authored andcommitted
ipv6: sr: Add seg6local action End.BPF
This patch adds the End.BPF action to the LWT seg6local infrastructure. This action works like any other seg6local End action, meaning that an IPv6 header with SRH is needed, whose DA has to be equal to the SID of the action. It will also advance the SRH to the next segment, the BPF program does not have to take care of this. Since the BPF program may not be a source of instability in the kernel, it is important to ensure that the integrity of the packet is maintained before yielding it back to the IPv6 layer. The hook hence keeps track if the SRH has been altered through the helpers, and re-validates its content if needed with seg6_validate_srh. The state kept for validation is stored in a per-CPU buffer. The BPF program is not allowed to directly write into the packet, and only some fields of the SRH can be altered through the helper bpf_lwt_seg6_store_bytes. Performances profiling has shown that the SRH re-validation does not induce a significant overhead. If the altered SRH is deemed as invalid, the packet is dropped. This validation is also done before executing any action through bpf_lwt_seg6_action, and will not be performed again if the SRH is not modified after calling the action. The BPF program may return 3 types of return codes: - BPF_OK: the End.BPF action will look up the next destination through seg6_lookup_nexthop. - BPF_REDIRECT: if an action has been executed through the bpf_lwt_seg6_action helper, the BPF program should return this value, as the skb's destination is already set and the default lookup should not be performed. - BPF_DROP : the packet will be dropped. Signed-off-by: Mathieu Xhonneux <m.xhonneux@gmail.com> Acked-by: David Lebrun <dlebrun@google.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
1 parent cd3092c commit 004d4b2

File tree

7 files changed

+207
-2
lines changed

7 files changed

+207
-2
lines changed

include/linux/bpf_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, cg_sock_addr)
1212
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_in)
1313
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_out)
1414
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit)
15+
BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_SEG6LOCAL, lwt_seg6local)
1516
BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops)
1617
BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb)
1718
BPF_PROG_TYPE(BPF_PROG_TYPE_SK_MSG, sk_msg)

include/uapi/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ enum bpf_prog_type {
141141
BPF_PROG_TYPE_SK_MSG,
142142
BPF_PROG_TYPE_RAW_TRACEPOINT,
143143
BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
144+
BPF_PROG_TYPE_LWT_SEG6LOCAL,
144145
};
145146

146147
enum bpf_attach_type {

include/uapi/linux/seg6_local.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum {
2525
SEG6_LOCAL_NH6,
2626
SEG6_LOCAL_IIF,
2727
SEG6_LOCAL_OIF,
28+
SEG6_LOCAL_BPF,
2829
__SEG6_LOCAL_MAX,
2930
};
3031
#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
@@ -59,10 +60,21 @@ enum {
5960
SEG6_LOCAL_ACTION_END_AS = 13,
6061
/* forward to SR-unaware VNF with masquerading */
6162
SEG6_LOCAL_ACTION_END_AM = 14,
63+
/* custom BPF action */
64+
SEG6_LOCAL_ACTION_END_BPF = 15,
6265

6366
__SEG6_LOCAL_ACTION_MAX,
6467
};
6568

6669
#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1)
6770

71+
enum {
72+
SEG6_LOCAL_BPF_PROG_UNSPEC,
73+
SEG6_LOCAL_BPF_PROG,
74+
SEG6_LOCAL_BPF_PROG_NAME,
75+
__SEG6_LOCAL_BPF_PROG_MAX,
76+
};
77+
78+
#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1)
79+
6880
#endif

kernel/bpf/verifier.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
12621262
switch (env->prog->type) {
12631263
case BPF_PROG_TYPE_LWT_IN:
12641264
case BPF_PROG_TYPE_LWT_OUT:
1265+
case BPF_PROG_TYPE_LWT_SEG6LOCAL:
12651266
/* dst_input() and dst_output() can't write for now */
12661267
if (t == BPF_WRITE)
12671268
return false;

net/core/filter.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4921,6 +4921,21 @@ lwt_xmit_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
49214921
}
49224922
}
49234923

4924+
static const struct bpf_func_proto *
4925+
lwt_seg6local_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
4926+
{
4927+
switch (func_id) {
4928+
case BPF_FUNC_lwt_seg6_store_bytes:
4929+
return &bpf_lwt_seg6_store_bytes_proto;
4930+
case BPF_FUNC_lwt_seg6_action:
4931+
return &bpf_lwt_seg6_action_proto;
4932+
case BPF_FUNC_lwt_seg6_adjust_srh:
4933+
return &bpf_lwt_seg6_adjust_srh_proto;
4934+
default:
4935+
return lwt_out_func_proto(func_id, prog);
4936+
}
4937+
}
4938+
49244939
static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type,
49254940
const struct bpf_prog *prog,
49264941
struct bpf_insn_access_aux *info)
@@ -6629,6 +6644,16 @@ const struct bpf_prog_ops lwt_xmit_prog_ops = {
66296644
.test_run = bpf_prog_test_run_skb,
66306645
};
66316646

6647+
const struct bpf_verifier_ops lwt_seg6local_verifier_ops = {
6648+
.get_func_proto = lwt_seg6local_func_proto,
6649+
.is_valid_access = lwt_is_valid_access,
6650+
.convert_ctx_access = bpf_convert_ctx_access,
6651+
};
6652+
6653+
const struct bpf_prog_ops lwt_seg6local_prog_ops = {
6654+
.test_run = bpf_prog_test_run_skb,
6655+
};
6656+
66326657
const struct bpf_verifier_ops cg_sock_verifier_ops = {
66336658
.get_func_proto = sock_filter_func_proto,
66346659
.is_valid_access = sock_filter_is_valid_access,

net/ipv6/seg6_local.c

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/*
22
* SR-IPv6 implementation
33
*
4-
* Author:
4+
* Authors:
55
* David Lebrun <david.lebrun@uclouvain.be>
6+
* eBPF support: Mathieu Xhonneux <m.xhonneux@gmail.com>
67
*
78
*
89
* This program is free software; you can redistribute it and/or
@@ -32,6 +33,7 @@
3233
#endif
3334
#include <net/seg6_local.h>
3435
#include <linux/etherdevice.h>
36+
#include <linux/bpf.h>
3537

3638
struct seg6_local_lwt;
3739

@@ -42,6 +44,11 @@ struct seg6_action_desc {
4244
int static_headroom;
4345
};
4446

47+
struct bpf_lwt_prog {
48+
struct bpf_prog *prog;
49+
char *name;
50+
};
51+
4552
struct seg6_local_lwt {
4653
int action;
4754
struct ipv6_sr_hdr *srh;
@@ -50,6 +57,7 @@ struct seg6_local_lwt {
5057
struct in6_addr nh6;
5158
int iif;
5259
int oif;
60+
struct bpf_lwt_prog bpf;
5361

5462
int headroom;
5563
struct seg6_action_desc *desc;
@@ -451,6 +459,69 @@ static int input_action_end_b6_encap(struct sk_buff *skb,
451459

452460
DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
453461

462+
static int input_action_end_bpf(struct sk_buff *skb,
463+
struct seg6_local_lwt *slwt)
464+
{
465+
struct seg6_bpf_srh_state *srh_state =
466+
this_cpu_ptr(&seg6_bpf_srh_states);
467+
struct seg6_bpf_srh_state local_srh_state;
468+
struct ipv6_sr_hdr *srh;
469+
int srhoff = 0;
470+
int ret;
471+
472+
srh = get_and_validate_srh(skb);
473+
if (!srh)
474+
goto drop;
475+
advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
476+
477+
/* preempt_disable is needed to protect the per-CPU buffer srh_state,
478+
* which is also accessed by the bpf_lwt_seg6_* helpers
479+
*/
480+
preempt_disable();
481+
srh_state->hdrlen = srh->hdrlen << 3;
482+
srh_state->valid = 1;
483+
484+
rcu_read_lock();
485+
bpf_compute_data_pointers(skb);
486+
ret = bpf_prog_run_save_cb(slwt->bpf.prog, skb);
487+
rcu_read_unlock();
488+
489+
local_srh_state = *srh_state;
490+
preempt_enable();
491+
492+
switch (ret) {
493+
case BPF_OK:
494+
case BPF_REDIRECT:
495+
break;
496+
case BPF_DROP:
497+
goto drop;
498+
default:
499+
pr_warn_once("bpf-seg6local: Illegal return value %u\n", ret);
500+
goto drop;
501+
}
502+
503+
if (unlikely((local_srh_state.hdrlen & 7) != 0))
504+
goto drop;
505+
506+
if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0)
507+
goto drop;
508+
srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
509+
srh->hdrlen = (u8)(local_srh_state.hdrlen >> 3);
510+
511+
if (!local_srh_state.valid &&
512+
unlikely(!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3)))
513+
goto drop;
514+
515+
if (ret != BPF_REDIRECT)
516+
seg6_lookup_nexthop(skb, NULL, 0);
517+
518+
return dst_input(skb);
519+
520+
drop:
521+
kfree_skb(skb);
522+
return -EINVAL;
523+
}
524+
454525
static struct seg6_action_desc seg6_action_table[] = {
455526
{
456527
.action = SEG6_LOCAL_ACTION_END,
@@ -497,7 +568,13 @@ static struct seg6_action_desc seg6_action_table[] = {
497568
.attrs = (1 << SEG6_LOCAL_SRH),
498569
.input = input_action_end_b6_encap,
499570
.static_headroom = sizeof(struct ipv6hdr),
500-
}
571+
},
572+
{
573+
.action = SEG6_LOCAL_ACTION_END_BPF,
574+
.attrs = (1 << SEG6_LOCAL_BPF),
575+
.input = input_action_end_bpf,
576+
},
577+
501578
};
502579

503580
static struct seg6_action_desc *__get_action_desc(int action)
@@ -542,6 +619,7 @@ static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = {
542619
.len = sizeof(struct in6_addr) },
543620
[SEG6_LOCAL_IIF] = { .type = NLA_U32 },
544621
[SEG6_LOCAL_OIF] = { .type = NLA_U32 },
622+
[SEG6_LOCAL_BPF] = { .type = NLA_NESTED },
545623
};
546624

547625
static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
@@ -719,6 +797,75 @@ static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
719797
return 0;
720798
}
721799

800+
#define MAX_PROG_NAME 256
801+
static const struct nla_policy bpf_prog_policy[SEG6_LOCAL_BPF_PROG_MAX + 1] = {
802+
[SEG6_LOCAL_BPF_PROG] = { .type = NLA_U32, },
803+
[SEG6_LOCAL_BPF_PROG_NAME] = { .type = NLA_NUL_STRING,
804+
.len = MAX_PROG_NAME },
805+
};
806+
807+
static int parse_nla_bpf(struct nlattr **attrs, struct seg6_local_lwt *slwt)
808+
{
809+
struct nlattr *tb[SEG6_LOCAL_BPF_PROG_MAX + 1];
810+
struct bpf_prog *p;
811+
int ret;
812+
u32 fd;
813+
814+
ret = nla_parse_nested(tb, SEG6_LOCAL_BPF_PROG_MAX,
815+
attrs[SEG6_LOCAL_BPF], bpf_prog_policy, NULL);
816+
if (ret < 0)
817+
return ret;
818+
819+
if (!tb[SEG6_LOCAL_BPF_PROG] || !tb[SEG6_LOCAL_BPF_PROG_NAME])
820+
return -EINVAL;
821+
822+
slwt->bpf.name = nla_memdup(tb[SEG6_LOCAL_BPF_PROG_NAME], GFP_KERNEL);
823+
if (!slwt->bpf.name)
824+
return -ENOMEM;
825+
826+
fd = nla_get_u32(tb[SEG6_LOCAL_BPF_PROG]);
827+
p = bpf_prog_get_type(fd, BPF_PROG_TYPE_LWT_SEG6LOCAL);
828+
if (IS_ERR(p)) {
829+
kfree(slwt->bpf.name);
830+
return PTR_ERR(p);
831+
}
832+
833+
slwt->bpf.prog = p;
834+
return 0;
835+
}
836+
837+
static int put_nla_bpf(struct sk_buff *skb, struct seg6_local_lwt *slwt)
838+
{
839+
struct nlattr *nest;
840+
841+
if (!slwt->bpf.prog)
842+
return 0;
843+
844+
nest = nla_nest_start(skb, SEG6_LOCAL_BPF);
845+
if (!nest)
846+
return -EMSGSIZE;
847+
848+
if (nla_put_u32(skb, SEG6_LOCAL_BPF_PROG, slwt->bpf.prog->aux->id))
849+
return -EMSGSIZE;
850+
851+
if (slwt->bpf.name &&
852+
nla_put_string(skb, SEG6_LOCAL_BPF_PROG_NAME, slwt->bpf.name))
853+
return -EMSGSIZE;
854+
855+
return nla_nest_end(skb, nest);
856+
}
857+
858+
static int cmp_nla_bpf(struct seg6_local_lwt *a, struct seg6_local_lwt *b)
859+
{
860+
if (!a->bpf.name && !b->bpf.name)
861+
return 0;
862+
863+
if (!a->bpf.name || !b->bpf.name)
864+
return 1;
865+
866+
return strcmp(a->bpf.name, b->bpf.name);
867+
}
868+
722869
struct seg6_action_param {
723870
int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt);
724871
int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt);
@@ -749,6 +896,11 @@ static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = {
749896
[SEG6_LOCAL_OIF] = { .parse = parse_nla_oif,
750897
.put = put_nla_oif,
751898
.cmp = cmp_nla_oif },
899+
900+
[SEG6_LOCAL_BPF] = { .parse = parse_nla_bpf,
901+
.put = put_nla_bpf,
902+
.cmp = cmp_nla_bpf },
903+
752904
};
753905

754906
static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt)
@@ -834,6 +986,13 @@ static void seg6_local_destroy_state(struct lwtunnel_state *lwt)
834986
struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt);
835987

836988
kfree(slwt->srh);
989+
990+
if (slwt->desc->attrs & (1 << SEG6_LOCAL_BPF)) {
991+
kfree(slwt->bpf.name);
992+
bpf_prog_put(slwt->bpf.prog);
993+
}
994+
995+
return;
837996
}
838997

839998
static int seg6_local_fill_encap(struct sk_buff *skb,
@@ -886,6 +1045,11 @@ static int seg6_local_get_encap_size(struct lwtunnel_state *lwt)
8861045
if (attrs & (1 << SEG6_LOCAL_OIF))
8871046
nlsize += nla_total_size(4);
8881047

1048+
if (attrs & (1 << SEG6_LOCAL_BPF))
1049+
nlsize += nla_total_size(sizeof(struct nlattr)) +
1050+
nla_total_size(MAX_PROG_NAME) +
1051+
nla_total_size(4);
1052+
8891053
return nlsize;
8901054
}
8911055

tools/lib/bpf/libbpf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
14561456
case BPF_PROG_TYPE_LWT_IN:
14571457
case BPF_PROG_TYPE_LWT_OUT:
14581458
case BPF_PROG_TYPE_LWT_XMIT:
1459+
case BPF_PROG_TYPE_LWT_SEG6LOCAL:
14591460
case BPF_PROG_TYPE_SOCK_OPS:
14601461
case BPF_PROG_TYPE_SK_SKB:
14611462
case BPF_PROG_TYPE_CGROUP_DEVICE:

0 commit comments

Comments
 (0)