bpf: add read/write access to skb->tstamp from tc clsact progs
Subsystems (6)
PATCH 1/1 bpf: add read/write access to skb->tstamp from tc clsact progs
This could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 26 +++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 4 ++++ 4 files changed, 32 insertions(+)
include/uapi/linux/bpf.h +1 -0
@@ -2468,6 +2468,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys; +__u64 tstamp; };struct bpf_tunnel_key {
net/core/filter.c +26 -0
@@ -5573,6 +5573,10 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type typeif (size !=sizeof (struct bpf_flow_keys *))return false;break ; + case bpf_ctx_range(struct __sk_buff, tstamp): +if (size !=sizeof (__u64)) +return false; +break ;default :/* Only narrow read access allowed for now. */ if (type ==BPF_WRITE ) {
@@ -5600,6 +5604,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_end): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -5624,6 +5629,7 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end):
@@ -5665,6 +5671,7 @@ static bool lwt_is_valid_access(int off, int size, case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -5874,6 +5881,7 @@ static bool tc_cls_act_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, priority): case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]): + case bpf_ctx_range(struct __sk_buff, tstamp):break ;default :return false;
@@ -6093,6 +6101,7 @@ static bool sk_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -6179,6 +6188,7 @@ static bool flow_dissector_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -6488,6 +6498,22 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, si->src_reg, off);break ; + +case offsetof(struct __sk_buff , tstamp): + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tstamp) != 8); + +if (type ==BPF_WRITE ) + *insn++ =BPF_STX_MEM (BPF_DW , + si->dst_reg, si->src_reg, + bpf_target_off(struct sk_buff, + tstamp,8 , + target_size)); +else + *insn++ =BPF_LDX_MEM (BPF_DW , + si->dst_reg, si->src_reg, + bpf_target_off(struct sk_buff, + tstamp,8 , + target_size)); }return insn - insn_buf;
tools/include/uapi/linux/bpf.h +1 -0
@@ -2468,6 +2468,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys; +__u64 tstamp; };struct bpf_tunnel_key {
tools/testing/selftests/bpf/test_verifier.c +4 -0
@@ -2446,6 +2446,10 @@ static struct bpf_test tests[] = { offsetof(struct __sk_buff, tc_index)), BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, offsetof(struct __sk_buff, cb[3])), +BPF_LDX_MEM (BPF_DW ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , tstamp)), +BPF_STX_MEM (BPF_DW ,BPF_REG_1 ,BPF_REG_0 , + offsetof(struct __sk_buff , tstamp)), BPF_EXIT_INSN(), }, .errstr_unpriv = "",
Signature · 1 line
2.19.1.1215.g8438c0b245-goog
Quoted 11 lines · Vlad Dumitrescu
This could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
Signed-off-by Vlad Dumitrescu <[email protected]>--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 26 +++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 4 ++++ 4 files changed, 32 insertions(+)
Awesome, thanks Vlad
Note that this also can be used to implement a delay (a la netem).
Willem de Bruijn
Acked-byQuoted 16 lines · Eric Dumazet
Quoted 11 lines · Vlad Dumitrescu
This could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
Signed-off-by Vlad Dumitrescu <[email protected]>--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 26 +++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 4 ++++ 4 files changed, 32 insertions(+)
Quoted 5 lines
Awesome, thanks Vlad
Note that this also can be used to implement a delay (a la netem).
Acked-by Eric Dumazet <[email protected]>
Quoted 47 lines · Vlad Dumitrescu
This could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
Signed-off-by Vlad Dumitrescu <[email protected]>--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 26 +++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 4 ++++ 4 files changed, 32 insertions(+)include/uapi/linux/bpf.h +1
-0@@ -2468,6 +2468,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys; +__u64 tstamp; };struct bpf_tunnel_key {net/core/filter.c +5
-0@@ -5573,6 +5573,10 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type typeif (size !=sizeof (struct bpf_flow_keys *))return false;break ; + case bpf_ctx_range(struct __sk_buff, tstamp): +if (size !=sizeof (__u64)) +return false; +break ;default :/* Only narrow read access allowed for now. */ if (type ==BPF_WRITE ) {@@ -5600,6 +5604,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_end): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
net/core/filter.c static bool cg_skb_is_valid_access(int off, int size, · line 5633
Quoted 5 lines
net/core/filter.c +1
-0@@ -5624,6 +5629,7 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false;
looks good to me.
Any particular reason you decided to disable it for cg_skb ? It seems to me the same EDT approach will work from cgroup-bpf skb hooks just as well and then we can have neat way of controlling traffic per-container instead of tc-clsbpf global. If you're already on cgroup v2 it will save you a lot of classifier cycles, since you'd be able to group apps by cgroup instead of relying on ip only.
Quoted 10 lines · Alexei Starovoitov
looks good to me.
Any particular reason you decided to disable it for
cg_skb? It seems to me the same EDT approach will work from cgroup-bpf skb hooks just as well and then we can have neat way of controlling traffic per-container instead of tc-clsbpf global. If you're already on cgroup v2 it will save you a lot of classifier cycles, since you'd be able to group apps by cgroup instead of relying on ip only.
Vlad first wrote a complete version, but we felt explaining the _why_ was probably harder.
No particular reason, other than having to write more tests perhaps.
Quoted 16 lines · Eric Dumazet
Quoted 10 lines · Alexei Starovoitov
looks good to me.
Any particular reason you decided to disable it for
cg_skb? It seems to me the same EDT approach will work from cgroup-bpf skb hooks just as well and then we can have neat way of controlling traffic per-container instead of tc-clsbpf global. If you're already on cgroup v2 it will save you a lot of classifier cycles, since you'd be able to group apps by cgroup instead of relying on ip only.
Quoted 4 lines
Vlad first wrote a complete version, but we felt explaining the _why_ was probably harder.
No particular reason, other than having to write more tests perhaps.
This sounds reasonable to me. I can prepare a v2.
Any concerns regarding capabilities? For example data and data_end are only available to CAP_SYS_ADMIN. Note that enforcement of this would be done by a global component later in the pipeline (e.g., FQ qdisc).
Any opinions on sk_filter, lwt, and sk_skb before I send v2?
Quoted 17 lines · Vlad Dumitrescu
Quoted 16 lines · Eric Dumazet
Quoted 10 lines · Alexei Starovoitov
looks good to me.
Any particular reason you decided to disable it for
cg_skb? It seems to me the same EDT approach will work from cgroup-bpf skb hooks just as well and then we can have neat way of controlling traffic per-container instead of tc-clsbpf global. If you're already on cgroup v2 it will save you a lot of classifier cycles, since you'd be able to group apps by cgroup instead of relying on ip only.
Quoted 6 lines
Quoted 4 lines
Vlad first wrote a complete version, but we felt explaining the _why_ was probably harder.
No particular reason, other than having to write more tests perhaps.
This sounds reasonable to me. I can prepare a v2.
thank you
Quoted 3 lines
Any concerns regarding capabilities? For example data and
data_endare only available toCAP_SYS_ADMIN. Note that enforcement of this would be done by a global component later in the pipeline (e.g., FQ qdisc).
I'd do cap_sys_admin for now, since i'm not sure whether any tstamp values will be acceptable to fq.
Quoted 1 line
Any opinions on
sk_filter, lwt, andsk_skbbefore I send v2?
sk_filter not appealing, since it's too late in the stack. lwt could be interesting, but I'd wait until first user appears. sk_skb - useful, but it requires more work.
We'll follow up to that sk_skb with our own patches.
Thanks!
Daniel Borkmann
maintainerQuoted 16 lines · Vlad Dumitrescu
Quoted 15 lines · Eric Dumazet
Quoted 10 lines · Alexei Starovoitov
looks good to me.
Any particular reason you decided to disable it for
cg_skb? It seems to me the same EDT approach will work from cgroup-bpf skb hooks just as well and then we can have neat way of controlling traffic per-container instead of tc-clsbpf global. If you're already on cgroup v2 it will save you a lot of classifier cycles, since you'd be able to group apps by cgroup instead of relying on ip only.Vlad first wrote a complete version, but we felt explaining the _why_ was probably harder.
Quoted 7 lines
Quoted 1 line
No particular reason, other than having to write more tests perhaps.
This sounds reasonable to me. I can prepare a v2.
Any concerns regarding capabilities? For example data and
data_endare only available toCAP_SYS_ADMIN. Note that enforcement of this would be done by a global component later in the pipeline (e.g., FQ qdisc).
cg_skb_is_valid_access() has the CAP_SYS_ADMIN enforcement for direct packet access since cg_skb can also run from unprivileged. Makes sense to do the same for skb->tstamp for the STX_MEM part at least.
Quoted 1 line
Any opinions on
sk_filter, lwt, andsk_skbbefore I send v2?
I'd probably leave that out for the time being if there is no concrete use at this point.
Thanks,
Daniel
PATCH v2 1/1 bpf: add skb->tstamp r/w access from tc clsact and cg skb progs
In reply to Vlad DumitrescuThis could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
Write access from cg skb progs only with CAP_SYS_ADMIN, since the value will be used by downstream qdiscs. It might make sense to relax this.
Changes v1 -> v2:
- allow access from cg skb, write only with CAP_SYS_ADMIN
--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 29 +++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 29 +++++++++++++++++++++ 4 files changed, 60 insertions(+)
include/uapi/linux/bpf.h +1 -0
@@ -2468,6 +2468,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys; +__u64 tstamp; };struct bpf_tunnel_key {
net/core/filter.c +29 -0
@@ -5573,6 +5573,10 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type typeif (size !=sizeof (struct bpf_flow_keys *))return false;break ; + case bpf_ctx_range(struct __sk_buff, tstamp): +if (size !=sizeof (__u64)) +return false; +break ;default :/* Only narrow read access allowed for now. */ if (type ==BPF_WRITE ) {
@@ -5600,6 +5604,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_end): case bpf_ctx_range(struct __sk_buff, flow_keys): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -5638,6 +5643,10 @@ static bool cg_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, priority): case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]):break ; + case bpf_ctx_range(struct __sk_buff, tstamp): +if (!capable (CAP_SYS_ADMIN )) +return false; +break ;default :return false; }
@@ -5665,6 +5674,7 @@ static bool lwt_is_valid_access(int off, int size, case bpf_ctx_range_till(struct __sk_buff, family, local_port): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -5874,6 +5884,7 @@ static bool tc_cls_act_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, priority): case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range_till(struct __sk_buff, cb[0], cb[4]): + case bpf_ctx_range(struct __sk_buff, tstamp):break ;default :return false;
@@ -6093,6 +6104,7 @@ static bool sk_skb_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, flow_keys): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -6179,6 +6191,7 @@ static bool flow_dissector_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, tstamp):return false; }
@@ -6488,6 +6501,22 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, si->src_reg, off);break ; + +case offsetof(struct __sk_buff , tstamp): + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tstamp) != 8); + +if (type ==BPF_WRITE ) + *insn++ =BPF_STX_MEM (BPF_DW , + si->dst_reg, si->src_reg, + bpf_target_off(struct sk_buff, + tstamp,8 , + target_size)); +else + *insn++ =BPF_LDX_MEM (BPF_DW , + si->dst_reg, si->src_reg, + bpf_target_off(struct sk_buff, + tstamp,8 , + target_size)); }return insn - insn_buf;
tools/include/uapi/linux/bpf.h +1 -0
@@ -2468,6 +2468,7 @@ struct __sk_buff {__u32 data_meta;struct bpf_flow_keys *flow_keys; +__u64 tstamp; };struct bpf_tunnel_key {
tools/testing/selftests/bpf/test_verifier.c +29 -0
@@ -2446,6 +2446,10 @@ static struct bpf_test tests[] = { offsetof(struct __sk_buff, tc_index)), BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, offsetof(struct __sk_buff, cb[3])), +BPF_LDX_MEM (BPF_DW ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , tstamp)), +BPF_STX_MEM (BPF_DW ,BPF_REG_1 ,BPF_REG_0 , + offsetof(struct __sk_buff , tstamp)), BPF_EXIT_INSN(), }, .errstr_unpriv = "",
@@ -5297,6 +5301,31 @@ static struct bpf_test tests[] = { .errstr_unpriv = "R2 leaks addr into helper function", .prog_type = BPF_PROG_TYPE_CGROUP_SKB, }, + { + "write tstamp from CGROUP_SKB", + .insns = { +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_STX_MEM (BPF_DW ,BPF_REG_1 ,BPF_REG_0 , + offsetof(struct __sk_buff , tstamp)), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =ACCEPT , + .result_unpriv = REJECT, + .errstr_unpriv = "invalid bpf_context access off=152 size=8", + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, + { +"read tstamp from CGROUP_SKB" , + .insns = { +BPF_LDX_MEM (BPF_DW ,BPF_REG_0 ,BPF_REG_1 , + offsetof(struct __sk_buff , tstamp)), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =ACCEPT , + .prog_type = BPF_PROG_TYPE_CGROUP_SKB, + }, {"multiple registers share map_lookup_elem result" , .insns = {
Signature · 1 line
2.19.1.1215.g8438c0b245-goog
Alexei Starovoitov
AppliedRe: [PATCH v2 bpf-next] bpf: add skb->tstamp r/w access from tc clsact and cg skb progs
Quoted 10 lines · Vlad Dumitrescu
This could be used to rate limit egress traffic in concert with a qdisc which supports Earliest Departure Time, such as FQ.
Write access from cg skb progs only with
CAP_SYS_ADMIN, since the value will be used by downstream qdiscs. It might make sense to relax this.Changes v1 -> v2:
- allow access from cg skb, write only withCAP_SYS_ADMINSigned-off-by Vlad Dumitrescu <[email protected]>
Applied to bpf-next.
I copied Eric's and Willem's Acks from v1, since v2 is essentially the same. Thanks everyone!