mirror of
https://github.com/netdata/libbpf.git
synced 2026-03-25 02:39:39 +08:00
initial commit
This initial commit added the following files
from bpf-next repository:
src:
<files from linux:tools/lib/bpf>
bpf.c bpf.h btf.c btf.h libbpf.c libbpf.h
libbpf_errno.c netlink.c nlattr.c nlattr.h
str_error.c str_error.h
include:
<files from linux:tools/include/uapi/linux>
uapi/linux/{bpf.h, btf.h}
<files from linux:tools/include/tools>
tools/libc_compat.h
The following files are also added:
include/linux/{err.h, kernel.h, list.h, overflow.h, types.h}
These files are customized headers to satisfy compilation.
Their original counterparts are at linux:tools/include/linux
directory.
Signed-off-by: Yonghong Song <yhs@fb.com>
This commit is contained in:
11
include/linux/err.h
Normal file
11
include/linux/err.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_ERR_H
|
||||
#define __LINUX_ERR_H
|
||||
|
||||
static inline void * ERR_PTR(long error_)
|
||||
{
|
||||
return (void *) error_;
|
||||
}
|
||||
|
||||
#endif
|
||||
12
include/linux/kernel.h
Normal file
12
include/linux/kernel.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_KERNEL_H
|
||||
#define __LINUX_KERNEL_H
|
||||
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof(((type *)0)->member) * __mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); })
|
||||
|
||||
#endif
|
||||
17
include/linux/list.h
Normal file
17
include/linux/list.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_LIST_H
|
||||
#define __LINUX_LIST_H
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
#define list_next_entry(pos, member) \
|
||||
list_entry((pos)->member.next, typeof(*(pos)), member)
|
||||
|
||||
#endif
|
||||
217
include/linux/overflow.h
Normal file
217
include/linux/overflow.h
Normal file
@@ -0,0 +1,217 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_OVERFLOW_H
|
||||
#define __LINUX_OVERFLOW_H
|
||||
|
||||
#define is_signed_type(type) (((type)(-1)) < (type)1)
|
||||
#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
|
||||
#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
|
||||
#define type_min(T) ((T)((T)-type_max(T)-(T)1))
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
#if GCC_VERSION >= 50100
|
||||
#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW
|
||||
|
||||
#define check_mul_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
__builtin_mul_overflow(__a, __b, __d); \
|
||||
})
|
||||
|
||||
#else
|
||||
|
||||
|
||||
/* Checking for unsigned overflow is relatively easy without causing UB. */
|
||||
#define __unsigned_add_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = __a + __b; \
|
||||
*__d < __a; \
|
||||
})
|
||||
#define __unsigned_sub_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = __a - __b; \
|
||||
__a < __b; \
|
||||
})
|
||||
/*
|
||||
* If one of a or b is a compile-time constant, this avoids a division.
|
||||
*/
|
||||
#define __unsigned_mul_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = __a * __b; \
|
||||
__builtin_constant_p(__b) ? \
|
||||
__b > 0 && __a > type_max(typeof(__a)) / __b : \
|
||||
__a > 0 && __b > type_max(typeof(__b)) / __a; \
|
||||
})
|
||||
|
||||
/*
|
||||
* For signed types, detecting overflow is much harder, especially if
|
||||
* we want to avoid UB. But the interface of these macros is such that
|
||||
* we must provide a result in *d, and in fact we must produce the
|
||||
* result promised by gcc's builtins, which is simply the possibly
|
||||
* wrapped-around value. Fortunately, we can just formally do the
|
||||
* operations in the widest relevant unsigned type (u64) and then
|
||||
* truncate the result - gcc is smart enough to generate the same code
|
||||
* with and without the (u64) casts.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adding two signed integers can overflow only if they have the same
|
||||
* sign, and overflow has happened iff the result has the opposite
|
||||
* sign.
|
||||
*/
|
||||
#define __signed_add_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = (u64)__a + (u64)__b; \
|
||||
(((~(__a ^ __b)) & (*__d ^ __a)) \
|
||||
& type_min(typeof(__a))) != 0; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Subtraction is similar, except that overflow can now happen only
|
||||
* when the signs are opposite. In this case, overflow has happened if
|
||||
* the result has the opposite sign of a.
|
||||
*/
|
||||
#define __signed_sub_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = (u64)__a - (u64)__b; \
|
||||
((((__a ^ __b)) & (*__d ^ __a)) \
|
||||
& type_min(typeof(__a))) != 0; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Signed multiplication is rather hard. gcc always follows C99, so
|
||||
* division is truncated towards 0. This means that we can write the
|
||||
* overflow check like this:
|
||||
*
|
||||
* (a > 0 && (b > MAX/a || b < MIN/a)) ||
|
||||
* (a < -1 && (b > MIN/a || b < MAX/a) ||
|
||||
* (a == -1 && b == MIN)
|
||||
*
|
||||
* The redundant casts of -1 are to silence an annoying -Wtype-limits
|
||||
* (included in -Wextra) warning: When the type is u8 or u16, the
|
||||
* __b_c_e in check_mul_overflow obviously selects
|
||||
* __unsigned_mul_overflow, but unfortunately gcc still parses this
|
||||
* code and warns about the limited range of __b.
|
||||
*/
|
||||
|
||||
#define __signed_mul_overflow(a, b, d) ({ \
|
||||
typeof(a) __a = (a); \
|
||||
typeof(b) __b = (b); \
|
||||
typeof(d) __d = (d); \
|
||||
typeof(a) __tmax = type_max(typeof(a)); \
|
||||
typeof(a) __tmin = type_min(typeof(a)); \
|
||||
(void) (&__a == &__b); \
|
||||
(void) (&__a == __d); \
|
||||
*__d = (u64)__a * (u64)__b; \
|
||||
(__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \
|
||||
(__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \
|
||||
(__b == (typeof(__b))-1 && __a == __tmin); \
|
||||
})
|
||||
|
||||
|
||||
#define check_add_overflow(a, b, d) \
|
||||
__builtin_choose_expr(is_signed_type(typeof(a)), \
|
||||
__signed_add_overflow(a, b, d), \
|
||||
__unsigned_add_overflow(a, b, d))
|
||||
|
||||
#define check_sub_overflow(a, b, d) \
|
||||
__builtin_choose_expr(is_signed_type(typeof(a)), \
|
||||
__signed_sub_overflow(a, b, d), \
|
||||
__unsigned_sub_overflow(a, b, d))
|
||||
|
||||
#define check_mul_overflow(a, b, d) \
|
||||
__builtin_choose_expr(is_signed_type(typeof(a)), \
|
||||
__signed_mul_overflow(a, b, d), \
|
||||
__unsigned_mul_overflow(a, b, d))
|
||||
|
||||
|
||||
#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */
|
||||
|
||||
/**
|
||||
* array_size() - Calculate size of 2-dimensional array.
|
||||
*
|
||||
* @a: dimension one
|
||||
* @b: dimension two
|
||||
*
|
||||
* Calculates size of 2-dimensional array: @a * @b.
|
||||
*
|
||||
* Returns: number of bytes needed to represent the array or SIZE_MAX on
|
||||
* overflow.
|
||||
*/
|
||||
static inline size_t array_size(size_t a, size_t b)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (check_mul_overflow(a, b, &bytes))
|
||||
return SIZE_MAX;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* array3_size() - Calculate size of 3-dimensional array.
|
||||
*
|
||||
* @a: dimension one
|
||||
* @b: dimension two
|
||||
* @c: dimension three
|
||||
*
|
||||
* Calculates size of 3-dimensional array: @a * @b * @c.
|
||||
*
|
||||
* Returns: number of bytes needed to represent the array or SIZE_MAX on
|
||||
* overflow.
|
||||
*/
|
||||
static inline size_t array3_size(size_t a, size_t b, size_t c)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (check_mul_overflow(a, b, &bytes))
|
||||
return SIZE_MAX;
|
||||
if (check_mul_overflow(bytes, c, &bytes))
|
||||
return SIZE_MAX;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static inline size_t __ab_c_size(size_t n, size_t size, size_t c)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (check_mul_overflow(n, size, &bytes))
|
||||
return SIZE_MAX;
|
||||
if (check_add_overflow(bytes, c, &bytes))
|
||||
return SIZE_MAX;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
#endif
|
||||
44
include/linux/types.h
Normal file
44
include/linux/types.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
#ifndef __LINUX_TYPES_H
|
||||
#define __LINUX_TYPES_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
|
||||
#include <asm/types.h>
|
||||
#include <asm/posix_types.h>
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef int64_t s64;
|
||||
|
||||
typedef __u32 u32;
|
||||
typedef __s32 s32;
|
||||
|
||||
typedef __u16 u16;
|
||||
typedef __s16 s16;
|
||||
|
||||
typedef __u8 u8;
|
||||
typedef __s8 s8;
|
||||
|
||||
#define __bitwise__
|
||||
#define __bitwise __bitwise__
|
||||
|
||||
typedef __u16 __bitwise __le16;
|
||||
typedef __u16 __bitwise __be16;
|
||||
typedef __u32 __bitwise __le32;
|
||||
typedef __u32 __bitwise __be32;
|
||||
typedef __u64 __bitwise __le64;
|
||||
typedef __u64 __bitwise __be64;
|
||||
|
||||
#ifndef __aligned_u64
|
||||
# define __aligned_u64 __u64 __attribute__((aligned(8)))
|
||||
#endif
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#endif
|
||||
20
include/tools/libc_compat.h
Normal file
20
include/tools/libc_compat.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.0+ OR BSD-2-Clause)
|
||||
/* Copyright (C) 2018 Netronome Systems, Inc. */
|
||||
|
||||
#ifndef __TOOLS_LIBC_COMPAT_H
|
||||
#define __TOOLS_LIBC_COMPAT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#ifdef COMPAT_NEED_REALLOCARRAY
|
||||
static inline void *reallocarray(void *ptr, size_t nmemb, size_t size)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (unlikely(check_mul_overflow(nmemb, size, &bytes)))
|
||||
return NULL;
|
||||
return realloc(ptr, bytes);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
2899
include/uapi/linux/bpf.h
Normal file
2899
include/uapi/linux/bpf.h
Normal file
File diff suppressed because it is too large
Load Diff
113
include/uapi/linux/btf.h
Normal file
113
include/uapi/linux/btf.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef _UAPI__LINUX_BTF_H__
|
||||
#define _UAPI__LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_VERSION 1
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
__u32 hdr_len;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 type_len; /* length of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x0000ffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x0000ffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
struct btf_type {
|
||||
__u32 name_off;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-27: kind (e.g. int, ptr, array...etc)
|
||||
* bits 28-31: unused
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
|
||||
#define BTF_KIND_UNKN 0 /* Unknown */
|
||||
#define BTF_KIND_INT 1 /* Integer */
|
||||
#define BTF_KIND_PTR 2 /* Pointer */
|
||||
#define BTF_KIND_ARRAY 3 /* Array */
|
||||
#define BTF_KIND_STRUCT 4 /* Struct */
|
||||
#define BTF_KIND_UNION 5 /* Union */
|
||||
#define BTF_KIND_ENUM 6 /* Enumeration */
|
||||
#define BTF_KIND_FWD 7 /* Forward */
|
||||
#define BTF_KIND_TYPEDEF 8 /* Typedef */
|
||||
#define BTF_KIND_VOLATILE 9 /* Volatile */
|
||||
#define BTF_KIND_CONST 10 /* Const */
|
||||
#define BTF_KIND_RESTRICT 11 /* Restrict */
|
||||
#define BTF_KIND_MAX 11
|
||||
#define NR_BTF_KINDS 12
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL & 0x00ff0000)) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED (1 << 0)
|
||||
#define BTF_INT_CHAR (1 << 1)
|
||||
#define BTF_INT_BOOL (1 << 2)
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name_off;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name_off;
|
||||
__u32 type;
|
||||
__u32 offset; /* offset in bits */
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
||||
540
src/bpf.c
Normal file
540
src/bpf.c
Normal file
@@ -0,0 +1,540 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* common eBPF ELF operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf.h"
|
||||
#include "libbpf.h"
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* When building perf, unistd.h is overridden. __NR_bpf is
|
||||
* required to be defined explicitly.
|
||||
*/
|
||||
#ifndef __NR_bpf
|
||||
# if defined(__i386__)
|
||||
# define __NR_bpf 357
|
||||
# elif defined(__x86_64__)
|
||||
# define __NR_bpf 321
|
||||
# elif defined(__aarch64__)
|
||||
# define __NR_bpf 280
|
||||
# elif defined(__sparc__)
|
||||
# define __NR_bpf 349
|
||||
# elif defined(__s390__)
|
||||
# define __NR_bpf 351
|
||||
# else
|
||||
# error __NR_bpf not defined. libbpf does not support your arch.
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
static inline __u64 ptr_to_u64(const void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
||||
unsigned int size)
|
||||
{
|
||||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
}
|
||||
|
||||
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
|
||||
{
|
||||
__u32 name_len = create_attr->name ? strlen(create_attr->name) : 0;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
attr.map_type = create_attr->map_type;
|
||||
attr.key_size = create_attr->key_size;
|
||||
attr.value_size = create_attr->value_size;
|
||||
attr.max_entries = create_attr->max_entries;
|
||||
attr.map_flags = create_attr->map_flags;
|
||||
memcpy(attr.map_name, create_attr->name,
|
||||
min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
attr.numa_node = create_attr->numa_node;
|
||||
attr.btf_fd = create_attr->btf_fd;
|
||||
attr.btf_key_type_id = create_attr->btf_key_type_id;
|
||||
attr.btf_value_type_id = create_attr->btf_value_type_id;
|
||||
attr.map_ifindex = create_attr->map_ifindex;
|
||||
attr.inner_map_fd = create_attr->inner_map_fd;
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.name = name;
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
if (node >= 0) {
|
||||
map_attr.numa_node = node;
|
||||
map_attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
}
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries, __u32 map_flags)
|
||||
{
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags)
|
||||
{
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.name = name;
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
__u32 name_len = name ? strlen(name) : 0;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
attr.map_type = map_type;
|
||||
attr.key_size = key_size;
|
||||
attr.value_size = 4;
|
||||
attr.inner_map_fd = inner_map_fd;
|
||||
attr.max_entries = max_entries;
|
||||
attr.map_flags = map_flags;
|
||||
memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
if (node >= 0) {
|
||||
attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
attr.numa_node = node;
|
||||
}
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_in_map_node(map_type, name, key_size,
|
||||
inner_map_fd, max_entries, map_flags,
|
||||
-1);
|
||||
}
|
||||
|
||||
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
|
||||
char *log_buf, size_t log_buf_sz)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
__u32 name_len;
|
||||
int fd;
|
||||
|
||||
if (!load_attr)
|
||||
return -EINVAL;
|
||||
|
||||
name_len = load_attr->name ? strlen(load_attr->name) : 0;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = load_attr->prog_type;
|
||||
attr.expected_attach_type = load_attr->expected_attach_type;
|
||||
attr.insn_cnt = (__u32)load_attr->insns_cnt;
|
||||
attr.insns = ptr_to_u64(load_attr->insns);
|
||||
attr.license = ptr_to_u64(load_attr->license);
|
||||
attr.log_buf = ptr_to_u64(NULL);
|
||||
attr.log_size = 0;
|
||||
attr.log_level = 0;
|
||||
attr.kern_version = load_attr->kern_version;
|
||||
attr.prog_ifindex = load_attr->prog_ifindex;
|
||||
memcpy(attr.prog_name, load_attr->name,
|
||||
min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
if (fd >= 0 || !log_buf || !log_buf_sz)
|
||||
return fd;
|
||||
|
||||
/* Try again with log */
|
||||
attr.log_buf = ptr_to_u64(log_buf);
|
||||
attr.log_size = log_buf_sz;
|
||||
attr.log_level = 1;
|
||||
log_buf[0] = 0;
|
||||
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
__u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz)
|
||||
{
|
||||
struct bpf_load_program_attr load_attr;
|
||||
|
||||
memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
|
||||
load_attr.prog_type = type;
|
||||
load_attr.expected_attach_type = 0;
|
||||
load_attr.name = NULL;
|
||||
load_attr.insns = insns;
|
||||
load_attr.insns_cnt = insns_cnt;
|
||||
load_attr.license = license;
|
||||
load_attr.kern_version = kern_version;
|
||||
|
||||
return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
|
||||
}
|
||||
|
||||
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, int strict_alignment,
|
||||
const char *license, __u32 kern_version,
|
||||
char *log_buf, size_t log_buf_sz, int log_level)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_type = type;
|
||||
attr.insn_cnt = (__u32)insns_cnt;
|
||||
attr.insns = ptr_to_u64(insns);
|
||||
attr.license = ptr_to_u64(license);
|
||||
attr.log_buf = ptr_to_u64(log_buf);
|
||||
attr.log_size = log_buf_sz;
|
||||
attr.log_level = log_level;
|
||||
log_buf[0] = 0;
|
||||
attr.kern_version = kern_version;
|
||||
attr.prog_flags = strict_alignment ? BPF_F_STRICT_ALIGNMENT : 0;
|
||||
|
||||
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_update_elem(int fd, const void *key, const void *value,
|
||||
__u64 flags)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
attr.flags = flags;
|
||||
|
||||
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_lookup_elem(int fd, const void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
|
||||
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_delete_elem(int fd, const void *key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
|
||||
return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_get_next_key(int fd, const void *key, void *next_key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.next_key = ptr_to_u64(next_key);
|
||||
|
||||
return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_pin(int fd, const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
attr.bpf_fd = fd;
|
||||
|
||||
return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_get(const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
|
||||
return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
|
||||
unsigned int flags)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_bpf_fd = prog_fd;
|
||||
attr.attach_type = type;
|
||||
attr.attach_flags = flags;
|
||||
|
||||
return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_type = type;
|
||||
|
||||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.target_fd = target_fd;
|
||||
attr.attach_bpf_fd = prog_fd;
|
||||
attr.attach_type = type;
|
||||
|
||||
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.query.target_fd = target_fd;
|
||||
attr.query.attach_type = type;
|
||||
attr.query.query_flags = query_flags;
|
||||
attr.query.prog_cnt = *prog_cnt;
|
||||
attr.query.prog_ids = ptr_to_u64(prog_ids);
|
||||
|
||||
ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
|
||||
if (attach_flags)
|
||||
*attach_flags = attr.query.attach_flags;
|
||||
*prog_cnt = attr.query.prog_cnt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
void *data_out, __u32 *size_out, __u32 *retval,
|
||||
__u32 *duration)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.test.prog_fd = prog_fd;
|
||||
attr.test.data_in = ptr_to_u64(data);
|
||||
attr.test.data_out = ptr_to_u64(data_out);
|
||||
attr.test.data_size_in = size;
|
||||
attr.test.repeat = repeat;
|
||||
|
||||
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
||||
if (size_out)
|
||||
*size_out = attr.test.data_size_out;
|
||||
if (retval)
|
||||
*retval = attr.test.retval;
|
||||
if (duration)
|
||||
*duration = attr.test.duration;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.start_id = start_id;
|
||||
|
||||
err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
|
||||
if (!err)
|
||||
*next_id = attr.next_id;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.start_id = start_id;
|
||||
|
||||
err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
|
||||
if (!err)
|
||||
*next_id = attr.next_id;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bpf_prog_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.prog_id = id;
|
||||
|
||||
return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_id = id;
|
||||
|
||||
return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_btf_get_fd_by_id(__u32 id)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.btf_id = id;
|
||||
|
||||
return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
int err;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.info.bpf_fd = prog_fd;
|
||||
attr.info.info_len = *info_len;
|
||||
attr.info.info = ptr_to_u64(info);
|
||||
|
||||
err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
|
||||
if (!err)
|
||||
*info_len = attr.info.info_len;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bpf_raw_tracepoint_open(const char *name, int prog_fd)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.raw_tracepoint.name = ptr_to_u64(name);
|
||||
attr.raw_tracepoint.prog_fd = prog_fd;
|
||||
|
||||
return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
||||
bool do_log)
|
||||
{
|
||||
union bpf_attr attr = {};
|
||||
int fd;
|
||||
|
||||
attr.btf = ptr_to_u64(btf);
|
||||
attr.btf_size = btf_size;
|
||||
|
||||
retry:
|
||||
if (do_log && log_buf && log_buf_size) {
|
||||
attr.btf_log_level = 1;
|
||||
attr.btf_log_size = log_buf_size;
|
||||
attr.btf_log_buf = ptr_to_u64(log_buf);
|
||||
}
|
||||
|
||||
fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
|
||||
if (fd == -1 && !do_log && log_buf && log_buf_size) {
|
||||
do_log = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
|
||||
__u32 *prog_id, __u32 *fd_type, __u64 *probe_offset,
|
||||
__u64 *probe_addr)
|
||||
{
|
||||
union bpf_attr attr = {};
|
||||
int err;
|
||||
|
||||
attr.task_fd_query.pid = pid;
|
||||
attr.task_fd_query.fd = fd;
|
||||
attr.task_fd_query.flags = flags;
|
||||
attr.task_fd_query.buf = ptr_to_u64(buf);
|
||||
attr.task_fd_query.buf_len = *buf_len;
|
||||
|
||||
err = sys_bpf(BPF_TASK_FD_QUERY, &attr, sizeof(attr));
|
||||
*buf_len = attr.task_fd_query.buf_len;
|
||||
*prog_id = attr.task_fd_query.prog_id;
|
||||
*fd_type = attr.task_fd_query.fd_type;
|
||||
*probe_offset = attr.task_fd_query.probe_offset;
|
||||
*probe_addr = attr.task_fd_query.probe_addr;
|
||||
|
||||
return err;
|
||||
}
|
||||
114
src/bpf.h
Normal file
114
src/bpf.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/*
|
||||
* common eBPF ELF operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*/
|
||||
#ifndef __LIBBPF_BPF_H
|
||||
#define __LIBBPF_BPF_H
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct bpf_create_map_attr {
|
||||
const char *name;
|
||||
enum bpf_map_type map_type;
|
||||
__u32 map_flags;
|
||||
__u32 key_size;
|
||||
__u32 value_size;
|
||||
__u32 max_entries;
|
||||
__u32 numa_node;
|
||||
__u32 btf_fd;
|
||||
__u32 btf_key_type_id;
|
||||
__u32 btf_value_type_id;
|
||||
__u32 map_ifindex;
|
||||
__u32 inner_map_fd;
|
||||
};
|
||||
|
||||
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node);
|
||||
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags);
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries, __u32 map_flags);
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags, int node);
|
||||
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int inner_map_fd, int max_entries,
|
||||
__u32 map_flags);
|
||||
|
||||
struct bpf_load_program_attr {
|
||||
enum bpf_prog_type prog_type;
|
||||
enum bpf_attach_type expected_attach_type;
|
||||
const char *name;
|
||||
const struct bpf_insn *insns;
|
||||
size_t insns_cnt;
|
||||
const char *license;
|
||||
__u32 kern_version;
|
||||
__u32 prog_ifindex;
|
||||
};
|
||||
|
||||
/* Recommend log buffer size */
|
||||
#define BPF_LOG_BUF_SIZE (256 * 1024)
|
||||
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
|
||||
char *log_buf, size_t log_buf_sz);
|
||||
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, const char *license,
|
||||
__u32 kern_version, char *log_buf,
|
||||
size_t log_buf_sz);
|
||||
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
||||
size_t insns_cnt, int strict_alignment,
|
||||
const char *license, __u32 kern_version,
|
||||
char *log_buf, size_t log_buf_sz, int log_level);
|
||||
|
||||
int bpf_map_update_elem(int fd, const void *key, const void *value,
|
||||
__u64 flags);
|
||||
|
||||
int bpf_map_lookup_elem(int fd, const void *key, void *value);
|
||||
int bpf_map_delete_elem(int fd, const void *key);
|
||||
int bpf_map_get_next_key(int fd, const void *key, void *next_key);
|
||||
int bpf_obj_pin(int fd, const char *pathname);
|
||||
int bpf_obj_get(const char *pathname);
|
||||
int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type,
|
||||
unsigned int flags);
|
||||
int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
|
||||
int bpf_prog_detach2(int prog_fd, int attachable_fd, enum bpf_attach_type type);
|
||||
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
||||
void *data_out, __u32 *size_out, __u32 *retval,
|
||||
__u32 *duration);
|
||||
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
|
||||
int bpf_prog_get_fd_by_id(__u32 id);
|
||||
int bpf_map_get_fd_by_id(__u32 id);
|
||||
int bpf_btf_get_fd_by_id(__u32 id);
|
||||
int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
|
||||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
|
||||
int bpf_raw_tracepoint_open(const char *name, int prog_fd);
|
||||
int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
||||
bool do_log);
|
||||
int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
|
||||
__u32 *prog_id, __u32 *fd_type, __u64 *probe_offset,
|
||||
__u64 *probe_addr);
|
||||
#endif /* __LIBBPF_BPF_H */
|
||||
395
src/btf.c
Normal file
395
src/btf.c
Normal file
@@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/btf.h>
|
||||
#include "btf.h"
|
||||
#include "bpf.h"
|
||||
|
||||
#define elog(fmt, ...) { if (err_log) err_log(fmt, ##__VA_ARGS__); }
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define BTF_MAX_NR_TYPES 65535
|
||||
|
||||
#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \
|
||||
((k) == BTF_KIND_VOLATILE) || \
|
||||
((k) == BTF_KIND_CONST) || \
|
||||
((k) == BTF_KIND_RESTRICT))
|
||||
|
||||
static struct btf_type btf_void;
|
||||
|
||||
struct btf {
|
||||
union {
|
||||
struct btf_header *hdr;
|
||||
void *data;
|
||||
};
|
||||
struct btf_type **types;
|
||||
const char *strings;
|
||||
void *nohdr_data;
|
||||
__u32 nr_types;
|
||||
__u32 types_size;
|
||||
__u32 data_size;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static int btf_add_type(struct btf *btf, struct btf_type *t)
|
||||
{
|
||||
if (btf->types_size - btf->nr_types < 2) {
|
||||
struct btf_type **new_types;
|
||||
__u32 expand_by, new_size;
|
||||
|
||||
if (btf->types_size == BTF_MAX_NR_TYPES)
|
||||
return -E2BIG;
|
||||
|
||||
expand_by = max(btf->types_size >> 2, 16);
|
||||
new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by);
|
||||
|
||||
new_types = realloc(btf->types, sizeof(*new_types) * new_size);
|
||||
if (!new_types)
|
||||
return -ENOMEM;
|
||||
|
||||
if (btf->nr_types == 0)
|
||||
new_types[0] = &btf_void;
|
||||
|
||||
btf->types = new_types;
|
||||
btf->types_size = new_size;
|
||||
}
|
||||
|
||||
btf->types[++(btf->nr_types)] = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
const struct btf_header *hdr = btf->hdr;
|
||||
__u32 meta_left;
|
||||
|
||||
if (btf->data_size < sizeof(struct btf_header)) {
|
||||
elog("BTF header not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->magic != BTF_MAGIC) {
|
||||
elog("Invalid BTF magic:%x\n", hdr->magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->version != BTF_VERSION) {
|
||||
elog("Unsupported BTF version:%u\n", hdr->version);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (hdr->flags) {
|
||||
elog("Unsupported BTF flags:%x\n", hdr->flags);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
meta_left = btf->data_size - sizeof(*hdr);
|
||||
if (!meta_left) {
|
||||
elog("BTF has no data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (meta_left < hdr->type_off) {
|
||||
elog("Invalid BTF type section offset:%u\n", hdr->type_off);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (meta_left < hdr->str_off) {
|
||||
elog("Invalid BTF string section offset:%u\n", hdr->str_off);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->type_off >= hdr->str_off) {
|
||||
elog("BTF type section offset >= string section offset. No type?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->type_off & 0x02) {
|
||||
elog("BTF type section is not aligned to 4 bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf->nohdr_data = btf->hdr + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
const struct btf_header *hdr = btf->hdr;
|
||||
const char *start = btf->nohdr_data + hdr->str_off;
|
||||
const char *end = start + btf->hdr->str_len;
|
||||
|
||||
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
|
||||
start[0] || end[-1]) {
|
||||
elog("Invalid BTF string section\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf->strings = start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
struct btf_header *hdr = btf->hdr;
|
||||
void *nohdr_data = btf->nohdr_data;
|
||||
void *next_type = nohdr_data + hdr->type_off;
|
||||
void *end_type = nohdr_data + hdr->str_off;
|
||||
|
||||
while (next_type < end_type) {
|
||||
struct btf_type *t = next_type;
|
||||
__u16 vlen = BTF_INFO_VLEN(t->info);
|
||||
int err;
|
||||
|
||||
next_type += sizeof(*t);
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_INT:
|
||||
next_type += sizeof(int);
|
||||
break;
|
||||
case BTF_KIND_ARRAY:
|
||||
next_type += sizeof(struct btf_array);
|
||||
break;
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
next_type += vlen * sizeof(struct btf_member);
|
||||
break;
|
||||
case BTF_KIND_ENUM:
|
||||
next_type += vlen * sizeof(struct btf_enum);
|
||||
break;
|
||||
case BTF_KIND_TYPEDEF:
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_FWD:
|
||||
case BTF_KIND_VOLATILE:
|
||||
case BTF_KIND_CONST:
|
||||
case BTF_KIND_RESTRICT:
|
||||
break;
|
||||
default:
|
||||
elog("Unsupported BTF_KIND:%u\n",
|
||||
BTF_INFO_KIND(t->info));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = btf_add_type(btf, t);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
|
||||
{
|
||||
if (type_id > btf->nr_types)
|
||||
return NULL;
|
||||
|
||||
return btf->types[type_id];
|
||||
}
|
||||
|
||||
static bool btf_type_is_void(const struct btf_type *t)
|
||||
{
|
||||
return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
|
||||
}
|
||||
|
||||
static bool btf_type_is_void_or_null(const struct btf_type *t)
|
||||
{
|
||||
return !t || btf_type_is_void(t);
|
||||
}
|
||||
|
||||
static __s64 btf_type_size(const struct btf_type *t)
|
||||
{
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_INT:
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
case BTF_KIND_ENUM:
|
||||
return t->size;
|
||||
case BTF_KIND_PTR:
|
||||
return sizeof(void *);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_RESOLVE_DEPTH 32
|
||||
|
||||
__s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
|
||||
{
|
||||
const struct btf_array *array;
|
||||
const struct btf_type *t;
|
||||
__u32 nelems = 1;
|
||||
__s64 size = -1;
|
||||
int i;
|
||||
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
|
||||
i++) {
|
||||
size = btf_type_size(t);
|
||||
if (size >= 0)
|
||||
break;
|
||||
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_TYPEDEF:
|
||||
case BTF_KIND_VOLATILE:
|
||||
case BTF_KIND_CONST:
|
||||
case BTF_KIND_RESTRICT:
|
||||
type_id = t->type;
|
||||
break;
|
||||
case BTF_KIND_ARRAY:
|
||||
array = (const struct btf_array *)(t + 1);
|
||||
if (nelems && array->nelems > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
nelems *= array->nelems;
|
||||
type_id = array->type;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
}
|
||||
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (nelems && size > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
|
||||
return nelems * size;
|
||||
}
|
||||
|
||||
int btf__resolve_type(const struct btf *btf, __u32 type_id)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
int depth = 0;
|
||||
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
while (depth < MAX_RESOLVE_DEPTH &&
|
||||
!btf_type_is_void_or_null(t) &&
|
||||
IS_MODIFIER(BTF_INFO_KIND(t->info))) {
|
||||
type_id = t->type;
|
||||
t = btf__type_by_id(btf, type_id);
|
||||
depth++;
|
||||
}
|
||||
|
||||
if (depth == MAX_RESOLVE_DEPTH || btf_type_is_void_or_null(t))
|
||||
return -EINVAL;
|
||||
|
||||
return type_id;
|
||||
}
|
||||
|
||||
__s32 btf__find_by_name(const struct btf *btf, const char *type_name)
|
||||
{
|
||||
__u32 i;
|
||||
|
||||
if (!strcmp(type_name, "void"))
|
||||
return 0;
|
||||
|
||||
for (i = 1; i <= btf->nr_types; i++) {
|
||||
const struct btf_type *t = btf->types[i];
|
||||
const char *name = btf__name_by_offset(btf, t->name_off);
|
||||
|
||||
if (name && !strcmp(type_name, name))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
void btf__free(struct btf *btf)
|
||||
{
|
||||
if (!btf)
|
||||
return;
|
||||
|
||||
if (btf->fd != -1)
|
||||
close(btf->fd);
|
||||
|
||||
free(btf->data);
|
||||
free(btf->types);
|
||||
free(btf);
|
||||
}
|
||||
|
||||
struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log)
|
||||
{
|
||||
__u32 log_buf_size = 0;
|
||||
char *log_buf = NULL;
|
||||
struct btf *btf;
|
||||
int err;
|
||||
|
||||
btf = calloc(1, sizeof(struct btf));
|
||||
if (!btf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
btf->fd = -1;
|
||||
|
||||
if (err_log) {
|
||||
log_buf = malloc(BPF_LOG_BUF_SIZE);
|
||||
if (!log_buf) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
*log_buf = 0;
|
||||
log_buf_size = BPF_LOG_BUF_SIZE;
|
||||
}
|
||||
|
||||
btf->data = malloc(size);
|
||||
if (!btf->data) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(btf->data, data, size);
|
||||
btf->data_size = size;
|
||||
|
||||
btf->fd = bpf_load_btf(btf->data, btf->data_size,
|
||||
log_buf, log_buf_size, false);
|
||||
|
||||
if (btf->fd == -1) {
|
||||
err = -errno;
|
||||
elog("Error loading BTF: %s(%d)\n", strerror(errno), errno);
|
||||
if (log_buf && *log_buf)
|
||||
elog("%s\n", log_buf);
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = btf_parse_hdr(btf, err_log);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = btf_parse_str_sec(btf, err_log);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = btf_parse_type_sec(btf, err_log);
|
||||
|
||||
done:
|
||||
free(log_buf);
|
||||
|
||||
if (err) {
|
||||
btf__free(btf);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return btf;
|
||||
}
|
||||
|
||||
int btf__fd(const struct btf *btf)
|
||||
{
|
||||
return btf->fd;
|
||||
}
|
||||
|
||||
const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
|
||||
{
|
||||
if (offset < btf->hdr->str_len)
|
||||
return &btf->strings[offset];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
26
src/btf.h
Normal file
26
src/btf.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#ifndef __LIBBPF_BTF_H
|
||||
#define __LIBBPF_BTF_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_ELF_SEC ".BTF"
|
||||
|
||||
struct btf;
|
||||
struct btf_type;
|
||||
|
||||
typedef int (*btf_print_fn_t)(const char *, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
void btf__free(struct btf *btf);
|
||||
struct btf *btf__new(__u8 *data, __u32 size, btf_print_fn_t err_log);
|
||||
__s32 btf__find_by_name(const struct btf *btf, const char *type_name);
|
||||
const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 id);
|
||||
__s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
|
||||
int btf__resolve_type(const struct btf *btf, __u32 type_id);
|
||||
int btf__fd(const struct btf *btf);
|
||||
const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
|
||||
|
||||
#endif /* __LIBBPF_BTF_H */
|
||||
2465
src/libbpf.c
Normal file
2465
src/libbpf.c
Normal file
File diff suppressed because it is too large
Load Diff
305
src/libbpf.h
Normal file
305
src/libbpf.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/*
|
||||
* Common eBPF ELF object loading operations.
|
||||
*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
*/
|
||||
#ifndef __LIBBPF_LIBBPF_H
|
||||
#define __LIBBPF_LIBBPF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h> // for size_t
|
||||
#include <linux/bpf.h>
|
||||
|
||||
enum libbpf_errno {
|
||||
__LIBBPF_ERRNO__START = 4000,
|
||||
|
||||
/* Something wrong in libelf */
|
||||
LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
|
||||
LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */
|
||||
LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */
|
||||
LIBBPF_ERRNO__ENDIAN, /* Endian mismatch */
|
||||
LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */
|
||||
LIBBPF_ERRNO__RELOC, /* Relocation failed */
|
||||
LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */
|
||||
LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
|
||||
LIBBPF_ERRNO__PROG2BIG, /* Program too big */
|
||||
LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
|
||||
LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */
|
||||
LIBBPF_ERRNO__WRNGPID, /* Wrong pid in netlink message */
|
||||
LIBBPF_ERRNO__INVSEQ, /* Invalid netlink sequence */
|
||||
LIBBPF_ERRNO__NLPARSE, /* netlink parsing error */
|
||||
__LIBBPF_ERRNO__END,
|
||||
};
|
||||
|
||||
int libbpf_strerror(int err, char *buf, size_t size);
|
||||
|
||||
/*
|
||||
* __printf is defined in include/linux/compiler-gcc.h. However,
|
||||
* it would be better if libbpf.h didn't depend on Linux header files.
|
||||
* So instead of __printf, here we use gcc attribute directly.
|
||||
*/
|
||||
typedef int (*libbpf_print_fn_t)(const char *, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
void libbpf_set_print(libbpf_print_fn_t warn,
|
||||
libbpf_print_fn_t info,
|
||||
libbpf_print_fn_t debug);
|
||||
|
||||
/* Hide internal to user */
|
||||
struct bpf_object;
|
||||
|
||||
struct bpf_object_open_attr {
|
||||
const char *file;
|
||||
enum bpf_prog_type prog_type;
|
||||
};
|
||||
|
||||
struct bpf_object *bpf_object__open(const char *path);
|
||||
struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr);
|
||||
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
|
||||
size_t obj_buf_sz,
|
||||
const char *name);
|
||||
int bpf_object__pin(struct bpf_object *object, const char *path);
|
||||
void bpf_object__close(struct bpf_object *object);
|
||||
|
||||
/* Load/unload object into/from kernel */
|
||||
int bpf_object__load(struct bpf_object *obj);
|
||||
int bpf_object__unload(struct bpf_object *obj);
|
||||
const char *bpf_object__name(struct bpf_object *obj);
|
||||
unsigned int bpf_object__kversion(struct bpf_object *obj);
|
||||
int bpf_object__btf_fd(const struct bpf_object *obj);
|
||||
|
||||
struct bpf_program *
|
||||
bpf_object__find_program_by_title(struct bpf_object *obj, const char *title);
|
||||
|
||||
struct bpf_object *bpf_object__next(struct bpf_object *prev);
|
||||
#define bpf_object__for_each_safe(pos, tmp) \
|
||||
for ((pos) = bpf_object__next(NULL), \
|
||||
(tmp) = bpf_object__next(pos); \
|
||||
(pos) != NULL; \
|
||||
(pos) = (tmp), (tmp) = bpf_object__next(tmp))
|
||||
|
||||
typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
|
||||
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
||||
bpf_object_clear_priv_t clear_priv);
|
||||
void *bpf_object__priv(struct bpf_object *prog);
|
||||
|
||||
int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
|
||||
enum bpf_attach_type *expected_attach_type);
|
||||
int libbpf_attach_type_by_name(const char *name,
|
||||
enum bpf_attach_type *attach_type);
|
||||
|
||||
/* Accessors of bpf_program */
|
||||
struct bpf_program;
|
||||
struct bpf_program *bpf_program__next(struct bpf_program *prog,
|
||||
struct bpf_object *obj);
|
||||
|
||||
#define bpf_object__for_each_program(pos, obj) \
|
||||
for ((pos) = bpf_program__next(NULL, (obj)); \
|
||||
(pos) != NULL; \
|
||||
(pos) = bpf_program__next((pos), (obj)))
|
||||
|
||||
typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
|
||||
void *);
|
||||
|
||||
int bpf_program__set_priv(struct bpf_program *prog, void *priv,
|
||||
bpf_program_clear_priv_t clear_priv);
|
||||
|
||||
void *bpf_program__priv(struct bpf_program *prog);
|
||||
void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex);
|
||||
|
||||
const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
|
||||
|
||||
int bpf_program__load(struct bpf_program *prog, char *license,
|
||||
__u32 kern_version);
|
||||
int bpf_program__fd(struct bpf_program *prog);
|
||||
int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
|
||||
int instance);
|
||||
int bpf_program__pin(struct bpf_program *prog, const char *path);
|
||||
void bpf_program__unload(struct bpf_program *prog);
|
||||
|
||||
struct bpf_insn;
|
||||
|
||||
/*
|
||||
* Libbpf allows callers to adjust BPF programs before being loaded
|
||||
* into kernel. One program in an object file can be transformed into
|
||||
* multiple variants to be attached to different hooks.
|
||||
*
|
||||
* bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
|
||||
* form an API for this purpose.
|
||||
*
|
||||
* - bpf_program_prep_t:
|
||||
* Defines a 'preprocessor', which is a caller defined function
|
||||
* passed to libbpf through bpf_program__set_prep(), and will be
|
||||
* called before program is loaded. The processor should adjust
|
||||
* the program one time for each instance according to the instance id
|
||||
* passed to it.
|
||||
*
|
||||
* - bpf_program__set_prep:
|
||||
* Attaches a preprocessor to a BPF program. The number of instances
|
||||
* that should be created is also passed through this function.
|
||||
*
|
||||
* - bpf_program__nth_fd:
|
||||
* After the program is loaded, get resulting FD of a given instance
|
||||
* of the BPF program.
|
||||
*
|
||||
* If bpf_program__set_prep() is not used, the program would be loaded
|
||||
* without adjustment during bpf_object__load(). The program has only
|
||||
* one instance. In this case bpf_program__fd(prog) is equal to
|
||||
* bpf_program__nth_fd(prog, 0).
|
||||
*/
|
||||
|
||||
struct bpf_prog_prep_result {
|
||||
/*
|
||||
* If not NULL, load new instruction array.
|
||||
* If set to NULL, don't load this instance.
|
||||
*/
|
||||
struct bpf_insn *new_insn_ptr;
|
||||
int new_insn_cnt;
|
||||
|
||||
/* If not NULL, result FD is written to it. */
|
||||
int *pfd;
|
||||
};
|
||||
|
||||
/*
|
||||
* Parameters of bpf_program_prep_t:
|
||||
* - prog: The bpf_program being loaded.
|
||||
* - n: Index of instance being generated.
|
||||
* - insns: BPF instructions array.
|
||||
* - insns_cnt:Number of instructions in insns.
|
||||
* - res: Output parameter, result of transformation.
|
||||
*
|
||||
* Return value:
|
||||
* - Zero: pre-processing success.
|
||||
* - Non-zero: pre-processing error, stop loading.
|
||||
*/
|
||||
typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
|
||||
struct bpf_insn *insns, int insns_cnt,
|
||||
struct bpf_prog_prep_result *res);
|
||||
|
||||
int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
|
||||
bpf_program_prep_t prep);
|
||||
|
||||
int bpf_program__nth_fd(struct bpf_program *prog, int n);
|
||||
|
||||
/*
|
||||
* Adjust type of BPF program. Default is kprobe.
|
||||
*/
|
||||
int bpf_program__set_socket_filter(struct bpf_program *prog);
|
||||
int bpf_program__set_tracepoint(struct bpf_program *prog);
|
||||
int bpf_program__set_raw_tracepoint(struct bpf_program *prog);
|
||||
int bpf_program__set_kprobe(struct bpf_program *prog);
|
||||
int bpf_program__set_sched_cls(struct bpf_program *prog);
|
||||
int bpf_program__set_sched_act(struct bpf_program *prog);
|
||||
int bpf_program__set_xdp(struct bpf_program *prog);
|
||||
int bpf_program__set_perf_event(struct bpf_program *prog);
|
||||
void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type);
|
||||
void bpf_program__set_expected_attach_type(struct bpf_program *prog,
|
||||
enum bpf_attach_type type);
|
||||
|
||||
bool bpf_program__is_socket_filter(struct bpf_program *prog);
|
||||
bool bpf_program__is_tracepoint(struct bpf_program *prog);
|
||||
bool bpf_program__is_raw_tracepoint(struct bpf_program *prog);
|
||||
bool bpf_program__is_kprobe(struct bpf_program *prog);
|
||||
bool bpf_program__is_sched_cls(struct bpf_program *prog);
|
||||
bool bpf_program__is_sched_act(struct bpf_program *prog);
|
||||
bool bpf_program__is_xdp(struct bpf_program *prog);
|
||||
bool bpf_program__is_perf_event(struct bpf_program *prog);
|
||||
|
||||
/*
|
||||
* No need for __attribute__((packed)), all members of 'bpf_map_def'
|
||||
* are all aligned. In addition, using __attribute__((packed))
|
||||
* would trigger a -Wpacked warning message, and lead to an error
|
||||
* if -Werror is set.
|
||||
*/
|
||||
struct bpf_map_def {
|
||||
unsigned int type;
|
||||
unsigned int key_size;
|
||||
unsigned int value_size;
|
||||
unsigned int max_entries;
|
||||
unsigned int map_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* The 'struct bpf_map' in include/linux/bpf.h is internal to the kernel,
|
||||
* so no need to worry about a name clash.
|
||||
*/
|
||||
struct bpf_map;
|
||||
struct bpf_map *
|
||||
bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
|
||||
|
||||
/*
|
||||
* Get bpf_map through the offset of corresponding struct bpf_map_def
|
||||
* in the BPF object file.
|
||||
*/
|
||||
struct bpf_map *
|
||||
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
|
||||
|
||||
struct bpf_map *
|
||||
bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
|
||||
#define bpf_map__for_each(pos, obj) \
|
||||
for ((pos) = bpf_map__next(NULL, (obj)); \
|
||||
(pos) != NULL; \
|
||||
(pos) = bpf_map__next((pos), (obj)))
|
||||
|
||||
int bpf_map__fd(struct bpf_map *map);
|
||||
const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
|
||||
const char *bpf_map__name(struct bpf_map *map);
|
||||
__u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
|
||||
__u32 bpf_map__btf_value_type_id(const struct bpf_map *map);
|
||||
|
||||
typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
|
||||
int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
||||
bpf_map_clear_priv_t clear_priv);
|
||||
void *bpf_map__priv(struct bpf_map *map);
|
||||
int bpf_map__reuse_fd(struct bpf_map *map, int fd);
|
||||
bool bpf_map__is_offload_neutral(struct bpf_map *map);
|
||||
void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
|
||||
int bpf_map__pin(struct bpf_map *map, const char *path);
|
||||
|
||||
long libbpf_get_error(const void *ptr);
|
||||
|
||||
struct bpf_prog_load_attr {
|
||||
const char *file;
|
||||
enum bpf_prog_type prog_type;
|
||||
enum bpf_attach_type expected_attach_type;
|
||||
int ifindex;
|
||||
};
|
||||
|
||||
int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
|
||||
struct bpf_object **pobj, int *prog_fd);
|
||||
int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
||||
struct bpf_object **pobj, int *prog_fd);
|
||||
|
||||
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
|
||||
|
||||
enum bpf_perf_event_ret {
|
||||
LIBBPF_PERF_EVENT_DONE = 0,
|
||||
LIBBPF_PERF_EVENT_ERROR = -1,
|
||||
LIBBPF_PERF_EVENT_CONT = -2,
|
||||
};
|
||||
|
||||
typedef enum bpf_perf_event_ret (*bpf_perf_event_print_t)(void *event,
|
||||
void *priv);
|
||||
int bpf_perf_event_read_simple(void *mem, unsigned long size,
|
||||
unsigned long page_size,
|
||||
void **buf, size_t *buf_len,
|
||||
bpf_perf_event_print_t fn, void *priv);
|
||||
|
||||
struct nlattr;
|
||||
typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
|
||||
int libbpf_netlink_open(unsigned int *nl_pid);
|
||||
int libbpf_nl_get_link(int sock, unsigned int nl_pid,
|
||||
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
|
||||
int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
|
||||
libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie);
|
||||
int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
|
||||
libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
|
||||
int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
|
||||
libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
|
||||
#endif /* __LIBBPF_LIBBPF_H */
|
||||
62
src/libbpf_errno.c
Normal file
62
src/libbpf_errno.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
||||
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2015 Huawei Inc.
|
||||
* Copyright (C) 2017 Nicira, Inc.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libbpf.h"
|
||||
|
||||
#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
|
||||
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
|
||||
#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
|
||||
|
||||
static const char *libbpf_strerror_table[NR_ERRNO] = {
|
||||
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
|
||||
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
|
||||
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
|
||||
[ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
|
||||
[ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
|
||||
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
|
||||
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
|
||||
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
|
||||
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
|
||||
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
|
||||
[ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message",
|
||||
[ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence",
|
||||
[ERRCODE_OFFSET(NLPARSE)] = "Incorrect netlink message parsing",
|
||||
};
|
||||
|
||||
int libbpf_strerror(int err, char *buf, size_t size)
|
||||
{
|
||||
if (!buf || !size)
|
||||
return -1;
|
||||
|
||||
err = err > 0 ? err : -err;
|
||||
|
||||
if (err < __LIBBPF_ERRNO__START) {
|
||||
int ret;
|
||||
|
||||
ret = strerror_r(err, buf, size);
|
||||
buf[size - 1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (err < __LIBBPF_ERRNO__END) {
|
||||
const char *msg;
|
||||
|
||||
msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
|
||||
snprintf(buf, size, "%s", msg);
|
||||
buf[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(buf, size, "Unknown libbpf error %d", err);
|
||||
buf[size - 1] = '\0';
|
||||
return -1;
|
||||
}
|
||||
337
src/netlink.c
Normal file
337
src/netlink.c
Normal file
@@ -0,0 +1,337 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "bpf.h"
|
||||
#include "libbpf.h"
|
||||
#include "nlattr.h"
|
||||
|
||||
#ifndef SOL_NETLINK
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
|
||||
void *cookie);
|
||||
|
||||
int libbpf_netlink_open(__u32 *nl_pid)
|
||||
{
|
||||
struct sockaddr_nl sa;
|
||||
socklen_t addrlen;
|
||||
int one = 1, ret;
|
||||
int sock;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
|
||||
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||
if (sock < 0)
|
||||
return -errno;
|
||||
|
||||
if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
|
||||
&one, sizeof(one)) < 0) {
|
||||
fprintf(stderr, "Netlink error reporting not supported\n");
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
addrlen = sizeof(sa);
|
||||
if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (addrlen != sizeof(sa)) {
|
||||
ret = -LIBBPF_ERRNO__INTERNAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*nl_pid = sa.nl_pid;
|
||||
return sock;
|
||||
|
||||
cleanup:
|
||||
close(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
|
||||
__dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
|
||||
void *cookie)
|
||||
{
|
||||
bool multipart = true;
|
||||
struct nlmsgerr *err;
|
||||
struct nlmsghdr *nh;
|
||||
char buf[4096];
|
||||
int len, ret;
|
||||
|
||||
while (multipart) {
|
||||
multipart = false;
|
||||
len = recv(sock, buf, sizeof(buf), 0);
|
||||
if (len < 0) {
|
||||
ret = -errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
|
||||
nh = NLMSG_NEXT(nh, len)) {
|
||||
if (nh->nlmsg_pid != nl_pid) {
|
||||
ret = -LIBBPF_ERRNO__WRNGPID;
|
||||
goto done;
|
||||
}
|
||||
if (nh->nlmsg_seq != seq) {
|
||||
ret = -LIBBPF_ERRNO__INVSEQ;
|
||||
goto done;
|
||||
}
|
||||
if (nh->nlmsg_flags & NLM_F_MULTI)
|
||||
multipart = true;
|
||||
switch (nh->nlmsg_type) {
|
||||
case NLMSG_ERROR:
|
||||
err = (struct nlmsgerr *)NLMSG_DATA(nh);
|
||||
if (!err->error)
|
||||
continue;
|
||||
ret = err->error;
|
||||
libbpf_nla_dump_errormsg(nh);
|
||||
goto done;
|
||||
case NLMSG_DONE:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (_fn) {
|
||||
ret = _fn(nh, fn, cookie);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
|
||||
{
|
||||
int sock, seq = 0, ret;
|
||||
struct nlattr *nla, *nla_xdp;
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct ifinfomsg ifinfo;
|
||||
char attrbuf[64];
|
||||
} req;
|
||||
__u32 nl_pid;
|
||||
|
||||
sock = libbpf_netlink_open(&nl_pid);
|
||||
if (sock < 0)
|
||||
return sock;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
req.nh.nlmsg_type = RTM_SETLINK;
|
||||
req.nh.nlmsg_pid = 0;
|
||||
req.nh.nlmsg_seq = ++seq;
|
||||
req.ifinfo.ifi_family = AF_UNSPEC;
|
||||
req.ifinfo.ifi_index = ifindex;
|
||||
|
||||
/* started nested attribute for XDP */
|
||||
nla = (struct nlattr *)(((char *)&req)
|
||||
+ NLMSG_ALIGN(req.nh.nlmsg_len));
|
||||
nla->nla_type = NLA_F_NESTED | IFLA_XDP;
|
||||
nla->nla_len = NLA_HDRLEN;
|
||||
|
||||
/* add XDP fd */
|
||||
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
|
||||
nla_xdp->nla_type = IFLA_XDP_FD;
|
||||
nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
|
||||
memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
|
||||
nla->nla_len += nla_xdp->nla_len;
|
||||
|
||||
/* if user passed in any flags, add those too */
|
||||
if (flags) {
|
||||
nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
|
||||
nla_xdp->nla_type = IFLA_XDP_FLAGS;
|
||||
nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
|
||||
memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
|
||||
nla->nla_len += nla_xdp->nla_len;
|
||||
}
|
||||
|
||||
req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
|
||||
|
||||
if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
|
||||
ret = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
|
||||
|
||||
cleanup:
|
||||
close(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __dump_link_nlmsg(struct nlmsghdr *nlh,
|
||||
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
|
||||
{
|
||||
struct nlattr *tb[IFLA_MAX + 1], *attr;
|
||||
struct ifinfomsg *ifi = NLMSG_DATA(nlh);
|
||||
int len;
|
||||
|
||||
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
|
||||
attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
|
||||
if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
|
||||
return -LIBBPF_ERRNO__NLPARSE;
|
||||
|
||||
return dump_link_nlmsg(cookie, ifi, tb);
|
||||
}
|
||||
|
||||
int libbpf_nl_get_link(int sock, unsigned int nl_pid,
|
||||
libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct ifinfomsg ifm;
|
||||
} req = {
|
||||
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
|
||||
.nlh.nlmsg_type = RTM_GETLINK,
|
||||
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.ifm.ifi_family = AF_PACKET,
|
||||
};
|
||||
int seq = time(NULL);
|
||||
|
||||
req.nlh.nlmsg_seq = seq;
|
||||
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
|
||||
dump_link_nlmsg, cookie);
|
||||
}
|
||||
|
||||
static int __dump_class_nlmsg(struct nlmsghdr *nlh,
|
||||
libbpf_dump_nlmsg_t dump_class_nlmsg,
|
||||
void *cookie)
|
||||
{
|
||||
struct nlattr *tb[TCA_MAX + 1], *attr;
|
||||
struct tcmsg *t = NLMSG_DATA(nlh);
|
||||
int len;
|
||||
|
||||
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
|
||||
attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
|
||||
if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
|
||||
return -LIBBPF_ERRNO__NLPARSE;
|
||||
|
||||
return dump_class_nlmsg(cookie, t, tb);
|
||||
}
|
||||
|
||||
int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
|
||||
libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct tcmsg t;
|
||||
} req = {
|
||||
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
|
||||
.nlh.nlmsg_type = RTM_GETTCLASS,
|
||||
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.t.tcm_family = AF_UNSPEC,
|
||||
.t.tcm_ifindex = ifindex,
|
||||
};
|
||||
int seq = time(NULL);
|
||||
|
||||
req.nlh.nlmsg_seq = seq;
|
||||
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
|
||||
dump_class_nlmsg, cookie);
|
||||
}
|
||||
|
||||
static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
|
||||
libbpf_dump_nlmsg_t dump_qdisc_nlmsg,
|
||||
void *cookie)
|
||||
{
|
||||
struct nlattr *tb[TCA_MAX + 1], *attr;
|
||||
struct tcmsg *t = NLMSG_DATA(nlh);
|
||||
int len;
|
||||
|
||||
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
|
||||
attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
|
||||
if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
|
||||
return -LIBBPF_ERRNO__NLPARSE;
|
||||
|
||||
return dump_qdisc_nlmsg(cookie, t, tb);
|
||||
}
|
||||
|
||||
int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
|
||||
libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct tcmsg t;
|
||||
} req = {
|
||||
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
|
||||
.nlh.nlmsg_type = RTM_GETQDISC,
|
||||
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.t.tcm_family = AF_UNSPEC,
|
||||
.t.tcm_ifindex = ifindex,
|
||||
};
|
||||
int seq = time(NULL);
|
||||
|
||||
req.nlh.nlmsg_seq = seq;
|
||||
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
|
||||
dump_qdisc_nlmsg, cookie);
|
||||
}
|
||||
|
||||
static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
|
||||
libbpf_dump_nlmsg_t dump_filter_nlmsg,
|
||||
void *cookie)
|
||||
{
|
||||
struct nlattr *tb[TCA_MAX + 1], *attr;
|
||||
struct tcmsg *t = NLMSG_DATA(nlh);
|
||||
int len;
|
||||
|
||||
len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
|
||||
attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
|
||||
if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
|
||||
return -LIBBPF_ERRNO__NLPARSE;
|
||||
|
||||
return dump_filter_nlmsg(cookie, t, tb);
|
||||
}
|
||||
|
||||
int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
|
||||
libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct tcmsg t;
|
||||
} req = {
|
||||
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
|
||||
.nlh.nlmsg_type = RTM_GETTFILTER,
|
||||
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||
.t.tcm_family = AF_UNSPEC,
|
||||
.t.tcm_ifindex = ifindex,
|
||||
.t.tcm_parent = handle,
|
||||
};
|
||||
int seq = time(NULL);
|
||||
|
||||
req.nlh.nlmsg_seq = seq;
|
||||
if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
|
||||
return -errno;
|
||||
|
||||
return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
|
||||
dump_filter_nlmsg, cookie);
|
||||
}
|
||||
195
src/nlattr.c
Normal file
195
src/nlattr.c
Normal file
@@ -0,0 +1,195 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
|
||||
/*
|
||||
* NETLINK Netlink attributes
|
||||
*
|
||||
* Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "nlattr.h"
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static uint16_t nla_attr_minlen[LIBBPF_NLA_TYPE_MAX+1] = {
|
||||
[LIBBPF_NLA_U8] = sizeof(uint8_t),
|
||||
[LIBBPF_NLA_U16] = sizeof(uint16_t),
|
||||
[LIBBPF_NLA_U32] = sizeof(uint32_t),
|
||||
[LIBBPF_NLA_U64] = sizeof(uint64_t),
|
||||
[LIBBPF_NLA_STRING] = 1,
|
||||
[LIBBPF_NLA_FLAG] = 0,
|
||||
};
|
||||
|
||||
static struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
|
||||
{
|
||||
int totlen = NLA_ALIGN(nla->nla_len);
|
||||
|
||||
*remaining -= totlen;
|
||||
return (struct nlattr *) ((char *) nla + totlen);
|
||||
}
|
||||
|
||||
static int nla_ok(const struct nlattr *nla, int remaining)
|
||||
{
|
||||
return remaining >= sizeof(*nla) &&
|
||||
nla->nla_len >= sizeof(*nla) &&
|
||||
nla->nla_len <= remaining;
|
||||
}
|
||||
|
||||
static int nla_type(const struct nlattr *nla)
|
||||
{
|
||||
return nla->nla_type & NLA_TYPE_MASK;
|
||||
}
|
||||
|
||||
static int validate_nla(struct nlattr *nla, int maxtype,
|
||||
struct libbpf_nla_policy *policy)
|
||||
{
|
||||
struct libbpf_nla_policy *pt;
|
||||
unsigned int minlen = 0;
|
||||
int type = nla_type(nla);
|
||||
|
||||
if (type < 0 || type > maxtype)
|
||||
return 0;
|
||||
|
||||
pt = &policy[type];
|
||||
|
||||
if (pt->type > LIBBPF_NLA_TYPE_MAX)
|
||||
return 0;
|
||||
|
||||
if (pt->minlen)
|
||||
minlen = pt->minlen;
|
||||
else if (pt->type != LIBBPF_NLA_UNSPEC)
|
||||
minlen = nla_attr_minlen[pt->type];
|
||||
|
||||
if (libbpf_nla_len(nla) < minlen)
|
||||
return -1;
|
||||
|
||||
if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen)
|
||||
return -1;
|
||||
|
||||
if (pt->type == LIBBPF_NLA_STRING) {
|
||||
char *data = libbpf_nla_data(nla);
|
||||
|
||||
if (data[libbpf_nla_len(nla) - 1] != '\0')
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int nlmsg_len(const struct nlmsghdr *nlh)
|
||||
{
|
||||
return nlh->nlmsg_len - NLMSG_HDRLEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attribute index based on a stream of attributes.
|
||||
* @arg tb Index array to be filled (maxtype+1 elements).
|
||||
* @arg maxtype Maximum attribute type expected and accepted.
|
||||
* @arg head Head of attribute stream.
|
||||
* @arg len Length of attribute stream.
|
||||
* @arg policy Attribute validation policy.
|
||||
*
|
||||
* Iterates over the stream of attributes and stores a pointer to each
|
||||
* attribute in the index array using the attribute type as index to
|
||||
* the array. Attribute with a type greater than the maximum type
|
||||
* specified will be silently ignored in order to maintain backwards
|
||||
* compatibility. If \a policy is not NULL, the attribute will be
|
||||
* validated using the specified policy.
|
||||
*
|
||||
* @see nla_validate
|
||||
* @return 0 on success or a negative error code.
|
||||
*/
|
||||
int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
|
||||
int len, struct libbpf_nla_policy *policy)
|
||||
{
|
||||
struct nlattr *nla;
|
||||
int rem, err;
|
||||
|
||||
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
|
||||
|
||||
libbpf_nla_for_each_attr(nla, head, len, rem) {
|
||||
int type = nla_type(nla);
|
||||
|
||||
if (type > maxtype)
|
||||
continue;
|
||||
|
||||
if (policy) {
|
||||
err = validate_nla(nla, maxtype, policy);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (tb[type])
|
||||
fprintf(stderr, "Attribute of type %#x found multiple times in message, "
|
||||
"previous attribute is being ignored.\n", type);
|
||||
|
||||
tb[type] = nla;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
errout:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create attribute index based on nested attribute
|
||||
* @arg tb Index array to be filled (maxtype+1 elements).
|
||||
* @arg maxtype Maximum attribute type expected and accepted.
|
||||
* @arg nla Nested Attribute.
|
||||
* @arg policy Attribute validation policy.
|
||||
*
|
||||
* Feeds the stream of attributes nested into the specified attribute
|
||||
* to libbpf_nla_parse().
|
||||
*
|
||||
* @see libbpf_nla_parse
|
||||
* @return 0 on success or a negative error code.
|
||||
*/
|
||||
int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
|
||||
struct nlattr *nla,
|
||||
struct libbpf_nla_policy *policy)
|
||||
{
|
||||
return libbpf_nla_parse(tb, maxtype, libbpf_nla_data(nla),
|
||||
libbpf_nla_len(nla), policy);
|
||||
}
|
||||
|
||||
/* dump netlink extended ack error message */
|
||||
int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh)
|
||||
{
|
||||
struct libbpf_nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = {
|
||||
[NLMSGERR_ATTR_MSG] = { .type = LIBBPF_NLA_STRING },
|
||||
[NLMSGERR_ATTR_OFFS] = { .type = LIBBPF_NLA_U32 },
|
||||
};
|
||||
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr;
|
||||
struct nlmsgerr *err;
|
||||
char *errmsg = NULL;
|
||||
int hlen, alen;
|
||||
|
||||
/* no TLVs, nothing to do here */
|
||||
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
|
||||
return 0;
|
||||
|
||||
err = (struct nlmsgerr *)NLMSG_DATA(nlh);
|
||||
hlen = sizeof(*err);
|
||||
|
||||
/* if NLM_F_CAPPED is set then the inner err msg was capped */
|
||||
if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
|
||||
hlen += nlmsg_len(&err->msg);
|
||||
|
||||
attr = (struct nlattr *) ((void *) err + hlen);
|
||||
alen = nlh->nlmsg_len - hlen;
|
||||
|
||||
if (libbpf_nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen,
|
||||
extack_policy) != 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to parse extended error attributes\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tb[NLMSGERR_ATTR_MSG])
|
||||
errmsg = (char *) libbpf_nla_data(tb[NLMSGERR_ATTR_MSG]);
|
||||
|
||||
fprintf(stderr, "Kernel error message: %s\n", errmsg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
106
src/nlattr.h
Normal file
106
src/nlattr.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/*
|
||||
* NETLINK Netlink attributes
|
||||
*
|
||||
* Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
#ifndef __LIBBPF_NLATTR_H
|
||||
#define __LIBBPF_NLATTR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/netlink.h>
|
||||
/* avoid multiple definition of netlink features */
|
||||
#define __LINUX_NETLINK_H
|
||||
|
||||
/**
|
||||
* Standard attribute types to specify validation policy
|
||||
*/
|
||||
enum {
|
||||
LIBBPF_NLA_UNSPEC, /**< Unspecified type, binary data chunk */
|
||||
LIBBPF_NLA_U8, /**< 8 bit integer */
|
||||
LIBBPF_NLA_U16, /**< 16 bit integer */
|
||||
LIBBPF_NLA_U32, /**< 32 bit integer */
|
||||
LIBBPF_NLA_U64, /**< 64 bit integer */
|
||||
LIBBPF_NLA_STRING, /**< NUL terminated character string */
|
||||
LIBBPF_NLA_FLAG, /**< Flag */
|
||||
LIBBPF_NLA_MSECS, /**< Micro seconds (64bit) */
|
||||
LIBBPF_NLA_NESTED, /**< Nested attributes */
|
||||
__LIBBPF_NLA_TYPE_MAX,
|
||||
};
|
||||
|
||||
#define LIBBPF_NLA_TYPE_MAX (__LIBBPF_NLA_TYPE_MAX - 1)
|
||||
|
||||
/**
|
||||
* @ingroup attr
|
||||
* Attribute validation policy.
|
||||
*
|
||||
* See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
|
||||
*/
|
||||
struct libbpf_nla_policy {
|
||||
/** Type of attribute or LIBBPF_NLA_UNSPEC */
|
||||
uint16_t type;
|
||||
|
||||
/** Minimal length of payload required */
|
||||
uint16_t minlen;
|
||||
|
||||
/** Maximal length of payload allowed */
|
||||
uint16_t maxlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup attr
|
||||
* Iterate over a stream of attributes
|
||||
* @arg pos loop counter, set to current attribute
|
||||
* @arg head head of attribute stream
|
||||
* @arg len length of attribute stream
|
||||
* @arg rem initialized to len, holds bytes currently remaining in stream
|
||||
*/
|
||||
#define libbpf_nla_for_each_attr(pos, head, len, rem) \
|
||||
for (pos = head, rem = len; \
|
||||
nla_ok(pos, rem); \
|
||||
pos = nla_next(pos, &(rem)))
|
||||
|
||||
/**
|
||||
* libbpf_nla_data - head of payload
|
||||
* @nla: netlink attribute
|
||||
*/
|
||||
static inline void *libbpf_nla_data(const struct nlattr *nla)
|
||||
{
|
||||
return (char *) nla + NLA_HDRLEN;
|
||||
}
|
||||
|
||||
static inline uint8_t libbpf_nla_getattr_u8(const struct nlattr *nla)
|
||||
{
|
||||
return *(uint8_t *)libbpf_nla_data(nla);
|
||||
}
|
||||
|
||||
static inline uint32_t libbpf_nla_getattr_u32(const struct nlattr *nla)
|
||||
{
|
||||
return *(uint32_t *)libbpf_nla_data(nla);
|
||||
}
|
||||
|
||||
static inline const char *libbpf_nla_getattr_str(const struct nlattr *nla)
|
||||
{
|
||||
return (const char *)libbpf_nla_data(nla);
|
||||
}
|
||||
|
||||
/**
|
||||
* libbpf_nla_len - length of payload
|
||||
* @nla: netlink attribute
|
||||
*/
|
||||
static inline int libbpf_nla_len(const struct nlattr *nla)
|
||||
{
|
||||
return nla->nla_len - NLA_HDRLEN;
|
||||
}
|
||||
|
||||
int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
|
||||
int len, struct libbpf_nla_policy *policy);
|
||||
int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
|
||||
struct nlattr *nla,
|
||||
struct libbpf_nla_policy *policy);
|
||||
|
||||
int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh);
|
||||
|
||||
#endif /* __LIBBPF_NLATTR_H */
|
||||
18
src/str_error.c
Normal file
18
src/str_error.c
Normal file
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
#undef _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "str_error.h"
|
||||
|
||||
/*
|
||||
* Wrapper to allow for building in non-GNU systems such as Alpine Linux's musl
|
||||
* libc, while checking strerror_r() return to avoid having to check this in
|
||||
* all places calling it.
|
||||
*/
|
||||
char *libbpf_strerror_r(int err, char *dst, int len)
|
||||
{
|
||||
int ret = strerror_r(err, dst, len);
|
||||
if (ret)
|
||||
snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
|
||||
return dst;
|
||||
}
|
||||
6
src/str_error.h
Normal file
6
src/str_error.h
Normal file
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __LIBBPF_STR_ERROR_H
|
||||
#define __LIBBPF_STR_ERROR_H
|
||||
|
||||
char *libbpf_strerror_r(int err, char *dst, int len);
|
||||
#endif /* __LIBBPF_STR_ERROR_H */
|
||||
Reference in New Issue
Block a user