static int query_flow_dissector(struct bpf_attach_info *attach_info) { __u32 attach_flags; __u32 prog_ids[1]; __u32 prog_cnt; int err; int fd; fd = open("/proc/self/ns/net", O_RDONLY); if (fd < 0) { p_err("can't open /proc/self/ns/net: %d", strerror(errno)); return -1; } prog_cnt = ARRAY_SIZE(prog_ids); err = bpf_prog_query(fd, BPF_FLOW_DISSECTOR, 0, &attach_flags, prog_ids, &prog_cnt); close(fd); if (err) { if (errno == EINVAL) { /* Older kernel's don't support querying * flow dissector programs. */ errno = 0; return 0; } p_err("can't query prog: %s", strerror(errno)); return -1; } if (prog_cnt == 1) attach_info->flow_dissector_id = prog_ids[0]; return 0; }
static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type) { __u32 prog_cnt = 0; int ret; ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt); if (ret) return -1; return prog_cnt; }
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type, int level) { __u32 prog_ids[1024] = {0}; char *attach_flags_str; __u32 prog_cnt, iter; __u32 attach_flags; char buf[32]; int ret; prog_cnt = ARRAY_SIZE(prog_ids); ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids, &prog_cnt); if (ret) return ret; if (prog_cnt == 0) return 0; switch (attach_flags) { case BPF_F_ALLOW_MULTI: attach_flags_str = "multi"; break; case BPF_F_ALLOW_OVERRIDE: attach_flags_str = "override"; break; case 0: attach_flags_str = ""; break; default: snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags); attach_flags_str = buf; } for (iter = 0; iter < prog_cnt; iter++) show_bpf_prog(prog_ids[iter], attach_type_strings[type], attach_flags_str, level); return 0; }
static int test_multiprog(void) { __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; int drop_prog, allow_prog[6] = {}, rc = 0; unsigned long long value; int i = 0; for (i = 0; i < 6; i++) { allow_prog[i] = prog_load_cnt(1, 1 << i); if (!allow_prog[i]) goto err; } drop_prog = prog_load_cnt(0, 1); if (!drop_prog) goto err; if (setup_cgroup_environment()) goto err; cg1 = create_and_get_cgroup("/cg1"); if (cg1 < 0) goto err; cg2 = create_and_get_cgroup("/cg1/cg2"); if (cg2 < 0) goto err; cg3 = create_and_get_cgroup("/cg1/cg2/cg3"); if (cg3 < 0) goto err; cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4"); if (cg4 < 0) goto err; cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5"); if (cg5 < 0) goto err; if (join_cgroup("/cg1/cg2/cg3/cg4/cg5")) goto err; if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI)) { log_err("Attaching prog to cg1"); goto err; } if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI)) { log_err("Unexpected success attaching the same prog to cg1"); goto err; } if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI)) { log_err("Attaching prog2 to cg1"); goto err; } if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_OVERRIDE)) { log_err("Attaching prog to cg2"); goto err; } if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI)) { log_err("Attaching prog to cg3"); goto err; } if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_OVERRIDE)) { log_err("Attaching prog to cg4"); goto err; } if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) { log_err("Attaching prog to cg5"); goto err; } assert(system(PING_CMD) == 0); assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); assert(value == 1 + 2 + 8 + 32); /* query the number of effective progs in cg5 */ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, NULL, NULL, &prog_cnt) == 0); assert(prog_cnt == 4); /* retrieve prog_ids of effective progs in cg5 */ assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, &attach_flags, prog_ids, &prog_cnt) == 0); assert(prog_cnt == 4); assert(attach_flags == 0); saved_prog_id = prog_ids[0]; /* check enospc handling */ prog_ids[0] = 0; prog_cnt = 2; assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, &attach_flags, prog_ids, &prog_cnt) == -1 && errno == ENOSPC); assert(prog_cnt == 4); /* check that prog_ids are returned even when buffer is too small */ assert(prog_ids[0] == saved_prog_id); /* retrieve prog_id of single attached prog in cg5 */ prog_ids[0] = 0; assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, NULL, prog_ids, &prog_cnt) == 0); assert(prog_cnt == 1); assert(prog_ids[0] == saved_prog_id); /* detach bottom program and ping again */ if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) { log_err("Detaching prog from cg5"); goto err; } value = 0; assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); assert(system(PING_CMD) == 0); assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); assert(value == 1 + 2 + 8 + 16); /* detach 3rd from bottom program and ping again */ errno = 0; if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) { log_err("Unexpected success on detach from cg3"); goto err; } if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) { log_err("Detaching from cg3"); goto err; } value = 0; assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); assert(system(PING_CMD) == 0); assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); assert(value == 1 + 2 + 16); /* detach 2nd from bottom program and ping again */ if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) { log_err("Detaching prog from cg4"); goto err; } value = 0; assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); assert(system(PING_CMD) == 0); assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); assert(value == 1 + 2 + 4); prog_cnt = 4; assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, &attach_flags, prog_ids, &prog_cnt) == 0); assert(prog_cnt == 3); assert(attach_flags == 0); assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, NULL, prog_ids, &prog_cnt) == 0); assert(prog_cnt == 0); goto out; err: rc = 1; out: for (i = 0; i < 6; i++) if (allow_prog[i] > 0) close(allow_prog[i]); close(cg1); close(cg2); close(cg3); close(cg4); close(cg5); cleanup_cgroup_environment(); if (!rc) printf("### multi:PASS\n"); else printf("### multi:FAIL\n"); return rc; }
int main(int argc, char **argv) { struct percpu_net_cnt *percpu_netcnt; struct bpf_cgroup_storage_key key; int map_fd, percpu_map_fd; int error = EXIT_FAILURE; struct net_cnt netcnt; struct bpf_object *obj; int prog_fd, cgroup_fd; unsigned long packets; unsigned long bytes; int cpu, nproc; __u32 prog_cnt; nproc = get_nprocs_conf(); percpu_netcnt = malloc(sizeof(*percpu_netcnt) * nproc); if (!percpu_netcnt) { printf("Not enough memory for per-cpu area (%d cpus)\n", nproc); goto err; } if (bpf_prog_load(BPF_PROG, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd)) { printf("Failed to load bpf program\n"); goto out; } if (setup_cgroup_environment()) { printf("Failed to load bpf program\n"); goto err; } /* Create a cgroup, get fd, and join it */ cgroup_fd = create_and_get_cgroup(TEST_CGROUP); if (!cgroup_fd) { printf("Failed to create test cgroup\n"); goto err; } if (join_cgroup(TEST_CGROUP)) { printf("Failed to join cgroup\n"); goto err; } /* Attach bpf program */ if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_INET_EGRESS, 0)) { printf("Failed to attach bpf program"); goto err; } if (system("which ping6 &>/dev/null") == 0) assert(!system("ping6 localhost -c 10000 -f -q > /dev/null")); else assert(!system("ping -6 localhost -c 10000 -f -q > /dev/null")); if (bpf_prog_query(cgroup_fd, BPF_CGROUP_INET_EGRESS, 0, NULL, NULL, &prog_cnt)) { printf("Failed to query attached programs"); goto err; } map_fd = bpf_find_map(__func__, obj, "netcnt"); if (map_fd < 0) { printf("Failed to find bpf map with net counters"); goto err; } percpu_map_fd = bpf_find_map(__func__, obj, "percpu_netcnt"); if (percpu_map_fd < 0) { printf("Failed to find bpf map with percpu net counters"); goto err; } if (bpf_map_get_next_key(map_fd, NULL, &key)) { printf("Failed to get key in cgroup storage\n"); goto err; } if (bpf_map_lookup_elem(map_fd, &key, &netcnt)) { printf("Failed to lookup cgroup storage\n"); goto err; } if (bpf_map_lookup_elem(percpu_map_fd, &key, &percpu_netcnt[0])) { printf("Failed to lookup percpu cgroup storage\n"); goto err; } /* Some packets can be still in per-cpu cache, but not more than * MAX_PERCPU_PACKETS. */ packets = netcnt.packets; bytes = netcnt.bytes; for (cpu = 0; cpu < nproc; cpu++) { if (percpu_netcnt[cpu].packets > MAX_PERCPU_PACKETS) { printf("Unexpected percpu value: %llu\n", percpu_netcnt[cpu].packets); goto err; } packets += percpu_netcnt[cpu].packets; bytes += percpu_netcnt[cpu].bytes; } /* No packets should be lost */ if (packets != 10000) { printf("Unexpected packet count: %lu\n", packets); goto err; } /* Let's check that bytes counter matches the number of packets * multiplied by the size of ipv6 ICMP packet. */ if (bytes != packets * 104) { printf("Unexpected bytes count: %lu\n", bytes); goto err; } error = 0; printf("test_netcnt:PASS\n"); err: cleanup_cgroup_environment(); free(percpu_netcnt); out: return error; }