Skip to content

Commit 7723628

Browse files
rdnaborkmann
authored andcommitted
bpf: Introduce bpf_skb_ancestor_cgroup_id helper
== Problem description == It's useful to be able to identify cgroup associated with skb in TC so that a policy can be applied to this skb, and existing bpf_skb_cgroup_id helper can help with this. Though in real life cgroup hierarchy and hierarchy to apply a policy to don't map 1:1. It's often the case that there is a container and corresponding cgroup, but there are many more sub-cgroups inside container, e.g. because it's delegated to containerized application to control resources for its subsystems, or to separate application inside container from infra that belongs to containerization system (e.g. sshd). At the same time it may be useful to apply a policy to container as a whole. If multiple containers like this are run on a host (what is often the case) and many of them have sub-cgroups, it may not be possible to apply per-container policy in TC with existing helpers such as bpf_skb_under_cgroup or bpf_skb_cgroup_id: * bpf_skb_cgroup_id will return id of immediate cgroup associated with skb, i.e. if it's a sub-cgroup inside container, it can't be used to identify container's cgroup; * bpf_skb_under_cgroup can work only with one cgroup and doesn't scale, i.e. if there are N containers on a host and a policy has to be applied to M of them (0 <= M <= N), it'd require M calls to bpf_skb_under_cgroup, and, if M changes, it'd require to rebuild & load new BPF program. == Solution == The patch introduces new helper bpf_skb_ancestor_cgroup_id that can be used to get id of cgroup v2 that is an ancestor of cgroup associated with skb at specified level of cgroup hierarchy. That way admin can place all containers on one level of cgroup hierarchy (what is a good practice in general and already used in many configurations) and identify specific cgroup on this level no matter what sub-cgroup skb is associated with. E.g. if there is a cgroup hierarchy: root/ root/container1/ root/container1/app11/ root/container1/app11/sub-app-a/ root/container1/app12/ root/container2/ root/container2/app21/ root/container2/app22/ root/container2/app22/sub-app-b/ , then having skb associated with root/container1/app11/sub-app-a/ it's possible to get ancestor at level 1, what is container1 and apply policy for this container, or apply another policy if it's container2. Policies can be kept e.g. in a hash map where key is a container cgroup id and value is an action. Levels where container cgroups are created are usually known in advance whether cgroup hierarchy inside container may be hard to predict especially in case when its creation is delegated to containerized application. == Implementation details == The helper gets ancestor by walking parents up to specified level. Another option would be to get different kind of "id" from cgroup->ancestor_ids[level] and use it with idr_find() to get struct cgroup for ancestor. But that would require radix lookup what doesn't seem to be better (at least it's not obviously better). Format of return value of the new helper is same as that of bpf_skb_cgroup_id. Signed-off-by: Andrey Ignatov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent e8d2bec commit 7723628

File tree

3 files changed

+78
-1
lines changed

3 files changed

+78
-1
lines changed

Diff for: include/linux/cgroup.h

+30
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,36 @@ static inline bool cgroup_is_descendant(struct cgroup *cgrp,
553553
return cgrp->ancestor_ids[ancestor->level] == ancestor->id;
554554
}
555555

