pkt-loop-filter: Add get-bond-active utility

Add a small utility that uses a kprobe to extract the currently active
slave ifindex from a bond interface. This value is normally only exported
to userspace for bond types where it can be explicitly set, but the bond
driver has an internal notion of an active interface regardless of the bond
type. We can extract this value with a kprobe by attaching to a function in
the bond driver and triggering an operation that causes this function to be
called.

Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
This commit is contained in:
Toke Høiland-Jørgensen
2022-06-28 19:57:47 +02:00
parent f746871b1a
commit 032d9cde85
8 changed files with 238 additions and 23 deletions

View File

@@ -1 +1,2 @@
pkt-loop-filter
get-bond-active

View File

@@ -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

View File

@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/in.h>
#include <bpf/libbpf.h>
#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;
}

View File

@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef BOND_ACTIVE_H
#define BOND_ACTIVE_H
#include <bpf/libbpf.h>
int get_bond_active_ifindex(int bond_ifindex);
int get_netns_cookie(__u64 *cookie);
#endif

View File

@@ -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

View File

@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <net/if.h>
#include <linux/if_arp.h>
#include <bpf/libbpf.h>
#include "bond-active.h"
int main(int argc, char *argv[])
{
int ifindex, active_ifindex;
const char *ifname;
if (argc < 2) {
fprintf(stderr, "Usage: %s <ifname>\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;
}

View File

@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <linux/bpf.h>
#include <asm/ptrace.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#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";

View File

@@ -9,28 +9,10 @@
#include <linux/pkt_cls.h>
#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