2022-05-20 14:53:14 +02:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-06-28 15:24:30 +02:00
|
|
|
#include <stdbool.h>
|
2022-05-20 14:53:14 +02:00
|
|
|
#include <linux/bpf.h>
|
2022-06-14 16:52:04 +02:00
|
|
|
#include <asm/ptrace.h>
|
2022-05-20 14:53:14 +02:00
|
|
|
#include <bpf/bpf_helpers.h>
|
2022-06-14 16:52:04 +02:00
|
|
|
#include <bpf/bpf_tracing.h>
|
|
|
|
#include <bpf/bpf_core_read.h>
|
2022-05-20 14:53:14 +02:00
|
|
|
#include <xdp/parsing_helpers.h>
|
|
|
|
#include <linux/pkt_cls.h>
|
|
|
|
|
|
|
|
#include "pkt-loop-filter.h"
|
2022-06-28 19:57:47 +02:00
|
|
|
#include "bpf-defs.h"
|
2022-06-14 16:52:04 +02:00
|
|
|
|
|
|
|
#define NETDEV_GOING_DOWN 10
|
|
|
|
|
2022-06-14 17:36:54 +02:00
|
|
|
#define PKT_TYPE_UNICAST 1
|
|
|
|
#define PKT_TYPE_MULTICAST 2
|
2022-06-28 17:17:55 +02:00
|
|
|
#define PKT_TYPE_IGMP 3
|
2022-06-14 17:36:54 +02:00
|
|
|
|
2022-05-20 14:53:14 +02:00
|
|
|
/* We use an LRU map to avoid having to do cleanup: We just rely on the LRU
|
|
|
|
* mechanism to evict old entries as the map fills up.
|
|
|
|
*/
|
|
|
|
struct {
|
|
|
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
|
|
|
__type(key, struct pkt_loop_key);
|
|
|
|
__type(value, struct pkt_loop_data);
|
2022-06-14 17:41:41 +02:00
|
|
|
__uint(max_entries, 1); /* set from userspace before load */
|
2022-05-20 14:53:14 +02:00
|
|
|
} iface_state SEC(".maps");
|
|
|
|
|
2022-06-29 01:08:16 +02:00
|
|
|
int bond_ifindex = 0;
|
|
|
|
int active_ifindex = 0;
|
2022-06-28 16:18:09 +02:00
|
|
|
/* This being const means that the verifier will do dead code elimination to
|
|
|
|
* remove any code that depends on it being true entirely, incurring no runtime
|
|
|
|
* overhead if debug mode is disabled.
|
|
|
|
**/
|
|
|
|
volatile const int debug_output = 0;
|
2022-06-29 01:08:16 +02:00
|
|
|
volatile const int netns_cookie = INIT_NS;
|
2022-06-14 17:36:54 +02:00
|
|
|
|
2022-06-28 15:24:30 +02:00
|
|
|
/* copy of kernel's version - if the LSB of the first octet is 1 then it is
|
|
|
|
* a multicast address
|
|
|
|
*/
|
|
|
|
static bool is_multicast_ether_addr(const __u8 *addr)
|
|
|
|
{
|
|
|
|
return 0x01 & addr[0];
|
|
|
|
}
|
|
|
|
|
2022-05-20 14:53:14 +02:00
|
|
|
static int parse_pkt(struct __sk_buff *skb, struct pkt_loop_key *key)
|
|
|
|
{
|
|
|
|
void *data_end = (void *)(unsigned long long)skb->data_end;
|
|
|
|
void *data = (void *)(unsigned long long)skb->data;
|
|
|
|
struct hdr_cursor nh = { .pos = data };
|
2022-06-28 17:17:55 +02:00
|
|
|
int eth_type, ip_type;
|
2022-05-20 14:53:14 +02:00
|
|
|
struct ethhdr *eth;
|
|
|
|
|
|
|
|
/* Parse Ethernet and IP/IPv6 headers */
|
|
|
|
eth_type = parse_ethhdr(&nh, data_end, ð);
|
|
|
|
if (eth_type < 0)
|
|
|
|
return eth_type;
|
|
|
|
|
|
|
|
__builtin_memcpy(key->src_mac, eth->h_source, ETH_ALEN);
|
|
|
|
key->src_vlan = skb->vlan_tci;
|
|
|
|
|
2022-06-28 17:17:55 +02:00
|
|
|
if (is_multicast_ether_addr(eth->h_dest))
|
|
|
|
return PKT_TYPE_MULTICAST;
|
|
|
|
|
|
|
|
if (eth_type == bpf_htons(ETH_P_IP)) {
|
|
|
|
struct iphdr *iph;
|
|
|
|
|
|
|
|
ip_type = parse_iphdr(&nh, data_end, &iph);
|
|
|
|
if (ip_type == IPPROTO_IGMP)
|
|
|
|
return PKT_TYPE_IGMP;
|
|
|
|
|
|
|
|
} else if (eth_type == bpf_htons(ETH_P_IPV6)) {
|
|
|
|
struct icmp6hdr *icmp6;
|
|
|
|
struct ipv6hdr *ip6h;
|
|
|
|
int icmp6_type;
|
|
|
|
|
|
|
|
ip_type = parse_ip6hdr(&nh, data_end, &ip6h);
|
|
|
|
if (ip_type != IPPROTO_ICMPV6)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
icmp6_type = parse_icmp6hdr(&nh, data_end, &icmp6);
|
|
|
|
if (icmp6_type == ICMPV6_MGM_QUERY || icmp6_type == ICMPV6_MGM_REPORT ||
|
|
|
|
icmp6_type == ICMPV6_MGM_REDUCTION || icmp6_type == ICMPV6_MLD2_REPORT ||
|
|
|
|
icmp6_type == ICMPV6_MRDISC_ADV)
|
|
|
|
return PKT_TYPE_IGMP;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return PKT_TYPE_UNICAST;
|
2022-05-20 14:53:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SEC("tc")
|
|
|
|
int record_egress_pkt(struct __sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct pkt_loop_data value = { .ifindex = skb->ifindex }, *v;
|
|
|
|
struct pkt_loop_key key;
|
2022-06-14 17:36:54 +02:00
|
|
|
int pkt_type;
|
2022-05-20 14:53:14 +02:00
|
|
|
|
2022-06-14 17:36:54 +02:00
|
|
|
pkt_type = parse_pkt(skb, &key);
|
|
|
|
if (pkt_type < 0)
|
2022-05-20 14:53:14 +02:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
v = bpf_map_lookup_elem(&iface_state, &key);
|
|
|
|
if (!v) {
|
|
|
|
bpf_map_update_elem(&iface_state, &key, &value, BPF_NOEXIST);
|
|
|
|
v = bpf_map_lookup_elem(&iface_state, &key);
|
|
|
|
if (!v)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
v->expiry_time = bpf_ktime_get_coarse_ns() + STATE_LIFETIME;
|
|
|
|
v->ifindex = skb->ifindex;
|
|
|
|
out:
|
|
|
|
return TC_ACT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
SEC("tc")
|
|
|
|
int filter_ingress_pkt(struct __sk_buff *skb)
|
|
|
|
{
|
2022-06-29 01:08:16 +02:00
|
|
|
int pkt_type, ifindex = active_ifindex;
|
2022-05-20 14:53:14 +02:00
|
|
|
struct pkt_loop_data *value;
|
|
|
|
struct pkt_loop_key key;
|
|
|
|
|
2022-06-14 17:36:54 +02:00
|
|
|
pkt_type = parse_pkt(skb, &key);
|
|
|
|
if (pkt_type < 0)
|
2022-05-20 14:53:14 +02:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
value = bpf_map_lookup_elem(&iface_state, &key);
|
|
|
|
if (value && value->expiry_time > bpf_ktime_get_coarse_ns()) {
|
|
|
|
value->drops++;
|
2022-06-28 16:18:09 +02:00
|
|
|
if (debug_output)
|
|
|
|
/* bpf_trace_printk doesn't know how to format MAC
|
|
|
|
* addresses, and we don't have enough arguments to do
|
|
|
|
* it ourselves; so just pass the whole key as a u64 and
|
|
|
|
* hex-print that
|
|
|
|
*/
|
|
|
|
bpf_printk("Dropping packet with SMAC/vlan %llx - not found in hash table\n",
|
|
|
|
*(__u64 *)&key);
|
2022-05-20 14:53:14 +02:00
|
|
|
return TC_ACT_SHOT;
|
|
|
|
}
|
|
|
|
|
2022-06-28 16:18:09 +02:00
|
|
|
|
2022-06-28 17:17:55 +02:00
|
|
|
/* Only allow multicast and IGMP pkts on the currently active interface */
|
|
|
|
if ((pkt_type == PKT_TYPE_MULTICAST || pkt_type == PKT_TYPE_IGMP) &&
|
|
|
|
skb->ifindex != ifindex) {
|
2022-06-28 16:18:09 +02:00
|
|
|
if (debug_output)
|
2022-06-28 17:17:55 +02:00
|
|
|
bpf_printk("Dropping packet type %d - ifindex %d != active %d\n",
|
|
|
|
pkt_type, skb->ifindex, ifindex);
|
2022-06-14 17:36:54 +02:00
|
|
|
return TC_ACT_SHOT;
|
2022-06-28 16:18:09 +02:00
|
|
|
}
|
2022-06-14 17:36:54 +02:00
|
|
|
|
2022-05-20 14:53:14 +02:00
|
|
|
out:
|
|
|
|
return TC_ACT_OK;
|
|
|
|
}
|
|
|
|
|
2022-06-29 01:08:16 +02:00
|
|
|
SEC("kprobe/bond_change_active_slave")
|
|
|
|
int BPF_KPROBE(handle_change_slave, struct bonding *bond, struct slave *new_active)
|
2022-06-14 16:52:04 +02:00
|
|
|
{
|
2022-06-29 01:08:16 +02:00
|
|
|
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 && new_active) {
|
|
|
|
struct net_device *new_dev;
|
|
|
|
int ifindex;
|
|
|
|
|
|
|
|
new_dev = BPF_PROBE_READ(new_active, dev);
|
|
|
|
ifindex = BPF_CORE_READ(new_dev, ifindex);
|
|
|
|
if (ifindex) {
|
|
|
|
active_ifindex = ifindex;
|
|
|
|
if (debug_output)
|
|
|
|
bpf_printk("Active ifindex changed, new value: %d\n", ifindex);
|
|
|
|
}
|
|
|
|
}
|
2022-06-14 16:52:04 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-20 14:53:14 +02:00
|
|
|
char _license[] SEC("license") = "GPL";
|