Add eBPF hooks for cgroups
Subsystems (6)
v5 cover Add eBPF hooks for cgroups
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
After chatting with Daniel Borkmann and Alexei off-list, we concluded that __dev_queue_xmit() is the place where the egress hooks should live when eBPF programs need access to the L2 bits of the skb.
Changes from v4:
* Plug an skb leak when dropping packets due to eBPF verdicts in __dev_queue_xmit(). Spotted by Daniel Borkmann.
* Check for sk_fullsock(sk) in __cgroup_bpf_run_filter() so we don't operate on timewait or request sockets. Suggested by Daniel Borkmann.
* Add missing @parent parameter in kerneldoc of __cgroup_bpf_update(). Spotted by Rami Rosen.
* Include linux/jump_label.h from bpf-cgroup.h to fix a kbuild error.
Changes from v3:
* Dropped the _FILTER suffix from BPF_PROG_TYPE_CGROUP_SOCKET_FILTER, renamed BPF_ATTACH_TYPE_CGROUP_INET_{E,IN}GRESS to BPF_CGROUP_INET_{IN,E}GRESS and alias BPF_MAX_ATTACH_TYPE to __BPF_MAX_ATTACH_TYPE, as suggested by Daniel Borkmann.
* Dropped the attach_flags member from the anonymous struct for BPF attach operations in union bpf_attr. They can be added later on via CHECK_ATTR. Requested by Daniel Borkmann and Alexei.
* Release old_prog at the end of __cgroup_bpf_update rather that at the beginning to fix a race gap between program updates and their users. Spotted by Daniel Borkmann.
* Plugged an skb leak when dropping packets on the egress path. Spotted by Daniel Borkmann.
* Add [email protected] to the loop, as suggested by Rami Rosen.
* Some minor coding style adoptions not worth mentioning in particular.
Changes from v2:
* Fixed the RCU locking details Tejun pointed out.
* Assert bpf_attr.flags == 0 in BPF_PROG_DETACH syscall handler.
Changes from v1:
* Moved all bpf specific cgroup code into its own file, and stub out related functions for !CONFIG_CGROUP_BPF as static inline nops. This way, the call sites are not cluttered with #ifdef guards while the feature remains compile-time configurable.
* Implemented the new scheme proposed by Tejun. Per cgroup, store one set of pointers that are pinned to the cgroup, and one for the programs that are effective. When a program is attached or detached, the change is propagated to all the cgroup's descendants. If a subcgroup has its own pinned program, skip the whole subbranch in order to allow delegation models.
* The hookup for egress packets is now done from __dev_queue_xmit().
* A static key is now used in both the ingress and egress fast paths to keep performance penalties close to zero if the feature is not in use.
* Overall cleanup to make the accessors use the program arrays. This should make it much easier to add new program types, which will then automatically follow the pinned vs. effective logic.
* Fixed locking issues, as pointed out by Eric Dumazet and Alexei Starovoitov. Changes to the program array are now done with xchg() and are protected by cgroup_mutex.
* eBPF programs are now expected to return 1 to let the packet pass, not >= 0. Pointed out by Alexei.
* Operation is now limited to INET sockets, so local AF_UNIX sockets are not affected. The enum members are renamed accordingly. In case other socket families should be supported, this can be extended in the future.
* The sample program learned to support both ingress and egress, and can now optionally make the eBPF program drop packets by making it return 0.
As always, feedback is much appreciated.
Thanks,
Daniel
Daniel Mack (6):
bpf: add new prog type for cgroup socket filtering
cgroup: add support for eBPF programs
bpf: add BPF_PROG_ATTACH and BPF_PROG_DETACH commands
net: filter: run cgroup eBPF ingress programs
net: core: run cgroup eBPF egress programs
samples: bpf: add userspace example for attaching eBPF programs to
cgroups
include/linux/bpf-cgroup.h | 71 +++++++++++++++++ include/linux/cgroup-defs.h | 4 + include/uapi/linux/bpf.h | 17 ++++ init/Kconfig | 12 +++ kernel/bpf/Makefile | 1 + kernel/bpf/cgroup.c | 166 ++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 81 ++++++++++++++++++++ kernel/bpf/verifier.c | 1 + kernel/cgroup.c | 18 +++++ net/core/dev.c | 6 ++ net/core/filter.c | 10 +++ samples/bpf/Makefile | 2 + samples/bpf/libbpf.c | 21 +++++ samples/bpf/libbpf.h | 3 + samples/bpf/test_cgrp2_attach.c | 147 +++++++++++++++++++++++++++++++++++ 15 files changed, 560 insertions(+) create mode 100644 include/linux/bpf-cgroup.h create mode 100644 kernel/bpf/cgroup.c create mode 100644 samples/bpf/test_cgrp2_attach.c
Signature · 1 line
2.5.5
PATCH v5 1/6 bpf: add new prog type for cgroup socket filtering
For now, this program type is equivalent to BPF_PROG_TYPE_SOCKET_FILTER in terms of checks during the verification process. It may access the skb as well.
Programs of this type will be attached to cgroups for network filtering and accounting.
--- include/uapi/linux/bpf.h | 9 +++++++++ kernel/bpf/verifier.c | 1 + net/core/filter.c | 6 ++++++ 3 files changed, 16 insertions(+)
include/uapi/linux/bpf.h +9 -0
@@ -96,8 +96,17 @@ enum bpf_prog_type {BPF_PROG_TYPE_TRACEPOINT ,BPF_PROG_TYPE_XDP ,BPF_PROG_TYPE_PERF_EVENT , + BPF_PROG_TYPE_CGROUP_SOCKET, }; +enum bpf_attach_type { +BPF_CGROUP_INET_INGRESS , +BPF_CGROUP_INET_EGRESS , + __MAX_BPF_ATTACH_TYPE +}; + +#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE +#define BPF_PSEUDO_MAP_FD 1/* flags for BPF_MAP_UPDATE_ELEM command */
kernel/bpf/verifier.c +1 -0
@@ -1830,6 +1830,7 @@ static bool may_access_skb(enum bpf_prog_type type)case BPF_PROG_TYPE_SOCKET_FILTER :case BPF_PROG_TYPE_SCHED_CLS :case BPF_PROG_TYPE_SCHED_ACT : +case BPF_PROG_TYPE_CGROUP_SOCKET :return true;default :return false;
net/core/filter.c +6 -0
@@ -2848,12 +2848,18 @@ static struct bpf_prog_type_list xdp_type __read_mostly = { .type = BPF_PROG_TYPE_XDP, }; +static struct bpf_prog_type_list cg_sk_type__read_mostly = { + .ops = &sk_filter_ops, + .type =BPF_PROG_TYPE_CGROUP_SOCKET , +}; +static int __init register_sk_filter_ops (void ) {bpf_register_prog_type (&sk_filter_type);bpf_register_prog_type (&sched_cls_type);bpf_register_prog_type (&sched_act_type);bpf_register_prog_type (&xdp_type); +bpf_register_prog_type (&cg_sk_type);return 0 ; }
Signature · 1 line
2.5.5
PATCH v5 2/6 cgroup: add support for eBPF programs
In reply to Daniel MackThis patch adds two sets of eBPF program pointers to struct cgroup. One for such that are directly pinned to a cgroup, and one for such that are effective for it.
To illustrate the logic behind that, assume the following example cgroup hierarchy.
A - B - C
\ D - E
If only B has a program attached, it will be effective for B, C, D and E. If D then attaches a program itself, that will be effective for both D and E, and the program in B will only affect B and C. Only one program of a given type is effective for a cgroup.
Attaching and detaching programs will be done through the bpf(2) syscall. For now, ingress and egress inet socket filtering are the only supported use-cases.
--- include/linux/bpf-cgroup.h | 71 +++++++++++++++++++ include/linux/cgroup-defs.h | 4 ++ init/Kconfig | 12 ++++ kernel/bpf/Makefile | 1 + kernel/bpf/cgroup.c | 166 ++++++++++++++++++++++++++++++++++++++++++++ kernel/cgroup.c | 18 +++++ 6 files changed, 272 insertions(+) create mode 100644 include/linux/bpf-cgroup.h create mode 100644 kernel/bpf/cgroup.c
include/linux/bpf-cgroup.h new file +71 -0
@@ -0,0 +1,71 @@ +#ifndef _BPF_CGROUP_H +#define _BPF_CGROUP_H + +#include <linux/bpf.h> +#include <linux/jump_label.h> +#include <uapi/linux/bpf.h> + +struct sock ; +struct cgroup ; +struct sk_buff ; + +#ifdef CONFIG_CGROUP_BPF + +extern struct static_key_false cgroup_bpf_enabled_key; +#define cgroup_bpf_enabled static_branch_unlikely(&cgroup_bpf_enabled_key) + +struct cgroup_bpf { +/* +* Store two sets of bpf_prog pointers, one for programs that are +* pinned directly to this cgroup, and one for those that are effective +* when this cgroup is accessed. +*/ +struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE ]; +struct bpf_prog *effective[MAX_BPF_ATTACH_TYPE ]; +}; + +void cgroup_bpf_put (struct cgroup *cgrp); +void cgroup_bpf_inherit (struct cgroup *cgrp,struct cgroup *parent); + +void __cgroup_bpf_update (struct cgroup *cgrp, +struct cgroup *parent, +struct bpf_prog *prog, +enum bpf_attach_type type); + +/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */ +void cgroup_bpf_update (struct cgroup *cgrp, +struct bpf_prog *prog, +enum bpf_attach_type type); + +int __cgroup_bpf_run_filter (struct sock *sk, +struct sk_buff *skb, +enum bpf_attach_type type); + +/* Wrapper for __cgroup_bpf_run_filter() guarded by cgroup_bpf_enabled */ +static inline int cgroup_bpf_run_filter (struct sock *sk, +struct sk_buff *skb, +enum bpf_attach_type type) +{ +if (cgroup_bpf_enabled) +return __cgroup_bpf_run_filter (sk, skb, type); + +return 0 ; +} + +#else + +struct cgroup_bpf {}; +static inline void cgroup_bpf_put (struct cgroup *cgrp) {} +static inline void cgroup_bpf_inherit (struct cgroup *cgrp, +struct cgroup *parent) {} + +static inline int cgroup_bpf_run_filter (struct sock *sk, +struct sk_buff *skb, +enum bpf_attach_type type) +{ +return 0 ; +} + +#endif /* CONFIG_CGROUP_BPF */ + +#endif /* _BPF_CGROUP_H */
include/linux/cgroup-defs.h +4 -0
@@ -16,6 +16,7 @@#include <linux/percpu-refcount.h> #include <linux/percpu-rwsem.h> #include <linux/workqueue.h> +#include <linux/bpf-cgroup.h> #ifdef CONFIG_CGROUPS
@@ -300,6 +301,9 @@ struct cgroup {/* used to schedule release agent */ struct work_struct release_agent_work; +/* used to store eBPF programs */ +struct cgroup_bpf bpf; +/* ids of the ancestors at each level including self */ int ancestor_ids[]; };
init/Kconfig +12 -0
@@ -1144,6 +1144,18 @@ config CGROUP_PERF Say N if unsure. +config CGROUP_BPF + bool "Support for eBPF programs attached to cgroups" + depends on BPF_SYSCALL && SOCK_CGROUP_DATA + help + Allow attaching eBPF programs to a cgroup using the bpf(2) + syscall command BPF_PROG_ATTACH. + + In which context these programs are accessed depends on the type + of attachment. For instance, programs that are attached using + BPF_CGROUP_INET_INGRESS will be executed on the ingress path of + inet sockets. + config CGROUP_DEBUG bool "Example controller" default n
kernel/bpf/Makefile +1 -0
@@ -5,3 +5,4 @@ obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o endif +obj-$(CONFIG_CGROUP_BPF) += cgroup.o
kernel/bpf/cgroup.c new file +166 -0
166 lines not on this page · raw message
kernel/cgroup.c +18 -0
@@ -5038,6 +5038,8 @@ static void css_release_work_fn(struct work_struct *work)if (cgrp->kn)RCU_INIT_POINTER (*(void __rcu __force **)&cgrp->kn->priv,NULL ); + +cgroup_bpf_put (cgrp); }mutex_unlock (&cgroup_mutex);
@@ -5245,6 +5247,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent)if (!cgroup_on_dfl (cgrp)) cgrp->subtree_control =cgroup_control (cgrp); +if (parent) +cgroup_bpf_inherit (cgrp, parent); +cgroup_propagate_control (cgrp);/* @cgrp doesn't have dir yet so the following will only create csses */
@@ -6417,6 +6422,19 @@ static __init int cgroup_namespaces_init(void) }subsys_initcall (cgroup_namespaces_init); +#ifdef CONFIG_CGROUP_BPF +void cgroup_bpf_update (struct cgroup *cgrp, +struct bpf_prog *prog, +enum bpf_attach_type type) +{ +struct cgroup *parent =cgroup_parent (cgrp); + +mutex_lock (&cgroup_mutex); +__cgroup_bpf_update (cgrp, parent, prog, type); +mutex_unlock (&cgroup_mutex); +} +#endif /* CONFIG_CGROUP_BPF */ +#ifdef CONFIG_CGROUP_DEBUG static struct cgroup_subsys_state *debug_css_alloc (struct cgroup_subsys_state *parent_css)
Signature · 1 line
2.5.5
PATCH v5 3/6 bpf: add BPF_PROG_ATTACH and BPF_PROG_DETACH commands
In reply to Daniel MackExtend the bpf(2) syscall by two new commands, BPF_PROG_ATTACH and BPF_PROG_DETACH which allow attaching and detaching eBPF programs to a target.
On the API level, the target could be anything that has an fd in userspace, hence the name of the field in union bpf_attr is called 'target_fd'.
When called with BPF_ATTACH_TYPE_CGROUP_INET_{E,IN}GRESS, the target is expected to be a valid file descriptor of a cgroup v2 directory which has the bpf controller enabled. These are the only use-cases implemented by this patch at this point, but more can be added.
If a program of the given type already exists in the given cgroup, the program is swapped automically, so userspace does not have to drop an existing program first before installing a new one, which would otherwise leave a gap in which no program is attached.
For more information on the propagation logic to subcgroups, please refer to the bpf cgroup controller implementation.
The API is guarded by CAP_NET_ADMIN.
--- include/uapi/linux/bpf.h | 8 +++++ kernel/bpf/syscall.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)
include/uapi/linux/bpf.h +8 -0
@@ -73,6 +73,8 @@ enum bpf_cmd {BPF_PROG_LOAD ,BPF_OBJ_PIN ,BPF_OBJ_GET , +BPF_PROG_ATTACH , + BPF_PROG_DETACH, }; enum bpf_map_type {
@@ -150,6 +152,12 @@ union bpf_attr {__aligned_u64 pathname;__u32 bpf_fd; }; + +struct {/* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ +__u32 target_fd;/* container object to attach to */ +__u32 attach_bpf_fd;/* eBPF program to attach */ +__u32 attach_type; + }; } __attribute__((aligned(8)));/* integer value in 'imm' field of BPF_CALL instruction selects which helper
kernel/bpf/syscall.c +81 -0
93 lines not on this page · raw message
Signature · 1 line
2.5.5
A message that is not in the archive (sent off-list or not yet received): 1473696735-11269-1-git-send-email-daniel-cYrQPVfZoowdnm+yROfE0A@public.gmane.org
PATCH v5 4/6 net: filter: run cgroup eBPF ingress programs
If the cgroup associated with the receiving socket has an eBPF programs installed, run them from sk_filter_trim_cap().
eBPF programs used in this context are expected to either return 1 to let the packet pass, or != 1 to drop them. The programs have access to the full skb, including the MAC headers.
Note that cgroup_bpf_run_filter() is stubbed out as static inline nop for !CONFIG_CGROUP_BPF, and is otherwise guarded by a static key if the feature is unused.
--- net/core/filter.c | 4 ++++ 1 file changed, 4 insertions(+)
net/core/filter.c +4 -0
@@ -78,6 +78,10 @@ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)if (skb_pfmemalloc (skb) && !sock_flag (sk,SOCK_MEMALLOC ))return -ENOMEM ; + err =cgroup_bpf_run_filter (sk, skb,BPF_CGROUP_INET_INGRESS ); +if (err) +return err; + err =security_sock_rcv_skb (sk, skb);if (err)return err;
Signature · 1 line
2.5.5
PATCH v5 5/6 net: core: run cgroup eBPF egress programs
In reply to a message not in the archiveIf the cgroup associated with the receiving socket has an eBPF programs installed, run them from __dev_queue_xmit().
eBPF programs used in this context are expected to either return 1 to let the packet pass, or != 1 to drop them. The programs have access to the full skb, including the MAC headers.
Note that cgroup_bpf_run_filter() is stubbed out as static inline nop for !CONFIG_CGROUP_BPF, and is otherwise guarded by a static key if the feature is unused.
--- net/core/dev.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/core/dev.c +6 -0
@@ -141,6 +141,7 @@#include <linux/netfilter_ingress.h> #include <linux/sctp.h> #include <linux/crash_dump.h> +#include <linux/bpf-cgroup.h> #include "net-sysfs.h"
@@ -3329,6 +3330,10 @@ static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)if (unlikely (skb_shinfo (skb)->tx_flags &SKBTX_SCHED_TSTAMP ))__skb_tstamp_tx (skb,NULL , skb->sk,SCM_TSTAMP_SCHED ); + rc =cgroup_bpf_run_filter (skb->sk, skb,BPF_CGROUP_INET_EGRESS ); +if (rc) + goto free_skb_list; +/* Disable soft irqs for various locks below. Also * stops preemption for RCU. */
@@ -3416,6 +3421,7 @@ recursion_alert:rcu_read_unlock_bh ();atomic_long_inc (&dev->tx_dropped); +free_skb_list:kfree_skb_list (skb);return rc; out:
Signature · 1 line
2.5.5
PATCH v5 6/6 samples: bpf: add userspace example for attaching eBPF programs to cgroups
In reply to a message not in the archiveAdd a simple userpace program to demonstrate the new API to attach eBPF programs to cgroups. This is what it does:
* Create arraymap in kernel with 4 byte keys and 8 byte values
* Load eBPF program
The eBPF program accesses the map passed in to store two pieces of information. The number of invocations of the program, which maps to the number of packets received, is stored to key 0. Key 1 is incremented on each iteration by the number of bytes stored in the skb.
* Detach any eBPF program previously attached to the cgroup
* Attach the new program to the cgroup using BPF_PROG_ATTACH
* Once a second, read map[0] and map[1] to see how many bytes and packets were seen on any socket of tasks in the given cgroup.
The program takes a cgroup path as 1st argument, and either "ingress" or "egress" as 2nd. Optionally, "drop" can be passed as 3rd argument, which will make the generated eBPF program return 0 instead of 1, so the kernel will drop the packet.
libbpf gained two new wrappers for the new syscall commands.
--- samples/bpf/Makefile | 2 + samples/bpf/libbpf.c | 21 ++++++ samples/bpf/libbpf.h | 3 + samples/bpf/test_cgrp2_attach.c | 147 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 samples/bpf/test_cgrp2_attach.c
samples/bpf/Makefile +2 -0
@@ -22,6 +22,7 @@ hostprogs-y += spintest hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin +hostprogs-y += test_cgrp2_attach hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += test_current_task_under_cgroup
@@ -49,6 +50,7 @@ spintest-objs := bpf_load.o libbpf.o spintest_user.o map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o +test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o
samples/bpf/libbpf.c +21 -0
@@ -104,6 +104,27 @@ int bpf_prog_load(enum bpf_prog_type prog_type,return syscall (__NR_bpf,BPF_PROG_LOAD , &attr,sizeof (attr)); } +int bpf_prog_attach (int prog_fd,int target_fd,enum bpf_attach_type type) +{ +union bpf_attr attr = { + .target_fd = target_fd, + .attach_bpf_fd = prog_fd, + .attach_type = type, + }; + +return syscall (__NR_bpf,BPF_PROG_ATTACH , &attr,sizeof (attr)); +} + +int bpf_prog_detach (int target_fd,enum bpf_attach_type type) +{ +union bpf_attr attr = { + .target_fd = target_fd, + .attach_type = type, + }; + +return syscall (__NR_bpf,BPF_PROG_DETACH , &attr,sizeof (attr)); +} +int bpf_obj_pin (int fd,const char *pathname) {union bpf_attr attr = {
samples/bpf/libbpf.h +3 -0
@@ -15,6 +15,9 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, int insn_len, const char *license, int kern_version); +int bpf_prog_attach (int prog_fd,int attachable_fd,enum bpf_attach_type type); +int bpf_prog_detach (int attachable_fd,enum bpf_attach_type type); +int bpf_obj_pin (int fd,const char *pathname);int bpf_obj_get (const char *pathname);
samples/bpf/test_cgrp2_attach.c new file +147 -0
147 lines not on this page · raw message
Signature · 1 line
2.5.5
Hi,
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes. For client processes this infrastructure is *racy*, you have to add new processes in runtime to the cgroup, thus there will be time some little time where no filtering policy will be applied. For quality of service, this may be an acceptable race, but this is aiming to deploy a filtering policy.
2) This aproach looks uninfrastructured to me. This provides a hook to push a bpf blob at a place in the stack that deploys a filtering policy that is not visible to others. We have interfaces that allows us to dump the filtering policy that is being applied, report events to enable cooperation between several processes with similar capabilities and so on. For the XDP thing, this ability to push blobs may be fine as long as it will not interfer with the stack so we can provide an alternative to DPDK in Linux. For tracing, that's fine too since it is innocuous. And likely for other applications is a good fit. But I don't think this is the case.
Quoted 3 lines
After chatting with Daniel Borkmann and Alexei off-list, we concluded that
__dev_queue_xmit()is the place where the egress hooks should live when eBPF programs need access to the L2 bits of the skb.
3) This egress hook is coming very late, the only reason I find to place it at __dev_queue_xmit() is that bpf naturally works with layer 2 information in place. But this new hook is placed in _everyone's output ath_ that only works for the very specific usecase I exposed above.
The main concern during the workshop was that a hook only for cgroups is too specific, but this is actually even more specific than this.
I have nothing against systemd or the needs for more programmability/flexibility in the stack, but I think this needs to fulfill some requirements to fit into the infrastructure that we have in the right way.
Hi,
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line. Also, systemd is able to control userspace processes just fine, and it not limited to 'server processes'.
Quoted 5 lines
For client processes this infrastructure is
*racy*, you have to add new processes in runtime to the cgroup, thus there will be time some little time where no filtering policy will be applied. For quality of service, this may be an acceptable race, but this is aiming to deploy a filtering policy.
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
Quoted 3 lines
2) This aproach looks uninfrastructured to me. This provides a hook to push a bpf blob at a place in the stack that deploys a filtering policy that is not visible to others.
That's just as transparent as SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?
Quoted 4 lines
We have interfaces that allows
us to dump the filtering policy that is being applied, report events to enable cooperation between several processes with similar capabilities and so on.
Well, in practice, for netfilter, there can only be one instance in the system that acts as central authoritative, otherwise you'll end up with orphaned entries or with situation where some client deletes rules behind the back of the one that originally installed it. So I really think there is nothing wrong with demanding a single, privileged controller to manage things.
Quoted 9 lines
Quoted 3 lines
After chatting with Daniel Borkmann and Alexei off-list, we concluded that
__dev_queue_xmit()is the place where the egress hooks should live when eBPF programs need access to the L2 bits of the skb.3) This egress hook is coming very late, the only reason I find to place it at
__dev_queue_xmit()is that bpf naturally works with layer 2 information in place. But this new hook is placed in _everyone's outputath_that only works for the very specific usecase I exposed above.
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches. If used somewhere in the system but not for the packet in flight, costs are slightly higher but acceptable. In fact, it's not even measurable in my tests here. How is that different from the netfilter OUTPUT hook, btw?
That said, limiting it to L3 is still an option. It's just that we need ingress and egress to be in sync, so both would be L3 then. So far, the possible advantages for future use-cases having access to L2 outweighed the concerns of putting the hook to dev_queue_xmit(), but I'm open to discussing that.
Quoted 2 lines
The main concern during the workshop was that a hook only for cgroups is too specific, but this is actually even more specific than this.
This patch set merely implements an infrastructure that can accommodate many more things as well in the future. We could, in theory, even add hooks for forwarded packets specifically, or other eBPF programs, not even for network filtering etc.
Quoted 4 lines
I have nothing against systemd or the needs for more programmability/flexibility in the stack, but I think this needs to fulfill some requirements to fit into the infrastructure that we have in the right way.
Well, as I explained already, this patch set results from endless discussions that went nowhere, about how such a thing can be achieved with netfilter.
Thanks,
Daniel
A message that is not in the archive (sent off-list or not yet received): da300784-284c-0d1f-a82e-aa0a0f8ae116-cYrQPVfZoowdnm+yROfE0A@public.gmane.org
Quoted 66 lines · Daniel Mack
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line. Also, systemd is able to control userspace processes just fine, and it not limited to 'server processes'.
Quoted 5 lines
For client processes this infrastructure is
*racy*, you have to add new processes in runtime to the cgroup, thus there will be time some little time where no filtering policy will be applied. For quality of service, this may be an acceptable race, but this is aiming to deploy a filtering policy.That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
Quoted 3 lines
2) This aproach looks uninfrastructured to me. This provides a hook to push a bpf blob at a place in the stack that deploys a filtering policy that is not visible to others.
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?Quoted 4 lines
We have interfaces that allows
us to dump the filtering policy that is being applied, report events to enable cooperation between several processes with similar capabilities and so on.Well, in practice, for netfilter, there can only be one instance in the system that acts as central authoritative, otherwise you'll end up with orphaned entries or with situation where some client deletes rules behind the back of the one that originally installed it. So I really think there is nothing wrong with demanding a single, privileged controller to manage things.
Quoted 9 lines
Quoted 3 lines
After chatting with Daniel Borkmann and Alexei off-list, we concluded that
__dev_queue_xmit()is the place where the egress hooks should live when eBPF programs need access to the L2 bits of the skb.3) This egress hook is coming very late, the only reason I find to place it at
__dev_queue_xmit()is that bpf naturally works with layer 2 information in place. But this new hook is placed in _everyone's outputath_that only works for the very specific usecase I exposed above.It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches. If used somewhere in the system but not for the packet in flight, costs are slightly higher but acceptable. In fact, it's not even measurable in my tests here. How is that different from the netfilter OUTPUT hook, btw?
Quoted 5 lines
That said, limiting it to L3 is still an option. It's just that we need ingress and egress to be in sync, so both would be L3 then. So far, the possible advantages for future use-cases having access to L2 outweighed the concerns of putting the hook to
dev_queue_xmit(), but I'm open to discussing that.
While I fully disagree with Pablo's point 1) and 2), in the last set I raised a similar concern as in point 3) wrt __dev_queue_xmit(). The set as-is would indeed need the L2 info, since a filter could do a load via LLVM built-ins such as asm("llvm.bpf.load.byte") et al, with BPF_LL_OFF, where we're forced to do a load relative to skb_mac_header(). As stated by Daniel already, it would be nice to see the full frame, so it comes down to a trade-off, but the option of L3 onwards also exists and BPF can work just fine with it, too. This just means it's placed in the local output path and the verifier would need to disallow these built-ins during bpf(2) load time. They are a rather cumbersome legacy anyway, so bpf_skb_load_bytes() helper can be used instead, which is also easier to use.
Quoted 21 lines
Quoted 2 lines
The main concern during the workshop was that a hook only for cgroups is too specific, but this is actually even more specific than this.
This patch set merely implements an infrastructure that can accommodate many more things as well in the future. We could, in theory, even add hooks for forwarded packets specifically, or other eBPF programs, not even for network filtering etc.
Quoted 4 lines
I have nothing against systemd or the needs for more programmability/flexibility in the stack, but I think this needs to fulfill some requirements to fit into the infrastructure that we have in the right way.
Well, as I explained already, this patch set results from endless discussions that went nowhere, about how such a thing can be achieved with netfilter.
Thanks,
Daniel
Quoted 9 lines · Daniel Mack
Hi,
Quoted 6 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
Quoted 8 lines
Quoted 4 lines
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Then you have to explain me how can anyone else than systemd use this infrastructure?
Quoted 2 lines
Also, systemd is able to control userspace processes just fine, and it not limited to 'server processes'.
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
Quoted 8 lines
Quoted 5 lines
For client processes this infrastructure is
*racy*, you have to add new processes in runtime to the cgroup, thus there will be time some little time where no filtering policy will be applied. For quality of service, this may be an acceptable race, but this is aiming to deploy a filtering policy.That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup net_cls and friends are broken for filtering for things that you have no control to fork+exec.
To use this infrastructure from a non-launcher process, you'll have to rely on the proc connection to subscribe to new process events, then echo that pid to the cgroup, and that interface is asynchronous so *adding new processes to the cgroup is subject to races*.
Quoted 6 lines
Quoted 3 lines
2) This aproach looks uninfrastructured to me. This provides a hook to push a bpf blob at a place in the stack that deploys a filtering policy that is not visible to others.
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?
SO_ATTACH_FILTER is called from the process itself, so this is a local filtering policy that you apply to your own process.
In this case, this filtering policy is *global*, other processes with similar capabilities can get just a bpf blob at best...
[...]
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
After chatting with Daniel Borkmann and Alexei off-list, we concluded that
__dev_queue_xmit()is the place where the egress hooks should live when eBPF programs need access to the L2 bits of the skb.3) This egress hook is coming very late, the only reason I find to place it at
__dev_queue_xmit()is that bpf naturally works with layer 2 information in place. But this new hook is placed in _everyone's outputath_that only works for the very specific usecase I exposed above.
Quoted 6 lines
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches.
*You're proposing a socket filtering facility that hooks layer 2 output path*!
[...]
Quoted 8 lines
Quoted 4 lines
I have nothing against systemd or the needs for more programmability/flexibility in the stack, but I think this needs to fulfill some requirements to fit into the infrastructure that we have in the right way.
Well, as I explained already, this patch set results from endless discussions that went nowhere, about how such a thing can be achieved with netfilter.
That is only a rough ~30 lines kernel patchset to support this in netfilter and only one extra input hook, with potential access to conntrack and better integration with other existing subsystems.
Quoted 15 lines · Pablo Neira Ayuso
Quoted 14 lines · Daniel Mack
Hi,
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Quoted 6 lines
Quoted 3 lines
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Then you have to explain me how can anyone else than systemd use this infrastructure?
Sounds like systemd and bpf phobia combined :)
Jokes aside. I'm puzzled why systemd is even being mentioned here. Here we use tupperware (our internal container management system) that is heavily using cgroups and has nothing to do with systemd. we're working as part of open container initiative, so hopefully soon all container management systems will benefit from what we're building. cgroups and bpf are crucial part of this process.
Quoted 5 lines
Quoted 2 lines
Also, systemd is able to control userspace processes just fine, and it not limited to 'server processes'.
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
No experience in systemd, so cannot comment about it, but that statement is not true for our stuff.
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
For client processes this infrastructure is
*racy*, you have to add new processes in runtime to the cgroup, thus there will be time some little time where no filtering policy will be applied. For quality of service, this may be an acceptable race, but this is aiming to deploy a filtering policy.
Quoted 7 lines
Quoted 2 lines
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup
net_clsand friends are broken for filtering for things that you have no control to fork+exec.
not true
Quoted 4 lines
To use this infrastructure from a non-launcher process, you'll have to rely on the proc connection to subscribe to new process events, then echo that pid to the cgroup, and that interface is asynchronous so *adding new processes to the cgroup is subject to races*.
in general not true either. have you worked with cgroups or just speculating?
Quoted 2 lines
*You're proposing a socket filtering facility that hooks layer 2 output path*!
flashback. Not too long ago you were beating drums about netfilter ingress hook operating at layer 2... sounds like nobody used it and that was a bad call? Should we remove that netfilter hook then?
Our use case is different from Daniel's.
For us this cgroup+bpf is _not_ for filterting and _not_ for security. We run a ton of tasks in cgroups that launch all sorts of
things on their own. We need to monitor what they do from networking point of view. Therefore bpf programs need to monitor the traffic in particular part of cgroup hierarchy. Not globally and no pass/drop decisions. The monitoring itself is complicated. Like we need to group and aggregate within bpf program based on certain bits of ipv6 address and so on. bpf is only programmable engine that can do this job. nft is simply not flexible enough to do that.
I'd really love to have an alternative to bpf for such tasks,
but you seem to spend all the energy arguing against bpf whereas nft still has a lot to be desired.
[Sorry for the repost, gmail decided to start sending HTML crap along
overnight for some reason]
Quoted 7 lines · Alexei Starovoitov
Quoted 2 lines · Pablo Neira Ayuso
Then you have to explain me how can anyone else than systemd use this infrastructure?
Jokes aside. I'm puzzled why systemd is even being mentioned here. Here we use tupperware (our internal container management system) that is heavily using cgroups and has nothing to do with systemd.
Just confirming that we are planning to use this decoupled from systemd as well. I fail to see how this is at all systemd specific.
Quoted 5 lines
For us this cgroup+bpf is _not_ for filterting and _not_ for security. We run a ton of tasks in cgroups that launch all sorts of
things on their own. We need to monitor what they do from networking point of view. Therefore bpf programs need to monitor the traffic in particular part of cgroup hierarchy. Not globally and no pass/drop decisions.
+10, although filtering/drop is a valid use case, the really strong use case is definitely introspection at networking level. Statistics, monitoring, verification of application correctness, etc.
I don't see why this is at all an either or discussion. If nft wants cgroups integration similar to this effort, I see no reason why that should stop this effort.
A message that is not in the archive (sent off-list or not yet received): 20160914044217.GA44742-+o4/[email protected]
On Tue, Sep 13, 2016 at 09:42:19PM -0700, Alexei Starovoitov wrote:
[...]
Quoted 1 line
For us this cgroup+bpf is _not_ for filterting and _not_ for security.
If your goal is monitoring, then convert these hooks not to allow to issue a verdict on the packet, so this becomes inoquous in the same fashion as the tracing infrastructure.
[...]
Quoted 3 lines
I'd really love to have an alternative to bpf for such tasks, but you seem to spend all the energy arguing against bpf whereas nft still has a lot to be desired.
Please Alexei, stop that FUD. Anyone that has spent just one day using the bpf tooling and infrastructure knows you have problems to resolve...
Quoted 7 lines · Pablo Neira Ayuso
On Tue, Sep 13, 2016 at 09:42:19PM -0700, Alexei Starovoitov wrote:
[...]Quoted 1 line
For us this cgroup+bpf is _not_ for filterting and _not_ for security.
If your goal is monitoring, then convert these hooks not to allow to issue a verdict on the packet, so this becomes inoquous in the same fashion as the tracing infrastructure.
Why? How is this at all offensive? We have three parties voicing interest in this work for both monitoring and security. At least two specific use cases have been described. It builds on top of existing infrastructure and nicely complements other ongoing work. Why not both?
Daniel Borkmann
maintainerQuoted 8 lines · Pablo Neira Ayuso
On Tue, Sep 13, 2016 at 09:42:19PM -0700, Alexei Starovoitov wrote:
[...]Quoted 1 line
For us this cgroup+bpf is _not_ for filterting and _not_ for security.
If your goal is monitoring, then convert these hooks not to allow to issue a verdict on the packet, so this becomes inoquous in the same fashion as the tracing infrastructure.
Quoted 8 lines
[...]
Quoted 3 lines
I'd really love to have an alternative to bpf for such tasks, but you seem to spend all the energy arguing against bpf whereas nft still has a lot to be desired.
Please Alexei, stop that FUD. Anyone that has spent just one day using the bpf tooling and infrastructure knows you have problems to resolve...
Not quite sure on the spreading of FUD, but sounds like we should all get back to technical things to resolve. ;)
Hi Pablo,
Quoted 13 lines · Pablo Neira Ayuso
Quoted 12 lines · Daniel Mack
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Quoted 6 lines
Quoted 3 lines
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Then you have to explain me how can anyone else than systemd use this infrastructure?
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
Quoted 2 lines
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
Yes, that's right. But as I said, this rule applies to many other kernel concepts, so I don't see any real issue.
Quoted 7 lines
Quoted 2 lines
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup
net_clsand friends are broken for filtering for things that you have no control to fork+exec.
Probably, but that's only solvable with rules that store the full cgroup path then, and do a string comparison (!) for each packet flying by.
Quoted 5 lines
Quoted 2 lines
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?
SO_ATTACH_FILTERis called from the process itself, so this is a local filtering policy that you apply to your own process.
Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do SO_ATTACH_FILTER
+ SO_LOCK_FILTER in the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.
Quoted 9 lines
Quoted 6 lines
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches.
*You're proposing a socket filtering facility that hooks layer 2 output path*!
As I said, I'm open to discussing that. In order to make it work for L3, the LL_OFF issues need to be solved, as Daniel explained. Daniel, Alexei, any idea how much work that would be?
Quoted 3 lines
That is only a rough ~30 lines kernel patchset to support this in netfilter and only one extra input hook, with potential access to conntrack and better integration with other existing subsystems.
Care to share the patches for that? I'd really like to have a look.
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Thanks,
Daniel
A message that is not in the archive (sent off-list or not yet received): 6de6809a-13f5-4000-5639-c760dde30223-cYrQPVfZoowdnm+yROfE0A@public.gmane.org
Quoted 61 lines · Daniel Mack
Quoted 13 lines · Pablo Neira Ayuso
Quoted 12 lines · Daniel Mack
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Quoted 6 lines
Quoted 3 lines
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Then you have to explain me how can anyone else than systemd use this infrastructure?
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
Quoted 2 lines
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
Yes, that's right. But as I said, this rule applies to many other kernel concepts, so I don't see any real issue.
Quoted 7 lines
Quoted 2 lines
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup
net_clsand friends are broken for filtering for things that you have no control to fork+exec.Probably, but that's only solvable with rules that store the full cgroup path then, and do a string comparison (!) for each packet flying by.
Quoted 5 lines
Quoted 2 lines
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?
SO_ATTACH_FILTERis called from the process itself, so this is a local filtering policy that you apply to your own process.Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do
SO_ATTACH_FILTER
+SO_LOCK_FILTERin the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.Quoted 7 lines
Quoted 6 lines
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches.
Quoted 6 lines
Quoted 2 lines
*You're proposing a socket filtering facility that hooks layer 2 output path*!
As I said, I'm open to discussing that. In order to make it work for L3, the
LL_OFFissues need to be solved, as Daniel explained. Daniel, Alexei, any idea how much work that would be?
Not much. You simply need to declare your own struct bpf_verifier_ops with a get_func_proto() handler that handles BPF_FUNC_skb_load_bytes, and verifier in do_check() loop would need to handle that these ld_abs/ ld_ind are rejected for BPF_PROG_TYPE_CGROUP_SOCKET.
Quoted 13 lines
Quoted 3 lines
That is only a rough ~30 lines kernel patchset to support this in netfilter and only one extra input hook, with potential access to conntrack and better integration with other existing subsystems.
Care to share the patches for that? I'd really like to have a look.
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Thanks,
Daniel
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 8 lines · Daniel Borkmann
Quoted 3 lines
As I said, I'm open to discussing that. In order to make it work for L3, the
LL_OFFissues need to be solved, as Daniel explained. Daniel, Alexei, any idea how much work that would be?Not much. You simply need to declare your own struct
bpf_verifier_opswith aget_func_proto()handler that handlesBPF_FUNC_skb_load_bytes, and verifier indo_check()loop would need to handle that these ld_abs/ld_indare rejected forBPF_PROG_TYPE_CGROUP_SOCKET.
yep. that part is solvable.
I'm still torn between l2 and l3.
On one side it sux to lose l2 information. yet we don't have a use case to look into l2 for our container monitoring, so the only thing lack of l2 will do is confuse byte accounting, since instead of skb->len, we'd need to do skb->len + ETH_HLEN...
but I guess vlan handling messes it up as well.
On the other side doing it at socket level we can drop these checks:
+ if (!sk || !sk_fullsock(sk))
+ return 0;
+
+ if (sk->sk_family != AF_INET &&
+ sk->sk_family != AF_INET6)
+ return 0;
which will make it even faster when it's on.
So I don't mind either l2 and l3. I guess if l3 approach will prove to be limiting, we can add l2 later?
Quoted 20 lines · Daniel Mack
Hi Pablo,
Quoted 17 lines · Pablo Neira Ayuso
Quoted 15 lines · Daniel Mack
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Quoted 7 lines
Quoted 2 lines
Then you have to explain me how can anyone else than systemd use this infrastructure?
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
So, at least in my work, we have Mesos, but on nearly every machine that Mesos runs, people also have systemd. Now, there's recently become a bit of a battle of ownership of things like cgroups on these machines. We can usually solve it by nesting under systemd cgroups, and thus so far we've avoided making too many systemd-specific concessions.
The reason this works (mostly), is because everything we touch has a sense of nesting, where we can apply policy at a place lower in the hierarchy, and yet systemd's monitoring and policy still stays in place.
Now, with this patch, we don't have that, but I think we can reasonably add some flag like "no override" when applying policies, or alternatively something like "no new privileges", to prevent children from applying policies that override top-level policy. I realize there is a speed concern as well, but I think for people who want nested policy, we're willing to make the tradeoff. The cost of traversing a few extra pointers still outweighs the overhead of network namespaces, iptables, etc.. for many of us.
What do you think Daniel?
Quoted 6 lines
Quoted 2 lines
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
Yes, that's right. But as I said, this rule applies to many other kernel concepts, so I don't see any real issue.
Also, cgroups have become such a big part of how applications are managed that many of us have solved this problem.
Quoted 14 lines
Quoted 7 lines
Quoted 2 lines
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup
net_clsand friends are broken for filtering for things that you have no control to fork+exec.Probably, but that's only solvable with rules that store the full cgroup path then, and do a string comparison (!) for each packet flying by.
Quoted 3 lines
Quoted 2 lines
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?
Quoted 9 lines
Quoted 2 lines
SO_ATTACH_FILTERis called from the process itself, so this is a local filtering policy that you apply to your own process.Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do
SO_ATTACH_FILTER
+SO_LOCK_FILTERin the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.
It would be nice to be able to see whether or not a filter is attached to a cgroup, but given this is going through syscalls, at least introspection is possible as opposed to something like netlink.
Quoted 14 lines
Quoted 9 lines
Quoted 6 lines
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches.
*You're proposing a socket filtering facility that hooks layer 2 output path*!
As I said, I'm open to discussing that. In order to make it work for L3, the
LL_OFFissues need to be solved, as Daniel explained. Daniel, Alexei, any idea how much work that would be?
Quoted 8 lines
Quoted 3 lines
That is only a rough ~30 lines kernel patchset to support this in netfilter and only one extra input hook, with potential access to conntrack and better integration with other existing subsystems.
Care to share the patches for that? I'd really like to have a look.
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Right now, for containers, we have netfilter and network namespaces. There's a lot of performance overhead that comes with this. Not only that, but iptables doesn't really have a simple way of usage by automated infrastructure. We (firewalld, systemd, dockerd, mesos) end up fighting with one another for ownership over firewall rules.
Although, I have problems with this approach, I think that it's a good baseline where we can have top level owned by systemd, docker underneath that, and Mesos underneath that. We can add additional hooks for things like Checmate and Landlock, and with a little more work, we can do compositition, solving all of our problems.
Quoted 5 lines
Thanks,
Daniel
A message that is not in the archive (sent off-list or not yet received): 20160916195728.GA14736-I4sfFR6g6EicJoAdRrHjTrzMkBWIpU9tytq7g7fCXyjEk0E+pv7Png@public.gmane.org
Quoted 112 lines · Sargun Dhillon
Quoted 20 lines · Daniel Mack
Hi Pablo,
Quoted 17 lines · Pablo Neira Ayuso
Quoted 15 lines · Daniel Mack
Quoted 10 lines · Pablo Neira Ayuso
Quoted 4 lines · Daniel Mack
This is v5 of the patch set to allow eBPF programs for network filtering and accounting to be attached to cgroups, so that they apply to all sockets of all tasks placed in that cgroup. The logic also allows to be extendeded for other cgroup based eBPF logic.
1) This infrastructure can only be useful to systemd, or any similar orchestration daemon. Look, you can only apply filtering policies to processes that are launched by systemd, so this only works for server processes.
Sorry, but both statements aren't true. The eBPF policies apply to every process that is placed in a cgroup, and my example program in 6/6 shows how that can be done from the command line.
Quoted 7 lines
Quoted 2 lines
Then you have to explain me how can anyone else than systemd use this infrastructure?
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
So, at least in my work, we have Mesos, but on nearly every machine that Mesos runs, people also have systemd. Now, there's recently become a bit of a battle of ownership of things like cgroups on these machines. We can usually solve it by nesting under systemd cgroups, and thus so far we've avoided making too many systemd-specific concessions.
The reason this works (mostly), is because everything we touch has a sense of nesting, where we can apply policy at a place lower in the hierarchy, and yet systemd's monitoring and policy still stays in place.
Now, with this patch, we don't have that, but I think we can reasonably add some flag like "no override" when applying policies, or alternatively something like "no new privileges", to prevent children from applying policies that override top-level policy. I realize there is a speed concern as well, but I think for people who want nested policy, we're willing to make the tradeoff. The cost of traversing a few extra pointers still outweighs the overhead of network namespaces, iptables, etc.. for many of us.
What do you think Daniel?
Quoted 6 lines
Quoted 2 lines
My main point is that those processes *need* to be launched by the orchestrator, which is was refering as 'server processes'.
Yes, that's right. But as I said, this rule applies to many other kernel concepts, so I don't see any real issue.
Also, cgroups have become such a big part of how applications are managed that many of us have solved this problem.
Quoted 14 lines
Quoted 7 lines
Quoted 2 lines
That's a limitation that applies to many more control mechanisms in the kernel, and it's something that can easily be solved with fork+exec.
As long as you have control to launch the processes yes, but this will not work in other scenarios. Just like cgroup
net_clsand friends are broken for filtering for things that you have no control to fork+exec.Probably, but that's only solvable with rules that store the full cgroup path then, and do a string comparison (!) for each packet flying by.
Quoted 3 lines
Quoted 2 lines
That's just as transparent as
SO_ATTACH_FILTER. What kind of introspection mechanism do you have in mind?Quoted 9 lines
Quoted 2 lines
SO_ATTACH_FILTERis called from the process itself, so this is a local filtering policy that you apply to your own process.Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do
SO_ATTACH_FILTER
+SO_LOCK_FILTERin the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.It would be nice to be able to see whether or not a filter is attached to a cgroup, but given this is going through syscalls, at least introspection is possible as opposed to something like netlink.
Quoted 14 lines
Quoted 9 lines
Quoted 6 lines
It's about filtering outgoing network packets of applications, and providing them with L2 information for filtering purposes. I don't think that's a very specific use-case.
When the feature is not used at all, the added costs on the output path are close to zero, due to the use of static branches.
*You're proposing a socket filtering facility that hooks layer 2 output path*!
As I said, I'm open to discussing that. In order to make it work for L3, the
LL_OFFissues need to be solved, as Daniel explained. Daniel, Alexei, any idea how much work that would be?Quoted 8 lines
Quoted 3 lines
That is only a rough ~30 lines kernel patchset to support this in netfilter and only one extra input hook, with potential access to conntrack and better integration with other existing subsystems.
Care to share the patches for that? I'd really like to have a look.
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Right now, for containers, we have netfilter and network namespaces. There's a lot of performance overhead that comes with this. Not only that, but iptables doesn't really have a simple way of usage by automated infrastructure. We (firewalld, systemd, dockerd, mesos) end up fighting with one another for ownership over firewall rules.
Quoted 12 lines
Although, I have problems with this approach, I think that it's a good baseline where we can have top level owned by systemd, docker underneath that, and Mesos underneath that. We can add additional hooks for things like Checmate and Landlock, and with a little more work, we can do compositition, solving all of our problems.
Quoted 5 lines
Thanks,
Daniel
Another thing --
It probably makes sense to make the warning in cgroup.c highlight the fact that it disables these filters as well. Perhaps, it makes sense to make it so you can't disable it (boot flag, say?). Alternatively, maybe it makes sense to introduce some exclusivity? So, that when you load a filter, it disables net_cls, and when you load net_cls, it throws warnings.
Hi,
Quoted 12 lines · Sargun Dhillon
Quoted 4 lines · Daniel Mack
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
So, at least in my work, we have Mesos, but on nearly every machine that Mesos runs, people also have systemd. Now, there's recently become a bit of a battle of ownership of things like cgroups on these machines. We can usually solve it by nesting under systemd cgroups, and thus so far we've avoided making too many systemd-specific concessions.
Quoted 8 lines
The reason this works (mostly), is because everything we touch has a sense of nesting, where we can apply policy at a place lower in the hierarchy, and yet systemd's monitoring and policy still stays in place.
Now, with this patch, we don't have that, but I think we can reasonably add some flag like "no override" when applying policies, or alternatively something like "no new privileges", to prevent children from applying policies that override top-level policy.
Yes, but the API is already guarded by CAP_NET_ADMIN. Take that capability away from your children, and they can't tamper with the policy. Does that work for you?
Quoted 4 lines
I realize there is a speed concern as well, but I think for people who want nested policy, we're willing to make the tradeoff. The cost of traversing a few extra pointers still outweighs the overhead of network namespaces, iptables, etc.. for many of us.
Not sure. Have you tried it?
Quoted 1 line
What do you think Daniel?
I think we should look at an implementation once we really need it, and then revisit the performance impact. In any case, this can be changed under the hood, without touching the userspace API (except for adding flags if we need them).
Quoted 9 lines
Quoted 5 lines
Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do
SO_ATTACH_FILTER
+SO_LOCK_FILTERin the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.It would be nice to be able to see whether or not a filter is attached to a cgroup, but given this is going through syscalls, at least introspection is possible as opposed to something like netlink.
Sure, there are many ways. I implemented the bpf cgroup logic using an own cgroup controller once, which made it possible to read out the status. But as we agreed on attaching programs through the bpf(2) system call, I moved back to the implementation that directly stores the pointers in the cgroup.
First enabling the controller through the fs-backed cgroup interface, then come back through the bpf(2) syscall and then go back to the fs interface to read out status values is a bit weird.
Quoted 5 lines
Quoted 2 lines
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Right now, for containers, we have netfilter and network namespaces. There's a lot of performance overhead that comes with this.
Out of curiosity: Could you express that in numbers? And how exactly are you testing?
Quoted 4 lines
Not only
that, but iptables doesn't really have a simple way of usage by automated infrastructure. We (firewalld, systemd, dockerd, mesos) end up fighting with one another for ownership over firewall rules.
Yes, that's a common problem.
Quoted 6 lines
Although, I have problems with this approach, I think that it's a good baseline where we can have top level owned by systemd, docker underneath that, and Mesos underneath that. We can add additional hooks for things like Checmate and Landlock, and with a little more work, we can do compositition, solving all of our problems.
It is supposed to be just a baseline, yes.
Thanks for your feedback,
Daniel
Quoted 19 lines · Daniel Mack
Hi,
Quoted 16 lines · Sargun Dhillon
Quoted 4 lines · Daniel Mack
I have no idea what makes you think this is limited to systemd. As I said, I provided an example for userspace that works from the command line. The same limitation apply as for all other users of cgroups.
So, at least in my work, we have Mesos, but on nearly every machine that Mesos runs, people also have systemd. Now, there's recently become a bit of a battle of ownership of things like cgroups on these machines. We can usually solve it by nesting under systemd cgroups, and thus so far we've avoided making too many systemd-specific concessions.
The reason this works (mostly), is because everything we touch has a sense of nesting, where we can apply policy at a place lower in the hierarchy, and yet systemd's monitoring and policy still stays in place.
Quoted 9 lines
Quoted 4 lines
Now, with this patch, we don't have that, but I think we can reasonably add some flag like "no override" when applying policies, or alternatively something like "no new privileges", to prevent children from applying policies that override top-level policy.
Yes, but the API is already guarded by
CAP_NET_ADMIN. Take that capability away from your children, and they can't tamper with the policy. Does that work for you?
No. This can be addressed in a follow-on patch, but the use-case is that I have a container orchestrator (Docker, or Mesos), and systemd. The sysadmin controls systemd, and Docker is controlled by devs. Typically, the system owner wants some system level statistics, and filtering, and then we want to do per-container filtering.
We really want to be able to do nesting with userspace tools that are oblivious, and we want to delegate a level of the cgroup hierarchy to the tool that created it. I do not see Docker integrating with systemd any time soon, and that's really the only other alternative.
Quoted 7 lines
Quoted 4 lines
I realize there is a speed concern as well, but I think for people who want nested policy, we're willing to make the tradeoff. The cost of traversing a few extra pointers still outweighs the overhead of network namespaces, iptables, etc.. for many of us.
Not sure. Have you tried it?
Tried nested policies? Yes. I tried nested policy execution with syscalls, and I tested with bind and connect. The performance overhead was pretty minimal, but latency increased by 100 microseconds+ once the number of BPF hooks increased beyond 30. The BPF programs were trivial, and essentially did a map lookup, and returned 0.
I don't think that it's just raw cycles / execution time, but I didn't spend enough time digging into it to determine the performance hit. I'm waiting for your patchset to land, and then I plan to work off of it.
Quoted 7 lines
Quoted 1 line
What do you think Daniel?
I think we should look at an implementation once we really need it, and then revisit the performance impact. In any case, this can be changed under the hood, without touching the userspace API (except for adding flags if we need them).
+1
Quoted 16 lines
Quoted 9 lines
Quoted 5 lines
Not necessarily. You can as well do it the inetd way, and pass the socket to a process that is launched on demand, but do
SO_ATTACH_FILTER
+SO_LOCK_FILTERin the middle. What happens with payload on the socket is not transparent to the launched binary at all. The proposed cgroup eBPF solution implements a very similar behavior in that regard.It would be nice to be able to see whether or not a filter is attached to a cgroup, but given this is going through syscalls, at least introspection is possible as opposed to something like netlink.
Sure, there are many ways. I implemented the bpf cgroup logic using an own cgroup controller once, which made it possible to read out the status. But as we agreed on attaching programs through the bpf(2) system call, I moved back to the implementation that directly stores the pointers in the cgroup.
Quoted 4 lines
First enabling the controller through the fs-backed cgroup interface, then come back through the bpf(2) syscall and then go back to the fs interface to read out status values is a bit weird.
Hrm, that makes sense. with the BPF syscall, would there be a way to get file descriptor of the currently attached BPF program?
Quoted 9 lines
Quoted 5 lines
Quoted 2 lines
And FWIW, I agree with Thomas - there is nothing wrong with having multiple options to use for such use-cases.
Right now, for containers, we have netfilter and network namespaces. There's a lot of performance overhead that comes with this.
Out of curiosity: Could you express that in numbers? And how exactly are you testing?
Sure. Our workload that we use as a baseline is Redis with redis-benchmark. We reconnect after every connection, and we're running "isolation" between two containers on the same machine to try to rule out any physical infrastructure overhead.
So, we ran two tests with network namespaces. The first one was putting Redis into its own network namespace, and using tc to do some basic shaping:
Client--Veth---Host Namespace---Veth---Redis
The second was:
Client--Veth--Host Namespace+Iptables filtering--Veth--Redis.
The second test required us to use conntrack, as we wanted stateful filtering.
Ops/sec:
Original: 4275
Situation 1: 3823
Situation 2: 1489
Latency (milliseconds):
Original: 0.69
Situation 1: 0.82
Situation 2: 2.11
This was on a (KVM) machine with 16GB of RAM, and 8 Cores where the machine was supposed to be dedicated to me. Given that it's not bare metal, take these numbers with a grain of salt.
Quoted 20 lines
Quoted 4 lines
Not only
that, but iptables doesn't really have a simple way of usage by automated infrastructure. We (firewalld, systemd, dockerd, mesos) end up fighting with one another for ownership over firewall rules.Yes, that's a common problem.
Quoted 6 lines
Although, I have problems with this approach, I think that it's a good baseline where we can have top level owned by systemd, docker underneath that, and Mesos underneath that. We can add additional hooks for things like Checmate and Landlock, and with a little more work, we can do compositition, solving all of our problems.
It is supposed to be just a baseline, yes.
Thanks for your feedback,
Daniel
A message that is not in the archive (sent off-list or not yet received): 20160919215311.GA9723-I4sfFR6g6EicJoAdRrHjTrzMkBWIpU9tytq7g7fCXyjEk0E+pv7Png@public.gmane.org
Quoted 18 lines · Sargun Dhillon
Quoted 10 lines · Daniel Mack
Quoted 4 lines · Sargun Dhillon
Now, with this patch, we don't have that, but I think we can reasonably add some flag like "no override" when applying policies, or alternatively something like "no new privileges", to prevent children from applying policies that override top-level policy.
Yes, but the API is already guarded by
CAP_NET_ADMIN. Take that capability away from your children, and they can't tamper with the policy. Does that work for you?No. This can be addressed in a follow-on patch, but the use-case is that I have a container orchestrator (Docker, or Mesos), and systemd. The sysadmin controls systemd, and Docker is controlled by devs. Typically, the system owner wants some system level statistics, and filtering, and then we want to do per-container filtering.
Quoted 4 lines
We really want to be able to do nesting with userspace tools that are oblivious, and we want to delegate a level of the cgroup hierarchy to the tool that created it. I do not see Docker integrating with systemd any time soon, and that's really the only other alternative.
Then we'd need to find out whether you want to block other users from installing (thus overriding) an existing eBPF program, or if you want to allow that but execute them all. Both is possible.
[...]
Quoted 10 lines
Quoted 10 lines
Quoted 3 lines
It would be nice to be able to see whether or not a filter is attached to a cgroup, but given this is going through syscalls, at least introspection is possible as opposed to something like netlink.
Sure, there are many ways. I implemented the bpf cgroup logic using an own cgroup controller once, which made it possible to read out the status. But as we agreed on attaching programs through the bpf(2) system call, I moved back to the implementation that directly stores the pointers in the cgroup.
Quoted 6 lines
Quoted 4 lines
First enabling the controller through the fs-backed cgroup interface, then come back through the bpf(2) syscall and then go back to the fs interface to read out status values is a bit weird.
Hrm, that makes sense. with the BPF syscall, would there be a way to get file descriptor of the currently attached BPF program?
A file descriptor is local to a task, so we would need to install a new fd and return its number. But I'm not sure what we'd gain from that.
Thanks,
Daniel
❦ 12 septembre 2016 18:12 CEST, Daniel Mack <[email protected]> :
Quoted 3 lines
* The sample program learned to support both ingress and egress, and can now optionally make the eBPF program drop packets by making it return 0.
Ability to lock the eBPF program to avoid modification from a later program or in a subcgroup would be pretty interesting from a security perspective.
Signature · 2 lines
Use recursive procedures for recursively-defined data structures.
- The Elements of Programming Style (Kernighan & Plauger)A message that is not in the archive (sent off-list or not yet received): m3y42tlldz.fsf-PiWSfznZvZU/[email protected]
Quoted 9 lines · Vincent Bernat
❦ 12 septembre 2016 18:12 CEST, Daniel Mack <[email protected]> :
Quoted 3 lines
* The sample program learned to support both ingress and egress, and can now optionally make the eBPF program drop packets by making it return 0.
Ability to lock the eBPF program to avoid modification from a later program or in a subcgroup would be pretty interesting from a security perspective.
For now, you can achieve that by dropping CAP_NET_ADMIN after installing a program between fork and exec. I think that should suffice for a first version. Flags to further limit that could be be added later.
Thanks,
Daniel