mirror of https://github.com/OISF/suricata
af-packet: add support for eBPF cluster and filter
This patch introduces the ebpf cluster mode. This mode is using an extended BPF function that is loaded into the kernel and provide the load balancing. An example of cluster function is provided in the ebpf subdirectory and provide ippair load balancing function. This is a function which uses the same method as the one used in autofp ippair to provide a symetrical load balancing based on IP addresses. A simple filter example allowing to drop IPv6 is added to the source. This patch also prepares the infrastructure to be able to load and use map inside eBPF files. This will be used later for flow bypass.pull/3221/head
parent
d2121945c9
commit
91e1256b01
@ -0,0 +1,10 @@
|
||||
if BUILD_EBPF
|
||||
|
||||
all: lb.bpf filter.bpf
|
||||
|
||||
%.bpf: %.c
|
||||
${CC} -Wall -O2 -D__KERNEL__ -D__ASM_SYSREG_H -emit-llvm -c $< -o - | ${LLC} -march=bpf -filetype=obj -o $@
|
||||
|
||||
CLEANFILES = *.bpf
|
||||
|
||||
endif
|
||||
@ -0,0 +1,137 @@
|
||||
#ifndef __BPF_HELPERS_H
|
||||
#define __BPF_HELPERS_H
|
||||
|
||||
/* helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by elf_bpf loader
|
||||
*/
|
||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||
|
||||
/* helper functions called from eBPF programs written in C */
|
||||
static void *(*bpf_map_lookup_elem)(void *map, void *key) =
|
||||
(void *) BPF_FUNC_map_lookup_elem;
|
||||
static int (*bpf_map_update_elem)(void *map, void *key, void *value,
|
||||
unsigned long long flags) =
|
||||
(void *) BPF_FUNC_map_update_elem;
|
||||
static int (*bpf_map_delete_elem)(void *map, void *key) =
|
||||
(void *) BPF_FUNC_map_delete_elem;
|
||||
static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) =
|
||||
(void *) BPF_FUNC_probe_read;
|
||||
static unsigned long long (*bpf_ktime_get_ns)(void) =
|
||||
(void *) BPF_FUNC_ktime_get_ns;
|
||||
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
|
||||
(void *) BPF_FUNC_trace_printk;
|
||||
static void (*bpf_tail_call)(void *ctx, void *map, int index) =
|
||||
(void *) BPF_FUNC_tail_call;
|
||||
static unsigned long long (*bpf_get_smp_processor_id)(void) =
|
||||
(void *) BPF_FUNC_get_smp_processor_id;
|
||||
static unsigned long long (*bpf_get_current_pid_tgid)(void) =
|
||||
(void *) BPF_FUNC_get_current_pid_tgid;
|
||||
static unsigned long long (*bpf_get_current_uid_gid)(void) =
|
||||
(void *) BPF_FUNC_get_current_uid_gid;
|
||||
static int (*bpf_get_current_comm)(void *buf, int buf_size) =
|
||||
(void *) BPF_FUNC_get_current_comm;
|
||||
static int (*bpf_perf_event_read)(void *map, int index) =
|
||||
(void *) BPF_FUNC_perf_event_read;
|
||||
static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
|
||||
(void *) BPF_FUNC_clone_redirect;
|
||||
static int (*bpf_redirect)(int ifindex, int flags) =
|
||||
(void *) BPF_FUNC_redirect;
|
||||
static int (*bpf_perf_event_output)(void *ctx, void *map, int index, void *data, int size) =
|
||||
(void *) BPF_FUNC_perf_event_output;
|
||||
static int (*bpf_get_stackid)(void *ctx, void *map, int flags) =
|
||||
(void *) BPF_FUNC_get_stackid;
|
||||
|
||||
/* llvm builtin functions that eBPF C program may use to
|
||||
* emit BPF_LD_ABS and BPF_LD_IND instructions
|
||||
*/
|
||||
struct sk_buff;
|
||||
unsigned long long load_byte(void *skb,
|
||||
unsigned long long off) asm("llvm.bpf.load.byte");
|
||||
unsigned long long load_half(void *skb,
|
||||
unsigned long long off) asm("llvm.bpf.load.half");
|
||||
unsigned long long load_word(void *skb,
|
||||
unsigned long long off) asm("llvm.bpf.load.word");
|
||||
|
||||
/* a helper structure used by eBPF C program
|
||||
* to describe map attributes to elf_bpf loader
|
||||
*/
|
||||
struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
};
|
||||
|
||||
static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) =
|
||||
(void *) BPF_FUNC_skb_store_bytes;
|
||||
static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) =
|
||||
(void *) BPF_FUNC_l3_csum_replace;
|
||||
static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) =
|
||||
(void *) BPF_FUNC_l4_csum_replace;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->di)
|
||||
#define PT_REGS_PARM2(x) ((x)->si)
|
||||
#define PT_REGS_PARM3(x) ((x)->dx)
|
||||
#define PT_REGS_PARM4(x) ((x)->cx)
|
||||
#define PT_REGS_PARM5(x) ((x)->r8)
|
||||
#define PT_REGS_RET(x) ((x)->sp)
|
||||
#define PT_REGS_FP(x) ((x)->bp)
|
||||
#define PT_REGS_RC(x) ((x)->ax)
|
||||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->ip)
|
||||
|
||||
#elif defined(__s390x__)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->gprs[2])
|
||||
#define PT_REGS_PARM2(x) ((x)->gprs[3])
|
||||
#define PT_REGS_PARM3(x) ((x)->gprs[4])
|
||||
#define PT_REGS_PARM4(x) ((x)->gprs[5])
|
||||
#define PT_REGS_PARM5(x) ((x)->gprs[6])
|
||||
#define PT_REGS_RET(x) ((x)->gprs[14])
|
||||
#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_RC(x) ((x)->gprs[2])
|
||||
#define PT_REGS_SP(x) ((x)->gprs[15])
|
||||
#define PT_REGS_IP(x) ((x)->ip)
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->regs[0])
|
||||
#define PT_REGS_PARM2(x) ((x)->regs[1])
|
||||
#define PT_REGS_PARM3(x) ((x)->regs[2])
|
||||
#define PT_REGS_PARM4(x) ((x)->regs[3])
|
||||
#define PT_REGS_PARM5(x) ((x)->regs[4])
|
||||
#define PT_REGS_RET(x) ((x)->regs[30])
|
||||
#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define PT_REGS_RC(x) ((x)->regs[0])
|
||||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->pc)
|
||||
|
||||
#elif defined(__powerpc__)
|
||||
|
||||
#define PT_REGS_PARM1(x) ((x)->gpr[3])
|
||||
#define PT_REGS_PARM2(x) ((x)->gpr[4])
|
||||
#define PT_REGS_PARM3(x) ((x)->gpr[5])
|
||||
#define PT_REGS_PARM4(x) ((x)->gpr[6])
|
||||
#define PT_REGS_PARM5(x) ((x)->gpr[7])
|
||||
#define PT_REGS_RC(x) ((x)->gpr[3])
|
||||
#define PT_REGS_SP(x) ((x)->sp)
|
||||
#define PT_REGS_IP(x) ((x)->nip)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __powerpc__
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
#else
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \
|
||||
bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \
|
||||
bpf_probe_read(&(ip), sizeof(ip), \
|
||||
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,43 @@
|
||||
//#include <bcc/proto.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define LINUX_VERSION_CODE 263682
|
||||
|
||||
int SEC("filter") hashfilter(struct __sk_buff *skb) {
|
||||
__u32 nhoff = BPF_LL_OFF + ETH_HLEN;
|
||||
|
||||
skb->cb[0] = nhoff;
|
||||
switch (skb->protocol) {
|
||||
case __constant_htons(ETH_P_IP):
|
||||
return -1;
|
||||
case __constant_htons(ETH_P_IPV6):
|
||||
return 0;
|
||||
default:
|
||||
#if 0
|
||||
{
|
||||
char fmt[] = "Got proto %u\n";
|
||||
bpf_trace_printk(fmt, sizeof(fmt), h_proto);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
|
||||
uint32_t __version SEC("version") = LINUX_VERSION_CODE;
|
||||
@ -0,0 +1,92 @@
|
||||
//#include <bcc/proto.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
#define LINUX_VERSION_CODE 263682
|
||||
|
||||
#ifndef __section
|
||||
# define __section(x) __attribute__((section(x), used))
|
||||
#endif
|
||||
|
||||
static __always_inline int ipv4_hash(struct __sk_buff *skb)
|
||||
{
|
||||
uint32_t nhoff;
|
||||
uint32_t src, dst;
|
||||
|
||||
nhoff = skb->cb[0];
|
||||
src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
|
||||
dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
|
||||
|
||||
#if 0
|
||||
char fmt[] = "Got addr: %u -> %u\n";
|
||||
bpf_trace_printk(fmt, sizeof(fmt), src, dst);
|
||||
char fmt2[] = "Got hash %u\n";
|
||||
bpf_trace_printk(fmt2, sizeof(fmt2), src + dst);
|
||||
#endif
|
||||
return src + dst;
|
||||
}
|
||||
|
||||
static __always_inline int ipv6_hash(struct __sk_buff *skb)
|
||||
{
|
||||
uint32_t nhoff;
|
||||
uint32_t src, dst, hash;
|
||||
|
||||
nhoff = skb->cb[0];
|
||||
hash = 0;
|
||||
src = load_word(skb, nhoff + offsetof(struct ipv6hdr, saddr) + 4 * 0 );
|
||||
dst = load_word(skb, nhoff + offsetof(struct ipv6hdr, daddr) + 4 * 0 );
|
||||
hash += src + dst;
|
||||
|
||||
src = load_word(skb, nhoff + offsetof(struct ipv6hdr, saddr) + 4 * 1 );
|
||||
dst = load_word(skb, nhoff + offsetof(struct ipv6hdr, daddr) + 4 * 1 );
|
||||
hash += src + dst;
|
||||
|
||||
src = load_word(skb, nhoff + offsetof(struct ipv6hdr, saddr) + 4 * 2 );
|
||||
dst = load_word(skb, nhoff + offsetof(struct ipv6hdr, daddr) + 4 * 2 );
|
||||
hash += src + dst;
|
||||
|
||||
src = load_word(skb, nhoff + offsetof(struct ipv6hdr, saddr) + 4 * 3 );
|
||||
dst = load_word(skb, nhoff + offsetof(struct ipv6hdr, daddr) + 4 * 3 );
|
||||
hash += src + dst;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
int __section("loadbalancer") lb(struct __sk_buff *skb) {
|
||||
__u32 nhoff = BPF_LL_OFF + ETH_HLEN;
|
||||
|
||||
skb->cb[0] = nhoff;
|
||||
|
||||
switch (skb->protocol) {
|
||||
case __constant_htons(ETH_P_IP):
|
||||
return ipv4_hash(skb);
|
||||
case __constant_htons(ETH_P_IPV6):
|
||||
return ipv6_hash(skb);
|
||||
default:
|
||||
#if 0
|
||||
{
|
||||
char fmt[] = "Got proto %u\n";
|
||||
bpf_trace_printk(fmt, sizeof(fmt), h_proto);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
/* hash on proto by default */
|
||||
return skb->protocol;
|
||||
}
|
||||
|
||||
char __license[] __section("license") = "GPL";
|
||||
|
||||
uint32_t __version __section("version") = LINUX_VERSION_CODE;
|
||||
@ -0,0 +1,177 @@
|
||||
/* Copyright (C) 2018 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \ingroup afppacket
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Eric Leblond <eric@regit.org>
|
||||
*
|
||||
* eBPF utility
|
||||
*
|
||||
*/
|
||||
|
||||
#define PCAP_DONT_INCLUDE_PCAP_BPF_H 1
|
||||
#define SC_PCAP_DONT_INCLUDE_PCAP_H 1
|
||||
|
||||
#include "suricata-common.h"
|
||||
|
||||
#ifdef HAVE_PACKET_EBPF
|
||||
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include "config.h"
|
||||
|
||||
#include "util-ebpf.h"
|
||||
|
||||
#define BPF_MAP_MAX_COUNT 16
|
||||
|
||||
#define MAX_ERRNO 4095
|
||||
|
||||
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
|
||||
|
||||
static inline long IS_ERR(const void *ptr)
|
||||
{
|
||||
return IS_ERR_VALUE((unsigned long)ptr);
|
||||
}
|
||||
|
||||
struct bpf_map_item {
|
||||
const char * name;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static struct bpf_map_item bpf_map_array[BPF_MAP_MAX_COUNT];
|
||||
static int bpf_map_last = 0;
|
||||
|
||||
int EBPFGetMapFDByName(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
for (i = 0; i < BPF_MAP_MAX_COUNT; i++) {
|
||||
if (!strcmp(bpf_map_array[i].name, name)) {
|
||||
SCLogNotice("Got fd %d for eBPF map '%s'", bpf_map_array[i].fd, name);
|
||||
return bpf_map_array[i].fd;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a section of an eBPF file
|
||||
*
|
||||
* This function loads a section inside an eBPF and return
|
||||
* via a parameter the file descriptor that will be used to
|
||||
* inject the eBPF code into the kernel via a syscall.
|
||||
*
|
||||
* \param path the path of the eBPF file to load
|
||||
* \param section the section in the eBPF file to load
|
||||
* \param val a pointer to an integer that will be the file desc
|
||||
* \return -1 in case of error and 0 in case of success
|
||||
*/
|
||||
int EBPFLoadFile(const char *path, const char * section, int *val)
|
||||
{
|
||||
int err, pfd;
|
||||
bool found = false;
|
||||
struct bpf_object *bpfobj = NULL;
|
||||
struct bpf_program *bpfprog = NULL;
|
||||
struct bpf_map *map = NULL;
|
||||
/* FIXME we will need to close BPF at exit of runmode */
|
||||
if (! path) {
|
||||
SCLogError(SC_ERR_INVALID_VALUE, "No file defined to load eBPF from");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bpfobj = bpf_object__open(path);
|
||||
|
||||
if (IS_ERR(bpfobj)) {
|
||||
SCLogError(SC_ERR_INVALID_VALUE,
|
||||
"Unable to load eBPF objects in '%s'",
|
||||
path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bpf_object__for_each_program(bpfprog, bpfobj) {
|
||||
const char *title = bpf_program__title(bpfprog, 0);
|
||||
if (!strcmp(title, section)) {
|
||||
bpf_program__set_socket_filter(bpfprog);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
SCLogError(SC_ERR_INVALID_VALUE,
|
||||
"No section '%s' in '%s' file. Will not be able to use the file",
|
||||
section,
|
||||
path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_object__load(bpfobj);
|
||||
if (err < 0) {
|
||||
if (err == -EPERM) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC,
|
||||
"Permission issue when loading eBPF object try to "
|
||||
"increase memlock limit: %s (%d)",
|
||||
strerror(err),
|
||||
err);
|
||||
} else {
|
||||
char buf[129];
|
||||
libbpf_strerror(err, buf, sizeof(buf));
|
||||
SCLogError(SC_ERR_INVALID_VALUE,
|
||||
"Unable to load eBPF object: %s (%d)",
|
||||
buf,
|
||||
err);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* store the map in our array */
|
||||
bpf_map__for_each(map, bpfobj) {
|
||||
SCLogNotice("Got a map '%s' with fd '%d'", bpf_map__name(map), bpf_map__fd(map));
|
||||
bpf_map_array[bpf_map_last].fd = bpf_map__fd(map);
|
||||
bpf_map_array[bpf_map_last].name = SCStrdup(bpf_map__name(map));
|
||||
if (!bpf_map_array[bpf_map_last].name) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Unable to duplicate map name");
|
||||
return -1;
|
||||
}
|
||||
bpf_map_last++;
|
||||
if (bpf_map_last == BPF_MAP_MAX_COUNT) {
|
||||
SCLogError(SC_ERR_NOT_SUPPORTED, "Too many BPF maps in eBPF files");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
pfd = bpf_program__fd(bpfprog);
|
||||
if (pfd == -1) {
|
||||
SCLogError(SC_ERR_INVALID_VALUE,
|
||||
"Unable to find %s section", section);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*val = pfd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,30 @@
|
||||
/* Copyright (C) 2016 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Eric Leblond <eric@regit.org>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_EBPF_H__
|
||||
#define __UTIL_EBPF_H__
|
||||
|
||||
int EBPFGetMapFDByName(const char *name);
|
||||
int EBPFLoadFile(const char *path, const char * section, int *val);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue