Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add test for CGROUP_STORAGE map on multiple attaches

This test creates a parent cgroup, and a child of that cgroup.
It attaches a cgroup_skb/egress program that simply counts packets,
to a global variable (ARRAY map), and to a CGROUP_STORAGE map.
The program is first attached to the parent cgroup only, then to
parent and child.

The test cases sends a message within the child cgroup, and because
the program is inherited across parent / child cgroups, it will
trigger the egress program for both the parent and child, if they
exist. The program, when looking up a CGROUP_STORAGE map, uses the
cgroup and attach type of the attachment parameters; therefore,
both attaches uses different cgroup storages.

We assert that all packet counts returns what we expects.

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/5a20206afa4606144691c7caa0d1b997cd60dec0.1595565795.git.zhuyifei@google.com

authored by

YiFei Zhu and committed by
Alexei Starovoitov
d4a89c1e 90065c06

+191
+161
tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <test_progs.h> 8 + #include <cgroup_helpers.h> 9 + #include <network_helpers.h> 10 + 11 + #include "cg_storage_multi_egress_only.skel.h" 12 + 13 + #define PARENT_CGROUP "/cgroup_storage" 14 + #define CHILD_CGROUP "/cgroup_storage/child" 15 + 16 + static int duration; 17 + 18 + static bool assert_storage(struct bpf_map *map, const char *cgroup_path, 19 + __u32 expected) 20 + { 21 + struct bpf_cgroup_storage_key key = {0}; 22 + __u32 value; 23 + int map_fd; 24 + 25 + map_fd = bpf_map__fd(map); 26 + 27 + key.cgroup_inode_id = get_cgroup_id(cgroup_path); 28 + key.attach_type = BPF_CGROUP_INET_EGRESS; 29 + if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0, 30 + "map-lookup", "errno %d", errno)) 31 + return true; 32 + if (CHECK(value != expected, 33 + "assert-storage", "got %u expected %u", value, expected)) 34 + return true; 35 + 36 + return false; 37 + } 38 + 39 + static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path) 40 + { 41 + struct bpf_cgroup_storage_key key = {0}; 42 + __u32 value; 43 + int map_fd; 44 + 45 + map_fd = bpf_map__fd(map); 46 + 47 + key.cgroup_inode_id = get_cgroup_id(cgroup_path); 48 + key.attach_type = BPF_CGROUP_INET_EGRESS; 49 + if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) == 0, 50 + "map-lookup", "succeeded, expected ENOENT")) 51 + return true; 52 + if (CHECK(errno != ENOENT, 53 + "map-lookup", "errno %d, expected ENOENT", errno)) 54 + return true; 55 + 56 + return false; 57 + } 58 + 59 + static bool connect_send(const char *cgroup_path) 60 + { 61 + bool res = true; 62 + int server_fd = -1, client_fd = -1; 63 + 64 + if (join_cgroup(cgroup_path)) 65 + goto out_clean; 66 + 67 + server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0); 68 + if (server_fd < 0) 69 + goto out_clean; 70 + 71 + client_fd = connect_to_fd(server_fd, 0); 72 + if (client_fd < 0) 73 + goto out_clean; 74 + 75 + if (send(client_fd, "message", strlen("message"), 0) < 0) 76 + goto out_clean; 77 + 78 + res = false; 79 + 80 + out_clean: 81 + close(client_fd); 82 + close(server_fd); 83 + return res; 84 + } 85 + 86 + static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd) 87 + { 88 + struct cg_storage_multi_egress_only *obj; 89 + struct bpf_link *parent_link = NULL, *child_link = NULL; 90 + bool err; 91 + 92 + obj = cg_storage_multi_egress_only__open_and_load(); 93 + if (CHECK(!obj, "skel-load", "errno %d", errno)) 94 + return; 95 + 96 + /* Attach to parent cgroup, trigger packet from child. 97 + * Assert that there is only one run and in that run the storage is 98 + * parent cgroup's storage. 99 + * Also assert that child cgroup's storage does not exist 100 + */ 101 + parent_link = bpf_program__attach_cgroup(obj->progs.egress, 102 + parent_cgroup_fd); 103 + if (CHECK(IS_ERR(parent_link), "parent-cg-attach", 104 + "err %ld", PTR_ERR(parent_link))) 105 + goto close_bpf_object; 106 + err = connect_send(CHILD_CGROUP); 107 + if (CHECK(err, "first-connect-send", "errno %d", errno)) 108 + goto close_bpf_object; 109 + if (CHECK(obj->bss->invocations != 1, 110 + "first-invoke", "invocations=%d", obj->bss->invocations)) 111 + goto close_bpf_object; 112 + if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 1)) 113 + goto close_bpf_object; 114 + if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP)) 115 + goto close_bpf_object; 116 + 117 + /* Attach to parent and child cgroup, trigger packet from child. 118 + * Assert that there are two additional runs, one that run with parent 119 + * cgroup's storage and one with child cgroup's storage. 120 + */ 121 + child_link = bpf_program__attach_cgroup(obj->progs.egress, 122 + child_cgroup_fd); 123 + if (CHECK(IS_ERR(child_link), "child-cg-attach", 124 + "err %ld", PTR_ERR(child_link))) 125 + goto close_bpf_object; 126 + err = connect_send(CHILD_CGROUP); 127 + if (CHECK(err, "second-connect-send", "errno %d", errno)) 128 + goto close_bpf_object; 129 + if (CHECK(obj->bss->invocations != 3, 130 + "second-invoke", "invocations=%d", obj->bss->invocations)) 131 + goto close_bpf_object; 132 + if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 2)) 133 + goto close_bpf_object; 134 + if (assert_storage(obj->maps.cgroup_storage, CHILD_CGROUP, 1)) 135 + goto close_bpf_object; 136 + 137 + close_bpf_object: 138 + bpf_link__destroy(parent_link); 139 + bpf_link__destroy(child_link); 140 + 141 + cg_storage_multi_egress_only__destroy(obj); 142 + } 143 + 144 + void test_cg_storage_multi(void) 145 + { 146 + int parent_cgroup_fd = -1, child_cgroup_fd = -1; 147 + 148 + parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP); 149 + if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno)) 150 + goto close_cgroup_fd; 151 + child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP); 152 + if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno)) 153 + goto close_cgroup_fd; 154 + 155 + if (test__start_subtest("egress_only")) 156 + test_egress_only(parent_cgroup_fd, child_cgroup_fd); 157 + 158 + close_cgroup_fd: 159 + close(child_cgroup_fd); 160 + close(parent_cgroup_fd); 161 + }
+30
tools/testing/selftests/bpf/progs/cg_storage_multi_egress_only.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <errno.h> 8 + #include <linux/bpf.h> 9 + #include <linux/ip.h> 10 + #include <linux/udp.h> 11 + #include <bpf/bpf_helpers.h> 12 + 13 + struct { 14 + __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE); 15 + __type(key, struct bpf_cgroup_storage_key); 16 + __type(value, __u32); 17 + } cgroup_storage SEC(".maps"); 18 + 19 + __u32 invocations = 0; 20 + 21 + SEC("cgroup_skb/egress") 22 + int egress(struct __sk_buff *skb) 23 + { 24 + __u32 *ptr_cg_storage = bpf_get_local_storage(&cgroup_storage, 0); 25 + 26 + __sync_fetch_and_add(ptr_cg_storage, 1); 27 + __sync_fetch_and_add(&invocations, 1); 28 + 29 + return 1; 30 + }