diff --git a/pkt-loop-filter/.gitignore b/pkt-loop-filter/.gitignore index 8b19c30..e90dc8a 100644 --- a/pkt-loop-filter/.gitignore +++ b/pkt-loop-filter/.gitignore @@ -1 +1,2 @@ pkt-loop-filter +get-bond-active diff --git a/pkt-loop-filter/Makefile b/pkt-loop-filter/Makefile index 20d24f6..7b37a88 100644 --- a/pkt-loop-filter/Makefile +++ b/pkt-loop-filter/Makefile @@ -1,9 +1,10 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -USER_TARGETS += pkt-loop-filter -BPF_TARGETS += pkt-loop-filter.kern -BPF_SKEL_OBJ := pkt-loop-filter.kern.o -EXTRA_DEPS := pkt-loop-filter.h +USER_TARGETS += pkt-loop-filter get-bond-active +BPF_TARGETS += pkt-loop-filter.kern get-bond-active.kern +BPF_SKEL_OBJ := pkt-loop-filter.kern.o get-bond-active.kern.o +EXTRA_DEPS := pkt-loop-filter.h bond-active.h bpf-defs.h +USER_TARGETS_OBJS := bond-active.o LIB_DIR = ../lib diff --git a/pkt-loop-filter/bond-active.c b/pkt-loop-filter/bond-active.c new file mode 100644 index 0000000..72bb0f0 --- /dev/null +++ b/pkt-loop-filter/bond-active.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "get-bond-active.kern.skel.h" + +int get_netns_cookie(__u64 *cookie) +{ + unsigned int sockopt_sz = sizeof(__u64); + __u64 value; + int fd, err; + + fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) + return fd; + + err = getsockopt(fd, SOL_SOCKET, SO_NETNS_COOKIE, &value, &sockopt_sz); + if (err) { + err = -errno; + fprintf(stderr, "Couldn't getsockopt(): %s\n", strerror(-err)); + goto out; + } + + *cookie = value; +out: + close(fd); + return err; +} + +int get_bond_active_ifindex(int bond_ifindex) +{ + char ifname[IF_NAMESIZE], fname[100], buf[50]; + struct get_bond_active_kern *skel = NULL; + struct bpf_link *trace_link = NULL; + int ret = 0, fd = -1; + __u64 netns_cookie; + size_t len; + + if (!if_indextoname(bond_ifindex, ifname)) + return -errno; + + /* We write the current value back to this file to trigger the kprobe + * that allows us to read the active ifindex + */ + snprintf(fname, sizeof(fname), "/sys/class/net/%s/bonding/primary_reselect", ifname); + fname[sizeof(fname)-1] = '\0'; + + ret = get_netns_cookie(&netns_cookie); + if (ret) + return ret; + + skel = get_bond_active_kern__open(); + ret = libbpf_get_error(skel); + if (ret) { + fprintf(stderr, "Couldn't open BPF skeleton: %s\n", strerror(errno)); + return ret; + } + + skel->bss->bond_ifindex = bond_ifindex; + skel->rodata->netns_cookie = netns_cookie; + + ret = get_bond_active_kern__load(skel); + if (ret) { + fprintf(stderr, "Failed to load object\n"); + goto out; + } + + trace_link = bpf_program__attach(skel->progs.handle_select_slave); + if (!trace_link) { + fprintf(stderr, "Couldn't attach tracing prog: %s\n", strerror(errno)); + ret = -EFAULT; + goto out; + } + + fd = open(fname, O_RDWR); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "Couldn't open %s: %s\n", fname, strerror(-ret)); + goto out; + } + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + ret = -errno; + fprintf(stderr, "Couldn't read from %s: %s\n", fname, strerror(-ret)); + } + + ret = write(fd, buf, len); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "Couldn't write to %s: %s\n", fname, strerror(-ret)); + goto out; + } + + ret = skel->bss->active_slave_ifindex; + +out: + bpf_link__destroy(trace_link); + get_bond_active_kern__destroy(skel); + if (fd >= 0) + close(fd); + + return ret; +} diff --git a/pkt-loop-filter/bond-active.h b/pkt-loop-filter/bond-active.h new file mode 100644 index 0000000..d4b34f2 --- /dev/null +++ b/pkt-loop-filter/bond-active.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef BOND_ACTIVE_H +#define BOND_ACTIVE_H + +#include + +int get_bond_active_ifindex(int bond_ifindex); +int get_netns_cookie(__u64 *cookie); + +#endif diff --git a/pkt-loop-filter/bpf-defs.h b/pkt-loop-filter/bpf-defs.h new file mode 100644 index 0000000..dd1de0f --- /dev/null +++ b/pkt-loop-filter/bpf-defs.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef BPF_DEFS_H +#define BPF_DEFS_H + +/* cookie for init ns; hoping this is stable */ +#define INIT_NS 1 + +/* partial structs for reading bond parameters. + * + * These are deliberately *not* declared with the preserve_access_index, as + * we'll read them with plan BPF_PROBE_READ() below; this is to make sure they + * work even without module BTF, and the fields we need are only the first ones + * of each struct which have been stable for a long time. + */ +struct slave { + struct net_device *dev; /* first - useful for panic debug */ +}; + +struct bonding { + struct net_device *dev; /* first - useful for panic debug */ + struct slave *curr_active_slave; +}; + +/* local partial kernel struct definitions with just the members we need */ +struct net { + __u64 net_cookie; +} __attribute__((preserve_access_index)); + +struct net_device { + int ifindex; + struct { + struct net *net; + } nd_net; +} __attribute__((preserve_access_index)); + +struct netdev_notifier_info { + struct net_device *dev; +} __attribute__((preserve_access_index)); + +#endif diff --git a/pkt-loop-filter/get-bond-active.c b/pkt-loop-filter/get-bond-active.c new file mode 100644 index 0000000..f07fd68 --- /dev/null +++ b/pkt-loop-filter/get-bond-active.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "bond-active.h" + +int main(int argc, char *argv[]) +{ + int ifindex, active_ifindex; + const char *ifname; + + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + ifname = argv[1]; + ifindex = if_nametoindex(ifname); + if (!ifindex) { + fprintf(stderr, "Couldn't find interface '%s'\n", ifname); + return 1; + } + + active_ifindex = get_bond_active_ifindex(ifindex); + if (active_ifindex < 0) + return active_ifindex; + + printf("Bond with ifindex %d has active ifindex: %d\n", ifindex, active_ifindex); + return 0; +} diff --git a/pkt-loop-filter/get-bond-active.kern.c b/pkt-loop-filter/get-bond-active.kern.c new file mode 100644 index 0000000..bca35b9 --- /dev/null +++ b/pkt-loop-filter/get-bond-active.kern.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include +#include +#include +#include +#include + +#include "bpf-defs.h" + +int bond_ifindex = 0; +int active_slave_ifindex = 0; +volatile const int netns_cookie = INIT_NS; + +SEC("kprobe/bond_select_active_slave") +int BPF_KPROBE(handle_select_slave, struct bonding *bond) +{ + struct net_device *dev = BPF_PROBE_READ(bond, dev); + int ifindex = BPF_CORE_READ(dev, ifindex); + __u64 cookie = BPF_CORE_READ(dev, nd_net.net, net_cookie); + + if (cookie == netns_cookie && ifindex == bond_ifindex) { + struct net_device *active_dev = BPF_PROBE_READ(bond, curr_active_slave, dev); + active_slave_ifindex = BPF_CORE_READ(active_dev, ifindex); + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/pkt-loop-filter/pkt-loop-filter.kern.c b/pkt-loop-filter/pkt-loop-filter.kern.c index 4e64a3a..cecfbe5 100644 --- a/pkt-loop-filter/pkt-loop-filter.kern.c +++ b/pkt-loop-filter/pkt-loop-filter.kern.c @@ -9,28 +9,10 @@ #include #include "pkt-loop-filter.h" - -/* local partial kernel struct definitions with just the members we need */ -struct net { - __u64 net_cookie; -} __attribute__((preserve_access_index)); - -struct net_device { - int ifindex; - struct { - struct net *net; - } nd_net; -} __attribute__((preserve_access_index)); - -struct netdev_notifier_info { - struct net_device *dev; -} __attribute__((preserve_access_index)); +#include "bpf-defs.h" #define NETDEV_GOING_DOWN 10 -/* cookie for init ns; hoping this is stable */ -#define INIT_NS 1 - #define PKT_TYPE_UNICAST 1 #define PKT_TYPE_MULTICAST 2 #define PKT_TYPE_IGMP 3