556+
/**
557+
* cgroup_ancestor - find ancestor of cgroup
558+
* @cgrp: cgroup to find ancestor of
559+
* @ancestor_level: level of ancestor to find starting from root
560+
*
561+
* Find ancestor of cgroup at specified level starting from root if it exists
562+
* and return pointer to it. Return NULL if @cgrp doesn't have ancestor at
563+
* @ancestor_level.
564+
*
565+
* This function is safe to call as long as @cgrp is accessible.
566+
*/
567+
static inline struct cgroup *cgroup_ancestor(struct cgroup *cgrp,
568+
int ancestor_level)
569+
{
570+
struct cgroup *ptr;
571+
572+
if (cgrp->level < ancestor_level)
573+
return NULL;
574+
575+
for (ptr = cgrp;
576+
ptr && ptr->level > ancestor_level;
577+
ptr = cgroup_parent(ptr))
578+
;
579+
580+
if (ptr && ptr->level == ancestor_level)
581+
return ptr;
582+
583+
return NULL;
584+
}
585+
556586
/**
557587
* task_under_cgroup_hierarchy - test task's membership of cgroup ancestry
558588
* @task: the task to be tested

Diff for: include/uapi/linux/bpf.h

+20-1
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,24 @@ union bpf_attr {
20932093
* Return
20942094
* The id is returned or 0 in case the id could not be retrieved.
20952095
*
2096+
* u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level)
2097+
* Description
2098+
* Return id of cgroup v2 that is ancestor of cgroup associated
2099+
* with the *skb* at the *ancestor_level*. The root cgroup is at
2100+
* *ancestor_level* zero and each step down the hierarchy
2101+
* increments the level. If *ancestor_level* == level of cgroup
2102+
* associated with *skb*, then return value will be same as that
2103+
* of **bpf_skb_cgroup_id**\ ().
2104+
*
2105+
* The helper is useful to implement policies based on cgroups
2106+
* that are upper in hierarchy than immediate cgroup associated
2107+
* with *skb*.
2108+
*
2109+
* The format of returned id and helper limitations are same as in
2110+
* **bpf_skb_cgroup_id**\ ().
2111+
* Return
2112+
* The id is returned or 0 in case the id could not be retrieved.
2113+
*
20962114
* u64 bpf_get_current_cgroup_id(void)
20972115
* Return
20982116
* A 64-bit integer containing the current cgroup id based
@@ -2207,7 +2225,8 @@ union bpf_attr {
22072225
FN(skb_cgroup_id), \
22082226
FN(get_current_cgroup_id), \
22092227
FN(get_local_storage), \
2210-
FN(sk_select_reuseport),
2228+
FN(sk_select_reuseport), \
2229+
FN(skb_ancestor_cgroup_id),
22112230

22122231
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
22132232
* function eBPF program intends to call

Diff for: net/core/filter.c

+28
Original file line numberDiff line numberDiff line change
@@ -3778,6 +3778,32 @@ static const struct bpf_func_proto bpf_skb_cgroup_id_proto = {
37783778
.ret_type = RET_INTEGER,
37793779
.arg1_type = ARG_PTR_TO_CTX,
37803780
};
3781+
3782+
BPF_CALL_2(bpf_skb_ancestor_cgroup_id, const struct sk_buff *, skb, int,
3783+
ancestor_level)
3784+
{
3785+
struct sock *sk = skb_to_full_sk(skb);
3786+
struct cgroup *ancestor;
3787+
struct cgroup *cgrp;
3788+
3789+
if (!sk || !sk_fullsock(sk))
3790+
return 0;
3791+
3792+
cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
3793+
ancestor = cgroup_ancestor(cgrp, ancestor_level);
3794+
if (!ancestor)
3795+
return 0;
3796+
3797+
return ancestor->kn->id.id;
3798+
}
3799+
3800+
static const struct bpf_func_proto bpf_skb_ancestor_cgroup_id_proto = {
3801+
.func = bpf_skb_ancestor_cgroup_id,
3802+
.gpl_only = false,
3803+
.ret_type = RET_INTEGER,
3804+
.arg1_type = ARG_PTR_TO_CTX,
3805+
.arg2_type = ARG_ANYTHING,
3806+
};
37813807
#endif
37823808

37833809
static unsigned long bpf_xdp_copy(void *dst_buff, const void *src_buff,
@@ -4966,6 +4992,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
49664992
#ifdef CONFIG_SOCK_CGROUP_DATA
49674993
case BPF_FUNC_skb_cgroup_id:
49684994
return &bpf_skb_cgroup_id_proto;
4995+
case BPF_FUNC_skb_ancestor_cgroup_id:
4996+
return &bpf_skb_ancestor_cgroup_id_proto;
49694997
#endif
49704998
default:
49714999
return bpf_base_func_proto(func_id);

0 commit comments

Comments
 (0)