Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+335
View File
@@ -0,0 +1,335 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig SAMPLES
bool "Sample kernel code"
help
You can build and test sample kernel code here.
if SAMPLES
config SAMPLE_AUXDISPLAY
bool "auxdisplay sample"
depends on CC_CAN_LINK
config SAMPLE_TRACE_EVENTS
tristate "Build trace_events examples -- loadable modules only"
depends on EVENT_TRACING && m
help
This builds the trace event example module.
config SAMPLE_TRACE_CUSTOM_EVENTS
tristate "Build custom trace event example -- loadable modules only"
depends on EVENT_TRACING && m
help
This builds the custom trace event example module.
config SAMPLE_TRACE_PRINTK
tristate "Build trace_printk module - tests various trace_printk formats"
depends on EVENT_TRACING && m
help
This builds a module that calls trace_printk() and can be used to
test various trace_printk() calls from a module.
config SAMPLE_FTRACE_DIRECT
tristate "Build register_ftrace_direct() example"
depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
depends on HAVE_SAMPLE_FTRACE_DIRECT
help
This builds an ftrace direct function example
that hooks to wake_up_process and prints the parameters.
config SAMPLE_FTRACE_DIRECT_MULTI
tristate "Build register_ftrace_direct() on multiple ips example"
depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m
depends on HAVE_SAMPLE_FTRACE_DIRECT_MULTI
help
This builds an ftrace direct function example
that hooks to wake_up_process and schedule, and prints
the function addresses.
config SAMPLE_FTRACE_OPS
tristate "Build custom ftrace ops example"
depends on FUNCTION_TRACER
help
This builds an ftrace ops example that hooks two functions and
measures the time taken to invoke one function a number of times.
config SAMPLE_TRACE_ARRAY
tristate "Build sample module for kernel access to Ftrace instances"
depends on EVENT_TRACING && m
help
This builds a module that demonstrates the use of various APIs to
access Ftrace instances from within the kernel.
config SAMPLE_KOBJECT
tristate "Build kobject examples"
help
This config option will allow you to build a number of
different kobject sample modules showing how to use kobjects,
ksets, and ktypes properly.
If in doubt, say "N" here.
config SAMPLE_KPROBES
tristate "Build kprobes examples -- loadable modules only"
depends on KPROBES && m
help
This build several kprobes example modules.
config SAMPLE_KRETPROBES
tristate "Build kretprobes example -- loadable modules only"
default m
depends on SAMPLE_KPROBES && KRETPROBES
config SAMPLE_HW_BREAKPOINT
tristate "Build kernel hardware breakpoint examples -- loadable module only"
depends on HAVE_HW_BREAKPOINT && m
help
This builds kernel hardware breakpoint example modules.
config SAMPLE_FPROBE
tristate "Build fprobe examples -- loadable modules only"
depends on FPROBE && m
help
This builds a fprobe example module. This module has an option 'symbol'.
You can specify a probed symbol or symbols separated with ','.
config SAMPLE_KFIFO
tristate "Build kfifo examples -- loadable modules only"
depends on m
help
This config option will allow you to build a number of
different kfifo sample modules showing how to use the
generic kfifo API.
If in doubt, say "N" here.
config SAMPLE_KDB
tristate "Build kdb command example -- loadable modules only"
depends on KGDB_KDB && m
help
Build an example of how to dynamically add the hello
command to the kdb shell.
config SAMPLE_QMI_CLIENT
tristate "Build qmi client sample -- loadable modules only"
depends on m
depends on ARCH_QCOM
depends on NET
select QCOM_QMI_HELPERS
help
Build an QMI client sample driver, which demonstrates how to
communicate with a remote QRTR service, using QMI encoded messages.
config SAMPLE_RPMSG_CLIENT
tristate "Build rpmsg client sample -- loadable modules only"
depends on RPMSG && m
help
Build an rpmsg client sample driver, which demonstrates how
to communicate with an AMP-configured remote processor over
the rpmsg bus.
config SAMPLE_LIVEPATCH
tristate "Build live patching samples -- loadable modules only"
depends on LIVEPATCH && m
help
Build sample live patch demonstrations.
config SAMPLE_CONFIGFS
tristate "Build configfs patching sample -- loadable modules only"
depends on CONFIGFS_FS && m
help
Builds a sample configfs interface.
config SAMPLE_CONNECTOR
tristate "Build connector sample -- loadable modules only"
depends on CONNECTOR && HEADERS_INSTALL && m
help
When enabled, this builds both a sample kernel module for
the connector interface and a user space tool to communicate
with it.
See also Documentation/driver-api/connector.rst
config SAMPLE_FANOTIFY_ERROR
bool "Build fanotify error monitoring sample"
depends on FANOTIFY && CC_CAN_LINK && HEADERS_INSTALL
help
When enabled, this builds an example code that uses the
FAN_FS_ERROR fanotify mechanism to monitor filesystem
errors.
See also Documentation/admin-guide/filesystem-monitoring.rst.
config SAMPLE_HIDRAW
bool "hidraw sample"
depends on CC_CAN_LINK && HEADERS_INSTALL
config SAMPLE_LANDLOCK
bool "Landlock example"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build a simple Landlock sandbox manager able to start a process
restricted by a user-defined filesystem access control policy.
config SAMPLE_PIDFD
bool "pidfd sample"
depends on CC_CAN_LINK && HEADERS_INSTALL
config SAMPLE_SECCOMP
bool "Build seccomp sample code"
depends on SECCOMP_FILTER && CC_CAN_LINK && HEADERS_INSTALL
help
Build samples of seccomp filters using various methods of
BPF filter construction.
config SAMPLE_TIMER
bool "Timer sample"
depends on CC_CAN_LINK && HEADERS_INSTALL
config SAMPLE_TSM_MR
tristate "TSM measurement sample"
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_SHA512
select TSM_MEASUREMENTS
select VIRT_DRIVERS
help
Build a sample module that emulates MRs (Measurement Registers) and
exposes them to user mode applications through the TSM sysfs
interface (/sys/class/misc/tsm_mr_sample/emulated_mr/).
The module name will be tsm-mr-sample when built as a module.
config SAMPLE_UHID
bool "UHID sample"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build UHID sample program.
config SAMPLE_VFIO_MDEV_MTTY
tristate "Build VFIO mtty example mediated device sample code"
depends on VFIO
select VFIO_MDEV
help
Build a virtual tty sample driver for use as a VFIO
mediated device
config SAMPLE_VFIO_MDEV_MDPY
tristate "Build VFIO mdpy example mediated device sample code"
depends on VFIO
select VFIO_MDEV
help
Build a virtual display sample driver for use as a VFIO
mediated device. It is a simple framebuffer and supports
the region display interface (VFIO_GFX_PLANE_TYPE_REGION).
config SAMPLE_VFIO_MDEV_MDPY_FB
tristate "Build VFIO mdpy example guest fbdev driver"
depends on FB
select FB_IOMEM_HELPERS
help
Guest fbdev driver for the virtual display sample driver.
config SAMPLE_VFIO_MDEV_MBOCHS
tristate "Build VFIO mbochs example mediated device sample code"
depends on VFIO
select VFIO_MDEV
select DMA_SHARED_BUFFER
help
Build a virtual display sample driver for use as a VFIO
mediated device. It supports the region display interface
(VFIO_GFX_PLANE_TYPE_DMABUF).
Emulate enough of qemu stdvga to make bochs-drm.ko happy.
That is basically the vram memory bar and the bochs dispi
interface vbe registers in the mmio register bar.
Specifically it does *not* include any legacy vga stuff.
Device looks a lot like "qemu -device secondary-vga".
config SAMPLE_ANDROID_BINDERFS
bool "Build Android binderfs example"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Builds a sample program to illustrate the use of the Android binderfs
filesystem.
config SAMPLE_VFS
bool "Build example programs that use new VFS system calls"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build example userspace programs that use new VFS system calls such
as mount API and statx(). Note that this is restricted to the x86
arch whilst it accesses system calls that aren't yet in all arches.
config SAMPLE_INTEL_MEI
bool "Build example program working with intel mei driver"
depends on INTEL_MEI
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build a sample program to work with mei device.
config SAMPLE_TPS6594_PFSM
bool "Build example program working with TPS6594 PFSM driver"
depends on HEADERS_INSTALL
depends on CC_CAN_LINK
help
Build a sample program to work with PFSM devices.
config SAMPLE_WATCHDOG
bool "watchdog sample"
depends on CC_CAN_LINK
config SAMPLE_WATCH_QUEUE
bool "Build example watch_queue notification API consumer"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build example userspace program to use the new mount_notify(),
sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
config SAMPLE_CORESIGHT_SYSCFG
tristate "Build example loadable module for CoreSight config"
depends on CORESIGHT && m
help
Build an example loadable module that adds new CoreSight features
and configuration using the CoreSight system configuration API.
This demonstrates how a user may create their own CoreSight
configurations and easily load them into the system at runtime.
config SAMPLE_KMEMLEAK
tristate "Simple test for the kernel memory leak detector"
depends on DEBUG_KMEMLEAK && m
help
Build a sample program which have explicitly leaks memory to test
kmemleak.
config SAMPLE_CGROUP
bool "Build cgroup sample code"
depends on CGROUPS && CC_CAN_LINK && HEADERS_INSTALL
help
Build samples that demonstrate the usage of the cgroup API.
config SAMPLE_CHECK_EXEC
bool "Exec secure bits examples"
depends on CC_CAN_LINK && HEADERS_INSTALL
help
Build a tool to easily configure SECBIT_EXEC_RESTRICT_FILE and
SECBIT_EXEC_DENY_INTERACTIVE, and a simple script interpreter to
demonstrate how they should be used with execveat(2) +
AT_EXECVE_CHECK.
config SAMPLE_HUNG_TASK
tristate "Hung task detector test code"
depends on DETECT_HUNG_TASK && DEBUG_FS
help
Build a module that provides debugfs files (e.g., mutex, semaphore,
rw_semaphore_read, rw_semaphore_write) under <debugfs>/hung_task.
Reading these files with multiple processes triggers hung task
detection by holding locks for a long time (256 seconds).
source "samples/rust/Kconfig"
source "samples/damon/Kconfig"
endif # SAMPLES
config HAVE_SAMPLE_FTRACE_DIRECT
bool
config HAVE_SAMPLE_FTRACE_DIRECT_MULTI
bool
+47
View File
@@ -0,0 +1,47 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for Linux samples code
subdir-$(CONFIG_SAMPLE_AUXDISPLAY) += auxdisplay
subdir-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs
subdir-$(CONFIG_SAMPLE_CHECK_EXEC) += check-exec
subdir-$(CONFIG_SAMPLE_CGROUP) += cgroup
obj-$(CONFIG_SAMPLE_CONFIGFS) += configfs/
obj-$(CONFIG_SAMPLE_CONNECTOR) += connector/
obj-$(CONFIG_SAMPLE_FANOTIFY_ERROR) += fanotify/
subdir-$(CONFIG_SAMPLE_HIDRAW) += hidraw
obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += hw_breakpoint/
obj-$(CONFIG_SAMPLE_KDB) += kdb/
obj-$(CONFIG_SAMPLE_KFIFO) += kfifo/
obj-$(CONFIG_SAMPLE_KOBJECT) += kobject/
obj-$(CONFIG_SAMPLE_KPROBES) += kprobes/
subdir-$(CONFIG_SAMPLE_LANDLOCK) += landlock
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch/
subdir-$(CONFIG_SAMPLE_PIDFD) += pidfd
obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi/
obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg/
subdir-$(CONFIG_SAMPLE_SECCOMP) += seccomp
subdir-$(CONFIG_SAMPLE_TIMER) += timers
obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace_events/
obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_events/
obj-$(CONFIG_SAMPLE_TRACE_PRINTK) += trace_printk/
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace/
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace/
obj-$(CONFIG_SAMPLE_FTRACE_OPS) += ftrace/
obj-$(CONFIG_SAMPLE_TRACE_ARRAY) += ftrace/
subdir-$(CONFIG_SAMPLE_UHID) += uhid
obj-$(CONFIG_VIDEO_PCI_SKELETON) += v4l/
obj-y += vfio-mdev/
subdir-$(CONFIG_SAMPLE_VFS) += vfs
obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/
obj-$(CONFIG_SAMPLE_TPS6594_PFSM) += pfsm/
subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog
subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue
obj-$(CONFIG_SAMPLE_KMEMLEAK) += kmemleak/
obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/
obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/
obj-$(CONFIG_SAMPLES_RUST) += rust/
obj-$(CONFIG_SAMPLE_DAMON_WSSE) += damon/
obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/
obj-$(CONFIG_SAMPLE_DAMON_MTIER) += damon/
obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/
obj-$(CONFIG_SAMPLE_TSM_MR) += tsm-mr/
+12
View File
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
.PHONY: vm-sample
vm-sample: vm-sample.o payload.o
$(CC) $^ -o $@
payload.o: payload.ld guest16.o
$(LD) -T $< -o $@
clean:
rm *.o vm-sample
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
OUTPUT_FORMAT(binary)
SECTIONS
{
.start : { *(.start) }
.text : { *(.text*) }
.rodata : { *(.rodata) }
.data : { *(.data) }
}
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
SECTIONS
{
.payload16 0 : {
guest16 = .;
guest16.o(.text)
guest16_end = .;
}
}
+132
View File
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0
/*
* A sample program to run a User VM on the ACRN hypervisor
*
* This sample runs in a Service VM, which is a privileged VM of ACRN.
* CONFIG_ACRN_HSM needs to be enabled in the Service VM.
*
* Guest VM code in guest16.s will be executed after the VM launched.
*
* Copyright (C) 2020 Intel Corporation. All rights reserved.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/acrn.h>
#define GUEST_MEMORY_SIZE (1024*1024)
void *guest_memory;
extern const unsigned char guest16[], guest16_end[];
static char io_request_page[4096] __attribute__((aligned(4096)));
static struct acrn_io_request *io_req_buf = (struct acrn_io_request *)io_request_page;
__u16 vcpu_num;
__u16 vmid;
int hsm_fd;
int is_running = 1;
void vm_exit(int sig)
{
sig = sig;
is_running = 0;
ioctl(hsm_fd, ACRN_IOCTL_PAUSE_VM, vmid);
ioctl(hsm_fd, ACRN_IOCTL_DESTROY_IOREQ_CLIENT, 0);
}
int main(int argc, char **argv)
{
int vcpu_id, ret;
struct acrn_vm_creation create_vm = {0};
struct acrn_vm_memmap ram_map = {0};
struct acrn_vcpu_regs regs;
struct acrn_io_request *io_req;
struct acrn_ioreq_notify __attribute__((aligned(8))) notify;
argc = argc;
argv = argv;
ret = posix_memalign(&guest_memory, 4096, GUEST_MEMORY_SIZE);
if (ret < 0) {
printf("Not enough memory!\n");
return -1;
}
hsm_fd = open("/dev/acrn_hsm", O_RDWR|O_CLOEXEC);
create_vm.ioreq_buf = (__u64)io_req_buf;
ret = ioctl(hsm_fd, ACRN_IOCTL_CREATE_VM, &create_vm);
printf("Created VM! [%d]\n", ret);
vcpu_num = create_vm.vcpu_num;
vmid = create_vm.vmid;
/* setup guest memory */
ram_map.type = ACRN_MEMMAP_RAM;
ram_map.vma_base = (__u64)guest_memory;
ram_map.len = GUEST_MEMORY_SIZE;
ram_map.user_vm_pa = 0;
ram_map.attr = ACRN_MEM_ACCESS_RWX;
ret = ioctl(hsm_fd, ACRN_IOCTL_SET_MEMSEG, &ram_map);
printf("Set up VM memory! [%d]\n", ret);
memcpy(guest_memory, guest16, guest16_end-guest16);
/* setup vcpu registers */
memset(&regs, 0, sizeof(regs));
regs.vcpu_id = 0;
regs.vcpu_regs.rip = 0;
/* CR0_ET | CR0_NE */
regs.vcpu_regs.cr0 = 0x30U;
regs.vcpu_regs.cs_ar = 0x009FU;
regs.vcpu_regs.cs_sel = 0xF000U;
regs.vcpu_regs.cs_limit = 0xFFFFU;
regs.vcpu_regs.cs_base = 0 & 0xFFFF0000UL;
regs.vcpu_regs.rip = 0 & 0xFFFFUL;
ret = ioctl(hsm_fd, ACRN_IOCTL_SET_VCPU_REGS, &regs);
printf("Set up VM BSP registers! [%d]\n", ret);
/* create an ioreq client for this VM */
ret = ioctl(hsm_fd, ACRN_IOCTL_CREATE_IOREQ_CLIENT, 0);
printf("Created IO request client! [%d]\n", ret);
/* run vm */
ret = ioctl(hsm_fd, ACRN_IOCTL_START_VM, vmid);
printf("Start VM! [%d]\n", ret);
signal(SIGINT, vm_exit);
while (is_running) {
ret = ioctl(hsm_fd, ACRN_IOCTL_ATTACH_IOREQ_CLIENT, 0);
for (vcpu_id = 0; vcpu_id < vcpu_num; vcpu_id++) {
io_req = &io_req_buf[vcpu_id];
if ((__sync_add_and_fetch(&io_req->processed, 0) == ACRN_IOREQ_STATE_PROCESSING)
&& (!io_req->kernel_handled))
if (io_req->type == ACRN_IOREQ_TYPE_PORTIO) {
int bytes, port, in;
port = io_req->reqs.pio_request.address;
bytes = io_req->reqs.pio_request.size;
in = (io_req->reqs.pio_request.direction == ACRN_IOREQ_DIR_READ);
printf("Guest VM %s PIO[%x] with size[%x]\n", in ? "read" : "write", port, bytes);
notify.vmid = vmid;
notify.vcpu = vcpu_id;
ioctl(hsm_fd, ACRN_IOCTL_NOTIFY_REQUEST_FINISH, &notify);
}
}
}
ret = ioctl(hsm_fd, ACRN_IOCTL_DESTROY_VM, NULL);
printf("Destroy VM! [%d]\n", ret);
close(hsm_fd);
free(guest_memory);
return 0;
}
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
/cfag12864b-example
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
userprogs-always-y += cfag12864b-example
@@ -0,0 +1,267 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Filename: cfag12864b-example.c
* Version: 0.1.0
* Description: cfag12864b LCD userspace example program
*
* Author: Copyright (C) Miguel Ojeda <ojeda@kernel.org>
* Date: 2006-10-31
*/
/*
* ------------------------
* start of cfag12864b code
* ------------------------
*/
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define CFAG12864B_WIDTH (128)
#define CFAG12864B_HEIGHT (64)
#define CFAG12864B_SIZE (128 * 64 / 8)
#define CFAG12864B_BPB (8)
#define CFAG12864B_ADDRESS(x, y) ((y) * CFAG12864B_WIDTH / \
CFAG12864B_BPB + (x) / CFAG12864B_BPB)
#define CFAG12864B_BIT(n) (((unsigned char) 1) << (n))
#undef CFAG12864B_DOCHECK
#ifdef CFAG12864B_DOCHECK
#define CFAG12864B_CHECK(x, y) ((x) < CFAG12864B_WIDTH && \
(y) < CFAG12864B_HEIGHT)
#else
#define CFAG12864B_CHECK(x, y) (1)
#endif
int cfag12864b_fd;
unsigned char * cfag12864b_mem;
unsigned char cfag12864b_buffer[CFAG12864B_SIZE];
/*
* init a cfag12864b framebuffer device
*
* No error: return = 0
* Unable to open: return = -1
* Unable to mmap: return = -2
*/
static int cfag12864b_init(char *path)
{
cfag12864b_fd = open(path, O_RDWR);
if (cfag12864b_fd == -1)
return -1;
cfag12864b_mem = mmap(0, CFAG12864B_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, cfag12864b_fd, 0);
if (cfag12864b_mem == MAP_FAILED) {
close(cfag12864b_fd);
return -2;
}
return 0;
}
/*
* exit a cfag12864b framebuffer device
*/
static void cfag12864b_exit(void)
{
munmap(cfag12864b_mem, CFAG12864B_SIZE);
close(cfag12864b_fd);
}
/*
* set (x, y) pixel
*/
static void cfag12864b_set(unsigned char x, unsigned char y)
{
if (CFAG12864B_CHECK(x, y))
cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] |=
CFAG12864B_BIT(x % CFAG12864B_BPB);
}
/*
* unset (x, y) pixel
*/
static void cfag12864b_unset(unsigned char x, unsigned char y)
{
if (CFAG12864B_CHECK(x, y))
cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &=
~CFAG12864B_BIT(x % CFAG12864B_BPB);
}
/*
* is set (x, y) pixel?
*
* Pixel off: return = 0
* Pixel on: return = 1
*/
static unsigned char cfag12864b_isset(unsigned char x, unsigned char y)
{
if (CFAG12864B_CHECK(x, y))
if (cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &
CFAG12864B_BIT(x % CFAG12864B_BPB))
return 1;
return 0;
}
/*
* not (x, y) pixel
*/
static void cfag12864b_not(unsigned char x, unsigned char y)
{
if (cfag12864b_isset(x, y))
cfag12864b_unset(x, y);
else
cfag12864b_set(x, y);
}
/*
* fill (set all pixels)
*/
static void cfag12864b_fill(void)
{
unsigned short i;
for (i = 0; i < CFAG12864B_SIZE; i++)
cfag12864b_buffer[i] = 0xFF;
}
/*
* clear (unset all pixels)
*/
static void cfag12864b_clear(void)
{
unsigned short i;
for (i = 0; i < CFAG12864B_SIZE; i++)
cfag12864b_buffer[i] = 0;
}
/*
* format a [128*64] matrix
*
* Pixel off: src[i] = 0
* Pixel on: src[i] > 0
*/
static void cfag12864b_format(unsigned char * matrix)
{
unsigned char i, j, n;
for (i = 0; i < CFAG12864B_HEIGHT; i++)
for (j = 0; j < CFAG12864B_WIDTH / CFAG12864B_BPB; j++) {
cfag12864b_buffer[i * CFAG12864B_WIDTH / CFAG12864B_BPB +
j] = 0;
for (n = 0; n < CFAG12864B_BPB; n++)
if (matrix[i * CFAG12864B_WIDTH +
j * CFAG12864B_BPB + n])
cfag12864b_buffer[i * CFAG12864B_WIDTH /
CFAG12864B_BPB + j] |=
CFAG12864B_BIT(n);
}
}
/*
* blit buffer to lcd
*/
static void cfag12864b_blit(void)
{
memcpy(cfag12864b_mem, cfag12864b_buffer, CFAG12864B_SIZE);
}
/*
* ----------------------
* end of cfag12864b code
* ----------------------
*/
#include <stdio.h>
#define EXAMPLES 6
static void example(unsigned char n)
{
unsigned short i, j;
unsigned char matrix[CFAG12864B_WIDTH * CFAG12864B_HEIGHT];
if (n > EXAMPLES)
return;
printf("Example %i/%i - ", n, EXAMPLES);
switch (n) {
case 1:
printf("Draw points setting bits");
cfag12864b_clear();
for (i = 0; i < CFAG12864B_WIDTH; i += 2)
for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
cfag12864b_set(i, j);
break;
case 2:
printf("Clear the LCD");
cfag12864b_clear();
break;
case 3:
printf("Draw rows formatting a [128*64] matrix");
memset(matrix, 0, CFAG12864B_WIDTH * CFAG12864B_HEIGHT);
for (i = 0; i < CFAG12864B_WIDTH; i++)
for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
matrix[j * CFAG12864B_WIDTH + i] = 1;
cfag12864b_format(matrix);
break;
case 4:
printf("Fill the lcd");
cfag12864b_fill();
break;
case 5:
printf("Draw columns unsetting bits");
for (i = 0; i < CFAG12864B_WIDTH; i += 2)
for (j = 0; j < CFAG12864B_HEIGHT; j++)
cfag12864b_unset(i, j);
break;
case 6:
printf("Do negative not-ing all bits");
for (i = 0; i < CFAG12864B_WIDTH; i++)
for (j = 0; j < CFAG12864B_HEIGHT; j ++)
cfag12864b_not(i, j);
break;
}
puts(" - [Press Enter]");
}
int main(int argc, char *argv[])
{
unsigned char n;
if (argc != 2) {
printf(
"Syntax: %s fbdev\n"
"Usually: /dev/fb0, /dev/fb1...\n", argv[0]);
return -1;
}
if (cfag12864b_init(argv[1])) {
printf("Can't init %s fbdev\n", argv[1]);
return -2;
}
for (n = 1; n <= EXAMPLES; n++) {
example(n);
cfag12864b_blit();
while (getchar() != '\n');
}
cfag12864b_exit();
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0
/binderfs_example
+4
View File
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
userprogs-always-y += binderfs_example
userccflags += -I usr/include
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/android/binder.h>
#include <linux/android/binderfs.h>
int main(int argc, char *argv[])
{
int fd, ret, saved_errno;
struct binderfs_device device = { 0 };
ret = unshare(CLONE_NEWNS);
if (ret < 0) {
fprintf(stderr, "%s - Failed to unshare mount namespace\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
if (ret < 0) {
fprintf(stderr, "%s - Failed to mount / as private\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = mkdir("/dev/binderfs", 0755);
if (ret < 0 && errno != EEXIST) {
fprintf(stderr, "%s - Failed to create binderfs mountpoint\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = mount(NULL, "/dev/binderfs", "binder", 0, 0);
if (ret < 0) {
fprintf(stderr, "%s - Failed to mount binderfs\n",
strerror(errno));
exit(EXIT_FAILURE);
}
memcpy(device.name, "my-binder", strlen("my-binder"));
fd = open("/dev/binderfs/binder-control", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
fprintf(stderr, "%s - Failed to open binder-control device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
ret = ioctl(fd, BINDER_CTL_ADD, &device);
saved_errno = errno;
close(fd);
errno = saved_errno;
if (ret < 0) {
fprintf(stderr, "%s - Failed to allocate new binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
printf("Allocated new binder device with major %d, minor %d, and name %s\n",
device.major, device.minor, device.name);
ret = unlink("/dev/binderfs/my-binder");
if (ret < 0) {
fprintf(stderr, "%s - Failed to delete binder device\n",
strerror(errno));
exit(EXIT_FAILURE);
}
/* Cleanup happens when the mount namespace dies. */
exit(EXIT_SUCCESS);
}
+51
View File
@@ -0,0 +1,51 @@
# SPDX-License-Identifier: GPL-2.0-only
cpustat
fds_example
hbm
ibumad
lathist
lwt_len_hist
map_perf_test
offwaketime
per_socket_stats_example
sampleip
sock_example
sockex1
sockex2
sockex3
spintest
syscall_nrs.h
syscall_tp
task_fd_query
tc_l2_redirect
test_cgrp2_array_pin
test_cgrp2_attach
test_cgrp2_attach2
test_cgrp2_sock
test_cgrp2_sock2
test_current_task_under_cgroup
test_lru_dist
test_map_in_map
test_overhead
test_probe_write_user
trace_event
trace_output
tracex1
tracex2
tracex3
tracex4
tracex5
tracex6
tracex7
xdp_adjust_tail
xdp_fwd
xdp_router_ipv4
xdp_tx_iptunnel
testfile.img
hbm_out.log
iperf.*
*.out
*.skel.h
/vmlinux.h
/bpftool/
/libbpf/
+392
View File
@@ -0,0 +1,392 @@
# SPDX-License-Identifier: GPL-2.0
BPF_SAMPLES_PATH ?= $(abspath $(src))
TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools
pound := \#
# List of programs to build
tprogs-y := test_lru_dist
tprogs-y += sock_example
tprogs-y += fds_example
tprogs-y += sockex1
tprogs-y += sockex2
tprogs-y += sockex3
tprogs-y += tracex1
tprogs-y += tracex3
tprogs-y += tracex4
tprogs-y += tracex5
tprogs-y += tracex6
tprogs-y += trace_output
tprogs-y += lathist
tprogs-y += offwaketime
tprogs-y += spintest
tprogs-y += map_perf_test
tprogs-y += xdp_router_ipv4
tprogs-y += trace_event
tprogs-y += sampleip
tprogs-y += tc_l2_redirect
tprogs-y += lwt_len_hist
tprogs-y += xdp_tx_iptunnel
tprogs-y += test_map_in_map
tprogs-y += per_socket_stats_example
tprogs-y += syscall_tp
tprogs-y += cpustat
tprogs-y += xdp_adjust_tail
tprogs-y += xdp_fwd
tprogs-y += task_fd_query
tprogs-y += ibumad
tprogs-y += hbm
# Libbpf dependencies
LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf
LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf
LIBBPF_DESTDIR = $(LIBBPF_OUTPUT)
LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include
LIBBPF = $(LIBBPF_OUTPUT)/libbpf.a
CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
TRACE_HELPERS := ../../tools/testing/selftests/bpf/trace_helpers.o
XDP_SAMPLE := xdp_sample_user.o
fds_example-objs := fds_example.o
sockex1-objs := sockex1_user.o
sockex2-objs := sockex2_user.o
sockex3-objs := sockex3_user.o
tracex1-objs := tracex1_user.o $(TRACE_HELPERS)
tracex3-objs := tracex3_user.o
tracex4-objs := tracex4_user.o
tracex5-objs := tracex5_user.o $(TRACE_HELPERS)
tracex6-objs := tracex6_user.o
trace_output-objs := trace_output_user.o
lathist-objs := lathist_user.o
offwaketime-objs := offwaketime_user.o $(TRACE_HELPERS)
spintest-objs := spintest_user.o $(TRACE_HELPERS)
map_perf_test-objs := map_perf_test_user.o
test_overhead-objs := test_overhead_user.o
trace_event-objs := trace_event_user.o $(TRACE_HELPERS)
sampleip-objs := sampleip_user.o $(TRACE_HELPERS)
tc_l2_redirect-objs := tc_l2_redirect_user.o
lwt_len_hist-objs := lwt_len_hist_user.o
xdp_tx_iptunnel-objs := xdp_tx_iptunnel_user.o
test_map_in_map-objs := test_map_in_map_user.o
per_socket_stats_example-objs := cookie_uid_helper_example.o
syscall_tp-objs := syscall_tp_user.o
cpustat-objs := cpustat_user.o
xdp_adjust_tail-objs := xdp_adjust_tail_user.o
xdp_fwd-objs := xdp_fwd_user.o
task_fd_query-objs := task_fd_query_user.o $(TRACE_HELPERS)
ibumad-objs := ibumad_user.o
hbm-objs := hbm.o $(CGROUP_HELPERS)
xdp_router_ipv4-objs := xdp_router_ipv4_user.o $(XDP_SAMPLE)
# Tell kbuild to always build the programs
always-y := $(tprogs-y)
always-y += sockex1_kern.o
always-y += sockex2_kern.o
always-y += sockex3_kern.o
always-y += tracex1.bpf.o
always-y += tracex3.bpf.o
always-y += tracex4.bpf.o
always-y += tracex5.bpf.o
always-y += tracex6.bpf.o
always-y += trace_output.bpf.o
always-y += tcbpf1_kern.o
always-y += tc_l2_redirect_kern.o
always-y += lathist_kern.o
always-y += offwaketime.bpf.o
always-y += spintest.bpf.o
always-y += map_perf_test.bpf.o
always-y += parse_varlen.o parse_simple.o parse_ldabs.o
always-y += trace_event_kern.o
always-y += sampleip_kern.o
always-y += lwt_len_hist.bpf.o
always-y += xdp_tx_iptunnel_kern.o
always-y += test_map_in_map.bpf.o
always-y += tcp_synrto_kern.o
always-y += tcp_rwnd_kern.o
always-y += tcp_bufs_kern.o
always-y += tcp_cong_kern.o
always-y += tcp_iw_kern.o
always-y += tcp_clamp_kern.o
always-y += tcp_basertt_kern.o
always-y += tcp_tos_reflect_kern.o
always-y += tcp_dumpstats_kern.o
always-y += xdp2skb_meta_kern.o
always-y += syscall_tp_kern.o
always-y += cpustat_kern.o
always-y += xdp_adjust_tail_kern.o
always-y += xdp_fwd_kern.o
always-y += task_fd_query_kern.o
always-y += ibumad_kern.o
always-y += hbm_out_kern.o
always-y += hbm_edt_kern.o
COMMON_CFLAGS = $(TPROGS_USER_CFLAGS)
TPROGS_LDFLAGS = $(TPROGS_USER_LDFLAGS)
ifeq ($(ARCH), arm)
# Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux
# headers when arm instruction set identification is requested.
ARM_ARCH_SELECTOR := $(filter -D__LINUX_ARM_ARCH__%, $(KBUILD_CFLAGS))
BPF_EXTRA_CFLAGS := $(ARM_ARCH_SELECTOR)
TPROGS_CFLAGS += $(ARM_ARCH_SELECTOR)
endif
ifeq ($(ARCH), mips)
TPROGS_CFLAGS += -D__SANE_USERSPACE_TYPES__
ifdef CONFIG_MACH_LOONGSON64
BPF_EXTRA_CFLAGS += -I$(srctree)/arch/mips/include/asm/mach-loongson64
BPF_EXTRA_CFLAGS += -I$(srctree)/arch/mips/include/asm/mach-generic
endif
endif
ifeq ($(ARCH), x86)
BPF_EXTRA_CFLAGS += -fcf-protection
endif
COMMON_CFLAGS += -Wall -O2
COMMON_CFLAGS += -Wmissing-prototypes
COMMON_CFLAGS += -Wstrict-prototypes
COMMON_CFLAGS += $(call try-run,\
printf "int main() { return 0; }" |\
$(CC) -Werror -fsanitize=bounds -x c - -o "$$TMP",-fsanitize=bounds,)
TPROGS_CFLAGS += $(COMMON_CFLAGS)
TPROGS_CFLAGS += -I$(objtree)/usr/include
TPROGS_CFLAGS += -I$(srctree)/tools/testing/selftests/bpf/
TPROGS_CFLAGS += -I$(LIBBPF_INCLUDE)
TPROGS_CFLAGS += -I$(srctree)/tools/include
TPROGS_CFLAGS += -I$(srctree)/tools/perf
TPROGS_CFLAGS += -I$(srctree)/tools/lib
TPROGS_CFLAGS += -DHAVE_ATTR_TEST=0
ifdef SYSROOT
COMMON_CFLAGS += --sysroot=$(SYSROOT)
TPROGS_LDFLAGS := -L$(SYSROOT)/usr/lib
endif
TPROGS_LDLIBS += $(LIBBPF) -lelf -lz
TPROGLDLIBS_xdp_router_ipv4 += -lm -pthread
TPROGLDLIBS_tracex4 += -lrt
TPROGLDLIBS_trace_output += -lrt
TPROGLDLIBS_map_perf_test += -lrt
# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
# make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang
LLC ?= llc
CLANG ?= clang
OPT ?= opt
LLVM_DIS ?= llvm-dis
LLVM_OBJCOPY ?= llvm-objcopy
LLVM_READELF ?= llvm-readelf
BTF_PAHOLE ?= pahole
# Detect that we're cross compiling and use the cross compiler
ifdef CROSS_COMPILE
CLANG_ARCH_ARGS = --target=$(notdir $(CROSS_COMPILE:%-=%))
endif
# Don't evaluate probes and warnings if we need to run make recursively
ifneq ($(src),)
HDR_PROBE := $(shell printf "$(pound)include <linux/types.h>\n struct list_head { int a; }; int main() { return 0; }" | \
$(CC) $(TPROGS_CFLAGS) $(TPROGS_LDFLAGS) -x c - \
-o /dev/null 2>/dev/null && echo okay)
ifeq ($(HDR_PROBE),)
$(warning WARNING: Detected possible issues with include path.)
$(warning WARNING: Please install kernel headers locally (make headers_install).)
endif
BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
$(CLANG) --target=bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
$(LLVM_READELF) -S ./llvm_btf_verify.o | grep BTF; \
/bin/rm -f ./llvm_btf_verify.o)
BPF_EXTRA_CFLAGS += -fno-stack-protector
ifneq ($(BTF_LLVM_PROBE),)
BPF_EXTRA_CFLAGS += -g
else
ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),)
BPF_EXTRA_CFLAGS += -g
LLC_FLAGS += -mattr=dwarfris
DWARF2BTF = y
endif
endif
endif
# Trick to allow make to be run from this directory
all:
$(MAKE) -C ../../ M=$(CURDIR) BPF_SAMPLES_PATH=$(CURDIR)
clean:
$(MAKE) -C ../../ M=$(CURDIR) clean
@find $(CURDIR) -type f -name '*~' -delete
@$(RM) -r $(CURDIR)/libbpf $(CURDIR)/bpftool
$(LIBBPF): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(LIBBPF_OUTPUT)
# Fix up variables inherited from Kbuild that tools/ build system won't like
$(MAKE) -C $(LIBBPF_SRC) RM='rm -rf' EXTRA_CFLAGS="$(COMMON_CFLAGS)" \
LDFLAGS="$(TPROGS_LDFLAGS)" srctree=$(BPF_SAMPLES_PATH)/../../ \
O= OUTPUT=$(LIBBPF_OUTPUT)/ DESTDIR=$(LIBBPF_DESTDIR) prefix= \
$@ install_headers
BPFTOOLDIR := $(TOOLS_PATH)/bpf/bpftool
BPFTOOL_OUTPUT := $(abspath $(BPF_SAMPLES_PATH))/bpftool
DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)/bootstrap/bpftool
BPFTOOL ?= $(DEFAULT_BPFTOOL)
$(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) | $(BPFTOOL_OUTPUT)
$(MAKE) -C $(BPFTOOLDIR) srctree=$(BPF_SAMPLES_PATH)/../../ \
OUTPUT=$(BPFTOOL_OUTPUT)/ bootstrap
$(LIBBPF_OUTPUT) $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
$(obj)/syscall_nrs.h: $(obj)/syscall_nrs.s FORCE
$(call filechk,offsets,__SYSCALL_NRS_H__)
targets += syscall_nrs.s
clean-files += syscall_nrs.h
FORCE:
# Verify LLVM compiler tools are available and bpf target is supported by llc
.PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC)
verify_cmds: $(CLANG) $(LLC)
@for TOOL in $^ ; do \
if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
exit 1; \
else true; fi; \
done
verify_target_bpf: verify_cmds
@if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
echo " NOTICE: LLVM version >= 3.7.1 required" ;\
exit 2; \
else true; fi
$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
$(src)/*.c: verify_target_bpf $(LIBBPF)
libbpf_hdrs: $(LIBBPF)
$(obj)/$(TRACE_HELPERS) $(obj)/$(CGROUP_HELPERS) $(obj)/$(XDP_SAMPLE): | libbpf_hdrs
.PHONY: libbpf_hdrs
$(obj)/xdp_router_ipv4_user.o: $(obj)/xdp_router_ipv4.skel.h
$(obj)/tracex5.bpf.o: $(obj)/syscall_nrs.h
$(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
$(obj)/hbm.o: $(src)/hbm.h
$(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
# Override includes for xdp_sample_user.o because $(srctree)/usr/include in
# TPROGS_CFLAGS causes conflicts
XDP_SAMPLE_CFLAGS += -Wall -O2 \
-I$(src)/../../tools/include \
-I$(src)/../../tools/include/uapi \
-I$(LIBBPF_INCLUDE) \
-I$(src)/../../tools/testing/selftests/bpf
$(obj)/$(XDP_SAMPLE): TPROGS_CFLAGS = $(XDP_SAMPLE_CFLAGS) $(TPROGS_USER_CFLAGS)
$(obj)/$(XDP_SAMPLE): $(src)/xdp_sample_user.h $(src)/xdp_sample_shared.h
# Override includes for trace_helpers.o because __must_check won't be defined
# in our include path.
$(obj)/$(TRACE_HELPERS): TPROGS_CFLAGS := $(TPROGS_CFLAGS) -D__must_check=
-include $(BPF_SAMPLES_PATH)/Makefile.target
VMLINUX_BTF_PATHS ?= $(abspath $(if $(O),$(O)/vmlinux)) \
$(abspath $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)) \
$(abspath $(objtree)/vmlinux)
VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
$(obj)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL)
ifeq ($(VMLINUX_H),)
ifeq ($(VMLINUX_BTF),)
$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)",\
build the kernel or set VMLINUX_BTF like "VMLINUX_BTF=/sys/kernel/btf/vmlinux" or VMLINUX_H variable)
endif
$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@
else
$(Q)cp "$(VMLINUX_H)" $@
endif
clean-files += vmlinux.h
# Get Clang's default includes on this system, as opposed to those seen by
# '--target=bpf'. This fixes "missing" files on some architectures/distros,
# such as asm/byteorder.h, asm/socket.h, asm/sockios.h, sys/cdefs.h etc.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
define get_sys_includes
$(shell $(1) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') \
$(shell $(1) -dM -E - </dev/null | grep '#define __riscv_xlen ' | sed 's/#define /-D/' | sed 's/ /=/')
endef
CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
$(obj)/xdp_router_ipv4.bpf.o: $(obj)/xdp_sample.bpf.o
$(obj)/%.bpf.o: $(src)/%.bpf.c $(obj)/vmlinux.h $(src)/xdp_sample.bpf.h $(src)/xdp_sample_shared.h
@echo " CLANG-BPF " $@
$(Q)$(CLANG) -g -O2 --target=bpf -D__TARGET_ARCH_$(SRCARCH) \
-Wno-compare-distinct-pointer-types -I$(srctree)/include \
-I$(srctree)/samples/bpf -I$(srctree)/tools/include \
-I$(LIBBPF_INCLUDE) $(CLANG_SYS_INCLUDES) \
-c $(filter %.bpf.c,$^) -o $@
LINKED_SKELS := xdp_router_ipv4.skel.h
clean-files += $(LINKED_SKELS)
xdp_router_ipv4.skel.h-deps := xdp_router_ipv4.bpf.o xdp_sample.bpf.o
LINKED_BPF_SRCS := $(patsubst %.bpf.o,%.bpf.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))
BPF_SRCS_LINKED := $(notdir $(wildcard $(src)/*.bpf.c))
BPF_OBJS_LINKED := $(patsubst %.bpf.c,$(obj)/%.bpf.o, $(BPF_SRCS_LINKED))
BPF_SKELS_LINKED := $(addprefix $(obj)/,$(LINKED_SKELS))
$(BPF_SKELS_LINKED): $(BPF_OBJS_LINKED) $(BPFTOOL)
@echo " BPF GEN-OBJ " $(@:.skel.h=)
$(Q)$(BPFTOOL) gen object $(@:.skel.h=.lbpf.o) $(addprefix $(obj)/,$($(@F)-deps))
@echo " BPF GEN-SKEL" $(@:.skel.h=)
$(Q)$(BPFTOOL) gen skeleton $(@:.skel.h=.lbpf.o) name $(notdir $(@:.skel.h=)) > $@
# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
# But, there is no easy way to fix it, so just exclude it since it is
# useless for BPF samples.
# below we use long chain of commands, clang | opt | llvm-dis | llc,
# to generate final object file. 'clang' compiles the source into IR
# with native target, e.g., x64, arm64, etc. 'opt' does bpf CORE IR builtin
# processing (llvm12) and IR optimizations. 'llvm-dis' converts
# 'opt' output to IR, and finally 'llc' generates bpf byte code.
$(obj)/%.o: $(src)/%.c
@echo " CLANG-bpf " $@
$(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(BPF_EXTRA_CFLAGS) \
-I$(obj) -I$(srctree)/tools/testing/selftests/bpf/ \
-I$(LIBBPF_INCLUDE) $(CLANG_SYS_INCLUDES) \
-D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
-D__TARGET_ARCH_$(SRCARCH) -Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-address-of-packed-member -Wno-tautological-compare \
-Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \
-fno-asynchronous-unwind-tables \
-I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \
-O2 -emit-llvm -Xclang -disable-llvm-passes -c $< -o - | \
$(OPT) -O2 -mtriple=bpf-pc-linux | $(LLVM_DIS) | \
$(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@
ifeq ($(DWARF2BTF),y)
$(BTF_PAHOLE) -J $@
endif
+75
View File
@@ -0,0 +1,75 @@
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Building binaries on the host system
# Binaries are not used during the compilation of the kernel, and intended
# to be build for target board, target board can be host of course. Added to
# build binaries to run not on host system.
#
# Sample syntax
# tprogs-y := xsk_example
# Will compile xsk_example.c and create an executable named xsk_example
#
# tprogs-y := xdpsock
# xdpsock-objs := xdpsock_1.o xdpsock_2.o
# Will compile xdpsock_1.c and xdpsock_2.c, and then link the executable
# xdpsock, based on xdpsock_1.o and xdpsock_2.o
#
# Derived from scripts/Makefile.host
#
__tprogs := $(sort $(tprogs-y))
# C code
# Executables compiled from a single .c file
tprog-csingle := $(foreach m,$(__tprogs), \
$(if $($(m)-objs),,$(m)))
# C executables linked based on several .o files
tprog-cmulti := $(foreach m,$(__tprogs),\
$(if $($(m)-objs),$(m)))
# Object (.o) files compiled from .c files
tprog-cobjs := $(sort $(foreach m,$(__tprogs),$($(m)-objs)))
tprog-csingle := $(addprefix $(obj)/,$(tprog-csingle))
tprog-cmulti := $(addprefix $(obj)/,$(tprog-cmulti))
tprog-cobjs := $(addprefix $(obj)/,$(tprog-cobjs))
#####
# Handle options to gcc. Support building with separate output directory
_tprogc_flags = $(TPROGS_CFLAGS) \
$(TPROGCFLAGS_$(basetarget).o)
# $(objtree)/$(obj) for including generated headers from checkin source files
ifeq ($(KBUILD_EXTMOD),)
ifdef building_out_of_srctree
_tprogc_flags += -I $(objtree)/$(obj)
endif
endif
tprogc_flags = -Wp,-MD,$(depfile) $(_tprogc_flags)
# Create executable from a single .c file
# tprog-csingle -> Executable
quiet_cmd_tprog-csingle = CC $@
cmd_tprog-csingle = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ $< \
$(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
$(tprog-csingle): $(obj)/%: $(src)/%.c FORCE
$(call if_changed_dep,tprog-csingle)
# Link an executable based on list of .o files, all plain c
# tprog-cmulti -> executable
quiet_cmd_tprog-cmulti = LD $@
cmd_tprog-cmulti = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ \
$(addprefix $(obj)/,$($(@F)-objs)) \
$(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
$(tprog-cmulti): $(tprog-cobjs) FORCE
$(call if_changed,tprog-cmulti)
$(call multi_depend, $(tprog-cmulti), , -objs)
# Create .o file from a single .c file
# tprog-cobjs -> .o
quiet_cmd_tprog-cobjs = CC $@
cmd_tprog-cobjs = $(CC) $(tprogc_flags) -c -o $@ $<
$(tprog-cobjs): $(obj)/%.o: $(src)/%.c FORCE
$(call if_changed_dep,tprog-cobjs)
+121
View File
@@ -0,0 +1,121 @@
eBPF sample programs
====================
This directory contains a test stubs, verifier test-suite and examples
for using eBPF. The examples use libbpf from tools/lib/bpf.
Note that the XDP-specific samples have been removed from this directory and
moved to the xdp-tools repository: https://github.com/xdp-project/xdp-tools
See the commit messages removing each tool from this directory for how to
convert specific command invocations between the old samples and the utilities
in xdp-tools.
Build dependencies
==================
Compiling requires having installed:
* clang
* llvm
* pahole
Consult :ref:`Documentation/process/changes.rst <changes>` for the minimum
version numbers required and how to update them. Note that LLVM's tool
'llc' must support target 'bpf', list version and supported targets with
command: ``llc --version``
Clean and configuration
-----------------------
It can be needed to clean tools, samples or kernel before trying new arch or
after some changes (on demand)::
make -C tools clean
make -C samples/bpf clean
make clean
Configure kernel, defconfig for instance
(see "tools/testing/selftests/bpf/config" for a reference config)::
make defconfig
Kernel headers
--------------
There are usually dependencies to header files of the current kernel.
To avoid installing devel kernel headers system wide, as a normal
user, simply call::
make headers_install
This will create a local "usr/include" directory in the git/build top
level directory, that the make system will automatically pick up first.
Compiling
=========
For building the BPF samples, issue the below command from the kernel
top level directory::
make M=samples/bpf
It is also possible to call make from this directory. This will just
hide the invocation of make as above.
Manually compiling LLVM with 'bpf' support
------------------------------------------
Since version 3.7.0, LLVM adds a proper LLVM backend target for the
BPF bytecode architecture.
By default llvm will build all non-experimental backends including bpf.
To generate a smaller llc binary one can use::
-DLLVM_TARGETS_TO_BUILD="BPF"
We recommend that developers who want the fastest incremental builds
use the Ninja build system, you can find it in your system's package
manager, usually the package is ninja or ninja-build.
Quick sniplet for manually compiling LLVM and clang
(build dependencies are ninja, cmake and gcc-c++)::
$ git clone https://github.com/llvm/llvm-project.git
$ mkdir -p llvm-project/llvm/build
$ cd llvm-project/llvm/build
$ cmake .. -G "Ninja" -DLLVM_TARGETS_TO_BUILD="BPF;X86" \
-DLLVM_ENABLE_PROJECTS="clang" \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_BUILD_RUNTIME=OFF
$ ninja
It is also possible to point make to the newly compiled 'llc' or
'clang' command via redefining LLC or CLANG on the make command line::
make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang
Cross compiling samples
-----------------------
In order to cross-compile, say for arm64 targets, export CROSS_COMPILE and ARCH
environment variables before calling make. But do this before clean,
configuration and header install steps described above. This will direct make to
build samples for the cross target::
export ARCH=arm64
export CROSS_COMPILE="aarch64-linux-gnu-"
Headers can be also installed on RFS of target board if need to keep them in
sync (not necessarily and it creates a local "usr/include" directory also)::
make INSTALL_HDR_PATH=~/some_sysroot/usr headers_install
Pointing LLC and CLANG is not necessarily if it's installed on HOST and have
in its targets appropriate arm64 arch (usually it has several arches).
Build samples::
make M=samples/bpf
Or build samples with SYSROOT if some header or library is absent in toolchain,
say libelf, providing address to file system containing headers and libs,
can be RFS of target board::
make M=samples/bpf SYSROOT=~/some_sysroot
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2019 Facebook */
#ifndef __ASM_GOTO_WORKAROUND_H
#define __ASM_GOTO_WORKAROUND_H
/*
* This will bring in asm_goto_output and asm_inline macro definitions
* if enabled by compiler and config options.
*/
#include <linux/types.h>
#ifdef asm_goto_output
#undef asm_goto_output
#define asm_goto_output(x...) asm volatile("invalid use of asm_goto_output")
#endif
/*
* asm_inline is defined as asm __inline in "include/linux/compiler_types.h"
* if supported by the kernel's CC (i.e CONFIG_CC_HAS_ASM_INLINE) which is not
* supported by CLANG.
*/
#ifdef asm_inline
#undef asm_inline
#define asm_inline asm
#endif
#define volatile(x...) volatile("")
#endif
+233
View File
@@ -0,0 +1,233 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* eBPF instruction mini library */
#ifndef __BPF_INSN_H
#define __BPF_INSN_H
struct bpf_insn;
/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
#define BPF_ALU64_REG(OP, DST, SRC) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0 })
#define BPF_ALU32_REG(OP, DST, SRC) \
((struct bpf_insn) { \
.code = BPF_ALU | BPF_OP(OP) | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0 })
/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
#define BPF_ALU64_IMM(OP, DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
#define BPF_ALU32_IMM(OP, DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
/* Short form of mov, dst_reg = src_reg */
#define BPF_MOV64_REG(DST, SRC) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0 })
#define BPF_MOV32_REG(DST, SRC) \
((struct bpf_insn) { \
.code = BPF_ALU | BPF_MOV | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0 })
/* Short form of mov, dst_reg = imm32 */
#define BPF_MOV64_IMM(DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
#define BPF_MOV32_IMM(DST, IMM) \
((struct bpf_insn) { \
.code = BPF_ALU | BPF_MOV | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
#define BPF_LD_IMM64(DST, IMM) \
BPF_LD_IMM64_RAW(DST, 0, IMM)
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
((struct bpf_insn) { \
.code = BPF_LD | BPF_DW | BPF_IMM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = (__u32) (IMM) }), \
((struct bpf_insn) { \
.code = 0, /* zero is reserved opcode */ \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = ((__u64) (IMM)) >> 32 })
#ifndef BPF_PSEUDO_MAP_FD
# define BPF_PSEUDO_MAP_FD 1
#endif
/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
#define BPF_LD_MAP_FD(DST, MAP_FD) \
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
#define BPF_LD_ABS(SIZE, IMM) \
((struct bpf_insn) { \
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = IMM })
/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
((struct bpf_insn) { \
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0 })
/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
((struct bpf_insn) { \
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0 })
/*
* Atomic operations:
*
* BPF_ADD *(uint *) (dst_reg + off16) += src_reg
* BPF_AND *(uint *) (dst_reg + off16) &= src_reg
* BPF_OR *(uint *) (dst_reg + off16) |= src_reg
* BPF_XOR *(uint *) (dst_reg + off16) ^= src_reg
* BPF_ADD | BPF_FETCH src_reg = atomic_fetch_add(dst_reg + off16, src_reg);
* BPF_AND | BPF_FETCH src_reg = atomic_fetch_and(dst_reg + off16, src_reg);
* BPF_OR | BPF_FETCH src_reg = atomic_fetch_or(dst_reg + off16, src_reg);
* BPF_XOR | BPF_FETCH src_reg = atomic_fetch_xor(dst_reg + off16, src_reg);
* BPF_XCHG src_reg = atomic_xchg(dst_reg + off16, src_reg)
* BPF_CMPXCHG r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg)
*/
#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF) \
((struct bpf_insn) { \
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = OP })
/* Legacy alias */
#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF)
/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
((struct bpf_insn) { \
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
.dst_reg = DST, \
.src_reg = 0, \
.off = OFF, \
.imm = IMM })
/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
#define BPF_JMP_REG(OP, DST, SRC, OFF) \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_OP(OP) | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0 })
/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */
#define BPF_JMP32_REG(OP, DST, SRC, OFF) \
((struct bpf_insn) { \
.code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0 })
/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = OFF, \
.imm = IMM })
/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */
#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \
((struct bpf_insn) { \
.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = OFF, \
.imm = IMM })
/* Raw code statement block */
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
((struct bpf_insn) { \
.code = CODE, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = IMM })
/* Program exit */
#define BPF_EXIT_INSN() \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_EXIT, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = 0 })
#endif
@@ -0,0 +1,332 @@
/* This test is a demo of using get_socket_uid and get_socket_cookie
* helper function to do per socket based network traffic monitoring.
* It requires iptables version higher then 1.6.1. to load pinned eBPF
* program into the xt_bpf match.
*
* TEST:
* ./run_cookie_uid_helper_example.sh -option
* option:
* -t: do traffic monitoring test, the program will continuously
* print out network traffic happens after program started A sample
* output is shown below:
*
* cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058
* cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 286
* cookie: 812, uid: 0x3e8, Pakcet Count: 3, Bytes Count: 1726
* cookie: 802, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104
* cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058
* cookie: 831, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104
* cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 712
* cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 70
*
* -s: do getsockopt SO_COOKIE test, the program will set up a pair of
* UDP sockets and send packets between them. And read out the traffic data
* directly from the ebpf map based on the socket cookie.
*
* Clean up: if using shell script, the script file will delete the iptables
* rule and unmount the bpf program when exit. Else the iptables rule need
* to be deleted by hand, see run_cookie_uid_helper_example.sh for detail.
*/
#define _GNU_SOURCE
#define offsetof(type, member) __builtin_offsetof(type, member)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <limits.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include "bpf_insn.h"
#define PORT 8888
struct stats {
uint32_t uid;
uint64_t packets;
uint64_t bytes;
};
static int map_fd, prog_fd;
static bool test_finish;
static void maps_create(void)
{
map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(uint32_t),
sizeof(struct stats), 100, NULL);
if (map_fd < 0)
error(1, errno, "map create failed!\n");
}
static void prog_load(void)
{
static char log_buf[1 << 16];
struct bpf_insn prog[] = {
/*
* Save sk_buff for future usage. value stored in R6 to R10 will
* not be reset after a bpf helper function call.
*/
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
/*
* pc1: BPF_FUNC_get_socket_cookie takes one parameter,
* R1: sk_buff
*/
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_get_socket_cookie),
/* pc2-4: save &socketCookie to r7 for future usage*/
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
/*
* pc5-8: set up the registers for BPF_FUNC_map_lookup_elem,
* it takes two parameters (R1: map_fd, R2: &socket_cookie)
*/
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_map_lookup_elem),
/*
* pc9. if r0 != 0x0, go to pc+14, since we have the cookie
* stored already
* Otherwise do pc10-22 to setup a new data entry.
*/
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_get_socket_uid),
/*
* Place a struct stats in the R10 stack and sequentially
* place the member value into the memory. Packets value
* is set by directly place a IMM value 1 into the stack.
*/
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0,
-32 + (__s16)offsetof(struct stats, uid)),
BPF_ST_MEM(BPF_DW, BPF_REG_10,
-32 + (__s16)offsetof(struct stats, packets), 1),
/*
* __sk_buff is a special struct used for eBPF program to
* directly access some sk_buff field.
*/
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
offsetof(struct __sk_buff, len)),
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1,
-32 + (__s16)offsetof(struct stats, bytes)),
/*
* add new map entry using BPF_FUNC_map_update_elem, it takes
* 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats,
* R4: flags)
*/
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32),
BPF_MOV64_IMM(BPF_REG_4, 0),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
BPF_FUNC_map_update_elem),
BPF_JMP_IMM(BPF_JA, 0, 0, 5),
/*
* pc24-30 update the packet info to a exist data entry, it can
* be done by directly write to pointers instead of using
* BPF_FUNC_map_update_elem helper function
*/
BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
BPF_MOV64_IMM(BPF_REG_1, 1),
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
offsetof(struct stats, packets)),
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
offsetof(struct __sk_buff, len)),
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
offsetof(struct stats, bytes)),
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
offsetof(struct __sk_buff, len)),
BPF_EXIT_INSN(),
};
LIBBPF_OPTS(bpf_prog_load_opts, opts,
.log_buf = log_buf,
.log_size = sizeof(log_buf),
);
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
prog, ARRAY_SIZE(prog), &opts);
if (prog_fd < 0)
error(1, errno, "failed to load prog\n%s\n", log_buf);
}
static void prog_attach_iptables(char *file)
{
int ret;
char rules[256];
if (bpf_obj_pin(prog_fd, file))
error(1, errno, "bpf_obj_pin");
if (strlen(file) > 50) {
printf("file path too long: %s\n", file);
exit(1);
}
ret = snprintf(rules, sizeof(rules),
"iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT",
file);
if (ret < 0 || ret >= sizeof(rules)) {
printf("error constructing iptables command\n");
exit(1);
}
ret = system(rules);
if (ret < 0) {
printf("iptables rule update failed: %d/n", WEXITSTATUS(ret));
exit(1);
}
}
static void print_table(void)
{
struct stats curEntry;
uint32_t curN = UINT32_MAX;
uint32_t nextN;
int res;
while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) {
curN = nextN;
res = bpf_map_lookup_elem(map_fd, &curN, &curEntry);
if (res < 0) {
error(1, errno, "fail to get entry value of Key: %u\n",
curN);
} else {
printf("cookie: %u, uid: 0x%x, Packet Count: %lu,"
" Bytes Count: %lu\n", curN, curEntry.uid,
curEntry.packets, curEntry.bytes);
}
}
}
static void udp_client(void)
{
struct sockaddr_in si_other = {0};
struct sockaddr_in si_me = {0};
struct stats dataEntry;
int s_rcv, s_send, i, recv_len;
char message = 'a';
char buf;
uint64_t cookie;
int res;
socklen_t cookie_len = sizeof(cookie);
socklen_t slen = sizeof(si_other);
s_rcv = socket(PF_INET, SOCK_DGRAM, 0);
if (s_rcv < 0)
error(1, errno, "rcv socket creat failed!\n");
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
if (inet_aton("127.0.0.1", &si_other.sin_addr) == 0)
error(1, errno, "inet_aton\n");
if (bind(s_rcv, (struct sockaddr *)&si_other, sizeof(si_other)) == -1)
error(1, errno, "bind\n");
s_send = socket(PF_INET, SOCK_DGRAM, 0);
if (s_send < 0)
error(1, errno, "send socket creat failed!\n");
res = getsockopt(s_send, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len);
if (res < 0)
printf("get cookie failed: %s\n", strerror(errno));
res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);
if (res != -1)
error(1, errno, "socket stat found while flow not active\n");
for (i = 0; i < 10; i++) {
res = sendto(s_send, &message, sizeof(message), 0,
(struct sockaddr *)&si_other, slen);
if (res == -1)
error(1, errno, "send\n");
if (res != sizeof(message))
error(1, 0, "%uB != %luB\n", res, sizeof(message));
recv_len = recvfrom(s_rcv, &buf, sizeof(buf), 0,
(struct sockaddr *)&si_me, &slen);
if (recv_len < 0)
error(1, errno, "receive\n");
res = memcmp(&(si_other.sin_addr), &(si_me.sin_addr),
sizeof(si_me.sin_addr));
if (res != 0)
error(1, EFAULT, "sender addr error: %d\n", res);
printf("Message received: %c\n", buf);
res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry);
if (res < 0)
error(1, errno, "lookup sk stat failed, cookie: %lu\n",
cookie);
printf("cookie: %lu, uid: 0x%x, Packet Count: %lu,"
" Bytes Count: %lu\n\n", cookie, dataEntry.uid,
dataEntry.packets, dataEntry.bytes);
}
close(s_send);
close(s_rcv);
}
static int usage(void)
{
printf("Usage: ./run_cookie_uid_helper_example.sh"
" bpfObjName -option\n"
" -t traffic monitor test\n"
" -s getsockopt cookie test\n");
return 1;
}
static void finish(int ret)
{
test_finish = true;
}
int main(int argc, char *argv[])
{
int opt;
bool cfg_test_traffic = false;
bool cfg_test_cookie = false;
if (argc != 3)
return usage();
while ((opt = getopt(argc, argv, "ts")) != -1) {
switch (opt) {
case 't':
cfg_test_traffic = true;
break;
case 's':
cfg_test_cookie = true;
break;
default:
printf("unknown option %c\n", opt);
usage();
return -1;
}
}
maps_create();
prog_load();
prog_attach_iptables(argv[2]);
if (cfg_test_traffic) {
if (signal(SIGINT, finish) == SIG_ERR)
error(1, errno, "register SIGINT handler failed");
if (signal(SIGTERM, finish) == SIG_ERR)
error(1, errno, "register SIGTERM handler failed");
while (!test_finish) {
print_table();
printf("\n");
sleep(1);
}
} else if (cfg_test_cookie) {
udp_client();
}
close(prog_fd);
close(map_fd);
return 0;
}
+280
View File
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/version.h>
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
/*
* The CPU number, cstate number and pstate number are based
* on 96boards Hikey with octa CA53 CPUs.
*
* Every CPU have three idle states for cstate:
* WFI, CPU_OFF, CLUSTER_OFF
*
* Every CPU have 5 operating points:
* 208MHz, 432MHz, 729MHz, 960MHz, 1200MHz
*
* This code is based on these assumption and other platforms
* need to adjust these definitions.
*/
#define MAX_CPU 8
#define MAX_PSTATE_ENTRIES 5
#define MAX_CSTATE_ENTRIES 3
static int cpu_opps[] = { 208000, 432000, 729000, 960000, 1200000 };
/*
* my_map structure is used to record cstate and pstate index and
* timestamp (Idx, Ts), when new event incoming we need to update
* combination for new state index and timestamp (Idx`, Ts`).
*
* Based on (Idx, Ts) and (Idx`, Ts`) we can calculate the time
* interval for the previous state: Duration(Idx) = Ts` - Ts.
*
* Every CPU has one below array for recording state index and
* timestamp, and record for cstate and pstate saperately:
*
* +--------------------------+
* | cstate timestamp |
* +--------------------------+
* | cstate index |
* +--------------------------+
* | pstate timestamp |
* +--------------------------+
* | pstate index |
* +--------------------------+
*/
#define MAP_OFF_CSTATE_TIME 0
#define MAP_OFF_CSTATE_IDX 1
#define MAP_OFF_PSTATE_TIME 2
#define MAP_OFF_PSTATE_IDX 3
#define MAP_OFF_NUM 4
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u64);
__uint(max_entries, MAX_CPU * MAP_OFF_NUM);
} my_map SEC(".maps");
/* cstate_duration records duration time for every idle state per CPU */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u64);
__uint(max_entries, MAX_CPU * MAX_CSTATE_ENTRIES);
} cstate_duration SEC(".maps");
/* pstate_duration records duration time for every operating point per CPU */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u64);
__uint(max_entries, MAX_CPU * MAX_PSTATE_ENTRIES);
} pstate_duration SEC(".maps");
/*
* The trace events for cpu_idle and cpu_frequency are taken from:
* /sys/kernel/tracing/events/power/cpu_idle/format
* /sys/kernel/tracing/events/power/cpu_frequency/format
*
* These two events have same format, so define one common structure.
*/
struct cpu_args {
u64 pad;
u32 state;
u32 cpu_id;
};
/* calculate pstate index, returns MAX_PSTATE_ENTRIES for failure */
static u32 find_cpu_pstate_idx(u32 frequency)
{
u32 i;
for (i = 0; i < sizeof(cpu_opps) / sizeof(u32); i++) {
if (frequency == cpu_opps[i])
return i;
}
return i;
}
SEC("tracepoint/power/cpu_idle")
int bpf_prog1(struct cpu_args *ctx)
{
u64 *cts, *pts, *cstate, *pstate, prev_state, cur_ts, delta;
u32 key, cpu, pstate_idx;
u64 *val;
if (ctx->cpu_id > MAX_CPU)
return 0;
cpu = ctx->cpu_id;
key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_TIME;
cts = bpf_map_lookup_elem(&my_map, &key);
if (!cts)
return 0;
key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;
cstate = bpf_map_lookup_elem(&my_map, &key);
if (!cstate)
return 0;
key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;
pts = bpf_map_lookup_elem(&my_map, &key);
if (!pts)
return 0;
key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;
pstate = bpf_map_lookup_elem(&my_map, &key);
if (!pstate)
return 0;
prev_state = *cstate;
*cstate = ctx->state;
if (!*cts) {
*cts = bpf_ktime_get_ns();
return 0;
}
cur_ts = bpf_ktime_get_ns();
delta = cur_ts - *cts;
*cts = cur_ts;
/*
* When state doesn't equal to (u32)-1, the cpu will enter
* one idle state; for this case we need to record interval
* for the pstate.
*
* OPP2
* +---------------------+
* OPP1 | |
* ---------+ |
* | Idle state
* +---------------
*
* |<- pstate duration ->|
* ^ ^
* pts cur_ts
*/
if (ctx->state != (u32)-1) {
/* record pstate after have first cpu_frequency event */
if (!*pts)
return 0;
delta = cur_ts - *pts;
pstate_idx = find_cpu_pstate_idx(*pstate);
if (pstate_idx >= MAX_PSTATE_ENTRIES)
return 0;
key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;
val = bpf_map_lookup_elem(&pstate_duration, &key);
if (val)
__sync_fetch_and_add((long *)val, delta);
/*
* When state equal to (u32)-1, the cpu just exits from one
* specific idle state; for this case we need to record
* interval for the pstate.
*
* OPP2
* -----------+
* | OPP1
* | +-----------
* | Idle state |
* +---------------------+
*
* |<- cstate duration ->|
* ^ ^
* cts cur_ts
*/
} else {
key = cpu * MAX_CSTATE_ENTRIES + prev_state;
val = bpf_map_lookup_elem(&cstate_duration, &key);
if (val)
__sync_fetch_and_add((long *)val, delta);
}
/* Update timestamp for pstate as new start time */
if (*pts)
*pts = cur_ts;
return 0;
}
SEC("tracepoint/power/cpu_frequency")
int bpf_prog2(struct cpu_args *ctx)
{
u64 *pts, *cstate, *pstate, cur_ts, delta;
u32 key, cpu, pstate_idx;
u64 *val;
cpu = ctx->cpu_id;
key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_TIME;
pts = bpf_map_lookup_elem(&my_map, &key);
if (!pts)
return 0;
key = cpu * MAP_OFF_NUM + MAP_OFF_PSTATE_IDX;
pstate = bpf_map_lookup_elem(&my_map, &key);
if (!pstate)
return 0;
key = cpu * MAP_OFF_NUM + MAP_OFF_CSTATE_IDX;
cstate = bpf_map_lookup_elem(&my_map, &key);
if (!cstate)
return 0;
*pstate = ctx->state;
if (!*pts) {
*pts = bpf_ktime_get_ns();
return 0;
}
cur_ts = bpf_ktime_get_ns();
delta = cur_ts - *pts;
*pts = cur_ts;
/* When CPU is in idle, bail out to skip pstate statistics */
if (*cstate != (u32)(-1))
return 0;
/*
* The cpu changes to another different OPP (in below diagram
* change frequency from OPP3 to OPP1), need recording interval
* for previous frequency OPP3 and update timestamp as start
* time for new frequency OPP1.
*
* OPP3
* +---------------------+
* OPP2 | |
* ---------+ |
* | OPP1
* +---------------
*
* |<- pstate duration ->|
* ^ ^
* pts cur_ts
*/
pstate_idx = find_cpu_pstate_idx(*pstate);
if (pstate_idx >= MAX_PSTATE_ENTRIES)
return 0;
key = cpu * MAX_PSTATE_ENTRIES + pstate_idx;
val = bpf_map_lookup_elem(&pstate_duration, &key);
if (val)
__sync_fetch_and_add((long *)val, delta);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+251
View File
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sched.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <locale.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
static int cstate_map_fd, pstate_map_fd;
#define MAX_CPU 8
#define MAX_PSTATE_ENTRIES 5
#define MAX_CSTATE_ENTRIES 3
#define MAX_STARS 40
#define CPUFREQ_MAX_SYSFS_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
#define CPUFREQ_LOWEST_FREQ "208000"
#define CPUFREQ_HIGHEST_FREQ "12000000"
struct cpu_stat_data {
unsigned long cstate[MAX_CSTATE_ENTRIES];
unsigned long pstate[MAX_PSTATE_ENTRIES];
};
static struct cpu_stat_data stat_data[MAX_CPU];
static void cpu_stat_print(void)
{
int i, j;
char state_str[sizeof("cstate-9")];
struct cpu_stat_data *data;
/* Clear screen */
printf("\033[2J");
/* Header */
printf("\nCPU states statistics:\n");
printf("%-10s ", "state(ms)");
for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
sprintf(state_str, "cstate-%d", i);
printf("%-11s ", state_str);
}
for (i = 0; i < MAX_PSTATE_ENTRIES; i++) {
sprintf(state_str, "pstate-%d", i);
printf("%-11s ", state_str);
}
printf("\n");
for (j = 0; j < MAX_CPU; j++) {
data = &stat_data[j];
printf("CPU-%-6d ", j);
for (i = 0; i < MAX_CSTATE_ENTRIES; i++)
printf("%-11lu ", data->cstate[i] / 1000000);
for (i = 0; i < MAX_PSTATE_ENTRIES; i++)
printf("%-11lu ", data->pstate[i] / 1000000);
printf("\n");
}
}
static void cpu_stat_update(int cstate_fd, int pstate_fd)
{
unsigned long key, value;
int c, i;
for (c = 0; c < MAX_CPU; c++) {
for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
key = c * MAX_CSTATE_ENTRIES + i;
bpf_map_lookup_elem(cstate_fd, &key, &value);
stat_data[c].cstate[i] = value;
}
for (i = 0; i < MAX_PSTATE_ENTRIES; i++) {
key = c * MAX_PSTATE_ENTRIES + i;
bpf_map_lookup_elem(pstate_fd, &key, &value);
stat_data[c].pstate[i] = value;
}
}
}
/*
* This function is copied from 'idlestat' tool function
* idlestat_wake_all() in idlestate.c.
*
* It sets the self running task affinity to cpus one by one so can wake up
* the specific CPU to handle scheduling; this results in all cpus can be
* waken up once and produce ftrace event 'trace_cpu_idle'.
*/
static int cpu_stat_inject_cpu_idle_event(void)
{
int rcpu, i, ret;
cpu_set_t cpumask;
cpu_set_t original_cpumask;
ret = sysconf(_SC_NPROCESSORS_CONF);
if (ret < 0)
return -1;
rcpu = sched_getcpu();
if (rcpu < 0)
return -1;
/* Keep track of the CPUs we will run on */
sched_getaffinity(0, sizeof(original_cpumask), &original_cpumask);
for (i = 0; i < ret; i++) {
/* Pointless to wake up ourself */
if (i == rcpu)
continue;
/* Pointless to wake CPUs we will not run on */
if (!CPU_ISSET(i, &original_cpumask))
continue;
CPU_ZERO(&cpumask);
CPU_SET(i, &cpumask);
sched_setaffinity(0, sizeof(cpumask), &cpumask);
}
/* Enable all the CPUs of the original mask */
sched_setaffinity(0, sizeof(original_cpumask), &original_cpumask);
return 0;
}
/*
* It's possible to have no any frequency change for long time and cannot
* get ftrace event 'trace_cpu_frequency' for long period, this introduces
* big deviation for pstate statistics.
*
* To solve this issue, below code forces to set 'scaling_max_freq' to 208MHz
* for triggering ftrace event 'trace_cpu_frequency' and then recovery back to
* the maximum frequency value 1.2GHz.
*/
static int cpu_stat_inject_cpu_frequency_event(void)
{
int len, fd;
fd = open(CPUFREQ_MAX_SYSFS_PATH, O_WRONLY);
if (fd < 0) {
printf("failed to open scaling_max_freq, errno=%d\n", errno);
return fd;
}
len = write(fd, CPUFREQ_LOWEST_FREQ, strlen(CPUFREQ_LOWEST_FREQ));
if (len < 0) {
printf("failed to open scaling_max_freq, errno=%d\n", errno);
goto err;
}
len = write(fd, CPUFREQ_HIGHEST_FREQ, strlen(CPUFREQ_HIGHEST_FREQ));
if (len < 0) {
printf("failed to open scaling_max_freq, errno=%d\n", errno);
goto err;
}
err:
close(fd);
return len;
}
static void int_exit(int sig)
{
cpu_stat_inject_cpu_idle_event();
cpu_stat_inject_cpu_frequency_event();
cpu_stat_update(cstate_map_fd, pstate_map_fd);
cpu_stat_print();
exit(0);
}
int main(int argc, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int ret;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
cstate_map_fd = bpf_object__find_map_fd_by_name(obj, "cstate_duration");
pstate_map_fd = bpf_object__find_map_fd_by_name(obj, "pstate_duration");
if (cstate_map_fd < 0 || pstate_map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
ret = cpu_stat_inject_cpu_idle_event();
if (ret < 0)
return 1;
ret = cpu_stat_inject_cpu_frequency_event();
if (ret < 0)
return 1;
signal(SIGINT, int_exit);
signal(SIGTERM, int_exit);
while (1) {
cpu_stat_update(cstate_map_fd, pstate_map_fd);
cpu_stat_print();
sleep(5);
}
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
+438
View File
@@ -0,0 +1,438 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2019 Facebook
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
Usage() {
echo "Script for testing HBM (Host Bandwidth Manager) framework."
echo "It creates a cgroup to use for testing and load a BPF program to limit"
echo "egress or ingress bandwidth. It then uses iperf3 or netperf to create"
echo "loads. The output is the goodput in Mbps (unless -D was used)."
echo ""
echo "USAGE: $name [out] [-b=<prog>|--bpf=<prog>] [-c=<cc>|--cc=<cc>]"
echo " [-D] [-d=<delay>|--delay=<delay>] [--debug] [-E] [--edt]"
echo " [-f=<#flows>|--flows=<#flows>] [-h] [-i=<id>|--id=<id >]"
echo " [-l] [-N] [--no_cn] [-p=<port>|--port=<port>] [-P]"
echo " [-q=<qdisc>] [-R] [-s=<server>|--server=<server]"
echo " [-S|--stats] -t=<time>|--time=<time>] [-w] [cubic|dctcp]"
echo " Where:"
echo " out egress (default)"
echo " -b or --bpf BPF program filename to load and attach."
echo " Default is hbm_out_kern.o for egress,"
echo " -c or -cc TCP congestion control (cubic or dctcp)"
echo " --debug print BPF trace buffer"
echo " -d or --delay add a delay in ms using netem"
echo " -D In addition to the goodput in Mbps, it also outputs"
echo " other detailed information. This information is"
echo " test dependent (i.e. iperf3 or netperf)."
echo " -E enable ECN (not required for dctcp)"
echo " --edt use fq's Earliest Departure Time (requires fq)"
echo " -f or --flows number of concurrent flows (default=1)"
echo " -i or --id cgroup id (an integer, default is 1)"
echo " -N use netperf instead of iperf3"
echo " --no_cn Do not return CN notifications"
echo " -l do not limit flows using loopback"
echo " -h Help"
echo " -p or --port iperf3 port (default is 5201)"
echo " -P use an iperf3 instance for each flow"
echo " -q use the specified qdisc"
echo " -r or --rate rate in Mbps (default 1s 1Gbps)"
echo " -R Use TCP_RR for netperf. 1st flow has req"
echo " size of 10KB, rest of 1MB. Reply in all"
echo " cases is 1 byte."
echo " More detailed output for each flow can be found"
echo " in the files netperf.<cg>.<flow>, where <cg> is the"
echo " cgroup id as specified with the -i flag, and <flow>"
echo " is the flow id starting at 1 and increasing by 1 for"
echo " flow (as specified by -f)."
echo " -s or --server hostname of netperf server. Used to create netperf"
echo " test traffic between to hosts (default is within host)"
echo " netserver must be running on the host."
echo " -S or --stats whether to update hbm stats (default is yes)."
echo " -t or --time duration of iperf3 in seconds (default=5)"
echo " -w Work conserving flag. cgroup can increase its"
echo " bandwidth beyond the rate limit specified"
echo " while there is available bandwidth. Current"
echo " implementation assumes there is only one NIC"
echo " (eth0), but can be extended to support multiple"
echo " NICs."
echo " cubic or dctcp specify which TCP CC to use"
echo " "
exit
}
#set -x
debug_flag=0
args="$@"
name="$0"
netem=0
cc=x
dir="-o"
dir_name="out"
dur=5
flows=1
id=1
prog=""
port=5201
rate=1000
multi_iperf=0
flow_cnt=1
use_netperf=0
rr=0
ecn=0
details=0
server=""
qdisc=""
flags=""
do_stats=0
BPFFS=/sys/fs/bpf
function config_bpffs () {
if mount | grep $BPFFS > /dev/null; then
echo "bpffs already mounted"
else
echo "bpffs not mounted. Mounting..."
mount -t bpf none $BPFFS
fi
}
function start_hbm () {
rm -f hbm.out
echo "./hbm $dir -n $id -r $rate -t $dur $flags $dbg $prog" > hbm.out
echo " " >> hbm.out
./hbm $dir -n $id -r $rate -t $dur $flags $dbg $prog >> hbm.out 2>&1 &
echo $!
}
processArgs () {
for i in $args ; do
case $i in
# Support for upcoming ingress rate limiting
#in) # support for upcoming ingress rate limiting
# dir="-i"
# dir_name="in"
# ;;
out)
dir="-o"
dir_name="out"
;;
-b=*|--bpf=*)
prog="${i#*=}"
;;
-c=*|--cc=*)
cc="${i#*=}"
;;
--no_cn)
flags="$flags --no_cn"
;;
--debug)
flags="$flags -d"
debug_flag=1
;;
-d=*|--delay=*)
netem="${i#*=}"
;;
-D)
details=1
;;
-E)
ecn=1
;;
--edt)
flags="$flags --edt"
qdisc="fq"
;;
-f=*|--flows=*)
flows="${i#*=}"
;;
-i=*|--id=*)
id="${i#*=}"
;;
-l)
flags="$flags -l"
;;
-N)
use_netperf=1
;;
-p=*|--port=*)
port="${i#*=}"
;;
-P)
multi_iperf=1
;;
-q=*)
qdisc="${i#*=}"
;;
-r=*|--rate=*)
rate="${i#*=}"
;;
-R)
rr=1
;;
-s=*|--server=*)
server="${i#*=}"
;;
-S|--stats)
flags="$flags -s"
do_stats=1
;;
-t=*|--time=*)
dur="${i#*=}"
;;
-w)
flags="$flags -w"
;;
cubic)
cc=cubic
;;
dctcp)
cc=dctcp
;;
*)
echo "Unknown arg:$i"
Usage
;;
esac
done
}
processArgs
config_bpffs
if [ $debug_flag -eq 1 ] ; then
rm -f hbm_out.log
fi
hbm_pid=$(start_hbm)
usleep 100000
host=`hostname`
cg_base_dir=/sys/fs/cgroup/unified
cg_dir="$cg_base_dir/cgroup-test-work-dir/hbm$id"
echo $$ >> $cg_dir/cgroup.procs
ulimit -l unlimited
rm -f ss.out
rm -f hbm.[0-9]*.$dir_name
if [ $ecn -ne 0 ] ; then
sysctl -w -q -n net.ipv4.tcp_ecn=1
fi
if [ $use_netperf -eq 0 ] ; then
cur_cc=`sysctl -n net.ipv4.tcp_congestion_control`
if [ "$cc" != "x" ] ; then
sysctl -w -q -n net.ipv4.tcp_congestion_control=$cc
fi
fi
if [ "$netem" -ne "0" ] ; then
if [ "$qdisc" != "" ] ; then
echo "WARNING: Ignoring -q options because -d option used"
fi
tc qdisc del dev lo root > /dev/null 2>&1
tc qdisc add dev lo root netem delay $netem\ms > /dev/null 2>&1
elif [ "$qdisc" != "" ] ; then
tc qdisc del dev eth0 root > /dev/null 2>&1
tc qdisc add dev eth0 root $qdisc > /dev/null 2>&1
fi
n=0
m=$[$dur * 5]
hn="::1"
if [ $use_netperf -ne 0 ] ; then
if [ "$server" != "" ] ; then
hn=$server
fi
fi
( ping6 -i 0.2 -c $m $hn > ping.out 2>&1 ) &
if [ $use_netperf -ne 0 ] ; then
begNetserverPid=`ps ax | grep netserver | grep --invert-match "grep" | \
awk '{ print $1 }'`
if [ "$begNetserverPid" == "" ] ; then
if [ "$server" == "" ] ; then
( ./netserver > /dev/null 2>&1) &
usleep 100000
fi
fi
flow_cnt=1
if [ "$server" == "" ] ; then
np_server=$host
else
np_server=$server
fi
if [ "$cc" == "x" ] ; then
np_cc=""
else
np_cc="-K $cc,$cc"
fi
replySize=1
while [ $flow_cnt -le $flows ] ; do
if [ $rr -ne 0 ] ; then
reqSize=1M
if [ $flow_cnt -eq 1 ] ; then
reqSize=10K
fi
if [ "$dir" == "-i" ] ; then
replySize=$reqSize
reqSize=1
fi
( ./netperf -H $np_server -l $dur -f m -j -t TCP_RR -- -r $reqSize,$replySize $np_cc -k P50_lATENCY,P90_LATENCY,LOCAL_TRANSPORT_RETRANS,REMOTE_TRANSPORT_RETRANS,LOCAL_SEND_THROUGHPUT,LOCAL_RECV_THROUGHPUT,REQUEST_SIZE,RESPONSE_SIZE > netperf.$id.$flow_cnt ) &
else
if [ "$dir" == "-i" ] ; then
( ./netperf -H $np_server -l $dur -f m -j -t TCP_RR -- -r 1,10M $np_cc -k P50_LATENCY,P90_LATENCY,LOCAL_TRANSPORT_RETRANS,LOCAL_SEND_THROUGHPUT,REMOTE_TRANSPORT_RETRANS,REMOTE_SEND_THROUGHPUT,REQUEST_SIZE,RESPONSE_SIZE > netperf.$id.$flow_cnt ) &
else
( ./netperf -H $np_server -l $dur -f m -j -t TCP_STREAM -- $np_cc -k P50_lATENCY,P90_LATENCY,LOCAL_TRANSPORT_RETRANS,LOCAL_SEND_THROUGHPUT,REQUEST_SIZE,RESPONSE_SIZE > netperf.$id.$flow_cnt ) &
fi
fi
flow_cnt=$[flow_cnt+1]
done
# sleep for duration of test (plus some buffer)
n=$[dur+2]
sleep $n
# force graceful termination of netperf
pids=`pgrep netperf`
for p in $pids ; do
kill -SIGALRM $p
done
flow_cnt=1
rate=0
if [ $details -ne 0 ] ; then
echo ""
echo "Details for HBM in cgroup $id"
if [ $do_stats -eq 1 ] ; then
if [ -e hbm.$id.$dir_name ] ; then
cat hbm.$id.$dir_name
fi
fi
fi
while [ $flow_cnt -le $flows ] ; do
if [ "$dir" == "-i" ] ; then
r=`cat netperf.$id.$flow_cnt | grep -o "REMOTE_SEND_THROUGHPUT=[0-9]*" | grep -o "[0-9]*"`
else
r=`cat netperf.$id.$flow_cnt | grep -o "LOCAL_SEND_THROUGHPUT=[0-9]*" | grep -o "[0-9]*"`
fi
echo "rate for flow $flow_cnt: $r"
rate=$[rate+r]
if [ $details -ne 0 ] ; then
echo "-----"
echo "Details for cgroup $id, flow $flow_cnt"
cat netperf.$id.$flow_cnt
fi
flow_cnt=$[flow_cnt+1]
done
if [ $details -ne 0 ] ; then
echo ""
delay=`grep "avg" ping.out | grep -o "= [0-9.]*/[0-9.]*" | grep -o "[0-9.]*$"`
echo "PING AVG DELAY:$delay"
echo "AGGREGATE_GOODPUT:$rate"
else
echo $rate
fi
elif [ $multi_iperf -eq 0 ] ; then
(iperf3 -s -p $port -1 > /dev/null 2>&1) &
usleep 100000
iperf3 -c $host -p $port -i 0 -P $flows -f m -t $dur > iperf.$id
rates=`grep receiver iperf.$id | grep -o "[0-9.]* Mbits" | grep -o "^[0-9]*"`
rate=`echo $rates | grep -o "[0-9]*$"`
if [ $details -ne 0 ] ; then
echo ""
echo "Details for HBM in cgroup $id"
if [ $do_stats -eq 1 ] ; then
if [ -e hbm.$id.$dir_name ] ; then
cat hbm.$id.$dir_name
fi
fi
delay=`grep "avg" ping.out | grep -o "= [0-9.]*/[0-9.]*" | grep -o "[0-9.]*$"`
echo "PING AVG DELAY:$delay"
echo "AGGREGATE_GOODPUT:$rate"
else
echo $rate
fi
else
flow_cnt=1
while [ $flow_cnt -le $flows ] ; do
(iperf3 -s -p $port -1 > /dev/null 2>&1) &
( iperf3 -c $host -p $port -i 0 -P 1 -f m -t $dur | grep receiver | grep -o "[0-9.]* Mbits" | grep -o "^[0-9]*" | grep -o "[0-9]*$" > iperf3.$id.$flow_cnt ) &
port=$[port+1]
flow_cnt=$[flow_cnt+1]
done
n=$[dur+1]
sleep $n
flow_cnt=1
rate=0
if [ $details -ne 0 ] ; then
echo ""
echo "Details for HBM in cgroup $id"
if [ $do_stats -eq 1 ] ; then
if [ -e hbm.$id.$dir_name ] ; then
cat hbm.$id.$dir_name
fi
fi
fi
while [ $flow_cnt -le $flows ] ; do
r=`cat iperf3.$id.$flow_cnt`
# echo "rate for flow $flow_cnt: $r"
if [ $details -ne 0 ] ; then
echo "Rate for cgroup $id, flow $flow_cnt LOCAL_SEND_THROUGHPUT=$r"
fi
rate=$[rate+r]
flow_cnt=$[flow_cnt+1]
done
if [ $details -ne 0 ] ; then
delay=`grep "avg" ping.out | grep -o "= [0-9.]*/[0-9.]*" | grep -o "[0-9.]*$"`
echo "PING AVG DELAY:$delay"
echo "AGGREGATE_GOODPUT:$rate"
else
echo $rate
fi
fi
if [ $use_netperf -eq 0 ] ; then
sysctl -w -q -n net.ipv4.tcp_congestion_control=$cur_cc
fi
if [ $ecn -ne 0 ] ; then
sysctl -w -q -n net.ipv4.tcp_ecn=0
fi
if [ "$netem" -ne "0" ] ; then
tc qdisc del dev lo root > /dev/null 2>&1
fi
if [ "$qdisc" != "" ] ; then
tc qdisc del dev eth0 root > /dev/null 2>&1
fi
sleep 2
hbmPid=`ps ax | grep "hbm " | grep --invert-match "grep" | awk '{ print $1 }'`
if [ "$hbmPid" == "$hbm_pid" ] ; then
kill $hbm_pid
fi
sleep 1
# Detach any pinned BPF programs that may have lingered
rm -rf $BPFFS/hbm*
if [ $use_netperf -ne 0 ] ; then
if [ "$server" == "" ] ; then
if [ "$begNetserverPid" == "" ] ; then
netserverPid=`ps ax | grep netserver | grep --invert-match "grep" | awk '{ print $1 }'`
if [ "$netserverPid" != "" ] ; then
kill $netserverPid
fi
fi
fi
fi
exit
+195
View File
@@ -0,0 +1,195 @@
#include <linux/unistd.h>
#include <linux/bpf.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_insn.h"
#include "sock_example.h"
#include "bpf_util.h"
#define BPF_F_PIN (1 << 0)
#define BPF_F_GET (1 << 1)
#define BPF_F_PIN_GET (BPF_F_PIN | BPF_F_GET)
#define BPF_F_KEY (1 << 2)
#define BPF_F_VAL (1 << 3)
#define BPF_F_KEY_VAL (BPF_F_KEY | BPF_F_VAL)
#define BPF_M_UNSPEC 0
#define BPF_M_MAP 1
#define BPF_M_PROG 2
char bpf_log_buf[BPF_LOG_BUF_SIZE];
static void usage(void)
{
printf("Usage: fds_example [...]\n");
printf(" -F <file> File to pin/get object\n");
printf(" -P |- pin object\n");
printf(" -G `- get object\n");
printf(" -m eBPF map mode\n");
printf(" -k <key> |- map key\n");
printf(" -v <value> `- map value\n");
printf(" -p eBPF prog mode\n");
printf(" -o <object> `- object file\n");
printf(" -h Display this help.\n");
}
static int bpf_prog_create(const char *object)
{
static struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN(),
};
size_t insns_cnt = ARRAY_SIZE(insns);
struct bpf_object *obj;
int err;
if (object) {
obj = bpf_object__open_file(object, NULL);
assert(!libbpf_get_error(obj));
err = bpf_object__load(obj);
assert(!err);
return bpf_program__fd(bpf_object__next_program(obj, NULL));
} else {
LIBBPF_OPTS(bpf_prog_load_opts, opts,
.log_buf = bpf_log_buf,
.log_size = BPF_LOG_BUF_SIZE,
);
return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
insns, insns_cnt, &opts);
}
}
static int bpf_do_map(const char *file, uint32_t flags, uint32_t key,
uint32_t value)
{
int fd, ret;
if (flags & BPF_F_PIN) {
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(uint32_t),
sizeof(uint32_t), 1024, NULL);
printf("bpf: map fd:%d (%s)\n", fd, strerror(errno));
assert(fd > 0);
ret = bpf_obj_pin(fd, file);
printf("bpf: pin ret:(%d,%s)\n", ret, strerror(errno));
assert(ret == 0);
} else {
fd = bpf_obj_get(file);
printf("bpf: get fd:%d (%s)\n", fd, strerror(errno));
assert(fd > 0);
}
if ((flags & BPF_F_KEY_VAL) == BPF_F_KEY_VAL) {
ret = bpf_map_update_elem(fd, &key, &value, 0);
printf("bpf: fd:%d u->(%u:%u) ret:(%d,%s)\n", fd, key, value,
ret, strerror(errno));
assert(ret == 0);
} else if (flags & BPF_F_KEY) {
ret = bpf_map_lookup_elem(fd, &key, &value);
printf("bpf: fd:%d l->(%u):%u ret:(%d,%s)\n", fd, key, value,
ret, strerror(errno));
assert(ret == 0);
}
return 0;
}
static int bpf_do_prog(const char *file, uint32_t flags, const char *object)
{
int fd, sock, ret;
if (flags & BPF_F_PIN) {
fd = bpf_prog_create(object);
printf("bpf: prog fd:%d (%s)\n", fd, strerror(errno));
assert(fd > 0);
ret = bpf_obj_pin(fd, file);
printf("bpf: pin ret:(%d,%s)\n", ret, strerror(errno));
assert(ret == 0);
} else {
fd = bpf_obj_get(file);
printf("bpf: get fd:%d (%s)\n", fd, strerror(errno));
assert(fd > 0);
}
sock = open_raw_sock("lo");
assert(sock > 0);
ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &fd, sizeof(fd));
printf("bpf: sock:%d <- fd:%d attached ret:(%d,%s)\n", sock, fd,
ret, strerror(errno));
assert(ret == 0);
return 0;
}
int main(int argc, char **argv)
{
const char *file = NULL, *object = NULL;
uint32_t key = 0, value = 0, flags = 0;
int opt, mode = BPF_M_UNSPEC;
while ((opt = getopt(argc, argv, "F:PGmk:v:po:")) != -1) {
switch (opt) {
/* General args */
case 'F':
file = optarg;
break;
case 'P':
flags |= BPF_F_PIN;
break;
case 'G':
flags |= BPF_F_GET;
break;
/* Map-related args */
case 'm':
mode = BPF_M_MAP;
break;
case 'k':
key = strtoul(optarg, NULL, 0);
flags |= BPF_F_KEY;
break;
case 'v':
value = strtoul(optarg, NULL, 0);
flags |= BPF_F_VAL;
break;
/* Prog-related args */
case 'p':
mode = BPF_M_PROG;
break;
case 'o':
object = optarg;
break;
default:
goto out;
}
}
if (!(flags & BPF_F_PIN_GET) || !file)
goto out;
switch (mode) {
case BPF_M_MAP:
return bpf_do_map(file, flags, key, value);
case BPF_M_PROG:
return bpf_do_prog(file, flags, object);
}
out:
usage();
return -1;
}
+1
View File
@@ -0,0 +1 @@
/* dummy .h to trick /usr/include/features.h to work with 'clang --target=bpf' */
+55
View File
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: LGPL-2.1
*
* Based on Paul Hsieh's (LGPG 2.1) hash function
* From: http://www.azillionmonkeys.com/qed/hash.html
*/
#define get16bits(d) (*((const __u16 *) (d)))
static __always_inline
__u32 SuperFastHash (const char *data, int len, __u32 initval) {
__u32 hash = initval;
__u32 tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
#pragma clang loop unroll(full)
for (;len > 0; len--) {
hash += get16bits (data);
tmp = (get16bits (data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof (__u16);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += get16bits (data);
hash ^= hash << 16;
hash ^= ((signed char)data[sizeof (__u16)]) << 18;
hash += hash >> 11;
break;
case 2: hash += get16bits (data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += (signed char)*data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
+515
View File
@@ -0,0 +1,515 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Example program for Host Bandwidth Management
*
* This program loads a cgroup skb BPF program to enforce cgroup output
* (egress) or input (ingress) bandwidth limits.
*
* USAGE: hbm [-d] [-l] [-n <id>] [-r <rate>] [-s] [-t <secs>] [-w] [-h] [prog]
* Where:
* -d Print BPF trace debug buffer
* -l Also limit flows doing loopback
* -n <#> To create cgroup \"/hbm#\" and attach prog
* Default is /hbm1
* --no_cn Do not return cn notifications
* -r <rate> Rate limit in Mbps
* -s Get HBM stats (marked, dropped, etc.)
* -t <time> Exit after specified seconds (default is 0)
* -w Work conserving flag. cgroup can increase its bandwidth
* beyond the rate limit specified while there is available
* bandwidth. Current implementation assumes there is only
* NIC (eth0), but can be extended to support multiple NICs.
* Currently only supported for egress.
* -h Print this info
* prog BPF program file name. Name defaults to hbm_out_kern.o
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/unistd.h>
#include <linux/compiler.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <getopt.h>
#include "cgroup_helpers.h"
#include "hbm.h"
#include "bpf_util.h"
#include <bpf/libbpf.h>
bool outFlag = true;
int minRate = 1000; /* cgroup rate limit in Mbps */
int rate = 1000; /* can grow if rate conserving is enabled */
int dur = 1;
bool stats_flag;
bool loopback_flag;
bool debugFlag;
bool work_conserving_flag;
bool no_cn_flag;
bool edt_flag;
static void Usage(void);
static void read_trace_pipe2(void);
static void do_error(char *msg, bool errno_flag);
#define TRACEFS "/sys/kernel/tracing/"
static struct bpf_program *bpf_prog;
static struct bpf_object *obj;
static int queue_stats_fd;
static void read_trace_pipe2(void)
{
int trace_fd;
FILE *outf;
char *outFname = "hbm_out.log";
trace_fd = open(TRACEFS "trace_pipe", O_RDONLY, 0);
if (trace_fd < 0) {
printf("Error opening trace_pipe\n");
return;
}
// Future support of ingress
// if (!outFlag)
// outFname = "hbm_in.log";
outf = fopen(outFname, "w");
if (outf == NULL)
printf("Error creating %s\n", outFname);
while (1) {
static char buf[4097];
ssize_t sz;
sz = read(trace_fd, buf, sizeof(buf) - 1);
if (sz > 0) {
buf[sz] = 0;
puts(buf);
if (outf != NULL) {
fprintf(outf, "%s\n", buf);
fflush(outf);
}
}
}
}
static void do_error(char *msg, bool errno_flag)
{
if (errno_flag)
printf("ERROR: %s, errno: %d\n", msg, errno);
else
printf("ERROR: %s\n", msg);
exit(1);
}
static int prog_load(char *prog)
{
struct bpf_program *pos;
const char *sec_name;
obj = bpf_object__open_file(prog, NULL);
if (libbpf_get_error(obj)) {
printf("ERROR: opening BPF object file failed\n");
return 1;
}
/* load BPF program */
if (bpf_object__load(obj)) {
printf("ERROR: loading BPF object file failed\n");
goto err;
}
bpf_object__for_each_program(pos, obj) {
sec_name = bpf_program__section_name(pos);
if (sec_name && !strcmp(sec_name, "cgroup_skb/egress")) {
bpf_prog = pos;
break;
}
}
if (!bpf_prog) {
printf("ERROR: finding a prog in obj file failed\n");
goto err;
}
queue_stats_fd = bpf_object__find_map_fd_by_name(obj, "queue_stats");
if (queue_stats_fd < 0) {
printf("ERROR: finding a map in obj file failed\n");
goto err;
}
return 0;
err:
bpf_object__close(obj);
return 1;
}
static int run_bpf_prog(char *prog, int cg_id)
{
struct hbm_queue_stats qstats = {0};
char cg_dir[100], cg_pin_path[100];
struct bpf_link *link = NULL;
int key = 0;
int cg1 = 0;
int rc = 0;
sprintf(cg_dir, "/hbm%d", cg_id);
rc = prog_load(prog);
if (rc != 0)
return rc;
if (setup_cgroup_environment()) {
printf("ERROR: setting cgroup environment\n");
goto err;
}
cg1 = create_and_get_cgroup(cg_dir);
if (!cg1) {
printf("ERROR: create_and_get_cgroup\n");
goto err;
}
if (join_cgroup(cg_dir)) {
printf("ERROR: join_cgroup\n");
goto err;
}
qstats.rate = rate;
qstats.stats = stats_flag ? 1 : 0;
qstats.loopback = loopback_flag ? 1 : 0;
qstats.no_cn = no_cn_flag ? 1 : 0;
if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY)) {
printf("ERROR: Could not update map element\n");
goto err;
}
if (!outFlag)
bpf_program__set_expected_attach_type(bpf_prog, BPF_CGROUP_INET_INGRESS);
link = bpf_program__attach_cgroup(bpf_prog, cg1);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach_cgroup failed\n");
goto err;
}
sprintf(cg_pin_path, "/sys/fs/bpf/hbm%d", cg_id);
rc = bpf_link__pin(link, cg_pin_path);
if (rc < 0) {
printf("ERROR: bpf_link__pin failed: %d\n", rc);
goto err;
}
if (work_conserving_flag) {
struct timeval t0, t_last, t_new;
FILE *fin;
unsigned long long last_eth_tx_bytes, new_eth_tx_bytes;
signed long long last_cg_tx_bytes, new_cg_tx_bytes;
signed long long delta_time, delta_bytes, delta_rate;
int delta_ms;
#define DELTA_RATE_CHECK 10000 /* in us */
#define RATE_THRESHOLD 9500000000 /* 9.5 Gbps */
bpf_map_lookup_elem(queue_stats_fd, &key, &qstats);
if (gettimeofday(&t0, NULL) < 0)
do_error("gettimeofday failed", true);
t_last = t0;
fin = fopen("/sys/class/net/eth0/statistics/tx_bytes", "r");
if (fscanf(fin, "%llu", &last_eth_tx_bytes) != 1)
do_error("fscanf fails", false);
fclose(fin);
last_cg_tx_bytes = qstats.bytes_total;
while (true) {
usleep(DELTA_RATE_CHECK);
if (gettimeofday(&t_new, NULL) < 0)
do_error("gettimeofday failed", true);
delta_ms = (t_new.tv_sec - t0.tv_sec) * 1000 +
(t_new.tv_usec - t0.tv_usec)/1000;
if (delta_ms > dur * 1000)
break;
delta_time = (t_new.tv_sec - t_last.tv_sec) * 1000000 +
(t_new.tv_usec - t_last.tv_usec);
if (delta_time == 0)
continue;
t_last = t_new;
fin = fopen("/sys/class/net/eth0/statistics/tx_bytes",
"r");
if (fscanf(fin, "%llu", &new_eth_tx_bytes) != 1)
do_error("fscanf fails", false);
fclose(fin);
printf(" new_eth_tx_bytes:%llu\n",
new_eth_tx_bytes);
bpf_map_lookup_elem(queue_stats_fd, &key, &qstats);
new_cg_tx_bytes = qstats.bytes_total;
delta_bytes = new_eth_tx_bytes - last_eth_tx_bytes;
last_eth_tx_bytes = new_eth_tx_bytes;
delta_rate = (delta_bytes * 8000000) / delta_time;
printf("%5d - eth_rate:%.1fGbps cg_rate:%.3fGbps",
delta_ms, delta_rate/1000000000.0,
rate/1000.0);
if (delta_rate < RATE_THRESHOLD) {
/* can increase cgroup rate limit, but first
* check if we are using the current limit.
* Currently increasing by 6.25%, unknown
* if that is the optimal rate.
*/
int rate_diff100;
delta_bytes = new_cg_tx_bytes -
last_cg_tx_bytes;
last_cg_tx_bytes = new_cg_tx_bytes;
delta_rate = (delta_bytes * 8000000) /
delta_time;
printf(" rate:%.3fGbps",
delta_rate/1000000000.0);
rate_diff100 = (((long long)rate)*1000000 -
delta_rate) * 100 /
(((long long) rate) * 1000000);
printf(" rdiff:%d", rate_diff100);
if (rate_diff100 <= 3) {
rate += (rate >> 4);
if (rate > RATE_THRESHOLD / 1000000)
rate = RATE_THRESHOLD / 1000000;
qstats.rate = rate;
printf(" INC\n");
} else {
printf("\n");
}
} else {
/* Need to decrease cgroup rate limit.
* Currently decreasing by 12.5%, unknown
* if that is optimal
*/
printf(" DEC\n");
rate -= (rate >> 3);
if (rate < minRate)
rate = minRate;
qstats.rate = rate;
}
if (bpf_map_update_elem(queue_stats_fd, &key, &qstats, BPF_ANY))
do_error("update map element fails", false);
}
} else {
sleep(dur);
}
// Get stats!
if (stats_flag && bpf_map_lookup_elem(queue_stats_fd, &key, &qstats)) {
char fname[100];
FILE *fout;
if (!outFlag)
sprintf(fname, "hbm.%d.in", cg_id);
else
sprintf(fname, "hbm.%d.out", cg_id);
fout = fopen(fname, "w");
fprintf(fout, "id:%d\n", cg_id);
fprintf(fout, "ERROR: Could not lookup queue_stats\n");
fclose(fout);
} else if (stats_flag && qstats.lastPacketTime >
qstats.firstPacketTime) {
long long delta_us = (qstats.lastPacketTime -
qstats.firstPacketTime)/1000;
unsigned int rate_mbps = ((qstats.bytes_total -
qstats.bytes_dropped) * 8 /
delta_us);
double percent_pkts, percent_bytes;
char fname[100];
FILE *fout;
int k;
static const char *returnValNames[] = {
"DROP_PKT",
"ALLOW_PKT",
"DROP_PKT_CWR",
"ALLOW_PKT_CWR"
};
#define RET_VAL_COUNT 4
// Future support of ingress
// if (!outFlag)
// sprintf(fname, "hbm.%d.in", cg_id);
// else
sprintf(fname, "hbm.%d.out", cg_id);
fout = fopen(fname, "w");
fprintf(fout, "id:%d\n", cg_id);
fprintf(fout, "rate_mbps:%d\n", rate_mbps);
fprintf(fout, "duration:%.1f secs\n",
(qstats.lastPacketTime - qstats.firstPacketTime) /
1000000000.0);
fprintf(fout, "packets:%d\n", (int)qstats.pkts_total);
fprintf(fout, "bytes_MB:%d\n", (int)(qstats.bytes_total /
1000000));
fprintf(fout, "pkts_dropped:%d\n", (int)qstats.pkts_dropped);
fprintf(fout, "bytes_dropped_MB:%d\n",
(int)(qstats.bytes_dropped /
1000000));
// Marked Pkts and Bytes
percent_pkts = (qstats.pkts_marked * 100.0) /
(qstats.pkts_total + 1);
percent_bytes = (qstats.bytes_marked * 100.0) /
(qstats.bytes_total + 1);
fprintf(fout, "pkts_marked_percent:%6.2f\n", percent_pkts);
fprintf(fout, "bytes_marked_percent:%6.2f\n", percent_bytes);
// Dropped Pkts and Bytes
percent_pkts = (qstats.pkts_dropped * 100.0) /
(qstats.pkts_total + 1);
percent_bytes = (qstats.bytes_dropped * 100.0) /
(qstats.bytes_total + 1);
fprintf(fout, "pkts_dropped_percent:%6.2f\n", percent_pkts);
fprintf(fout, "bytes_dropped_percent:%6.2f\n", percent_bytes);
// ECN CE markings
percent_pkts = (qstats.pkts_ecn_ce * 100.0) /
(qstats.pkts_total + 1);
fprintf(fout, "pkts_ecn_ce:%6.2f (%d)\n", percent_pkts,
(int)qstats.pkts_ecn_ce);
// Average cwnd
fprintf(fout, "avg cwnd:%d\n",
(int)(qstats.sum_cwnd / (qstats.sum_cwnd_cnt + 1)));
// Average rtt
fprintf(fout, "avg rtt:%d\n",
(int)(qstats.sum_rtt / (qstats.pkts_total + 1)));
// Average credit
if (edt_flag)
fprintf(fout, "avg credit_ms:%.03f\n",
(qstats.sum_credit /
(qstats.pkts_total + 1.0)) / 1000000.0);
else
fprintf(fout, "avg credit:%d\n",
(int)(qstats.sum_credit /
(1500 * ((int)qstats.pkts_total ) + 1)));
// Return values stats
for (k = 0; k < RET_VAL_COUNT; k++) {
percent_pkts = (qstats.returnValCount[k] * 100.0) /
(qstats.pkts_total + 1);
fprintf(fout, "%s:%6.2f (%d)\n", returnValNames[k],
percent_pkts, (int)qstats.returnValCount[k]);
}
fclose(fout);
}
if (debugFlag)
read_trace_pipe2();
goto cleanup;
err:
rc = 1;
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
if (cg1 != -1)
close(cg1);
if (rc != 0)
cleanup_cgroup_environment();
return rc;
}
static void Usage(void)
{
printf("This program loads a cgroup skb BPF program to enforce\n"
"cgroup output (egress) bandwidth limits.\n\n"
"USAGE: hbm [-o] [-d] [-l] [-n <id>] [--no_cn] [-r <rate>]\n"
" [-s] [-t <secs>] [-w] [-h] [prog]\n"
" Where:\n"
" -o indicates egress direction (default)\n"
" -d print BPF trace debug buffer\n"
" --edt use fq's Earliest Departure Time\n"
" -l also limit flows using loopback\n"
" -n <#> to create cgroup \"/hbm#\" and attach prog\n"
" Default is /hbm1\n"
" --no_cn disable CN notifications\n"
" -r <rate> Rate in Mbps\n"
" -s Update HBM stats\n"
" -t <time> Exit after specified seconds (default is 0)\n"
" -w Work conserving flag. cgroup can increase\n"
" bandwidth beyond the rate limit specified\n"
" while there is available bandwidth. Current\n"
" implementation assumes there is only eth0\n"
" but can be extended to support multiple NICs\n"
" -h print this info\n"
" prog BPF program file name. Name defaults to\n"
" hbm_out_kern.o\n");
}
int main(int argc, char **argv)
{
char *prog = "hbm_out_kern.o";
int k;
int cg_id = 1;
char *optstring = "iodln:r:st:wh";
struct option loptions[] = {
{"no_cn", 0, NULL, 1},
{"edt", 0, NULL, 2},
{NULL, 0, NULL, 0}
};
while ((k = getopt_long(argc, argv, optstring, loptions, NULL)) != -1) {
switch (k) {
case 1:
no_cn_flag = true;
break;
case 2:
prog = "hbm_edt_kern.o";
edt_flag = true;
break;
case'o':
break;
case 'd':
debugFlag = true;
break;
case 'l':
loopback_flag = true;
break;
case 'n':
cg_id = atoi(optarg);
break;
case 'r':
minRate = atoi(optarg) * 1.024;
rate = minRate;
break;
case 's':
stats_flag = true;
break;
case 't':
dur = atoi(optarg);
break;
case 'w':
work_conserving_flag = true;
break;
case '?':
if (optopt == 'n' || optopt == 'r' || optopt == 't')
fprintf(stderr,
"Option -%c requires an argument.\n\n",
optopt);
case 'h':
default:
Usage();
return 0;
}
}
if (optind < argc)
prog = argv[optind];
printf("HBM prog: %s\n", prog != NULL ? prog : "NULL");
/* Use libbpf 1.0 API mode */
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
return run_bpf_prog(prog, cg_id);
}
+38
View File
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (c) 2019 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Include file for Host Bandwidth Management (HBM) programs
*/
struct hbm_vqueue {
struct bpf_spin_lock lock;
/* 4 byte hole */
unsigned long long lasttime; /* In ns */
int credit; /* In bytes */
unsigned int rate; /* In bytes per NS << 20 */
};
struct hbm_queue_stats {
unsigned long rate; /* in Mbps*/
unsigned long stats:1, /* get HBM stats (marked, dropped,..) */
loopback:1, /* also limit flows using loopback */
no_cn:1; /* do not use cn flags */
unsigned long long pkts_marked;
unsigned long long bytes_marked;
unsigned long long pkts_dropped;
unsigned long long bytes_dropped;
unsigned long long pkts_total;
unsigned long long bytes_total;
unsigned long long firstPacketTime;
unsigned long long lastPacketTime;
unsigned long long pkts_ecn_ce;
unsigned long long returnValCount[4];
unsigned long long sum_cwnd;
unsigned long long sum_rtt;
unsigned long long sum_cwnd_cnt;
long long sum_credit;
};
+168
View File
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Sample Host Bandwidth Manager (HBM) BPF program.
*
* A cgroup skb BPF egress program to limit cgroup output bandwidth.
* It uses a modified virtual token bucket queue to limit average
* egress bandwidth. The implementation uses credits instead of tokens.
* Negative credits imply that queueing would have happened (this is
* a virtual queue, so no queueing is done by it. However, queueing may
* occur at the actual qdisc (which is not used for rate limiting).
*
* This implementation uses 3 thresholds, one to start marking packets and
* the other two to drop packets:
* CREDIT
* - <--------------------------|------------------------> +
* | | | 0
* | Large pkt |
* | drop thresh |
* Small pkt drop Mark threshold
* thresh
*
* The effect of marking depends on the type of packet:
* a) If the packet is ECN enabled and it is a TCP packet, then the packet
* is ECN marked.
* b) If the packet is a TCP packet, then we probabilistically call tcp_cwr
* to reduce the congestion window. The current implementation uses a linear
* distribution (0% probability at marking threshold, 100% probability
* at drop threshold).
* c) If the packet is not a TCP packet, then it is dropped.
*
* If the credit is below the drop threshold, the packet is dropped. If it
* is a TCP packet, then it also calls tcp_cwr since packets dropped by
* a cgroup skb BPF program do not automatically trigger a call to
* tcp_cwr in the current kernel code.
*
* This BPF program actually uses 2 drop thresholds, one threshold
* for larger packets (>= 120 bytes) and another for smaller packets. This
* protects smaller packets such as SYNs, ACKs, etc.
*
* The default bandwidth limit is set at 1Gbps but this can be changed by
* a user program through a shared BPF map. In addition, by default this BPF
* program does not limit connections using loopback. This behavior can be
* overwritten by the user program. There is also an option to calculate
* some statistics, such as percent of packets marked or dropped, which
* a user program, such as hbm, can access.
*/
#include "hbm_kern.h"
SEC("cgroup_skb/egress")
int _hbm_out_cg(struct __sk_buff *skb)
{
long long delta = 0, delta_send;
unsigned long long curtime, sendtime;
struct hbm_queue_stats *qsp = NULL;
unsigned int queue_index = 0;
bool congestion_flag = false;
bool ecn_ce_flag = false;
struct hbm_pkt_info pkti = {};
struct hbm_vqueue *qdp;
bool drop_flag = false;
bool cwr_flag = false;
int len = skb->len;
int rv = ALLOW_PKT;
qsp = bpf_map_lookup_elem(&queue_stats, &queue_index);
// Check if we should ignore loopback traffic
if (qsp != NULL && !qsp->loopback && (skb->ifindex == 1))
return ALLOW_PKT;
hbm_get_pkt_info(skb, &pkti);
// We may want to account for the length of headers in len
// calculation, like ETH header + overhead, specially if it
// is a gso packet. But I am not doing it right now.
qdp = bpf_get_local_storage(&queue_state, 0);
if (!qdp)
return ALLOW_PKT;
if (qdp->lasttime == 0)
hbm_init_edt_vqueue(qdp, 1024);
curtime = bpf_ktime_get_ns();
// Begin critical section
bpf_spin_lock(&qdp->lock);
delta = qdp->lasttime - curtime;
// bound bursts to 100us
if (delta < -BURST_SIZE_NS) {
// negative delta is a credit that allows bursts
qdp->lasttime = curtime - BURST_SIZE_NS;
delta = -BURST_SIZE_NS;
}
sendtime = qdp->lasttime;
delta_send = BYTES_TO_NS(len, qdp->rate);
__sync_add_and_fetch(&(qdp->lasttime), delta_send);
bpf_spin_unlock(&qdp->lock);
// End critical section
// Set EDT of packet
skb->tstamp = sendtime;
// Check if we should update rate
if (qsp != NULL && (qsp->rate * 128) != qdp->rate)
qdp->rate = qsp->rate * 128;
// Set flags (drop, congestion, cwr)
// last packet will be sent in the future, bound latency
if (delta > DROP_THRESH_NS || (delta > LARGE_PKT_DROP_THRESH_NS &&
len > LARGE_PKT_THRESH)) {
drop_flag = true;
if (pkti.is_tcp && pkti.ecn == 0)
cwr_flag = true;
} else if (delta > MARK_THRESH_NS) {
if (pkti.is_tcp)
congestion_flag = true;
else
drop_flag = true;
}
if (congestion_flag) {
if (bpf_skb_ecn_set_ce(skb)) {
ecn_ce_flag = true;
} else {
if (pkti.is_tcp) {
unsigned int rand = bpf_get_prandom_u32();
if (delta >= MARK_THRESH_NS +
(rand % MARK_REGION_SIZE_NS)) {
// Do congestion control
cwr_flag = true;
}
} else if (len > LARGE_PKT_THRESH) {
// Problem if too many small packets?
drop_flag = true;
congestion_flag = false;
}
}
}
if (pkti.is_tcp && drop_flag && pkti.packets_out <= 1) {
drop_flag = false;
cwr_flag = true;
congestion_flag = false;
}
if (qsp != NULL && qsp->no_cn)
cwr_flag = false;
hbm_update_stats(qsp, len, curtime, congestion_flag, drop_flag,
cwr_flag, ecn_ce_flag, &pkti, (int) delta);
if (drop_flag) {
__sync_add_and_fetch(&(qdp->lasttime), -delta_send);
rv = DROP_PKT;
}
if (cwr_flag)
rv |= CWR;
return rv;
}
char _license[] SEC("license") = "GPL";
+215
View File
@@ -0,0 +1,215 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (c) 2019 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Include file for sample Host Bandwidth Manager (HBM) BPF programs
*/
#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/in.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/filter.h>
#include <uapi/linux/pkt_cls.h>
#include <net/ipv6.h>
#include <net/inet_ecn.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include "hbm.h"
#define DROP_PKT 0
#define ALLOW_PKT 1
#define TCP_ECN_OK 1
#define CWR 2
#ifndef HBM_DEBUG // Define HBM_DEBUG to enable debugging
#undef bpf_printk
#define bpf_printk(fmt, ...)
#endif
#define INITIAL_CREDIT_PACKETS 100
#define MAX_BYTES_PER_PACKET 1500
#define MARK_THRESH (40 * MAX_BYTES_PER_PACKET)
#define DROP_THRESH (80 * 5 * MAX_BYTES_PER_PACKET)
#define LARGE_PKT_DROP_THRESH (DROP_THRESH - (15 * MAX_BYTES_PER_PACKET))
#define MARK_REGION_SIZE (LARGE_PKT_DROP_THRESH - MARK_THRESH)
#define LARGE_PKT_THRESH 120
#define MAX_CREDIT (100 * MAX_BYTES_PER_PACKET)
#define INIT_CREDIT (INITIAL_CREDIT_PACKETS * MAX_BYTES_PER_PACKET)
// Time base accounting for fq's EDT
#define BURST_SIZE_NS 100000 // 100us
#define MARK_THRESH_NS 50000 // 50us
#define DROP_THRESH_NS 500000 // 500us
// Reserve 20us of queuing for small packets (less than 120 bytes)
#define LARGE_PKT_DROP_THRESH_NS (DROP_THRESH_NS - 20000)
#define MARK_REGION_SIZE_NS (LARGE_PKT_DROP_THRESH_NS - MARK_THRESH_NS)
// rate in bytes per ns << 20
#define CREDIT_PER_NS(delta, rate) ((((u64)(delta)) * (rate)) >> 20)
#define BYTES_PER_NS(delta, rate) ((((u64)(delta)) * (rate)) >> 20)
#define BYTES_TO_NS(bytes, rate) div64_u64(((u64)(bytes)) << 20, (u64)(rate))
struct {
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, struct hbm_vqueue);
} queue_state SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, struct hbm_queue_stats);
} queue_stats SEC(".maps");
struct hbm_pkt_info {
int cwnd;
int rtt;
int packets_out;
bool is_ip;
bool is_tcp;
short ecn;
};
static int get_tcp_info(struct __sk_buff *skb, struct hbm_pkt_info *pkti)
{
struct bpf_sock *sk;
struct bpf_tcp_sock *tp;
sk = skb->sk;
if (sk) {
sk = bpf_sk_fullsock(sk);
if (sk) {
if (sk->protocol == IPPROTO_TCP) {
tp = bpf_tcp_sock(sk);
if (tp) {
pkti->cwnd = tp->snd_cwnd;
pkti->rtt = tp->srtt_us >> 3;
pkti->packets_out = tp->packets_out;
return 0;
}
}
}
}
pkti->cwnd = 0;
pkti->rtt = 0;
pkti->packets_out = 0;
return 1;
}
static void hbm_get_pkt_info(struct __sk_buff *skb,
struct hbm_pkt_info *pkti)
{
struct iphdr iph;
struct ipv6hdr *ip6h;
pkti->cwnd = 0;
pkti->rtt = 0;
bpf_skb_load_bytes(skb, 0, &iph, 12);
if (iph.version == 6) {
ip6h = (struct ipv6hdr *)&iph;
pkti->is_ip = true;
pkti->is_tcp = (ip6h->nexthdr == 6);
pkti->ecn = (ip6h->flow_lbl[0] >> 4) & INET_ECN_MASK;
} else if (iph.version == 4) {
pkti->is_ip = true;
pkti->is_tcp = (iph.protocol == 6);
pkti->ecn = iph.tos & INET_ECN_MASK;
} else {
pkti->is_ip = false;
pkti->is_tcp = false;
pkti->ecn = 0;
}
if (pkti->is_tcp)
get_tcp_info(skb, pkti);
}
static __always_inline void hbm_init_vqueue(struct hbm_vqueue *qdp, int rate)
{
bpf_printk("Initializing queue_state, rate:%d\n", rate * 128);
qdp->lasttime = bpf_ktime_get_ns();
qdp->credit = INIT_CREDIT;
qdp->rate = rate * 128;
}
static __always_inline void hbm_init_edt_vqueue(struct hbm_vqueue *qdp,
int rate)
{
unsigned long long curtime;
curtime = bpf_ktime_get_ns();
bpf_printk("Initializing queue_state, rate:%d\n", rate * 128);
qdp->lasttime = curtime - BURST_SIZE_NS; // support initial burst
qdp->credit = 0; // not used
qdp->rate = rate * 128;
}
static __always_inline void hbm_update_stats(struct hbm_queue_stats *qsp,
int len,
unsigned long long curtime,
bool congestion_flag,
bool drop_flag,
bool cwr_flag,
bool ecn_ce_flag,
struct hbm_pkt_info *pkti,
int credit)
{
int rv = ALLOW_PKT;
if (qsp != NULL) {
// Following is needed for work conserving
__sync_add_and_fetch(&(qsp->bytes_total), len);
if (qsp->stats) {
// Optionally update statistics
if (qsp->firstPacketTime == 0)
qsp->firstPacketTime = curtime;
qsp->lastPacketTime = curtime;
__sync_add_and_fetch(&(qsp->pkts_total), 1);
if (congestion_flag) {
__sync_add_and_fetch(&(qsp->pkts_marked), 1);
__sync_add_and_fetch(&(qsp->bytes_marked), len);
}
if (drop_flag) {
__sync_add_and_fetch(&(qsp->pkts_dropped), 1);
__sync_add_and_fetch(&(qsp->bytes_dropped),
len);
}
if (ecn_ce_flag)
__sync_add_and_fetch(&(qsp->pkts_ecn_ce), 1);
if (pkti->cwnd) {
__sync_add_and_fetch(&(qsp->sum_cwnd),
pkti->cwnd);
__sync_add_and_fetch(&(qsp->sum_cwnd_cnt), 1);
}
if (pkti->rtt)
__sync_add_and_fetch(&(qsp->sum_rtt),
pkti->rtt);
__sync_add_and_fetch(&(qsp->sum_credit), credit);
if (drop_flag)
rv = DROP_PKT;
if (cwr_flag)
rv |= 2;
if (rv == DROP_PKT)
__sync_add_and_fetch(&(qsp->returnValCount[0]),
1);
else if (rv == ALLOW_PKT)
__sync_add_and_fetch(&(qsp->returnValCount[1]),
1);
else if (rv == 2)
__sync_add_and_fetch(&(qsp->returnValCount[2]),
1);
else if (rv == 3)
__sync_add_and_fetch(&(qsp->returnValCount[3]),
1);
}
}
}
+179
View File
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Sample Host Bandwidth Manager (HBM) BPF program.
*
* A cgroup skb BPF egress program to limit cgroup output bandwidth.
* It uses a modified virtual token bucket queue to limit average
* egress bandwidth. The implementation uses credits instead of tokens.
* Negative credits imply that queueing would have happened (this is
* a virtual queue, so no queueing is done by it. However, queueing may
* occur at the actual qdisc (which is not used for rate limiting).
*
* This implementation uses 3 thresholds, one to start marking packets and
* the other two to drop packets:
* CREDIT
* - <--------------------------|------------------------> +
* | | | 0
* | Large pkt |
* | drop thresh |
* Small pkt drop Mark threshold
* thresh
*
* The effect of marking depends on the type of packet:
* a) If the packet is ECN enabled and it is a TCP packet, then the packet
* is ECN marked.
* b) If the packet is a TCP packet, then we probabilistically call tcp_cwr
* to reduce the congestion window. The current implementation uses a linear
* distribution (0% probability at marking threshold, 100% probability
* at drop threshold).
* c) If the packet is not a TCP packet, then it is dropped.
*
* If the credit is below the drop threshold, the packet is dropped. If it
* is a TCP packet, then it also calls tcp_cwr since packets dropped by
* by a cgroup skb BPF program do not automatically trigger a call to
* tcp_cwr in the current kernel code.
*
* This BPF program actually uses 2 drop thresholds, one threshold
* for larger packets (>= 120 bytes) and another for smaller packets. This
* protects smaller packets such as SYNs, ACKs, etc.
*
* The default bandwidth limit is set at 1Gbps but this can be changed by
* a user program through a shared BPF map. In addition, by default this BPF
* program does not limit connections using loopback. This behavior can be
* overwritten by the user program. There is also an option to calculate
* some statistics, such as percent of packets marked or dropped, which
* the user program can access.
*
* A latter patch provides such a program (hbm.c)
*/
#include "hbm_kern.h"
SEC("cgroup_skb/egress")
int _hbm_out_cg(struct __sk_buff *skb)
{
struct hbm_pkt_info pkti;
int len = skb->len;
unsigned int queue_index = 0;
unsigned long long curtime;
int credit;
signed long long delta = 0, new_credit;
int max_credit = MAX_CREDIT;
bool congestion_flag = false;
bool drop_flag = false;
bool cwr_flag = false;
bool ecn_ce_flag = false;
struct hbm_vqueue *qdp;
struct hbm_queue_stats *qsp = NULL;
int rv = ALLOW_PKT;
qsp = bpf_map_lookup_elem(&queue_stats, &queue_index);
if (qsp != NULL && !qsp->loopback && (skb->ifindex == 1))
return ALLOW_PKT;
hbm_get_pkt_info(skb, &pkti);
// We may want to account for the length of headers in len
// calculation, like ETH header + overhead, specially if it
// is a gso packet. But I am not doing it right now.
qdp = bpf_get_local_storage(&queue_state, 0);
if (!qdp)
return ALLOW_PKT;
else if (qdp->lasttime == 0)
hbm_init_vqueue(qdp, 1024);
curtime = bpf_ktime_get_ns();
// Begin critical section
bpf_spin_lock(&qdp->lock);
credit = qdp->credit;
delta = curtime - qdp->lasttime;
/* delta < 0 implies that another process with a curtime greater
* than ours beat us to the critical section and already added
* the new credit, so we should not add it ourselves
*/
if (delta > 0) {
qdp->lasttime = curtime;
new_credit = credit + CREDIT_PER_NS(delta, qdp->rate);
if (new_credit > MAX_CREDIT)
credit = MAX_CREDIT;
else
credit = new_credit;
}
credit -= len;
qdp->credit = credit;
bpf_spin_unlock(&qdp->lock);
// End critical section
// Check if we should update rate
if (qsp != NULL && (qsp->rate * 128) != qdp->rate) {
qdp->rate = qsp->rate * 128;
bpf_printk("Updating rate: %d (1sec:%llu bits)\n",
(int)qdp->rate,
CREDIT_PER_NS(1000000000, qdp->rate) * 8);
}
// Set flags (drop, congestion, cwr)
// Dropping => we are congested, so ignore congestion flag
if (credit < -DROP_THRESH ||
(len > LARGE_PKT_THRESH && credit < -LARGE_PKT_DROP_THRESH)) {
// Very congested, set drop packet
drop_flag = true;
if (pkti.ecn)
congestion_flag = true;
else if (pkti.is_tcp)
cwr_flag = true;
} else if (credit < 0) {
// Congested, set congestion flag
if (pkti.ecn || pkti.is_tcp) {
if (credit < -MARK_THRESH)
congestion_flag = true;
else
congestion_flag = false;
} else {
congestion_flag = true;
}
}
if (congestion_flag) {
if (bpf_skb_ecn_set_ce(skb)) {
ecn_ce_flag = true;
} else {
if (pkti.is_tcp) {
unsigned int rand = bpf_get_prandom_u32();
if (-credit >= MARK_THRESH +
(rand % MARK_REGION_SIZE)) {
// Do congestion control
cwr_flag = true;
}
} else if (len > LARGE_PKT_THRESH) {
// Problem if too many small packets?
drop_flag = true;
}
}
}
if (qsp != NULL)
if (qsp->no_cn)
cwr_flag = false;
hbm_update_stats(qsp, len, curtime, congestion_flag, drop_flag,
cwr_flag, ecn_ce_flag, &pkti, credit);
if (drop_flag) {
__sync_add_and_fetch(&(qdp->credit), len);
rv = DROP_PKT;
}
if (cwr_flag)
rv |= 2;
return rv;
}
char _license[] SEC("license") = "GPL";
+138
View File
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* ibumad BPF sample kernel side
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Copyright(c) 2018 Ira Weiny, Intel Corporation
*/
#define KBUILD_MODNAME "ibumad_count_pkts_by_class"
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32); /* class; u32 required */
__type(value, u64); /* count of mads read */
__uint(max_entries, 256); /* Room for all Classes */
} read_count SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32); /* class; u32 required */
__type(value, u64); /* count of mads written */
__uint(max_entries, 256); /* Room for all Classes */
} write_count SEC(".maps");
#undef DEBUG
#ifndef DEBUG
#undef bpf_printk
#define bpf_printk(fmt, ...)
#endif
/* Taken from the current format defined in
* include/trace/events/ib_umad.h
* and
* /sys/kernel/tracing/events/ib_umad/ib_umad_read/format
* /sys/kernel/tracing/events/ib_umad/ib_umad_write/format
*/
struct ib_umad_rw_args {
u64 pad;
u8 port_num;
u8 sl;
u8 path_bits;
u8 grh_present;
u32 id;
u32 status;
u32 timeout_ms;
u32 retires;
u32 length;
u32 qpn;
u32 qkey;
u8 gid_index;
u8 hop_limit;
u16 lid;
u16 attr_id;
u16 pkey_index;
u8 base_version;
u8 mgmt_class;
u8 class_version;
u8 method;
u32 flow_label;
u16 mad_status;
u16 class_specific;
u32 attr_mod;
u64 tid;
u8 gid[16];
u32 dev_index;
u8 traffic_class;
};
SEC("tracepoint/ib_umad/ib_umad_read_recv")
int on_ib_umad_read_recv(struct ib_umad_rw_args *ctx)
{
u64 zero = 0, *val;
u8 class = ctx->mgmt_class;
bpf_printk("ib_umad read recv : class 0x%x\n", class);
val = bpf_map_lookup_elem(&read_count, &class);
if (!val) {
bpf_map_update_elem(&read_count, &class, &zero, BPF_NOEXIST);
val = bpf_map_lookup_elem(&read_count, &class);
if (!val)
return 0;
}
(*val) += 1;
return 0;
}
SEC("tracepoint/ib_umad/ib_umad_read_send")
int on_ib_umad_read_send(struct ib_umad_rw_args *ctx)
{
u64 zero = 0, *val;
u8 class = ctx->mgmt_class;
bpf_printk("ib_umad read send : class 0x%x\n", class);
val = bpf_map_lookup_elem(&read_count, &class);
if (!val) {
bpf_map_update_elem(&read_count, &class, &zero, BPF_NOEXIST);
val = bpf_map_lookup_elem(&read_count, &class);
if (!val)
return 0;
}
(*val) += 1;
return 0;
}
SEC("tracepoint/ib_umad/ib_umad_write")
int on_ib_umad_write(struct ib_umad_rw_args *ctx)
{
u64 zero = 0, *val;
u8 class = ctx->mgmt_class;
bpf_printk("ib_umad write : class 0x%x\n", class);
val = bpf_map_lookup_elem(&write_count, &class);
if (!val) {
bpf_map_update_elem(&write_count, &class, &zero, BPF_NOEXIST);
val = bpf_map_lookup_elem(&write_count, &class);
if (!val)
return 0;
}
(*val) += 1;
return 0;
}
char _license[] SEC("license") = "GPL";
+158
View File
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* ibumad BPF sample user side
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Copyright(c) 2018 Ira Weiny, Intel Corporation
*/
#include <linux/bpf.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <limits.h>
#include <getopt.h>
#include <net/if.h>
#include <bpf/bpf.h>
#include "bpf_util.h"
#include <bpf/libbpf.h>
static struct bpf_link *tp_links[3];
static struct bpf_object *obj;
static int map_fd[2];
static int tp_cnt;
static void dump_counts(int fd)
{
__u32 key;
__u64 value;
for (key = 0; key < 256; key++) {
if (bpf_map_lookup_elem(fd, &key, &value)) {
printf("failed to read key %u\n", key);
continue;
}
if (value)
printf("0x%02x : %llu\n", key, value);
}
}
static void dump_all_counts(void)
{
printf("Read 'Class : count'\n");
dump_counts(map_fd[0]);
printf("Write 'Class : count'\n");
dump_counts(map_fd[1]);
}
static void dump_exit(int sig)
{
dump_all_counts();
/* Detach tracepoints */
while (tp_cnt)
bpf_link__destroy(tp_links[--tp_cnt]);
bpf_object__close(obj);
exit(0);
}
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"delay", required_argument, NULL, 'd'},
};
static void usage(char *cmd)
{
printf("eBPF test program to count packets from various IP addresses\n"
"Usage: %s <options>\n"
" --help, -h this menu\n"
" --delay, -d <delay> wait <delay> sec between prints [1 - 1000000]\n"
, cmd
);
}
int main(int argc, char **argv)
{
struct bpf_program *prog;
unsigned long delay = 5;
char filename[256];
int longindex = 0;
int opt, err = -1;
while ((opt = getopt_long(argc, argv, "hd:rSw",
long_options, &longindex)) != -1) {
switch (opt) {
case 'd':
delay = strtoul(optarg, NULL, 0);
if (delay == ULONG_MAX || delay < 0 ||
delay > 1000000) {
fprintf(stderr, "ERROR: invalid delay : %s\n",
optarg);
usage(argv[0]);
return 1;
}
break;
default:
case 'h':
usage(argv[0]);
return 1;
}
}
/* Do one final dump when exiting */
signal(SIGINT, dump_exit);
signal(SIGTERM, dump_exit);
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return err;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "read_count");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "write_count");
if (map_fd[0] < 0 || map_fd[1] < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
tp_links[tp_cnt] = bpf_program__attach(prog);
if (libbpf_get_error(tp_links[tp_cnt])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
tp_links[tp_cnt] = NULL;
goto cleanup;
}
tp_cnt++;
}
while (1) {
sleep(delay);
dump_all_counts();
}
err = 0;
cleanup:
/* Detach tracepoints */
while (tp_cnt)
bpf_link__destroy(tp_links[--tp_cnt]);
bpf_object__close(obj);
return err;
}
+99
View File
@@ -0,0 +1,99 @@
/* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
* Copyright (c) 2015 BMW Car IT GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <linux/version.h>
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
#define MAX_ENTRIES 20
#define MAX_CPU 4
/* We need to stick to static allocated memory (an array instead of
* hash table) because managing dynamic memory from the
* trace_preempt_[on|off] tracepoints hooks is not supported.
*/
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, u64);
__uint(max_entries, MAX_CPU);
} my_map SEC(".maps");
SEC("kprobe/trace_preempt_off")
int bpf_prog1(struct pt_regs *ctx)
{
int cpu = bpf_get_smp_processor_id();
u64 *ts = bpf_map_lookup_elem(&my_map, &cpu);
if (ts)
*ts = bpf_ktime_get_ns();
return 0;
}
static unsigned int log2(unsigned int v)
{
unsigned int r;
unsigned int shift;
r = (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
shift = (v > 0xF) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}
static unsigned int log2l(unsigned long v)
{
unsigned int hi = v >> 32;
if (hi)
return log2(hi) + 32;
else
return log2(v);
}
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, long);
__uint(max_entries, MAX_CPU * MAX_ENTRIES);
} my_lat SEC(".maps");
SEC("kprobe/trace_preempt_on")
int bpf_prog2(struct pt_regs *ctx)
{
u64 *ts, cur_ts, delta;
int key, cpu;
long *val;
cpu = bpf_get_smp_processor_id();
ts = bpf_map_lookup_elem(&my_map, &cpu);
if (!ts)
return 0;
cur_ts = bpf_ktime_get_ns();
delta = log2l(cur_ts - *ts);
if (delta > MAX_ENTRIES - 1)
delta = MAX_ENTRIES - 1;
key = cpu * MAX_ENTRIES + delta;
val = bpf_map_lookup_elem(&my_lat, &key);
if (val)
__sync_fetch_and_add((long *)val, 1);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+130
View File
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
* Copyright (c) 2015 BMW Car IT GmbH
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#define MAX_ENTRIES 20
#define MAX_CPU 4
#define MAX_STARS 40
struct cpu_hist {
long data[MAX_ENTRIES];
long max;
};
static struct cpu_hist cpu_hist[MAX_CPU];
static void stars(char *str, long val, long max, int width)
{
int i;
for (i = 0; i < (width * val / max) - 1 && i < width - 1; i++)
str[i] = '*';
if (val > max)
str[i - 1] = '+';
str[i] = '\0';
}
static void print_hist(void)
{
char starstr[MAX_STARS];
struct cpu_hist *hist;
int i, j;
/* clear screen */
printf("\033[2J");
for (j = 0; j < MAX_CPU; j++) {
hist = &cpu_hist[j];
/* ignore CPUs without data (maybe offline?) */
if (hist->max == 0)
continue;
printf("CPU %d\n", j);
printf(" latency : count distribution\n");
for (i = 1; i <= MAX_ENTRIES; i++) {
stars(starstr, hist->data[i - 1], hist->max, MAX_STARS);
printf("%8ld -> %-8ld : %-8ld |%-*s|\n",
(1l << i) >> 1, (1l << i) - 1,
hist->data[i - 1], MAX_STARS, starstr);
}
}
}
static void get_data(int fd)
{
long key, value;
int c, i;
for (i = 0; i < MAX_CPU; i++)
cpu_hist[i].max = 0;
for (c = 0; c < MAX_CPU; c++) {
for (i = 0; i < MAX_ENTRIES; i++) {
key = c * MAX_ENTRIES + i;
bpf_map_lookup_elem(fd, &key, &value);
cpu_hist[c].data[i] = value;
if (value > cpu_hist[c].max)
cpu_hist[c].max = value;
}
}
}
int main(int argc, char **argv)
{
struct bpf_link *links[2];
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int map_fd, i = 0;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "my_lat");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[i] = bpf_program__attach(prog);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[i] = NULL;
goto cleanup;
}
i++;
}
while (1) {
get_data(map_fd);
print_hist();
sleep(5);
}
cleanup:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
bpf_object__close(obj);
return 0;
}
@@ -0,0 +1,62 @@
/* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License 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.
*/
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__type(key, u64);
__type(value, u64);
__uint(pinning, LIBBPF_PIN_BY_NAME);
__uint(max_entries, 1024);
} lwt_len_hist_map SEC(".maps");
static unsigned int log2(unsigned int v)
{
unsigned int r;
unsigned int shift;
r = (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
shift = (v > 0xF) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);
return r;
}
static unsigned int log2l(unsigned long v)
{
unsigned int hi = v >> 32;
if (hi)
return log2(hi) + 32;
else
return log2(v);
}
SEC("len_hist")
int do_len_hist(struct __sk_buff *skb)
{
__u64 *value, key, init_val = 1;
key = log2l(skb->len);
value = bpf_map_lookup_elem(&lwt_len_hist_map, &key);
if (value)
__sync_fetch_and_add(value, 1);
else
bpf_map_update_elem(&lwt_len_hist_map, &key, &init_val, BPF_ANY);
return BPF_OK;
}
char _license[] SEC("license") = "GPL";
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
NS1=lwt_ns1
VETH0=tst_lwt1a
VETH1=tst_lwt1b
BPF_PROG=lwt_len_hist.bpf.o
TRACE_ROOT=/sys/kernel/tracing
function cleanup {
# To reset saved histogram, remove pinned map
rm /sys/fs/bpf/tc/globals/lwt_len_hist_map
ip route del 192.168.253.2/32 dev $VETH0 2> /dev/null
ip link del $VETH0 2> /dev/null
ip link del $VETH1 2> /dev/null
ip netns exec $NS1 killall netserver
ip netns delete $NS1 2> /dev/null
}
cleanup
ip netns add $NS1
ip link add $VETH0 type veth peer name $VETH1
ip link set dev $VETH0 up
ip addr add 192.168.253.1/24 dev $VETH0
ip link set $VETH1 netns $NS1
ip netns exec $NS1 ip link set dev $VETH1 up
ip netns exec $NS1 ip addr add 192.168.253.2/24 dev $VETH1
ip netns exec $NS1 netserver
echo 1 > ${TRACE_ROOT}/tracing_on
cp /dev/null ${TRACE_ROOT}/trace
ip route add 192.168.253.2/32 encap bpf out obj $BPF_PROG section len_hist dev $VETH0
netperf -H 192.168.253.2 -t TCP_STREAM
cat ${TRACE_ROOT}/trace | grep -v '^#'
./lwt_len_hist
cleanup
echo 0 > ${TRACE_ROOT}/tracing_on
exit 0
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/unistd.h>
#include <linux/bpf.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <bpf/bpf.h>
#include "bpf_util.h"
#define MAX_INDEX 64
#define MAX_STARS 38
static void stars(char *str, long val, long max, int width)
{
int i;
for (i = 0; i < (width * val / max) - 1 && i < width - 1; i++)
str[i] = '*';
if (val > max)
str[i - 1] = '+';
str[i] = '\0';
}
int main(int argc, char **argv)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
const char *map_filename = "/sys/fs/bpf/tc/globals/lwt_len_hist_map";
uint64_t values[nr_cpus], sum, max_value = 0, data[MAX_INDEX] = {};
uint64_t key = 0, next_key, max_key = 0;
char starstr[MAX_STARS];
int i, map_fd;
map_fd = bpf_obj_get(map_filename);
if (map_fd < 0) {
fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n",
map_filename, strerror(errno), errno);
return -1;
}
while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) {
if (next_key >= MAX_INDEX) {
fprintf(stderr, "Key %lu out of bounds\n", next_key);
continue;
}
bpf_map_lookup_elem(map_fd, &next_key, values);
sum = 0;
for (i = 0; i < nr_cpus; i++)
sum += values[i];
data[next_key] = sum;
if (sum && next_key > max_key)
max_key = next_key;
if (sum > max_value)
max_value = sum;
key = next_key;
}
for (i = 1; i <= max_key + 1; i++) {
stars(starstr, data[i - 1], max_value, MAX_STARS);
printf("%8ld -> %-8ld : %-8ld |%-*s|\n",
(1l << i) >> 1, (1l << i) - 1, data[i - 1],
MAX_STARS, starstr);
}
close(map_fd);
return 0;
}
+297
View File
@@ -0,0 +1,297 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include <errno.h>
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#define MAX_ENTRIES 1000
#define MAX_NR_CPUS 1024
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, MAX_ENTRIES);
} hash_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, 10000);
} lru_hash_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, 10000);
__uint(map_flags, BPF_F_NO_COMMON_LRU);
} nocommon_lru_hash_map SEC(".maps");
struct inner_lru {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, MAX_ENTRIES);
__uint(map_flags, BPF_F_NUMA_NODE);
__uint(numa_node, 0);
} inner_lru_hash_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, MAX_NR_CPUS);
__uint(key_size, sizeof(u32));
__array(values, struct inner_lru); /* use inner_lru as inner map */
} array_of_lru_hashs SEC(".maps") = {
/* statically initialize the first element */
.values = { &inner_lru_hash_map },
};
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(long));
__uint(max_entries, MAX_ENTRIES);
} percpu_hash_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, MAX_ENTRIES);
__uint(map_flags, BPF_F_NO_PREALLOC);
} hash_map_alloc SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(long));
__uint(max_entries, MAX_ENTRIES);
__uint(map_flags, BPF_F_NO_PREALLOC);
} percpu_hash_map_alloc SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__uint(key_size, 8);
__uint(value_size, sizeof(long));
__uint(max_entries, 10000);
__uint(map_flags, BPF_F_NO_PREALLOC);
} lpm_trie_map_alloc SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, long);
__uint(max_entries, MAX_ENTRIES);
} array_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, u32);
__type(value, long);
__uint(max_entries, MAX_ENTRIES);
} lru_hash_lookup_map SEC(".maps");
SEC("ksyscall/getuid")
int BPF_KSYSCALL(stress_hmap)
{
u32 key = bpf_get_current_pid_tgid();
long init_val = 1;
long *value;
int i;
for (i = 0; i < 10; i++) {
bpf_map_update_elem(&hash_map, &key, &init_val, BPF_ANY);
value = bpf_map_lookup_elem(&hash_map, &key);
if (value)
bpf_map_delete_elem(&hash_map, &key);
}
return 0;
}
SEC("ksyscall/geteuid")
int BPF_KSYSCALL(stress_percpu_hmap)
{
u32 key = bpf_get_current_pid_tgid();
long init_val = 1;
long *value;
int i;
for (i = 0; i < 10; i++) {
bpf_map_update_elem(&percpu_hash_map, &key, &init_val, BPF_ANY);
value = bpf_map_lookup_elem(&percpu_hash_map, &key);
if (value)
bpf_map_delete_elem(&percpu_hash_map, &key);
}
return 0;
}
SEC("ksyscall/getgid")
int BPF_KSYSCALL(stress_hmap_alloc)
{
u32 key = bpf_get_current_pid_tgid();
long init_val = 1;
long *value;
int i;
for (i = 0; i < 10; i++) {
bpf_map_update_elem(&hash_map_alloc, &key, &init_val, BPF_ANY);
value = bpf_map_lookup_elem(&hash_map_alloc, &key);
if (value)
bpf_map_delete_elem(&hash_map_alloc, &key);
}
return 0;
}
SEC("ksyscall/getegid")
int BPF_KSYSCALL(stress_percpu_hmap_alloc)
{
u32 key = bpf_get_current_pid_tgid();
long init_val = 1;
long *value;
int i;
for (i = 0; i < 10; i++) {
bpf_map_update_elem(&percpu_hash_map_alloc, &key, &init_val, BPF_ANY);
value = bpf_map_lookup_elem(&percpu_hash_map_alloc, &key);
if (value)
bpf_map_delete_elem(&percpu_hash_map_alloc, &key);
}
return 0;
}
SEC("ksyscall/connect")
int BPF_KSYSCALL(stress_lru_hmap_alloc, int fd, struct sockaddr_in *uservaddr,
int addrlen)
{
char fmt[] = "Failed at stress_lru_hmap_alloc. ret:%dn";
union {
u16 dst6[8];
struct {
u16 magic0;
u16 magic1;
u16 tcase;
u16 unused16;
u32 unused32;
u32 key;
};
} test_params;
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)uservaddr;
u16 test_case;
long val = 1;
u32 key = 0;
int ret;
if (addrlen != sizeof(*in6))
return 0;
ret = bpf_probe_read_user(test_params.dst6, sizeof(test_params.dst6),
&in6->sin6_addr);
if (ret)
goto done;
if (test_params.magic0 != 0xdead ||
test_params.magic1 != 0xbeef)
return 0;
test_case = test_params.tcase;
if (test_case != 3)
key = bpf_get_prandom_u32();
if (test_case == 0) {
ret = bpf_map_update_elem(&lru_hash_map, &key, &val, BPF_ANY);
} else if (test_case == 1) {
ret = bpf_map_update_elem(&nocommon_lru_hash_map, &key, &val,
BPF_ANY);
} else if (test_case == 2) {
void *nolocal_lru_map;
int cpu = bpf_get_smp_processor_id();
nolocal_lru_map = bpf_map_lookup_elem(&array_of_lru_hashs,
&cpu);
if (!nolocal_lru_map) {
ret = -ENOENT;
goto done;
}
ret = bpf_map_update_elem(nolocal_lru_map, &key, &val,
BPF_ANY);
} else if (test_case == 3) {
u32 i;
key = test_params.key;
#pragma clang loop unroll(full)
for (i = 0; i < 32; i++) {
bpf_map_lookup_elem(&lru_hash_lookup_map, &key);
key++;
}
} else {
ret = -EINVAL;
}
done:
if (ret)
bpf_trace_printk(fmt, sizeof(fmt), ret);
return 0;
}
SEC("ksyscall/gettid")
int BPF_KSYSCALL(stress_lpm_trie_map_alloc)
{
union {
u32 b32[2];
u8 b8[8];
} key;
unsigned int i;
key.b32[0] = 32;
key.b8[4] = 192;
key.b8[5] = 168;
key.b8[6] = 0;
key.b8[7] = 1;
#pragma clang loop unroll(full)
for (i = 0; i < 32; ++i)
bpf_map_lookup_elem(&lpm_trie_map_alloc, &key);
return 0;
}
SEC("ksyscall/getpgid")
int BPF_KSYSCALL(stress_hash_map_lookup)
{
u32 key = 1, i;
long *value;
#pragma clang loop unroll(full)
for (i = 0; i < 64; ++i)
value = bpf_map_lookup_elem(&hash_map, &key);
return 0;
}
SEC("ksyscall/getppid")
int BPF_KSYSCALL(stress_array_map_lookup)
{
u32 key = 1, i;
long *value;
#pragma clang loop unroll(full)
for (i = 0; i < 64; ++i)
value = bpf_map_lookup_elem(&array_map, &key);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
@@ -0,0 +1,503 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016 Facebook
*/
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <asm/unistd.h>
#include <unistd.h>
#include <assert.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <errno.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#define TEST_BIT(t) (1U << (t))
#define MAX_NR_CPUS 1024
static __u64 time_get_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000ull + ts.tv_nsec;
}
enum test_type {
HASH_PREALLOC,
PERCPU_HASH_PREALLOC,
HASH_KMALLOC,
PERCPU_HASH_KMALLOC,
LRU_HASH_PREALLOC,
NOCOMMON_LRU_HASH_PREALLOC,
LPM_KMALLOC,
HASH_LOOKUP,
ARRAY_LOOKUP,
INNER_LRU_HASH_PREALLOC,
LRU_HASH_LOOKUP,
NR_TESTS,
};
const char *test_map_names[NR_TESTS] = {
[HASH_PREALLOC] = "hash_map",
[PERCPU_HASH_PREALLOC] = "percpu_hash_map",
[HASH_KMALLOC] = "hash_map_alloc",
[PERCPU_HASH_KMALLOC] = "percpu_hash_map_alloc",
[LRU_HASH_PREALLOC] = "lru_hash_map",
[NOCOMMON_LRU_HASH_PREALLOC] = "nocommon_lru_hash_map",
[LPM_KMALLOC] = "lpm_trie_map_alloc",
[HASH_LOOKUP] = "hash_map",
[ARRAY_LOOKUP] = "array_map",
[INNER_LRU_HASH_PREALLOC] = "inner_lru_hash_map",
[LRU_HASH_LOOKUP] = "lru_hash_lookup_map",
};
enum map_idx {
array_of_lru_hashs_idx,
hash_map_alloc_idx,
lru_hash_lookup_idx,
NR_IDXES,
};
static int map_fd[NR_IDXES];
static int test_flags = ~0;
static uint32_t num_map_entries;
static uint32_t inner_lru_hash_size;
static int lru_hash_lookup_test_entries = 32;
static uint32_t max_cnt = 10000;
static int check_test_flags(enum test_type t)
{
return test_flags & TEST_BIT(t);
}
static void test_hash_prealloc(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_getuid);
printf("%d:hash_map_perf pre-alloc %lld events per sec\n",
cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static int pre_test_lru_hash_lookup(int tasks)
{
int fd = map_fd[lru_hash_lookup_idx];
uint32_t key;
long val = 1;
int ret;
if (num_map_entries > lru_hash_lookup_test_entries)
lru_hash_lookup_test_entries = num_map_entries;
/* Populate the lru_hash_map for LRU_HASH_LOOKUP perf test.
*
* It is fine that the user requests for a map with
* num_map_entries < 32 and some of the later lru hash lookup
* may return not found. For LRU map, we are not interested
* in such small map performance.
*/
for (key = 0; key < lru_hash_lookup_test_entries; key++) {
ret = bpf_map_update_elem(fd, &key, &val, BPF_NOEXIST);
if (ret)
return ret;
}
return 0;
}
static void do_test_lru(enum test_type test, int cpu)
{
static int inner_lru_map_fds[MAX_NR_CPUS];
struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 };
const char *test_name;
__u64 start_time;
int i, ret;
if (test == INNER_LRU_HASH_PREALLOC && cpu) {
/* If CPU is not 0, create inner_lru hash map and insert the fd
* value into the array_of_lru_hash map. In case of CPU 0,
* 'inner_lru_hash_map' was statically inserted on the map init
*/
int outer_fd = map_fd[array_of_lru_hashs_idx];
unsigned int mycpu, mynode;
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_flags = BPF_F_NUMA_NODE,
);
assert(cpu < MAX_NR_CPUS);
ret = syscall(__NR_getcpu, &mycpu, &mynode, NULL);
assert(!ret);
opts.numa_node = mynode;
inner_lru_map_fds[cpu] =
bpf_map_create(BPF_MAP_TYPE_LRU_HASH,
test_map_names[INNER_LRU_HASH_PREALLOC],
sizeof(uint32_t),
sizeof(long),
inner_lru_hash_size, &opts);
if (inner_lru_map_fds[cpu] == -1) {
printf("cannot create BPF_MAP_TYPE_LRU_HASH %s(%d)\n",
strerror(errno), errno);
exit(1);
}
ret = bpf_map_update_elem(outer_fd, &cpu,
&inner_lru_map_fds[cpu],
BPF_ANY);
if (ret) {
printf("cannot update ARRAY_OF_LRU_HASHS with key:%u. %s(%d)\n",
cpu, strerror(errno), errno);
exit(1);
}
}
in6.sin6_addr.s6_addr16[0] = 0xdead;
in6.sin6_addr.s6_addr16[1] = 0xbeef;
if (test == LRU_HASH_PREALLOC) {
test_name = "lru_hash_map_perf";
in6.sin6_addr.s6_addr16[2] = 0;
} else if (test == NOCOMMON_LRU_HASH_PREALLOC) {
test_name = "nocommon_lru_hash_map_perf";
in6.sin6_addr.s6_addr16[2] = 1;
} else if (test == INNER_LRU_HASH_PREALLOC) {
test_name = "inner_lru_hash_map_perf";
in6.sin6_addr.s6_addr16[2] = 2;
} else if (test == LRU_HASH_LOOKUP) {
test_name = "lru_hash_lookup_perf";
in6.sin6_addr.s6_addr16[2] = 3;
in6.sin6_addr.s6_addr32[3] = 0;
} else {
assert(0);
}
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++) {
ret = connect(-1, (const struct sockaddr *)&in6, sizeof(in6));
assert(ret == -1 && errno == EBADF);
if (in6.sin6_addr.s6_addr32[3] <
lru_hash_lookup_test_entries - 32)
in6.sin6_addr.s6_addr32[3] += 32;
else
in6.sin6_addr.s6_addr32[3] = 0;
}
printf("%d:%s pre-alloc %lld events per sec\n",
cpu, test_name,
max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static void test_lru_hash_prealloc(int cpu)
{
do_test_lru(LRU_HASH_PREALLOC, cpu);
}
static void test_nocommon_lru_hash_prealloc(int cpu)
{
do_test_lru(NOCOMMON_LRU_HASH_PREALLOC, cpu);
}
static void test_inner_lru_hash_prealloc(int cpu)
{
do_test_lru(INNER_LRU_HASH_PREALLOC, cpu);
}
static void test_lru_hash_lookup(int cpu)
{
do_test_lru(LRU_HASH_LOOKUP, cpu);
}
static void test_percpu_hash_prealloc(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_geteuid);
printf("%d:percpu_hash_map_perf pre-alloc %lld events per sec\n",
cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static void test_hash_kmalloc(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_getgid);
printf("%d:hash_map_perf kmalloc %lld events per sec\n",
cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static void test_percpu_hash_kmalloc(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_getegid);
printf("%d:percpu_hash_map_perf kmalloc %lld events per sec\n",
cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static void test_lpm_kmalloc(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_gettid);
printf("%d:lpm_perf kmalloc %lld events per sec\n",
cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time));
}
static void test_hash_lookup(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_getpgid, 0);
printf("%d:hash_lookup %lld lookups per sec\n",
cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time));
}
static void test_array_lookup(int cpu)
{
__u64 start_time;
int i;
start_time = time_get_ns();
for (i = 0; i < max_cnt; i++)
syscall(__NR_getppid, 0);
printf("%d:array_lookup %lld lookups per sec\n",
cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time));
}
typedef int (*pre_test_func)(int tasks);
const pre_test_func pre_test_funcs[] = {
[LRU_HASH_LOOKUP] = pre_test_lru_hash_lookup,
};
typedef void (*test_func)(int cpu);
const test_func test_funcs[] = {
[HASH_PREALLOC] = test_hash_prealloc,
[PERCPU_HASH_PREALLOC] = test_percpu_hash_prealloc,
[HASH_KMALLOC] = test_hash_kmalloc,
[PERCPU_HASH_KMALLOC] = test_percpu_hash_kmalloc,
[LRU_HASH_PREALLOC] = test_lru_hash_prealloc,
[NOCOMMON_LRU_HASH_PREALLOC] = test_nocommon_lru_hash_prealloc,
[LPM_KMALLOC] = test_lpm_kmalloc,
[HASH_LOOKUP] = test_hash_lookup,
[ARRAY_LOOKUP] = test_array_lookup,
[INNER_LRU_HASH_PREALLOC] = test_inner_lru_hash_prealloc,
[LRU_HASH_LOOKUP] = test_lru_hash_lookup,
};
static int pre_test(int tasks)
{
int i;
for (i = 0; i < NR_TESTS; i++) {
if (pre_test_funcs[i] && check_test_flags(i)) {
int ret = pre_test_funcs[i](tasks);
if (ret)
return ret;
}
}
return 0;
}
static void loop(int cpu)
{
cpu_set_t cpuset;
int i;
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
for (i = 0; i < NR_TESTS; i++) {
if (check_test_flags(i))
test_funcs[i](cpu);
}
}
static void run_perf_test(int tasks)
{
pid_t pid[tasks];
int i;
assert(!pre_test(tasks));
for (i = 0; i < tasks; i++) {
pid[i] = fork();
if (pid[i] == 0) {
loop(i);
exit(0);
} else if (pid[i] == -1) {
printf("couldn't spawn #%d process\n", i);
exit(1);
}
}
for (i = 0; i < tasks; i++) {
int status;
assert(waitpid(pid[i], &status, 0) == pid[i]);
assert(status == 0);
}
}
static void fill_lpm_trie(void)
{
struct bpf_lpm_trie_key_u8 *key;
unsigned long value = 0;
unsigned int i;
int r;
key = alloca(sizeof(*key) + 4);
key->prefixlen = 32;
for (i = 0; i < 512; ++i) {
key->prefixlen = rand() % 33;
key->data[0] = rand() & 0xff;
key->data[1] = rand() & 0xff;
key->data[2] = rand() & 0xff;
key->data[3] = rand() & 0xff;
r = bpf_map_update_elem(map_fd[hash_map_alloc_idx],
key, &value, 0);
assert(!r);
}
key->prefixlen = 32;
key->data[0] = 192;
key->data[1] = 168;
key->data[2] = 0;
key->data[3] = 1;
value = 128;
r = bpf_map_update_elem(map_fd[hash_map_alloc_idx], key, &value, 0);
assert(!r);
}
static void fixup_map(struct bpf_object *obj)
{
struct bpf_map *map;
int i;
bpf_object__for_each_map(map, obj) {
const char *name = bpf_map__name(map);
/* Only change the max_entries for the enabled test(s) */
for (i = 0; i < NR_TESTS; i++) {
if (!strcmp(test_map_names[i], name) &&
(check_test_flags(i))) {
bpf_map__set_max_entries(map, num_map_entries);
continue;
}
}
}
inner_lru_hash_size = num_map_entries;
}
int main(int argc, char **argv)
{
int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
struct bpf_link *links[8];
struct bpf_program *prog;
struct bpf_object *obj;
struct bpf_map *map;
char filename[256];
int i = 0;
if (argc > 1)
test_flags = atoi(argv[1]) ? : test_flags;
if (argc > 2)
nr_cpus = atoi(argv[2]) ? : nr_cpus;
if (argc > 3)
num_map_entries = atoi(argv[3]);
if (argc > 4)
max_cnt = atoi(argv[4]);
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
map = bpf_object__find_map_by_name(obj, "inner_lru_hash_map");
if (libbpf_get_error(map)) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
inner_lru_hash_size = bpf_map__max_entries(map);
if (!inner_lru_hash_size) {
fprintf(stderr, "ERROR: failed to get map attribute\n");
goto cleanup;
}
/* resize BPF map prior to loading */
if (num_map_entries > 0)
fixup_map(obj);
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "array_of_lru_hashs");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "hash_map_alloc");
map_fd[2] = bpf_object__find_map_fd_by_name(obj, "lru_hash_lookup_map");
if (map_fd[0] < 0 || map_fd[1] < 0 || map_fd[2] < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[i] = bpf_program__attach(prog);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[i] = NULL;
goto cleanup;
}
i++;
}
fill_lpm_trie();
run_perf_test(nr_cpus);
cleanup:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
bpf_object__close(obj);
return 0;
}
+34
View File
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef _NET_SHARED_H
#define _NET_SHARED_H
#define AF_INET 2
#define AF_INET6 10
#define ETH_ALEN 6
#define ETH_P_802_3_MIN 0x0600
#define ETH_P_8021Q 0x8100
#define ETH_P_8021AD 0x88A8
#define ETH_P_IP 0x0800
#define ETH_P_IPV6 0x86DD
#define ETH_P_ARP 0x0806
#define IPPROTO_ICMPV6 58
#define TC_ACT_OK 0
#define TC_ACT_SHOT 2
#define IFNAMSIZ 16
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define bpf_ntohs(x) __builtin_bswap16(x)
#define bpf_htons(x) __builtin_bswap16(x)
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define bpf_ntohs(x) (x)
#define bpf_htons(x) (x)
#else
# error "Endianness detection needs to be set up for your compiler?!"
#endif
#endif
+141
View File
@@ -0,0 +1,141 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#ifndef PERF_MAX_STACK_DEPTH
#define PERF_MAX_STACK_DEPTH 127
#endif
#define MINBLOCK_US 1
#define MAX_ENTRIES 10000
struct key_t {
char waker[TASK_COMM_LEN];
char target[TASK_COMM_LEN];
u32 wret;
u32 tret;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct key_t);
__type(value, u64);
__uint(max_entries, MAX_ENTRIES);
} counts SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64);
__uint(max_entries, MAX_ENTRIES);
} start SEC(".maps");
struct wokeby_t {
char name[TASK_COMM_LEN];
u32 ret;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, struct wokeby_t);
__uint(max_entries, MAX_ENTRIES);
} wokeby SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
__uint(max_entries, MAX_ENTRIES);
} stackmap SEC(".maps");
#define STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
SEC("kprobe/try_to_wake_up")
int waker(struct pt_regs *ctx)
{
struct task_struct *p = (void *)PT_REGS_PARM1_CORE(ctx);
u32 pid = BPF_CORE_READ(p, pid);
struct wokeby_t woke;
bpf_get_current_comm(&woke.name, sizeof(woke.name));
woke.ret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS);
bpf_map_update_elem(&wokeby, &pid, &woke, BPF_ANY);
return 0;
}
static inline int update_counts(void *ctx, u32 pid, u64 delta)
{
struct wokeby_t *woke;
u64 zero = 0, *val;
struct key_t key;
__builtin_memset(&key.waker, 0, sizeof(key.waker));
bpf_get_current_comm(&key.target, sizeof(key.target));
key.tret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS);
key.wret = 0;
woke = bpf_map_lookup_elem(&wokeby, &pid);
if (woke) {
key.wret = woke->ret;
__builtin_memcpy(&key.waker, woke->name, sizeof(key.waker));
bpf_map_delete_elem(&wokeby, &pid);
}
val = bpf_map_lookup_elem(&counts, &key);
if (!val) {
bpf_map_update_elem(&counts, &key, &zero, BPF_NOEXIST);
val = bpf_map_lookup_elem(&counts, &key);
if (!val)
return 0;
}
(*val) += delta;
return 0;
}
#if 1
/* taken from /sys/kernel/tracing/events/sched/sched_switch/format */
SEC("tracepoint/sched/sched_switch")
int oncpu(struct trace_event_raw_sched_switch *ctx)
{
/* record previous thread sleep time */
u32 pid = ctx->prev_pid;
#else
SEC("kprobe.multi/finish_task_switch*")
int oncpu(struct pt_regs *ctx)
{
struct task_struct *p = (void *)PT_REGS_PARM1_CORE(ctx);
/* record previous thread sleep time */
u32 pid = BPF_CORE_READ(p, pid);
#endif
u64 delta, ts, *tsp;
ts = bpf_ktime_get_ns();
bpf_map_update_elem(&start, &pid, &ts, BPF_ANY);
/* calculate current thread's delta time */
pid = bpf_get_current_pid_tgid();
tsp = bpf_map_lookup_elem(&start, &pid);
if (!tsp)
/* missed start or filtered */
return 0;
delta = bpf_ktime_get_ns() - *tsp;
bpf_map_delete_elem(&start, &pid);
delta = delta / 1000;
if (delta < MINBLOCK_US)
return 0;
return update_counts(ctx, pid, delta);
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+153
View File
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016 Facebook
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <linux/perf_event.h>
#include <errno.h>
#include <stdbool.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "trace_helpers.h"
#define PRINT_RAW_ADDR 0
/* counts, stackmap */
static int map_fd[2];
static void print_ksym(__u64 addr)
{
struct ksym *sym;
if (!addr)
return;
sym = ksym_search(addr);
if (!sym) {
printf("ksym not found. Is kallsyms loaded?\n");
return;
}
if (PRINT_RAW_ADDR)
printf("%s/%llx;", sym->name, addr);
else
printf("%s;", sym->name);
}
#define TASK_COMM_LEN 16
struct key_t {
char waker[TASK_COMM_LEN];
char target[TASK_COMM_LEN];
__u32 wret;
__u32 tret;
};
static void print_stack(struct key_t *key, __u64 count)
{
__u64 ip[PERF_MAX_STACK_DEPTH] = {};
static bool warned;
int i;
printf("%s;", key->target);
if (bpf_map_lookup_elem(map_fd[1], &key->tret, ip) != 0) {
printf("---;");
} else {
for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
print_ksym(ip[i]);
}
printf("-;");
if (bpf_map_lookup_elem(map_fd[1], &key->wret, ip) != 0) {
printf("---;");
} else {
for (i = 0; i < PERF_MAX_STACK_DEPTH; i++)
print_ksym(ip[i]);
}
printf(";%s %lld\n", key->waker, count);
if ((key->tret == -EEXIST || key->wret == -EEXIST) && !warned) {
printf("stackmap collisions seen. Consider increasing size\n");
warned = true;
} else if (((int)(key->tret) < 0 || (int)(key->wret) < 0)) {
printf("err stackid %d %d\n", key->tret, key->wret);
}
}
static void print_stacks(int fd)
{
struct key_t key = {}, next_key;
__u64 value;
while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(fd, &next_key, &value);
print_stack(&next_key, value);
key = next_key;
}
}
static void int_exit(int sig)
{
print_stacks(map_fd[0]);
exit(0);
}
int main(int argc, char **argv)
{
struct bpf_object *obj = NULL;
struct bpf_link *links[2];
struct bpf_program *prog;
int delay = 1, i = 0;
char filename[256];
if (load_kallsyms()) {
printf("failed to process /proc/kallsyms\n");
return 2;
}
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
obj = NULL;
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counts");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "stackmap");
if (map_fd[0] < 0 || map_fd[1] < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
signal(SIGINT, int_exit);
signal(SIGTERM, int_exit);
bpf_object__for_each_program(prog, obj) {
links[i] = bpf_program__attach(prog);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[i] = NULL;
goto cleanup;
}
i++;
}
if (argc > 1)
delay = atoi(argv[1]);
sleep(delay);
print_stacks(map_fd[0]);
cleanup:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
bpf_object__close(obj);
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#define KBUILD_MODNAME "foo"
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
#define DEFAULT_PKTGEN_UDP_PORT 9
#define IP_MF 0x2000
#define IP_OFFSET 0x1FFF
static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
{
return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
& (IP_MF | IP_OFFSET);
}
SEC("ldabs")
int handle_ingress(struct __sk_buff *skb)
{
__u64 troff = ETH_HLEN + sizeof(struct iphdr);
if (load_half(skb, offsetof(struct ethhdr, h_proto)) != ETH_P_IP)
return 0;
if (load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol)) != IPPROTO_UDP ||
load_byte(skb, ETH_HLEN) != 0x45)
return 0;
if (ip_is_fragment(skb, ETH_HLEN))
return 0;
if (load_half(skb, troff + offsetof(struct udphdr, dest)) == DEFAULT_PKTGEN_UDP_PORT)
return TC_ACT_SHOT;
return 0;
}
char _license[] SEC("license") = "GPL";
+49
View File
@@ -0,0 +1,49 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#define KBUILD_MODNAME "foo"
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <uapi/linux/bpf.h>
#include <net/ip.h>
#include <bpf/bpf_helpers.h>
#define DEFAULT_PKTGEN_UDP_PORT 9
/* copy of 'struct ethhdr' without __packed */
struct eth_hdr {
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
unsigned short h_proto;
};
SEC("simple")
int handle_ingress(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
struct eth_hdr *eth = data;
struct iphdr *iph = data + sizeof(*eth);
struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
void *data_end = (void *)(long)skb->data_end;
/* single length check */
if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
return 0;
if (eth->h_proto != htons(ETH_P_IP))
return 0;
if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
return 0;
if (ip_is_fragment(iph))
return 0;
if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT))
return TC_ACT_SHOT;
return 0;
}
char _license[] SEC("license") = "GPL";
+150
View File
@@ -0,0 +1,150 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#define KBUILD_MODNAME "foo"
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <uapi/linux/bpf.h>
#include <net/ip.h>
#include <bpf/bpf_helpers.h>
#define DEFAULT_PKTGEN_UDP_PORT 9
#define DEBUG 0
static int tcp(void *data, uint64_t tp_off, void *data_end)
{
struct tcphdr *tcp = data + tp_off;
if (tcp + 1 > data_end)
return 0;
if (tcp->dest == htons(80) || tcp->source == htons(80))
return TC_ACT_SHOT;
return 0;
}
static int udp(void *data, uint64_t tp_off, void *data_end)
{
struct udphdr *udp = data + tp_off;
if (udp + 1 > data_end)
return 0;
if (udp->dest == htons(DEFAULT_PKTGEN_UDP_PORT) ||
udp->source == htons(DEFAULT_PKTGEN_UDP_PORT)) {
if (DEBUG) {
char fmt[] = "udp port 9 indeed\n";
bpf_trace_printk(fmt, sizeof(fmt));
}
return TC_ACT_SHOT;
}
return 0;
}
static int parse_ipv4(void *data, uint64_t nh_off, void *data_end)
{
struct iphdr *iph;
uint64_t ihl_len;
iph = data + nh_off;
if (iph + 1 > data_end)
return 0;
if (ip_is_fragment(iph))
return 0;
ihl_len = iph->ihl * 4;
if (iph->protocol == IPPROTO_IPIP) {
iph = data + nh_off + ihl_len;
if (iph + 1 > data_end)
return 0;
ihl_len += iph->ihl * 4;
}
if (iph->protocol == IPPROTO_TCP)
return tcp(data, nh_off + ihl_len, data_end);
else if (iph->protocol == IPPROTO_UDP)
return udp(data, nh_off + ihl_len, data_end);
return 0;
}
static int parse_ipv6(void *data, uint64_t nh_off, void *data_end)
{
struct ipv6hdr *ip6h;
struct iphdr *iph;
uint64_t ihl_len = sizeof(struct ipv6hdr);
uint64_t nexthdr;
ip6h = data + nh_off;
if (ip6h + 1 > data_end)
return 0;
nexthdr = ip6h->nexthdr;
if (nexthdr == IPPROTO_IPIP) {
iph = data + nh_off + ihl_len;
if (iph + 1 > data_end)
return 0;
ihl_len += iph->ihl * 4;
nexthdr = iph->protocol;
} else if (nexthdr == IPPROTO_IPV6) {
ip6h = data + nh_off + ihl_len;
if (ip6h + 1 > data_end)
return 0;
ihl_len += sizeof(struct ipv6hdr);
nexthdr = ip6h->nexthdr;
}
if (nexthdr == IPPROTO_TCP)
return tcp(data, nh_off + ihl_len, data_end);
else if (nexthdr == IPPROTO_UDP)
return udp(data, nh_off + ihl_len, data_end);
return 0;
}
SEC("varlen")
int handle_ingress(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
struct ethhdr *eth = data;
void *data_end = (void *)(long)skb->data_end;
uint64_t h_proto, nh_off;
nh_off = sizeof(*eth);
if (data + nh_off > data_end)
return 0;
h_proto = eth->h_proto;
if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
struct vlan_hdr *vhdr;
vhdr = data + nh_off;
nh_off += sizeof(struct vlan_hdr);
if (data + nh_off > data_end)
return 0;
h_proto = vhdr->h_vlan_encapsulated_proto;
}
if (h_proto == ETH_P_8021Q || h_proto == ETH_P_8021AD) {
struct vlan_hdr *vhdr;
vhdr = data + nh_off;
nh_off += sizeof(struct vlan_hdr);
if (data + nh_off > data_end)
return 0;
h_proto = vhdr->h_vlan_encapsulated_proto;
}
if (h_proto == htons(ETH_P_IP))
return parse_ipv4(data, nh_off, data_end);
else if (h_proto == htons(ETH_P_IPV6))
return parse_ipv6(data, nh_off, data_end);
return 0;
}
char _license[] SEC("license") = "GPL";
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
local_dir="$(pwd)"
root_dir=$local_dir/../..
mnt_dir=$(mktemp -d --tmp)
on_exit() {
iptables -D OUTPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT
umount ${mnt_dir}
rm -r ${mnt_dir}
}
trap on_exit EXIT
mount -t bpf bpf ${mnt_dir}
./per_socket_stats_example ${mnt_dir}/bpf_prog $1
+38
View File
@@ -0,0 +1,38 @@
/* Copyright 2016 Netflix, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/bpf_perf_event.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define MAX_IPS 8192
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u64);
__type(value, u32);
__uint(max_entries, MAX_IPS);
} ip_map SEC(".maps");
SEC("perf_event")
int do_sample(struct bpf_perf_event_data *ctx)
{
u64 ip;
u32 *value, init_val = 1;
ip = PT_REGS_IP(&ctx->regs);
value = bpf_map_lookup_elem(&ip_map, &ip);
if (value)
*value += 1;
else
/* E2BIG not tested for this example only */
bpf_map_update_elem(&ip_map, &ip, &init_val, BPF_NOEXIST);
return 0;
}
char _license[] SEC("license") = "GPL";
+234
View File
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* sampleip: sample instruction pointer and frequency count in a BPF map.
*
* Copyright 2016 Netflix, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "perf-sys.h"
#include "trace_helpers.h"
#define DEFAULT_FREQ 99
#define DEFAULT_SECS 5
#define MAX_IPS 8192
static int map_fd;
static int nr_cpus;
static long _text_addr;
static void usage(void)
{
printf("USAGE: sampleip [-F freq] [duration]\n");
printf(" -F freq # sample frequency (Hertz), default 99\n");
printf(" duration # sampling duration (seconds), default 5\n");
}
static int sampling_start(int freq, struct bpf_program *prog,
struct bpf_link *links[])
{
int i, pmu_fd;
struct perf_event_attr pe_sample_attr = {
.type = PERF_TYPE_SOFTWARE,
.freq = 1,
.sample_period = freq,
.config = PERF_COUNT_SW_CPU_CLOCK,
.inherit = 1,
};
for (i = 0; i < nr_cpus; i++) {
pmu_fd = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i,
-1 /* group_fd */, 0 /* flags */);
if (pmu_fd < 0) {
fprintf(stderr, "ERROR: Initializing perf sampling\n");
return 1;
}
links[i] = bpf_program__attach_perf_event(prog, pmu_fd);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: Attach perf event\n");
links[i] = NULL;
close(pmu_fd);
return 1;
}
}
return 0;
}
static void sampling_end(struct bpf_link *links[])
{
int i;
for (i = 0; i < nr_cpus; i++)
bpf_link__destroy(links[i]);
}
struct ipcount {
__u64 ip;
__u32 count;
};
/* used for sorting */
struct ipcount counts[MAX_IPS];
static int count_cmp(const void *p1, const void *p2)
{
return ((struct ipcount *)p1)->count - ((struct ipcount *)p2)->count;
}
static void print_ip_map(int fd)
{
struct ksym *sym;
__u64 key, next_key;
__u32 value;
int i, max;
printf("%-19s %-32s %s\n", "ADDR", "KSYM", "COUNT");
/* fetch IPs and counts */
key = 0, i = 0;
while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(fd, &next_key, &value);
counts[i].ip = next_key;
counts[i++].count = value;
key = next_key;
}
max = i;
/* sort and print */
qsort(counts, max, sizeof(struct ipcount), count_cmp);
for (i = 0; i < max; i++) {
if (counts[i].ip > _text_addr) {
sym = ksym_search(counts[i].ip);
if (!sym) {
printf("ksym not found. Is kallsyms loaded?\n");
continue;
}
printf("0x%-17llx %-32s %u\n", counts[i].ip, sym->name,
counts[i].count);
} else {
printf("0x%-17llx %-32s %u\n", counts[i].ip, "(user)",
counts[i].count);
}
}
if (max == MAX_IPS) {
printf("WARNING: IP hash was full (max %d entries); ", max);
printf("may have dropped samples\n");
}
}
static void int_exit(int sig)
{
printf("\n");
print_ip_map(map_fd);
exit(0);
}
int main(int argc, char **argv)
{
int opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS, error = 1;
struct bpf_object *obj = NULL;
struct bpf_program *prog;
struct bpf_link **links;
char filename[256];
/* process arguments */
while ((opt = getopt(argc, argv, "F:h")) != -1) {
switch (opt) {
case 'F':
freq = atoi(optarg);
break;
case 'h':
default:
usage();
return 0;
}
}
if (argc - optind == 1)
secs = atoi(argv[optind]);
if (freq == 0 || secs == 0) {
usage();
return 1;
}
/* initialize kernel symbol translation */
if (load_kallsyms()) {
fprintf(stderr, "ERROR: loading /proc/kallsyms\n");
return 2;
}
/* used to determine whether the address is kernel space */
_text_addr = ksym_get_addr("_text");
if (!_text_addr) {
fprintf(stderr, "ERROR: no '_text' in /proc/kallsyms\n");
return 3;
}
/* create perf FDs for each CPU */
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
links = calloc(nr_cpus, sizeof(struct bpf_link *));
if (!links) {
fprintf(stderr, "ERROR: malloc of links\n");
goto cleanup;
}
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
obj = NULL;
goto cleanup;
}
prog = bpf_object__find_program_by_name(obj, "do_sample");
if (!prog) {
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "ip_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
signal(SIGINT, int_exit);
signal(SIGTERM, int_exit);
/* do sampling */
printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n",
freq, secs);
if (sampling_start(freq, prog, links) != 0)
goto cleanup;
sleep(secs);
error = 0;
cleanup:
sampling_end(links);
/* output sample counts */
if (!error)
print_ip_map(map_fd);
free(links);
bpf_object__close(obj);
return error;
}
+111
View File
@@ -0,0 +1,111 @@
/* eBPF example program:
* - creates arraymap in kernel with key 4 bytes and value 8 bytes
*
* - loads eBPF program:
* r0 = skb->data[ETH_HLEN + offsetof(struct iphdr, protocol)];
* *(u32*)(fp - 4) = r0;
* // assuming packet is IPv4, lookup ip->proto in a map
* value = bpf_map_lookup_elem(map_fd, fp - 4);
* if (value)
* (*(u64*)value) += 1;
*
* - attaches this program to loopback interface "lo" raw socket
*
* - every second user space reads map[tcp], map[udp], map[icmp] to see
* how many packets of given protocol were seen on "lo"
*/
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <linux/bpf.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <stddef.h>
#include <bpf/bpf.h>
#include "bpf_insn.h"
#include "sock_example.h"
#include "bpf_util.h"
char bpf_log_buf[BPF_LOG_BUF_SIZE];
static int test_sock(void)
{
int sock = -1, map_fd, prog_fd, i, key;
long long value = 0, tcp_cnt, udp_cnt, icmp_cnt;
map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(key), sizeof(value),
256, NULL);
if (map_fd < 0) {
printf("failed to create map '%s'\n", strerror(errno));
goto cleanup;
}
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol) /* R0 = ip->proto */),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */
BPF_EXIT_INSN(),
};
size_t insns_cnt = ARRAY_SIZE(prog);
LIBBPF_OPTS(bpf_prog_load_opts, opts,
.log_buf = bpf_log_buf,
.log_size = BPF_LOG_BUF_SIZE,
);
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
prog, insns_cnt, &opts);
if (prog_fd < 0) {
printf("failed to load prog '%s'\n", strerror(errno));
goto cleanup;
}
sock = open_raw_sock("lo");
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
sizeof(prog_fd)) < 0) {
printf("setsockopt %s\n", strerror(errno));
goto cleanup;
}
for (i = 0; i < 10; i++) {
key = IPPROTO_TCP;
assert(bpf_map_lookup_elem(map_fd, &key, &tcp_cnt) == 0);
key = IPPROTO_UDP;
assert(bpf_map_lookup_elem(map_fd, &key, &udp_cnt) == 0);
key = IPPROTO_ICMP;
assert(bpf_map_lookup_elem(map_fd, &key, &icmp_cnt) == 0);
printf("TCP %lld UDP %lld ICMP %lld packets\n",
tcp_cnt, udp_cnt, icmp_cnt);
sleep(1);
}
cleanup:
/* maps, programs, raw sockets will auto cleanup on process exit */
return 0;
}
int main(void)
{
FILE *f;
f = popen("ping -4 -c5 localhost", "r");
(void)f;
return test_sock();
}
+35
View File
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <stdlib.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <linux/if_packet.h>
#include <arpa/inet.h>
static inline int open_raw_sock(const char *name)
{
struct sockaddr_ll sll;
int sock;
sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
if (sock < 0) {
printf("cannot create raw socket\n");
return -1;
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = if_nametoindex(name);
sll.sll_protocol = htons(ETH_P_ALL);
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
printf("bind to %s: %s\n", name, strerror(errno));
close(sock);
return -1;
}
return sock;
}
+30
View File
@@ -0,0 +1,30 @@
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, long);
__uint(max_entries, 256);
} my_map SEC(".maps");
SEC("socket1")
int bpf_prog1(struct __sk_buff *skb)
{
int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
long *value;
if (skb->pkt_type != PACKET_OUTGOING)
return 0;
value = bpf_map_lookup_elem(&my_map, &index);
if (value)
__sync_fetch_and_add(value, skb->len);
return 0;
}
char _license[] SEC("license") = "GPL";
+63
View File
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <assert.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "sock_example.h"
#include <unistd.h>
#include <arpa/inet.h>
int main(int ac, char **argv)
{
struct bpf_object *obj;
struct bpf_program *prog;
int map_fd, prog_fd;
char filename[256];
int i, sock, err;
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj))
return 1;
prog = bpf_object__next_program(obj, NULL);
bpf_program__set_type(prog, BPF_PROG_TYPE_SOCKET_FILTER);
err = bpf_object__load(obj);
if (err)
return 1;
prog_fd = bpf_program__fd(prog);
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
sock = open_raw_sock("lo");
assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
sizeof(prog_fd)) == 0);
f = popen("ping -4 -c5 localhost", "r");
(void) f;
for (i = 0; i < 5; i++) {
long long tcp_cnt, udp_cnt, icmp_cnt;
int key;
key = IPPROTO_TCP;
assert(bpf_map_lookup_elem(map_fd, &key, &tcp_cnt) == 0);
key = IPPROTO_UDP;
assert(bpf_map_lookup_elem(map_fd, &key, &udp_cnt) == 0);
key = IPPROTO_ICMP;
assert(bpf_map_lookup_elem(map_fd, &key, &icmp_cnt) == 0);
printf("TCP %lld UDP %lld ICMP %lld bytes\n",
tcp_cnt, udp_cnt, icmp_cnt);
sleep(1);
}
return 0;
}
+222
View File
@@ -0,0 +1,222 @@
#include <uapi/linux/bpf.h>
#include <uapi/linux/in.h>
#include <uapi/linux/if.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/if_tunnel.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
#define IP_MF 0x2000
#define IP_OFFSET 0x1FFF
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
struct flow_key_record {
__be32 src;
__be32 dst;
union {
__be32 ports;
__be16 port16[2];
};
__u16 thoff;
__u8 ip_proto;
};
static inline int proto_ports_offset(__u64 proto)
{
switch (proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_ESP:
case IPPROTO_SCTP:
case IPPROTO_UDPLITE:
return 0;
case IPPROTO_AH:
return 4;
default:
return 0;
}
}
static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
{
return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
& (IP_MF | IP_OFFSET);
}
static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
{
__u64 w0 = load_word(ctx, off);
__u64 w1 = load_word(ctx, off + 4);
__u64 w2 = load_word(ctx, off + 8);
__u64 w3 = load_word(ctx, off + 12);
return (__u32)(w0 ^ w1 ^ w2 ^ w3);
}
static inline __u64 parse_ip(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
struct flow_key_record *flow)
{
__u64 verlen;
if (unlikely(ip_is_fragment(skb, nhoff)))
*ip_proto = 0;
else
*ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
if (*ip_proto != IPPROTO_GRE) {
flow->src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
flow->dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
}
verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
if (likely(verlen == 0x45))
nhoff += 20;
else
nhoff += (verlen & 0xF) << 2;
return nhoff;
}
static inline __u64 parse_ipv6(struct __sk_buff *skb, __u64 nhoff, __u64 *ip_proto,
struct flow_key_record *flow)
{
*ip_proto = load_byte(skb,
nhoff + offsetof(struct ipv6hdr, nexthdr));
flow->src = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, saddr));
flow->dst = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, daddr));
nhoff += sizeof(struct ipv6hdr);
return nhoff;
}
static inline bool flow_dissector(struct __sk_buff *skb,
struct flow_key_record *flow)
{
__u64 nhoff = ETH_HLEN;
__u64 ip_proto;
__u64 proto = load_half(skb, 12);
int poff;
if (proto == ETH_P_8021AD) {
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (proto == ETH_P_8021Q) {
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (likely(proto == ETH_P_IP))
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
else if (proto == ETH_P_IPV6)
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
else
return false;
switch (ip_proto) {
case IPPROTO_GRE: {
struct gre_hdr {
__be16 flags;
__be16 proto;
};
__u64 gre_flags = load_half(skb,
nhoff + offsetof(struct gre_hdr, flags));
__u64 gre_proto = load_half(skb,
nhoff + offsetof(struct gre_hdr, proto));
if (gre_flags & (GRE_VERSION|GRE_ROUTING))
break;
proto = gre_proto;
nhoff += 4;
if (gre_flags & GRE_CSUM)
nhoff += 4;
if (gre_flags & GRE_KEY)
nhoff += 4;
if (gre_flags & GRE_SEQ)
nhoff += 4;
if (proto == ETH_P_8021Q) {
proto = load_half(skb,
nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
}
if (proto == ETH_P_IP)
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
else if (proto == ETH_P_IPV6)
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
else
return false;
break;
}
case IPPROTO_IPIP:
nhoff = parse_ip(skb, nhoff, &ip_proto, flow);
break;
case IPPROTO_IPV6:
nhoff = parse_ipv6(skb, nhoff, &ip_proto, flow);
break;
default:
break;
}
flow->ip_proto = ip_proto;
poff = proto_ports_offset(ip_proto);
if (poff >= 0) {
nhoff += poff;
flow->ports = load_word(skb, nhoff);
}
flow->thoff = (__u16) nhoff;
return true;
}
struct pair {
long packets;
long bytes;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __be32);
__type(value, struct pair);
__uint(max_entries, 1024);
} hash_map SEC(".maps");
SEC("socket2")
int bpf_prog2(struct __sk_buff *skb)
{
struct flow_key_record flow = {};
struct pair *value;
u32 key;
if (!flow_dissector(skb, &flow))
return 0;
key = flow.dst;
value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
__sync_fetch_and_add(&value->packets, 1);
__sync_fetch_and_add(&value->bytes, skb->len);
} else {
struct pair val = {1, skb->len};
bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}
return 0;
}
char _license[] SEC("license") = "GPL";
+62
View File
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <assert.h>
#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "sock_example.h"
#include <unistd.h>
#include <arpa/inet.h>
struct pair {
__u64 packets;
__u64 bytes;
};
int main(int ac, char **argv)
{
struct bpf_program *prog;
struct bpf_object *obj;
int map_fd, prog_fd;
char filename[256];
int i, sock, err;
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj))
return 1;
prog = bpf_object__next_program(obj, NULL);
bpf_program__set_type(prog, BPF_PROG_TYPE_SOCKET_FILTER);
err = bpf_object__load(obj);
if (err)
return 1;
prog_fd = bpf_program__fd(prog);
map_fd = bpf_object__find_map_fd_by_name(obj, "hash_map");
sock = open_raw_sock("lo");
assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
sizeof(prog_fd)) == 0);
f = popen("ping -4 -c5 localhost", "r");
(void) f;
for (i = 0; i < 5; i++) {
int key = 0, next_key;
struct pair value;
while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(map_fd, &next_key, &value);
printf("ip %s bytes %lld packets %lld\n",
inet_ntoa((struct in_addr){htonl(next_key)}),
value.bytes, value.packets);
key = next_key;
}
sleep(1);
}
return 0;
}
+304
View File
@@ -0,0 +1,304 @@
/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/in.h>
#include <uapi/linux/if.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/if_tunnel.h>
#include <uapi/linux/mpls.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
#define IP_MF 0x2000
#define IP_OFFSET 0x1FFF
#define PARSE_VLAN 1
#define PARSE_MPLS 2
#define PARSE_IP 3
#define PARSE_IPV6 4
struct vlan_hdr {
__be16 h_vlan_TCI;
__be16 h_vlan_encapsulated_proto;
};
struct flow_key_record {
__be32 src;
__be32 dst;
union {
__be32 ports;
__be16 port16[2];
};
__u32 ip_proto;
};
static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto);
static inline int ip_is_fragment(struct __sk_buff *ctx, __u64 nhoff)
{
return load_half(ctx, nhoff + offsetof(struct iphdr, frag_off))
& (IP_MF | IP_OFFSET);
}
static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
{
__u64 w0 = load_word(ctx, off);
__u64 w1 = load_word(ctx, off + 4);
__u64 w2 = load_word(ctx, off + 8);
__u64 w3 = load_word(ctx, off + 12);
return (__u32)(w0 ^ w1 ^ w2 ^ w3);
}
struct globals {
struct flow_key_record flow;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, struct globals);
__uint(max_entries, 32);
} percpu_map SEC(".maps");
/* user poor man's per_cpu until native support is ready */
static struct globals *this_cpu_globals(void)
{
u32 key = bpf_get_smp_processor_id();
return bpf_map_lookup_elem(&percpu_map, &key);
}
/* some simple stats for user space consumption */
struct pair {
__u64 packets;
__u64 bytes;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct flow_key_record);
__type(value, struct pair);
__uint(max_entries, 1024);
} hash_map SEC(".maps");
static void update_stats(struct __sk_buff *skb, struct globals *g)
{
struct flow_key_record key = g->flow;
struct pair *value;
value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
__sync_fetch_and_add(&value->packets, 1);
__sync_fetch_and_add(&value->bytes, skb->len);
} else {
struct pair val = {1, skb->len};
bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}
}
static __always_inline void parse_ip_proto(struct __sk_buff *skb,
struct globals *g, __u32 ip_proto)
{
__u32 nhoff = skb->cb[0];
int poff;
switch (ip_proto) {
case IPPROTO_GRE: {
struct gre_hdr {
__be16 flags;
__be16 proto;
};
__u32 gre_flags = load_half(skb,
nhoff + offsetof(struct gre_hdr, flags));
__u32 gre_proto = load_half(skb,
nhoff + offsetof(struct gre_hdr, proto));
if (gre_flags & (GRE_VERSION|GRE_ROUTING))
break;
nhoff += 4;
if (gre_flags & GRE_CSUM)
nhoff += 4;
if (gre_flags & GRE_KEY)
nhoff += 4;
if (gre_flags & GRE_SEQ)
nhoff += 4;
skb->cb[0] = nhoff;
parse_eth_proto(skb, gre_proto);
break;
}
case IPPROTO_IPIP:
parse_eth_proto(skb, ETH_P_IP);
break;
case IPPROTO_IPV6:
parse_eth_proto(skb, ETH_P_IPV6);
break;
case IPPROTO_TCP:
case IPPROTO_UDP:
g->flow.ports = load_word(skb, nhoff);
case IPPROTO_ICMP:
g->flow.ip_proto = ip_proto;
update_stats(skb, g);
break;
default:
break;
}
}
SEC("socket")
int bpf_func_ip(struct __sk_buff *skb)
{
struct globals *g = this_cpu_globals();
__u32 nhoff, verlen, ip_proto;
if (!g)
return 0;
nhoff = skb->cb[0];
if (unlikely(ip_is_fragment(skb, nhoff)))
return 0;
ip_proto = load_byte(skb, nhoff + offsetof(struct iphdr, protocol));
if (ip_proto != IPPROTO_GRE) {
g->flow.src = load_word(skb, nhoff + offsetof(struct iphdr, saddr));
g->flow.dst = load_word(skb, nhoff + offsetof(struct iphdr, daddr));
}
verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
nhoff += (verlen & 0xF) << 2;
skb->cb[0] = nhoff;
parse_ip_proto(skb, g, ip_proto);
return 0;
}
SEC("socket")
int bpf_func_ipv6(struct __sk_buff *skb)
{
struct globals *g = this_cpu_globals();
__u32 nhoff, ip_proto;
if (!g)
return 0;
nhoff = skb->cb[0];
ip_proto = load_byte(skb,
nhoff + offsetof(struct ipv6hdr, nexthdr));
g->flow.src = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, saddr));
g->flow.dst = ipv6_addr_hash(skb,
nhoff + offsetof(struct ipv6hdr, daddr));
nhoff += sizeof(struct ipv6hdr);
skb->cb[0] = nhoff;
parse_ip_proto(skb, g, ip_proto);
return 0;
}
SEC("socket")
int bpf_func_vlan(struct __sk_buff *skb)
{
__u32 nhoff, proto;
nhoff = skb->cb[0];
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
h_vlan_encapsulated_proto));
nhoff += sizeof(struct vlan_hdr);
skb->cb[0] = nhoff;
parse_eth_proto(skb, proto);
return 0;
}
SEC("socket")
int bpf_func_mpls(struct __sk_buff *skb)
{
__u32 nhoff, label;
nhoff = skb->cb[0];
label = load_word(skb, nhoff);
nhoff += sizeof(struct mpls_label);
skb->cb[0] = nhoff;
if (label & MPLS_LS_S_MASK) {
__u8 verlen = load_byte(skb, nhoff);
if ((verlen & 0xF0) == 4)
parse_eth_proto(skb, ETH_P_IP);
else
parse_eth_proto(skb, ETH_P_IPV6);
} else {
parse_eth_proto(skb, ETH_P_MPLS_UC);
}
return 0;
}
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(key_size, sizeof(u32));
__uint(max_entries, 8);
__array(values, u32 (void *));
} prog_array_init SEC(".maps") = {
.values = {
[PARSE_VLAN] = (void *)&bpf_func_vlan,
[PARSE_IP] = (void *)&bpf_func_ip,
[PARSE_IPV6] = (void *)&bpf_func_ipv6,
[PARSE_MPLS] = (void *)&bpf_func_mpls,
},
};
/* Protocol dispatch routine. It tail-calls next BPF program depending
* on eth proto. Note, we could have used ...
*
* bpf_tail_call(skb, &prog_array_init, proto);
*
* ... but it would need large prog_array and cannot be optimised given
* the map key is not static.
*/
static inline void parse_eth_proto(struct __sk_buff *skb, u32 proto)
{
switch (proto) {
case ETH_P_8021Q:
case ETH_P_8021AD:
bpf_tail_call(skb, &prog_array_init, PARSE_VLAN);
break;
case ETH_P_MPLS_UC:
case ETH_P_MPLS_MC:
bpf_tail_call(skb, &prog_array_init, PARSE_MPLS);
break;
case ETH_P_IP:
bpf_tail_call(skb, &prog_array_init, PARSE_IP);
break;
case ETH_P_IPV6:
bpf_tail_call(skb, &prog_array_init, PARSE_IPV6);
break;
}
}
SEC("socket")
int main_prog(struct __sk_buff *skb)
{
__u32 nhoff = ETH_HLEN;
__u32 proto = load_half(skb, 12);
skb->cb[0] = nhoff;
parse_eth_proto(skb, proto);
return 0;
}
char _license[] SEC("license") = "GPL";
+100
View File
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <assert.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "sock_example.h"
#include <unistd.h>
#include <arpa/inet.h>
struct flow_key_record {
__be32 src;
__be32 dst;
union {
__be32 ports;
__be16 port16[2];
};
__u32 ip_proto;
};
struct pair {
__u64 packets;
__u64 bytes;
};
int main(int argc, char **argv)
{
int i, sock, fd, main_prog_fd, hash_map_fd;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
hash_map_fd = bpf_object__find_map_fd_by_name(obj, "hash_map");
if (hash_map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
/* find BPF main program */
main_prog_fd = 0;
bpf_object__for_each_program(prog, obj) {
fd = bpf_program__fd(prog);
if (!strcmp(bpf_program__name(prog), "main_prog"))
main_prog_fd = fd;
}
if (main_prog_fd == 0) {
fprintf(stderr, "ERROR: can't find main_prog\n");
goto cleanup;
}
sock = open_raw_sock("lo");
/* attach BPF program to socket */
assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &main_prog_fd,
sizeof(__u32)) == 0);
if (argc > 1)
f = popen("ping -4 -c5 localhost", "r");
else
f = popen("netperf -l 4 localhost", "r");
(void) f;
for (i = 0; i < 5; i++) {
struct flow_key_record key = {}, next_key;
struct pair value;
sleep(1);
printf("IP src.port -> dst.port bytes packets\n");
while (bpf_map_get_next_key(hash_map_fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(hash_map_fd, &next_key, &value);
printf("%s.%05d -> %s.%05d %12lld %12lld\n",
inet_ntoa((struct in_addr){htonl(next_key.src)}),
next_key.port16[0],
inet_ntoa((struct in_addr){htonl(next_key.dst)}),
next_key.port16[1],
value.bytes, value.packets);
key = next_key;
}
}
cleanup:
bpf_object__close(obj);
return 0;
}
+60
View File
@@ -0,0 +1,60 @@
/* Copyright (c) 2016, Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#ifndef PERF_MAX_STACK_DEPTH
#define PERF_MAX_STACK_DEPTH 127
#endif
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, long);
__type(value, long);
__uint(max_entries, 1024);
} my_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_HASH);
__uint(key_size, sizeof(long));
__uint(value_size, sizeof(long));
__uint(max_entries, 1024);
} my_map2 SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
__uint(max_entries, 10000);
} stackmap SEC(".maps");
#define PROG(foo) \
int foo(struct pt_regs *ctx) \
{ \
long v = PT_REGS_IP(ctx), *val; \
\
val = bpf_map_lookup_elem(&my_map, &v); \
bpf_map_update_elem(&my_map, &v, &v, BPF_ANY); \
bpf_map_update_elem(&my_map2, &v, &v, BPF_ANY); \
bpf_map_delete_elem(&my_map2, &v); \
bpf_get_stackid(ctx, &stackmap, BPF_F_REUSE_STACKID); \
return 0; \
}
/* add kprobes to all possible *spin* functions */
SEC("kprobe.multi/spin_*lock*")PROG(spin_lock)
SEC("kprobe.multi/*_spin_on_owner")PROG(spin_on_owner)
SEC("kprobe.multi/_raw_spin_*lock*")PROG(raw_spin_lock)
/* and to inner bpf helpers */
SEC("kprobe/htab_map_update_elem")PROG(p15)
SEC("kprobe/__htab_percpu_map_update_elem")PROG(p16)
SEC("kprobe/htab_map_alloc")PROG(p17)
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+84
View File
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "trace_helpers.h"
int main(int ac, char **argv)
{
struct bpf_object *obj = NULL;
struct bpf_link *links[20];
long key, next_key, value;
struct bpf_program *prog;
int map_fd, i, j = 0;
char filename[256];
struct ksym *sym;
if (load_kallsyms()) {
printf("failed to process /proc/kallsyms\n");
return 2;
}
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
obj = NULL;
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[j] = bpf_program__attach(prog);
if (libbpf_get_error(links[j])) {
fprintf(stderr, "bpf_program__attach failed\n");
links[j] = NULL;
goto cleanup;
}
j++;
}
for (i = 0; i < 5; i++) {
key = 0;
printf("kprobing funcs:");
while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(map_fd, &next_key, &value);
assert(next_key == value);
sym = ksym_search(value);
key = next_key;
if (!sym) {
printf("ksym not found. Is kallsyms loaded?\n");
continue;
}
printf(" %s", sym->name);
}
if (key)
printf("\n");
key = 0;
while (bpf_map_get_next_key(map_fd, &key, &next_key) == 0)
bpf_map_delete_elem(map_fd, &next_key);
sleep(1);
}
cleanup:
for (j--; j >= 0; j--)
bpf_link__destroy(links[j]);
bpf_object__close(obj);
return 0;
}
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
#include <uapi/linux/unistd.h>
#include <linux/kbuild.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#define SYSNR(_NR) DEFINE(SYS ## _NR, _NR)
void syscall_defines(void)
{
COMMENT("Linux system call numbers.");
SYSNR(__NR_write);
SYSNR(__NR_read);
#ifdef __NR_mmap2
SYSNR(__NR_mmap2);
#endif
#ifdef __NR_mmap
SYSNR(__NR_mmap);
#endif
}
#pragma GCC diagnostic pop
+102
View File
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
*/
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
#if !defined(__aarch64__)
struct syscalls_enter_open_args {
unsigned long long unused;
long syscall_nr;
long filename_ptr;
long flags;
long mode;
};
#endif
struct syscalls_exit_open_args {
unsigned long long unused;
long syscall_nr;
long ret;
};
struct syscalls_enter_open_at_args {
unsigned long long unused;
long syscall_nr;
long long dfd;
long filename_ptr;
long flags;
long mode;
};
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u32);
__uint(max_entries, 1);
} enter_open_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u32);
__uint(max_entries, 1);
} exit_open_map SEC(".maps");
static __always_inline void count(void *map)
{
u32 key = 0;
u32 *value, init_val = 1;
value = bpf_map_lookup_elem(map, &key);
if (value)
*value += 1;
else
bpf_map_update_elem(map, &key, &init_val, BPF_NOEXIST);
}
#if !defined(__aarch64__)
SEC("tracepoint/syscalls/sys_enter_open")
int trace_enter_open(struct syscalls_enter_open_args *ctx)
{
count(&enter_open_map);
return 0;
}
#endif
SEC("tracepoint/syscalls/sys_enter_openat")
int trace_enter_open_at(struct syscalls_enter_open_at_args *ctx)
{
count(&enter_open_map);
return 0;
}
SEC("tracepoint/syscalls/sys_enter_openat2")
int trace_enter_open_at2(struct syscalls_enter_open_at_args *ctx)
{
count(&enter_open_map);
return 0;
}
#if !defined(__aarch64__)
SEC("tracepoint/syscalls/sys_exit_open")
int trace_enter_exit(struct syscalls_exit_open_args *ctx)
{
count(&exit_open_map);
return 0;
}
#endif
SEC("tracepoint/syscalls/sys_exit_openat")
int trace_enter_exit_at(struct syscalls_exit_open_args *ctx)
{
count(&exit_open_map);
return 0;
}
SEC("tracepoint/syscalls/sys_exit_openat2")
int trace_enter_exit_at2(struct syscalls_exit_open_args *ctx)
{
count(&exit_open_map);
return 0;
}
+155
View File
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2017 Facebook
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <linux/perf_event.h>
#include <errno.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
/* This program verifies bpf attachment to tracepoint sys_enter_* and sys_exit_*.
* This requires kernel CONFIG_FTRACE_SYSCALLS to be set.
*/
static void usage(const char *cmd)
{
printf("USAGE: %s [-i nr_tests] [-h]\n", cmd);
printf(" -i nr_tests # rounds of test to run\n");
printf(" -h # help\n");
}
static void verify_map(int map_id)
{
__u32 key = 0;
__u32 val;
if (bpf_map_lookup_elem(map_id, &key, &val) != 0) {
fprintf(stderr, "map_lookup failed: %s\n", strerror(errno));
return;
}
if (val == 0) {
fprintf(stderr, "failed: map #%d returns value 0\n", map_id);
return;
}
printf("verify map:%d val: %d\n", map_id, val);
val = 0;
if (bpf_map_update_elem(map_id, &key, &val, BPF_ANY) != 0) {
fprintf(stderr, "map_update failed: %s\n", strerror(errno));
return;
}
}
static int test(char *filename, int nr_tests)
{
int map0_fds[nr_tests], map1_fds[nr_tests], fd, i, j = 0;
struct bpf_link **links = NULL;
struct bpf_object *objs[nr_tests];
struct bpf_program *prog;
for (i = 0; i < nr_tests; i++) {
objs[i] = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(objs[i])) {
fprintf(stderr, "opening BPF object file failed\n");
objs[i] = NULL;
goto cleanup;
}
/* One-time initialization */
if (!links) {
int nr_progs = 0;
bpf_object__for_each_program(prog, objs[i])
nr_progs += 1;
links = calloc(nr_progs * nr_tests, sizeof(struct bpf_link *));
if (!links)
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(objs[i])) {
fprintf(stderr, "loading BPF object file failed\n");
goto cleanup;
}
map0_fds[i] = bpf_object__find_map_fd_by_name(objs[i],
"enter_open_map");
map1_fds[i] = bpf_object__find_map_fd_by_name(objs[i],
"exit_open_map");
if (map0_fds[i] < 0 || map1_fds[i] < 0) {
fprintf(stderr, "finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, objs[i]) {
links[j] = bpf_program__attach(prog);
if (libbpf_get_error(links[j])) {
fprintf(stderr, "bpf_program__attach failed\n");
links[j] = NULL;
goto cleanup;
}
j++;
}
printf("prog #%d: map ids %d %d\n", i, map0_fds[i], map1_fds[i]);
}
/* current load_bpf_file has perf_event_open default pid = -1
* and cpu = 0, which permits attached bpf execution on
* all cpus for all pid's. bpf program execution ignores
* cpu affinity.
*/
/* trigger some "open" operations */
fd = open(filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open failed: %s\n", strerror(errno));
return 1;
}
close(fd);
/* verify the map */
for (i = 0; i < nr_tests; i++) {
verify_map(map0_fds[i]);
verify_map(map1_fds[i]);
}
cleanup:
if (links) {
for (j--; j >= 0; j--)
bpf_link__destroy(links[j]);
free(links);
}
for (i--; i >= 0; i--)
bpf_object__close(objs[i]);
return 0;
}
int main(int argc, char **argv)
{
int opt, nr_tests = 1;
char filename[256];
while ((opt = getopt(argc, argv, "i:h")) != -1) {
switch (opt) {
case 'i':
nr_tests = atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
return 0;
}
}
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
return test(filename, nr_tests);
}
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/version.h>
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("kprobe/blk_mq_start_request")
int bpf_prog1(struct pt_regs *ctx)
{
return 0;
}
SEC("kretprobe/__blk_account_io_done")
int bpf_prog2(struct pt_regs *ctx)
{
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
@@ -0,0 +1,423 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <linux/bpf.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/perf_event.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_util.h"
#include "perf-sys.h"
#include "trace_helpers.h"
static struct bpf_program *progs[2];
static struct bpf_link *links[2];
#define CHECK_PERROR_RET(condition) ({ \
int __ret = !!(condition); \
if (__ret) { \
printf("FAIL: %s:\n", __func__); \
perror(" "); \
return -1; \
} \
})
#define CHECK_AND_RET(condition) ({ \
int __ret = !!(condition); \
if (__ret) \
return -1; \
})
static __u64 ptr_to_u64(void *ptr)
{
return (__u64) (unsigned long) ptr;
}
#define PMU_TYPE_FILE "/sys/bus/event_source/devices/%s/type"
static int bpf_find_probe_type(const char *event_type)
{
char buf[256];
int fd, ret;
ret = snprintf(buf, sizeof(buf), PMU_TYPE_FILE, event_type);
CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));
fd = open(buf, O_RDONLY);
CHECK_PERROR_RET(fd < 0);
ret = read(fd, buf, sizeof(buf));
close(fd);
CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));
errno = 0;
ret = (int)strtol(buf, NULL, 10);
CHECK_PERROR_RET(errno);
return ret;
}
#define PMU_RETPROBE_FILE "/sys/bus/event_source/devices/%s/format/retprobe"
static int bpf_get_retprobe_bit(const char *event_type)
{
char buf[256];
int fd, ret;
ret = snprintf(buf, sizeof(buf), PMU_RETPROBE_FILE, event_type);
CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));
fd = open(buf, O_RDONLY);
CHECK_PERROR_RET(fd < 0);
ret = read(fd, buf, sizeof(buf));
close(fd);
CHECK_PERROR_RET(ret < 0 || ret >= sizeof(buf));
CHECK_PERROR_RET(strlen(buf) < strlen("config:"));
errno = 0;
ret = (int)strtol(buf + strlen("config:"), NULL, 10);
CHECK_PERROR_RET(errno);
return ret;
}
static int test_debug_fs_kprobe(int link_idx, const char *fn_name,
__u32 expected_fd_type)
{
__u64 probe_offset, probe_addr;
__u32 len, prog_id, fd_type;
int err, event_fd;
char buf[256];
len = sizeof(buf);
event_fd = bpf_link__fd(links[link_idx]);
err = bpf_task_fd_query(getpid(), event_fd, 0, buf, &len,
&prog_id, &fd_type, &probe_offset,
&probe_addr);
if (err < 0) {
printf("FAIL: %s, for event_fd idx %d, fn_name %s\n",
__func__, link_idx, fn_name);
perror(" :");
return -1;
}
if (strcmp(buf, fn_name) != 0 ||
fd_type != expected_fd_type ||
probe_offset != 0x0 || probe_addr != 0x0) {
printf("FAIL: bpf_trace_event_query(event_fd[%d]):\n",
link_idx);
printf("buf: %s, fd_type: %u, probe_offset: 0x%llx,"
" probe_addr: 0x%llx\n",
buf, fd_type, probe_offset, probe_addr);
return -1;
}
return 0;
}
static int test_nondebug_fs_kuprobe_common(const char *event_type,
const char *name, __u64 offset, __u64 addr, bool is_return,
char *buf, __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
__u64 *probe_offset, __u64 *probe_addr)
{
int is_return_bit = bpf_get_retprobe_bit(event_type);
int type = bpf_find_probe_type(event_type);
struct perf_event_attr attr = {};
struct bpf_link *link;
int fd, err = -1;
if (type < 0 || is_return_bit < 0) {
printf("FAIL: %s incorrect type (%d) or is_return_bit (%d)\n",
__func__, type, is_return_bit);
return err;
}
attr.sample_period = 1;
attr.wakeup_events = 1;
if (is_return)
attr.config |= 1 << is_return_bit;
if (name) {
attr.config1 = ptr_to_u64((void *)name);
attr.config2 = offset;
} else {
attr.config1 = 0;
attr.config2 = addr;
}
attr.size = sizeof(attr);
attr.type = type;
fd = sys_perf_event_open(&attr, -1, 0, -1, 0);
link = bpf_program__attach_perf_event(progs[0], fd);
if (libbpf_get_error(link)) {
printf("ERROR: bpf_program__attach_perf_event failed\n");
link = NULL;
close(fd);
goto cleanup;
}
CHECK_PERROR_RET(bpf_task_fd_query(getpid(), fd, 0, buf, buf_len,
prog_id, fd_type, probe_offset, probe_addr) < 0);
err = 0;
cleanup:
bpf_link__destroy(link);
return err;
}
static int test_nondebug_fs_probe(const char *event_type, const char *name,
__u64 offset, __u64 addr, bool is_return,
__u32 expected_fd_type,
__u32 expected_ret_fd_type,
char *buf, __u32 buf_len)
{
__u64 probe_offset, probe_addr;
__u32 prog_id, fd_type;
int err;
err = test_nondebug_fs_kuprobe_common(event_type, name,
offset, addr, is_return,
buf, &buf_len, &prog_id,
&fd_type, &probe_offset,
&probe_addr);
if (err < 0) {
printf("FAIL: %s, "
"for name %s, offset 0x%llx, addr 0x%llx, is_return %d\n",
__func__, name ? name : "", offset, addr, is_return);
perror(" :");
return -1;
}
if ((is_return && fd_type != expected_ret_fd_type) ||
(!is_return && fd_type != expected_fd_type)) {
printf("FAIL: %s, incorrect fd_type %u\n",
__func__, fd_type);
return -1;
}
if (name) {
if (strcmp(name, buf) != 0) {
printf("FAIL: %s, incorrect buf %s\n", __func__, buf);
return -1;
}
if (probe_offset != offset) {
printf("FAIL: %s, incorrect probe_offset 0x%llx\n",
__func__, probe_offset);
return -1;
}
} else {
if (buf_len != 0) {
printf("FAIL: %s, incorrect buf %p\n",
__func__, buf);
return -1;
}
if (probe_addr != addr) {
printf("FAIL: %s, incorrect probe_addr 0x%llx\n",
__func__, probe_addr);
return -1;
}
}
return 0;
}
static int test_debug_fs_uprobe(char *binary_path, long offset, bool is_return)
{
char buf[256], event_alias[sizeof("test_1234567890")];
const char *event_type = "uprobe";
struct perf_event_attr attr = {};
__u64 probe_offset, probe_addr;
__u32 len, prog_id, fd_type;
int err = -1, res, kfd, efd;
struct bpf_link *link;
ssize_t bytes;
snprintf(buf, sizeof(buf), "/sys/kernel/tracing/%s_events",
event_type);
kfd = open(buf, O_WRONLY | O_TRUNC, 0);
CHECK_PERROR_RET(kfd < 0);
res = snprintf(event_alias, sizeof(event_alias), "test_%d", getpid());
CHECK_PERROR_RET(res < 0 || res >= sizeof(event_alias));
res = snprintf(buf, sizeof(buf), "%c:%ss/%s %s:0x%lx",
is_return ? 'r' : 'p', event_type, event_alias,
binary_path, offset);
CHECK_PERROR_RET(res < 0 || res >= sizeof(buf));
CHECK_PERROR_RET(write(kfd, buf, strlen(buf)) < 0);
close(kfd);
kfd = -1;
snprintf(buf, sizeof(buf), "/sys/kernel/tracing/events/%ss/%s/id",
event_type, event_alias);
efd = open(buf, O_RDONLY, 0);
CHECK_PERROR_RET(efd < 0);
bytes = read(efd, buf, sizeof(buf));
CHECK_PERROR_RET(bytes <= 0 || bytes >= sizeof(buf));
close(efd);
buf[bytes] = '\0';
attr.config = strtol(buf, NULL, 0);
attr.type = PERF_TYPE_TRACEPOINT;
attr.sample_period = 1;
attr.wakeup_events = 1;
kfd = sys_perf_event_open(&attr, -1, 0, -1, PERF_FLAG_FD_CLOEXEC);
link = bpf_program__attach_perf_event(progs[0], kfd);
if (libbpf_get_error(link)) {
printf("ERROR: bpf_program__attach_perf_event failed\n");
link = NULL;
close(kfd);
goto cleanup;
}
len = sizeof(buf);
err = bpf_task_fd_query(getpid(), kfd, 0, buf, &len,
&prog_id, &fd_type, &probe_offset,
&probe_addr);
if (err < 0) {
printf("FAIL: %s, binary_path %s\n", __func__, binary_path);
perror(" :");
return -1;
}
if ((is_return && fd_type != BPF_FD_TYPE_URETPROBE) ||
(!is_return && fd_type != BPF_FD_TYPE_UPROBE)) {
printf("FAIL: %s, incorrect fd_type %u\n", __func__,
fd_type);
return -1;
}
if (strcmp(binary_path, buf) != 0) {
printf("FAIL: %s, incorrect buf %s\n", __func__, buf);
return -1;
}
if (probe_offset != offset) {
printf("FAIL: %s, incorrect probe_offset 0x%llx\n", __func__,
probe_offset);
return -1;
}
err = 0;
cleanup:
bpf_link__destroy(link);
return err;
}
int main(int argc, char **argv)
{
extern char __executable_start;
char filename[256], buf[256];
__u64 uprobe_file_offset;
struct bpf_program *prog;
struct bpf_object *obj;
int i = 0, err = -1;
if (load_kallsyms()) {
printf("failed to process /proc/kallsyms\n");
return err;
}
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return err;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
progs[i] = prog;
links[i] = bpf_program__attach(progs[i]);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[i] = NULL;
goto cleanup;
}
i++;
}
/* test two functions in the corresponding *_kern.c file */
CHECK_AND_RET(test_debug_fs_kprobe(0, "blk_mq_start_request",
BPF_FD_TYPE_KPROBE));
CHECK_AND_RET(test_debug_fs_kprobe(1, "__blk_account_io_done",
BPF_FD_TYPE_KRETPROBE));
/* test nondebug fs kprobe */
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x0, 0x0,
false, BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
buf, sizeof(buf)));
#ifdef __x86_64__
/* set a kprobe on "bpf_check + 0x5", which is x64 specific */
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x5, 0x0,
false, BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
buf, sizeof(buf)));
#endif
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", "bpf_check", 0x0, 0x0,
true, BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
buf, sizeof(buf)));
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,
ksym_get_addr("bpf_check"), false,
BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
buf, sizeof(buf)));
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,
ksym_get_addr("bpf_check"), false,
BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
NULL, 0));
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,
ksym_get_addr("bpf_check"), true,
BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
buf, sizeof(buf)));
CHECK_AND_RET(test_nondebug_fs_probe("kprobe", NULL, 0x0,
ksym_get_addr("bpf_check"), true,
BPF_FD_TYPE_KPROBE,
BPF_FD_TYPE_KRETPROBE,
0, 0));
/* test nondebug fs uprobe */
/* the calculation of uprobe file offset is based on gcc 7.3.1 on x64
* and the default linker script, which defines __executable_start as
* the start of the .text section. The calculation could be different
* on different systems with different compilers. The right way is
* to parse the ELF file. We took a shortcut here.
*/
uprobe_file_offset = (unsigned long)main - (unsigned long)&__executable_start;
CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0],
uprobe_file_offset, 0x0, false,
BPF_FD_TYPE_UPROBE,
BPF_FD_TYPE_URETPROBE,
buf, sizeof(buf)));
CHECK_AND_RET(test_nondebug_fs_probe("uprobe", (char *)argv[0],
uprobe_file_offset, 0x0, true,
BPF_FD_TYPE_UPROBE,
BPF_FD_TYPE_URETPROBE,
buf, sizeof(buf)));
/* test debug fs uprobe */
CHECK_AND_RET(test_debug_fs_uprobe((char *)argv[0], uprobe_file_offset,
false));
CHECK_AND_RET(test_debug_fs_uprobe((char *)argv[0], uprobe_file_offset,
true));
err = 0;
cleanup:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
bpf_object__close(obj);
return err;
}
+177
View File
@@ -0,0 +1,177 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
[[ -z $TC ]] && TC='tc'
[[ -z $IP ]] && IP='ip'
REDIRECT_USER='./tc_l2_redirect'
REDIRECT_BPF='./tc_l2_redirect_kern.o'
RP_FILTER=$(< /proc/sys/net/ipv4/conf/all/rp_filter)
IPV6_DISABLED=$(< /proc/sys/net/ipv6/conf/all/disable_ipv6)
IPV6_FORWARDING=$(< /proc/sys/net/ipv6/conf/all/forwarding)
function config_common {
local tun_type=$1
$IP netns add ns1
$IP netns add ns2
$IP link add ve1 type veth peer name vens1
$IP link add ve2 type veth peer name vens2
$IP link set dev ve1 up
$IP link set dev ve2 up
$IP link set dev ve1 mtu 1500
$IP link set dev ve2 mtu 1500
$IP link set dev vens1 netns ns1
$IP link set dev vens2 netns ns2
$IP -n ns1 link set dev lo up
$IP -n ns1 link set dev vens1 up
$IP -n ns1 addr add 10.1.1.101/24 dev vens1
$IP -n ns1 addr add 2401:db01::65/64 dev vens1 nodad
$IP -n ns1 route add default via 10.1.1.1 dev vens1
$IP -n ns1 route add default via 2401:db01::1 dev vens1
$IP -n ns2 link set dev lo up
$IP -n ns2 link set dev vens2 up
$IP -n ns2 addr add 10.2.1.102/24 dev vens2
$IP -n ns2 addr add 2401:db02::66/64 dev vens2 nodad
$IP -n ns2 addr add 10.10.1.102 dev lo
$IP -n ns2 addr add 2401:face::66/64 dev lo nodad
$IP -n ns2 link add ipt2 type ipip local 10.2.1.102 remote 10.2.1.1
$IP -n ns2 link add ip6t2 type ip6tnl mode any local 2401:db02::66 remote 2401:db02::1
$IP -n ns2 link set dev ipt2 up
$IP -n ns2 link set dev ip6t2 up
$IP netns exec ns2 $TC qdisc add dev vens2 clsact
$IP netns exec ns2 $TC filter add dev vens2 ingress bpf da obj $REDIRECT_BPF sec drop_non_tun_vip
if [[ $tun_type == "ipip" ]]; then
$IP -n ns2 route add 10.1.1.0/24 dev ipt2
$IP netns exec ns2 sysctl -q -w net.ipv4.conf.all.rp_filter=0
$IP netns exec ns2 sysctl -q -w net.ipv4.conf.ipt2.rp_filter=0
else
$IP -n ns2 route add 10.1.1.0/24 dev ip6t2
$IP -n ns2 route add 2401:db01::/64 dev ip6t2
$IP netns exec ns2 sysctl -q -w net.ipv4.conf.all.rp_filter=0
$IP netns exec ns2 sysctl -q -w net.ipv4.conf.ip6t2.rp_filter=0
fi
$IP addr add 10.1.1.1/24 dev ve1
$IP addr add 2401:db01::1/64 dev ve1 nodad
$IP addr add 10.2.1.1/24 dev ve2
$IP addr add 2401:db02::1/64 dev ve2 nodad
$TC qdisc add dev ve2 clsact
$TC filter add dev ve2 ingress bpf da obj $REDIRECT_BPF sec l2_to_iptun_ingress_forward
sysctl -q -w net.ipv4.conf.all.rp_filter=0
sysctl -q -w net.ipv6.conf.all.forwarding=1
sysctl -q -w net.ipv6.conf.all.disable_ipv6=0
}
function cleanup {
set +e
[[ -z $DEBUG ]] || set +x
$IP netns delete ns1 >& /dev/null
$IP netns delete ns2 >& /dev/null
$IP link del ve1 >& /dev/null
$IP link del ve2 >& /dev/null
$IP link del ipt >& /dev/null
$IP link del ip6t >& /dev/null
sysctl -q -w net.ipv4.conf.all.rp_filter=$RP_FILTER
sysctl -q -w net.ipv6.conf.all.forwarding=$IPV6_FORWARDING
sysctl -q -w net.ipv6.conf.all.disable_ipv6=$IPV6_DISABLED
rm -f /sys/fs/bpf/tc/globals/tun_iface
[[ -z $DEBUG ]] || set -x
set -e
}
function l2_to_ipip {
echo -n "l2_to_ipip $1: "
local dir=$1
config_common ipip
$IP link add ipt type ipip external
$IP link set dev ipt up
sysctl -q -w net.ipv4.conf.ipt.rp_filter=0
sysctl -q -w net.ipv4.conf.ipt.forwarding=1
if [[ $dir == "egress" ]]; then
$IP route add 10.10.1.0/24 via 10.2.1.102 dev ve2
$TC filter add dev ve2 egress bpf da obj $REDIRECT_BPF sec l2_to_iptun_ingress_redirect
sysctl -q -w net.ipv4.conf.ve1.forwarding=1
else
$TC qdisc add dev ve1 clsact
$TC filter add dev ve1 ingress bpf da obj $REDIRECT_BPF sec l2_to_iptun_ingress_redirect
fi
$REDIRECT_USER -U /sys/fs/bpf/tc/globals/tun_iface -i $(< /sys/class/net/ipt/ifindex)
$IP netns exec ns1 ping -c1 10.10.1.102 >& /dev/null
if [[ $dir == "egress" ]]; then
# test direct egress to ve2 (i.e. not forwarding from
# ve1 to ve2).
ping -c1 10.10.1.102 >& /dev/null
fi
cleanup
echo "OK"
}
function l2_to_ip6tnl {
echo -n "l2_to_ip6tnl $1: "
local dir=$1
config_common ip6tnl
$IP link add ip6t type ip6tnl mode any external
$IP link set dev ip6t up
sysctl -q -w net.ipv4.conf.ip6t.rp_filter=0
sysctl -q -w net.ipv4.conf.ip6t.forwarding=1
if [[ $dir == "egress" ]]; then
$IP route add 10.10.1.0/24 via 10.2.1.102 dev ve2
$IP route add 2401:face::/64 via 2401:db02::66 dev ve2
$TC filter add dev ve2 egress bpf da obj $REDIRECT_BPF sec l2_to_ip6tun_ingress_redirect
sysctl -q -w net.ipv4.conf.ve1.forwarding=1
else
$TC qdisc add dev ve1 clsact
$TC filter add dev ve1 ingress bpf da obj $REDIRECT_BPF sec l2_to_ip6tun_ingress_redirect
fi
$REDIRECT_USER -U /sys/fs/bpf/tc/globals/tun_iface -i $(< /sys/class/net/ip6t/ifindex)
$IP netns exec ns1 ping -c1 10.10.1.102 >& /dev/null
$IP netns exec ns1 ping -6 -c1 2401:face::66 >& /dev/null
if [[ $dir == "egress" ]]; then
# test direct egress to ve2 (i.e. not forwarding from
# ve1 to ve2).
ping -c1 10.10.1.102 >& /dev/null
ping -6 -c1 2401:face::66 >& /dev/null
fi
cleanup
echo "OK"
}
cleanup
test_names="l2_to_ipip l2_to_ip6tnl"
test_dirs="ingress egress"
if [[ $# -ge 2 ]]; then
test_names=$1
test_dirs=$2
elif [[ $# -ge 1 ]]; then
test_names=$1
fi
for t in $test_names; do
for d in $test_dirs; do
$t $d
done
done
@@ -0,0 +1,231 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/in.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/filter.h>
#include <uapi/linux/pkt_cls.h>
#include <net/ipv6.h>
#include <bpf/bpf_helpers.h>
#define _htonl __builtin_bswap32
#define PIN_GLOBAL_NS 2
struct bpf_elf_map {
__u32 type;
__u32 size_key;
__u32 size_value;
__u32 max_elem;
__u32 flags;
__u32 id;
__u32 pinning;
};
/* copy of 'struct ethhdr' without __packed */
struct eth_hdr {
unsigned char h_dest[ETH_ALEN];
unsigned char h_source[ETH_ALEN];
unsigned short h_proto;
};
struct bpf_elf_map SEC("maps") tun_iface = {
.type = BPF_MAP_TYPE_ARRAY,
.size_key = sizeof(int),
.size_value = sizeof(int),
.pinning = PIN_GLOBAL_NS,
.max_elem = 1,
};
static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr)
{
if (eth_proto == htons(ETH_P_IP))
return (_htonl(0xffffff00) & daddr) == _htonl(0x0a0a0100);
else if (eth_proto == htons(ETH_P_IPV6))
return (daddr == _htonl(0x2401face));
return false;
}
SEC("l2_to_iptun_ingress_forward")
int _l2_to_iptun_ingress_forward(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
struct eth_hdr *eth = data;
void *data_end = (void *)(long)skb->data_end;
int key = 0, *ifindex;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
ifindex = bpf_map_lookup_elem(&tun_iface, &key);
if (!ifindex)
return TC_ACT_OK;
if (eth->h_proto == htons(ETH_P_IP)) {
char fmt4[] = "ingress forward to ifindex:%d daddr4:%x\n";
struct iphdr *iph = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*iph) > data_end)
return TC_ACT_OK;
if (iph->protocol != IPPROTO_IPIP)
return TC_ACT_OK;
bpf_trace_printk(fmt4, sizeof(fmt4), *ifindex,
_htonl(iph->daddr));
return bpf_redirect(*ifindex, BPF_F_INGRESS);
} else if (eth->h_proto == htons(ETH_P_IPV6)) {
char fmt6[] = "ingress forward to ifindex:%d daddr6:%x::%x\n";
struct ipv6hdr *ip6h = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
return TC_ACT_OK;
if (ip6h->nexthdr != IPPROTO_IPIP &&
ip6h->nexthdr != IPPROTO_IPV6)
return TC_ACT_OK;
bpf_trace_printk(fmt6, sizeof(fmt6), *ifindex,
_htonl(ip6h->daddr.s6_addr32[0]),
_htonl(ip6h->daddr.s6_addr32[3]));
return bpf_redirect(*ifindex, BPF_F_INGRESS);
}
return TC_ACT_OK;
}
SEC("l2_to_iptun_ingress_redirect")
int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb)
{
struct bpf_tunnel_key tkey = {};
void *data = (void *)(long)skb->data;
struct eth_hdr *eth = data;
void *data_end = (void *)(long)skb->data_end;
int key = 0, *ifindex;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
ifindex = bpf_map_lookup_elem(&tun_iface, &key);
if (!ifindex)
return TC_ACT_OK;
if (eth->h_proto == htons(ETH_P_IP)) {
char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
struct iphdr *iph = data + sizeof(*eth);
__be32 daddr = iph->daddr;
if (data + sizeof(*eth) + sizeof(*iph) > data_end)
return TC_ACT_OK;
if (!is_vip_addr(eth->h_proto, daddr))
return TC_ACT_OK;
bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(daddr), *ifindex);
} else {
return TC_ACT_OK;
}
tkey.tunnel_id = 10000;
tkey.tunnel_ttl = 64;
tkey.remote_ipv4 = 0x0a020166; /* 10.2.1.102 */
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
return bpf_redirect(*ifindex, 0);
}
SEC("l2_to_ip6tun_ingress_redirect")
int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb)
{
struct bpf_tunnel_key tkey = {};
void *data = (void *)(long)skb->data;
struct eth_hdr *eth = data;
void *data_end = (void *)(long)skb->data_end;
int key = 0, *ifindex;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
ifindex = bpf_map_lookup_elem(&tun_iface, &key);
if (!ifindex)
return TC_ACT_OK;
if (eth->h_proto == htons(ETH_P_IP)) {
char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
struct iphdr *iph = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*iph) > data_end)
return TC_ACT_OK;
if (!is_vip_addr(eth->h_proto, iph->daddr))
return TC_ACT_OK;
bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(iph->daddr),
*ifindex);
} else if (eth->h_proto == htons(ETH_P_IPV6)) {
char fmt6[] = "e/ingress redirect daddr6:%x to ifindex:%d\n";
struct ipv6hdr *ip6h = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
return TC_ACT_OK;
if (!is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
return TC_ACT_OK;
bpf_trace_printk(fmt6, sizeof(fmt6),
_htonl(ip6h->daddr.s6_addr32[0]), *ifindex);
} else {
return TC_ACT_OK;
}
tkey.tunnel_id = 10000;
tkey.tunnel_ttl = 64;
/* 2401:db02:0:0:0:0:0:66 */
tkey.remote_ipv6[0] = _htonl(0x2401db02);
tkey.remote_ipv6[1] = 0;
tkey.remote_ipv6[2] = 0;
tkey.remote_ipv6[3] = _htonl(0x00000066);
bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), BPF_F_TUNINFO_IPV6);
return bpf_redirect(*ifindex, 0);
}
SEC("drop_non_tun_vip")
int _drop_non_tun_vip(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
struct eth_hdr *eth = data;
void *data_end = (void *)(long)skb->data_end;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
if (eth->h_proto == htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*iph) > data_end)
return TC_ACT_OK;
if (is_vip_addr(eth->h_proto, iph->daddr))
return TC_ACT_SHOT;
} else if (eth->h_proto == htons(ETH_P_IPV6)) {
struct ipv6hdr *ip6h = data + sizeof(*eth);
if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
return TC_ACT_OK;
if (is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
return TC_ACT_SHOT;
}
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016 Facebook
*/
#include <linux/unistd.h>
#include <linux/bpf.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <bpf/bpf.h>
static void usage(void)
{
printf("Usage: tc_l2_ipip_redirect [...]\n");
printf(" -U <file> Update an already pinned BPF array\n");
printf(" -i <ifindex> Interface index\n");
printf(" -h Display this help\n");
}
int main(int argc, char **argv)
{
const char *pinned_file = NULL;
int ifindex = -1;
int array_key = 0;
int array_fd = -1;
int ret = -1;
int opt;
while ((opt = getopt(argc, argv, "F:U:i:")) != -1) {
switch (opt) {
/* General args */
case 'U':
pinned_file = optarg;
break;
case 'i':
ifindex = atoi(optarg);
break;
default:
usage();
goto out;
}
}
if (ifindex < 0 || !pinned_file) {
usage();
goto out;
}
array_fd = bpf_obj_get(pinned_file);
if (array_fd < 0) {
fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n",
pinned_file, strerror(errno), errno);
goto out;
}
/* bpf_tunnel_key.remote_ipv4 expects host byte orders */
ret = bpf_map_update_elem(array_fd, &array_key, &ifindex, 0);
if (ret) {
perror("bpf_map_update_elem");
goto out;
}
out:
if (array_fd != -1)
close(array_fd);
return ret;
}
+91
View File
@@ -0,0 +1,91 @@
#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/in.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/filter.h>
#include <uapi/linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
#include "bpf_legacy.h"
/* compiler workaround */
#define _htonl __builtin_bswap32
static inline void set_dst_mac(struct __sk_buff *skb, char *mac)
{
bpf_skb_store_bytes(skb, 0, mac, ETH_ALEN, 1);
}
#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check))
#define TOS_OFF (ETH_HLEN + offsetof(struct iphdr, tos))
static inline void set_ip_tos(struct __sk_buff *skb, __u8 new_tos)
{
__u8 old_tos = load_byte(skb, TOS_OFF);
bpf_l3_csum_replace(skb, IP_CSUM_OFF, htons(old_tos), htons(new_tos), 2);
bpf_skb_store_bytes(skb, TOS_OFF, &new_tos, sizeof(new_tos), 0);
}
#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check))
#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr))
#define IS_PSEUDO 0x10
static inline void set_tcp_ip_src(struct __sk_buff *skb, __u32 new_ip)
{
__u32 old_ip = _htonl(load_word(skb, IP_SRC_OFF));
bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_ip, new_ip, IS_PSEUDO | sizeof(new_ip));
bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
}
#define TCP_DPORT_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest))
static inline void set_tcp_dest_port(struct __sk_buff *skb, __u16 new_port)
{
__u16 old_port = htons(load_half(skb, TCP_DPORT_OFF));
bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_port, new_port, sizeof(new_port));
bpf_skb_store_bytes(skb, TCP_DPORT_OFF, &new_port, sizeof(new_port), 0);
}
SEC("classifier")
int bpf_prog1(struct __sk_buff *skb)
{
__u8 proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
long *value;
if (proto == IPPROTO_TCP) {
set_ip_tos(skb, 8);
set_tcp_ip_src(skb, 0xA010101);
set_tcp_dest_port(skb, 5001);
}
return 0;
}
SEC("redirect_xmit")
int _redirect_xmit(struct __sk_buff *skb)
{
return bpf_redirect(skb->ifindex + 1, 0);
}
SEC("redirect_recv")
int _redirect_recv(struct __sk_buff *skb)
{
return bpf_redirect(skb->ifindex + 1, 1);
}
SEC("clone_redirect_xmit")
int _clone_redirect_xmit(struct __sk_buff *skb)
{
bpf_clone_redirect(skb, skb->ifindex + 1, 0);
return TC_ACT_SHOT;
}
SEC("clone_redirect_recv")
int _clone_redirect_recv(struct __sk_buff *skb)
{
bpf_clone_redirect(skb, skb->ifindex + 1, 1);
return TC_ACT_SHOT;
}
char _license[] SEC("license") = "GPL";
@@ -0,0 +1,71 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set base_rtt to 80us when host is running TCP-NV and
* both hosts are in the same datacenter (as determined by IPv6 prefix).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_basertt(struct bpf_sock_ops *skops)
{
char cong[20];
char nv[] = "nv";
int rv = 0, n;
int op;
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Check if both hosts are in the same datacenter. For this
* example they are if the 1st 5.5 bytes in the IPv6 address
* are the same.
*/
if (skops->family == AF_INET6 &&
skops->local_ip6[0] == skops->remote_ip6[0] &&
(bpf_ntohl(skops->local_ip6[1]) & 0xfff00000) ==
(bpf_ntohl(skops->remote_ip6[1]) & 0xfff00000)) {
switch (op) {
case BPF_SOCK_OPS_BASE_RTT:
n = bpf_getsockopt(skops, SOL_TCP, TCP_CONGESTION,
cong, sizeof(cong));
if (!n && !__builtin_memcmp(cong, nv, sizeof(nv))) {
/* Set base_rtt to 80us */
rv = 80;
} else if (n) {
rv = n;
} else {
rv = -1;
}
break;
default:
rv = -1;
}
} else {
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+28
View File
@@ -0,0 +1,28 @@
This file describes how to run the tcp_*_kern.o tcp_bpf (or socket_ops)
programs. These programs attach to a cgroupv2. The following commands create
a cgroupv2 and attach a bash shell to the group.
mkdir -p /tmp/cgroupv2
mount -t cgroup2 none /tmp/cgroupv2
mkdir -p /tmp/cgroupv2/foo
bash
echo $$ >> /tmp/cgroupv2/foo/cgroup.procs
Anything that runs under this shell belongs to the foo cgroupv2. To load
(attach) one of the tcp_*_kern.o programs:
bpftool prog load tcp_basertt_kern.o /sys/fs/bpf/tcp_prog
bpftool cgroup attach /tmp/cgroupv2/foo sock_ops pinned /sys/fs/bpf/tcp_prog
bpftool prog tracelog
"bpftool prog tracelog" will continue to run printing the BPF log buffer.
The tcp_*_kern.o programs use special print functions to print logging
information (if enabled by the ifdef).
If using netperf/netserver to create traffic, you need to run them under the
cgroupv2 to which the BPF programs are attached (i.e. under bash shell
attached to the cgroupv2).
To remove (unattach) a socket_ops BPF program from a cgroupv2:
bpftool cgroup detach /tmp/cgroupv2/foo sock_ops pinned /sys/fs/bpf/tcp_prog
+81
View File
@@ -0,0 +1,81 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set initial receive window to 40 packets and send
* and receive buffers to 1.5MB. This would usually be done after
* doing appropriate checks that indicate the hosts are far enough
* away (i.e. large RTT).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_bufs(struct bpf_sock_ops *skops)
{
int bufsize = 1500000;
int rwnd_init = 40;
int rv = 0;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) != 55601 &&
skops->local_port != 55601) {
skops->reply = -1;
return 1;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
/* Usually there would be a check to insure the hosts are far
* from each other so it makes sense to increase buffer sizes
*/
switch (op) {
case BPF_SOCK_OPS_RWND_INIT:
rv = rwnd_init;
break;
case BPF_SOCK_OPS_TCP_CONNECT_CB:
/* Set sndbuf and rcvbuf of active connections */
rv = bpf_setsockopt(skops, SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET, SO_RCVBUF,
&bufsize, sizeof(bufsize));
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
/* Nothing to do */
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
/* Set sndbuf and rcvbuf of passive connections */
rv = bpf_setsockopt(skops, SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET, SO_RCVBUF,
&bufsize, sizeof(bufsize));
break;
default:
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+97
View File
@@ -0,0 +1,97 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* Sample BPF program to set send and receive buffers to 150KB, sndcwnd clamp
* to 100 packets and SYN and SYN_ACK RTOs to 10ms when both hosts are within
* the same datacenter. For his example, we assume they are within the same
* datacenter when the first 5.5 bytes of their IPv6 addresses are the same.
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_clamp(struct bpf_sock_ops *skops)
{
int bufsize = 150000;
int to_init = 10;
int clamp = 100;
int rv = 0;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) != 55601 && skops->local_port != 55601) {
skops->reply = -1;
return 0;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Check that both hosts are within same datacenter. For this example
* it is the case when the first 5.5 bytes of their IPv6 addresses are
* the same.
*/
if (skops->family == AF_INET6 &&
skops->local_ip6[0] == skops->remote_ip6[0] &&
(bpf_ntohl(skops->local_ip6[1]) & 0xfff00000) ==
(bpf_ntohl(skops->remote_ip6[1]) & 0xfff00000)) {
switch (op) {
case BPF_SOCK_OPS_TIMEOUT_INIT:
rv = to_init;
break;
case BPF_SOCK_OPS_TCP_CONNECT_CB:
/* Set sndbuf and rcvbuf of active connections */
rv = bpf_setsockopt(skops, SOL_SOCKET, SO_SNDBUF,
&bufsize, sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET,
SO_RCVBUF, &bufsize,
sizeof(bufsize));
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
rv = bpf_setsockopt(skops, SOL_TCP,
TCP_BPF_SNDCWND_CLAMP,
&clamp, sizeof(clamp));
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
/* Set sndbuf and rcvbuf of passive connections */
rv = bpf_setsockopt(skops, SOL_TCP,
TCP_BPF_SNDCWND_CLAMP,
&clamp, sizeof(clamp));
rv += bpf_setsockopt(skops, SOL_SOCKET,
SO_SNDBUF, &bufsize,
sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET,
SO_RCVBUF, &bufsize,
sizeof(bufsize));
break;
default:
rv = -1;
}
} else {
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+78
View File
@@ -0,0 +1,78 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set congestion control to dctcp when both hosts are
* in the same datacenter (as determined by IPv6 prefix).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_cong(struct bpf_sock_ops *skops)
{
char cong[] = "dctcp";
int rv = 0;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) != 55601 &&
skops->local_port != 55601) {
skops->reply = -1;
return 1;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Check if both hosts are in the same datacenter. For this
* example they are if the 1st 5.5 bytes in the IPv6 address
* are the same.
*/
if (skops->family == AF_INET6 &&
skops->local_ip6[0] == skops->remote_ip6[0] &&
(bpf_ntohl(skops->local_ip6[1]) & 0xfff00000) ==
(bpf_ntohl(skops->remote_ip6[1]) & 0xfff00000)) {
switch (op) {
case BPF_SOCK_OPS_NEEDS_ECN:
rv = 1;
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
rv = bpf_setsockopt(skops, SOL_TCP, TCP_CONGESTION,
cong, sizeof(cong));
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
rv = bpf_setsockopt(skops, SOL_TCP, TCP_CONGESTION,
cong, sizeof(cong));
break;
default:
rv = -1;
}
} else {
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/* Refer to samples/bpf/tcp_bpf.readme for the instructions on
* how to run this sample program.
*/
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define INTERVAL 1000000000ULL
int _version SEC("version") = 1;
char _license[] SEC("license") = "GPL";
struct {
__u32 type;
__u32 map_flags;
int *key;
__u64 *value;
} bpf_next_dump SEC(".maps") = {
.type = BPF_MAP_TYPE_SK_STORAGE,
.map_flags = BPF_F_NO_PREALLOC,
};
SEC("sockops")
int _sockops(struct bpf_sock_ops *ctx)
{
struct bpf_tcp_sock *tcp_sk;
struct bpf_sock *sk;
__u64 *next_dump;
__u64 now;
switch (ctx->op) {
case BPF_SOCK_OPS_TCP_CONNECT_CB:
bpf_sock_ops_cb_flags_set(ctx, BPF_SOCK_OPS_RTT_CB_FLAG);
return 1;
case BPF_SOCK_OPS_RTT_CB:
break;
default:
return 1;
}
sk = ctx->sk;
if (!sk)
return 1;
next_dump = bpf_sk_storage_get(&bpf_next_dump, sk, 0,
BPF_SK_STORAGE_GET_F_CREATE);
if (!next_dump)
return 1;
now = bpf_ktime_get_ns();
if (now < *next_dump)
return 1;
tcp_sk = bpf_tcp_sock(sk);
if (!tcp_sk)
return 1;
*next_dump = now + INTERVAL;
bpf_printk("dsack_dups=%u delivered=%u\n",
tcp_sk->dsack_dups, tcp_sk->delivered);
bpf_printk("delivered_ce=%u icsk_retransmits=%u\n",
tcp_sk->delivered_ce, tcp_sk->icsk_retransmits);
return 1;
}
+83
View File
@@ -0,0 +1,83 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set initial congestion window and initial receive
* window to 40 packets and send and receive buffers to 1.5MB. This
* would usually be done after doing appropriate checks that indicate
* the hosts are far enough away (i.e. large RTT).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_iw(struct bpf_sock_ops *skops)
{
int bufsize = 1500000;
int rwnd_init = 40;
int iw = 40;
int rv = 0;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) != 55601 &&
skops->local_port != 55601) {
skops->reply = -1;
return 1;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Usually there would be a check to insure the hosts are far
* from each other so it makes sense to increase buffer sizes
*/
switch (op) {
case BPF_SOCK_OPS_RWND_INIT:
rv = rwnd_init;
break;
case BPF_SOCK_OPS_TCP_CONNECT_CB:
/* Set sndbuf and rcvbuf of active connections */
rv = bpf_setsockopt(skops, SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET, SO_RCVBUF,
&bufsize, sizeof(bufsize));
break;
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
rv = bpf_setsockopt(skops, SOL_TCP, TCP_BPF_IW, &iw,
sizeof(iw));
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
/* Set sndbuf and rcvbuf of passive connections */
rv = bpf_setsockopt(skops, SOL_SOCKET, SO_SNDBUF, &bufsize,
sizeof(bufsize));
rv += bpf_setsockopt(skops, SOL_SOCKET, SO_RCVBUF,
&bufsize, sizeof(bufsize));
break;
default:
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+64
View File
@@ -0,0 +1,64 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set initial receive window to 40 packets when using IPv6
* and the first 5.5 bytes of the IPv6 addresses are not the same (in this
* example that means both hosts are not the same datacenter).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_rwnd(struct bpf_sock_ops *skops)
{
int rv = -1;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) !=
55601 && skops->local_port != 55601) {
skops->reply = -1;
return 1;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Check for RWND_INIT operation and IPv6 addresses */
if (op == BPF_SOCK_OPS_RWND_INIT &&
skops->family == AF_INET6) {
/* If the first 5.5 bytes of the IPv6 address are not the same
* then both hosts are not in the same datacenter
* so use a larger initial advertized window (40 packets)
*/
if (skops->local_ip6[0] != skops->remote_ip6[0] ||
(bpf_ntohl(skops->local_ip6[1]) & 0xfffff000) !=
(bpf_ntohl(skops->remote_ip6[1]) & 0xfffff000))
rv = 40;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+64
View File
@@ -0,0 +1,64 @@
/* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* BPF program to set SYN and SYN-ACK RTOs to 10ms when using IPv6 addresses
* and the first 5.5 bytes of the IPv6 addresses are the same (in this example
* that means both hosts are in the same datacenter).
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_synrto(struct bpf_sock_ops *skops)
{
int rv = -1;
int op;
/* For testing purposes, only execute rest of BPF program
* if neither port numberis 55601
*/
if (bpf_ntohl(skops->remote_port) != 55601 &&
skops->local_port != 55601) {
skops->reply = -1;
return 1;
}
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
/* Check for TIMEOUT_INIT operation and IPv6 addresses */
if (op == BPF_SOCK_OPS_TIMEOUT_INIT &&
skops->family == AF_INET6) {
/* If the first 5.5 bytes of the IPv6 address are the same
* then both hosts are in the same datacenter
* so use an RTO of 10ms
*/
if (skops->local_ip6[0] == skops->remote_ip6[0] &&
(bpf_ntohl(skops->local_ip6[1]) & 0xfff00000) ==
(bpf_ntohl(skops->remote_ip6[1]) & 0xfff00000))
rv = 10;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 Facebook
*
* BPF program to automatically reflect TOS option from received syn packet
*
* Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <uapi/linux/ip.h>
#include <uapi/linux/ipv6.h>
#include <uapi/linux/in.h>
#include <linux/socket.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define DEBUG 1
SEC("sockops")
int bpf_basertt(struct bpf_sock_ops *skops)
{
char header[sizeof(struct ipv6hdr)];
struct ipv6hdr *hdr6;
struct iphdr *hdr;
int hdr_size = 0;
int save_syn = 1;
int tos = 0;
int rv = 0;
int op;
op = (int) skops->op;
#ifdef DEBUG
bpf_printk("BPF command: %d\n", op);
#endif
switch (op) {
case BPF_SOCK_OPS_TCP_LISTEN_CB:
rv = bpf_setsockopt(skops, SOL_TCP, TCP_SAVE_SYN,
&save_syn, sizeof(save_syn));
break;
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
if (skops->family == AF_INET)
hdr_size = sizeof(struct iphdr);
else
hdr_size = sizeof(struct ipv6hdr);
rv = bpf_getsockopt(skops, SOL_TCP, TCP_SAVED_SYN,
header, hdr_size);
if (!rv) {
if (skops->family == AF_INET) {
hdr = (struct iphdr *) header;
tos = hdr->tos;
if (tos != 0)
bpf_setsockopt(skops, SOL_IP, IP_TOS,
&tos, sizeof(tos));
} else {
hdr6 = (struct ipv6hdr *) header;
tos = ((hdr6->priority) << 4 |
(hdr6->flow_lbl[0]) >> 4);
if (tos)
bpf_setsockopt(skops, SOL_IPV6,
IPV6_TCLASS,
&tos, sizeof(tos));
}
rv = 0;
}
break;
default:
rv = -1;
}
#ifdef DEBUG
bpf_printk("Returning %d\n", rv);
#endif
skops->reply = rv;
return 1;
}
char _license[] SEC("license") = "GPL";
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
function pktgen {
../pktgen/pktgen_bench_xmit_mode_netif_receive.sh -i $IFC -s 64 \
-m 90:e2:ba:ff:ff:ff -d 192.168.0.1 -t 4
local dropped=`tc -s qdisc show dev $IFC | tail -3 | awk '/drop/{print $7}'`
if [ "$dropped" == "0," ]; then
echo "FAIL"
else
echo "Successfully filtered " $dropped " packets"
fi
}
function test {
echo -n "Loading bpf program '$2'... "
tc qdisc add dev $IFC clsact
tc filter add dev $IFC ingress bpf da obj $1 sec $2
local status=$?
if [ $status -ne 0 ]; then
echo "FAIL"
else
echo "ok"
pktgen
fi
tc qdisc del dev $IFC clsact
}
IFC=test_veth
ip link add name $IFC type veth peer name pair_$IFC
ip link set $IFC up
ip link set pair_$IFC up
test ./parse_simple.o simple
test ./parse_varlen.o varlen
test ./parse_ldabs.o ldabs
ip link del dev $IFC
+534
View File
@@ -0,0 +1,534 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 Facebook
*/
#define _GNU_SOURCE
#include <linux/types.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/bpf.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sched.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <bpf/bpf.h>
#include "bpf_util.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
#ifndef offsetof
# define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#endif
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
static int nr_cpus;
static unsigned long long *dist_keys;
static unsigned int dist_key_counts;
struct list_head {
struct list_head *next, *prev;
};
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
struct pfect_lru_node {
struct list_head list;
unsigned long long key;
};
struct pfect_lru {
struct list_head list;
struct pfect_lru_node *free_nodes;
unsigned int cur_size;
unsigned int lru_size;
unsigned int nr_unique;
unsigned int nr_misses;
unsigned int total;
int map_fd;
};
static void pfect_lru_init(struct pfect_lru *lru, unsigned int lru_size,
unsigned int nr_possible_elems)
{
lru->map_fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL,
sizeof(unsigned long long),
sizeof(struct pfect_lru_node *),
nr_possible_elems, NULL);
assert(lru->map_fd != -1);
lru->free_nodes = malloc(lru_size * sizeof(struct pfect_lru_node));
assert(lru->free_nodes);
INIT_LIST_HEAD(&lru->list);
lru->cur_size = 0;
lru->lru_size = lru_size;
lru->nr_unique = lru->nr_misses = lru->total = 0;
}
static void pfect_lru_destroy(struct pfect_lru *lru)
{
close(lru->map_fd);
free(lru->free_nodes);
}
static int pfect_lru_lookup_or_insert(struct pfect_lru *lru,
unsigned long long key)
{
struct pfect_lru_node *node = NULL;
int seen = 0;
lru->total++;
if (!bpf_map_lookup_elem(lru->map_fd, &key, &node)) {
if (node) {
list_move(&node->list, &lru->list);
return 1;
}
seen = 1;
}
if (lru->cur_size < lru->lru_size) {
node = &lru->free_nodes[lru->cur_size++];
INIT_LIST_HEAD(&node->list);
} else {
struct pfect_lru_node *null_node = NULL;
node = list_last_entry(&lru->list,
struct pfect_lru_node,
list);
bpf_map_update_elem(lru->map_fd, &node->key, &null_node, BPF_EXIST);
}
node->key = key;
list_move(&node->list, &lru->list);
lru->nr_misses++;
if (seen) {
assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_EXIST));
} else {
lru->nr_unique++;
assert(!bpf_map_update_elem(lru->map_fd, &key, &node, BPF_NOEXIST));
}
return seen;
}
static unsigned int read_keys(const char *dist_file,
unsigned long long **keys)
{
struct stat fst;
unsigned long long *retkeys;
unsigned int counts = 0;
int dist_fd;
char *b, *l;
int i;
dist_fd = open(dist_file, 0);
assert(dist_fd != -1);
assert(fstat(dist_fd, &fst) == 0);
b = malloc(fst.st_size);
assert(b);
assert(read(dist_fd, b, fst.st_size) == fst.st_size);
close(dist_fd);
for (i = 0; i < fst.st_size; i++) {
if (b[i] == '\n')
counts++;
}
counts++; /* in case the last line has no \n */
retkeys = malloc(counts * sizeof(unsigned long long));
assert(retkeys);
counts = 0;
for (l = strtok(b, "\n"); l; l = strtok(NULL, "\n"))
retkeys[counts++] = strtoull(l, NULL, 10);
free(b);
*keys = retkeys;
return counts;
}
static int create_map(int map_type, int map_flags, unsigned int size)
{
LIBBPF_OPTS(bpf_map_create_opts, opts,
.map_flags = map_flags,
);
int map_fd;
map_fd = bpf_map_create(map_type, NULL, sizeof(unsigned long long),
sizeof(unsigned long long), size, &opts);
if (map_fd == -1)
perror("bpf_create_map");
return map_fd;
}
static int sched_next_online(int pid, int next_to_try)
{
cpu_set_t cpuset;
if (next_to_try == nr_cpus)
return -1;
while (next_to_try < nr_cpus) {
CPU_ZERO(&cpuset);
CPU_SET(next_to_try++, &cpuset);
if (!sched_setaffinity(pid, sizeof(cpuset), &cpuset))
break;
}
return next_to_try;
}
static void run_parallel(unsigned int tasks, void (*fn)(int i, void *data),
void *data)
{
int next_sched_cpu = 0;
pid_t pid[tasks];
int i;
for (i = 0; i < tasks; i++) {
pid[i] = fork();
if (pid[i] == 0) {
next_sched_cpu = sched_next_online(0, next_sched_cpu);
fn(i, data);
exit(0);
} else if (pid[i] == -1) {
printf("couldn't spawn #%d process\n", i);
exit(1);
}
/* It is mostly redundant and just allow the parent
* process to update next_shced_cpu for the next child
* process
*/
next_sched_cpu = sched_next_online(pid[i], next_sched_cpu);
}
for (i = 0; i < tasks; i++) {
int status;
assert(waitpid(pid[i], &status, 0) == pid[i]);
assert(status == 0);
}
}
static void do_test_lru_dist(int task, void *data)
{
unsigned int nr_misses = 0;
struct pfect_lru pfect_lru;
unsigned long long key, value = 1234;
unsigned int i;
unsigned int lru_map_fd = ((unsigned int *)data)[0];
unsigned int lru_size = ((unsigned int *)data)[1];
unsigned long long key_offset = task * dist_key_counts;
pfect_lru_init(&pfect_lru, lru_size, dist_key_counts);
for (i = 0; i < dist_key_counts; i++) {
key = dist_keys[i] + key_offset;
pfect_lru_lookup_or_insert(&pfect_lru, key);
if (!bpf_map_lookup_elem(lru_map_fd, &key, &value))
continue;
if (bpf_map_update_elem(lru_map_fd, &key, &value, BPF_NOEXIST)) {
printf("bpf_map_update_elem(lru_map_fd, %llu): errno:%d\n",
key, errno);
assert(0);
}
nr_misses++;
}
printf(" task:%d BPF LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n",
task, pfect_lru.nr_unique, dist_key_counts, nr_misses,
dist_key_counts);
printf(" task:%d Perfect LRU: nr_unique:%u(/%u) nr_misses:%u(/%u)\n",
task, pfect_lru.nr_unique, pfect_lru.total,
pfect_lru.nr_misses, pfect_lru.total);
pfect_lru_destroy(&pfect_lru);
close(lru_map_fd);
}
static void test_parallel_lru_dist(int map_type, int map_flags,
int nr_tasks, unsigned int lru_size)
{
int child_data[2];
int lru_map_fd;
printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type,
map_flags);
if (map_flags & BPF_F_NO_COMMON_LRU)
lru_map_fd = create_map(map_type, map_flags,
nr_cpus * lru_size);
else
lru_map_fd = create_map(map_type, map_flags,
nr_tasks * lru_size);
assert(lru_map_fd != -1);
child_data[0] = lru_map_fd;
child_data[1] = lru_size;
run_parallel(nr_tasks, do_test_lru_dist, child_data);
close(lru_map_fd);
}
static void test_lru_loss0(int map_type, int map_flags)
{
unsigned long long key, value[nr_cpus];
unsigned int old_unused_losses = 0;
unsigned int new_unused_losses = 0;
unsigned int used_losses = 0;
int map_fd;
printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
map_flags);
assert(sched_next_online(0, 0) != -1);
if (map_flags & BPF_F_NO_COMMON_LRU)
map_fd = create_map(map_type, map_flags, 900 * nr_cpus);
else
map_fd = create_map(map_type, map_flags, 900);
assert(map_fd != -1);
value[0] = 1234;
for (key = 1; key <= 1000; key++) {
int start_key, end_key;
assert(bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST) == 0);
start_key = 101;
end_key = min(key, 900);
while (start_key <= end_key) {
bpf_map_lookup_elem(map_fd, &start_key, value);
start_key++;
}
}
for (key = 1; key <= 1000; key++) {
if (bpf_map_lookup_elem(map_fd, &key, value)) {
if (key <= 100)
old_unused_losses++;
else if (key <= 900)
used_losses++;
else
new_unused_losses++;
}
}
close(map_fd);
printf("older-elem-losses:%d(/100) active-elem-losses:%d(/800) "
"newer-elem-losses:%d(/100)\n",
old_unused_losses, used_losses, new_unused_losses);
}
static void test_lru_loss1(int map_type, int map_flags)
{
unsigned long long key, value[nr_cpus];
int map_fd;
unsigned int nr_losses = 0;
printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type,
map_flags);
assert(sched_next_online(0, 0) != -1);
if (map_flags & BPF_F_NO_COMMON_LRU)
map_fd = create_map(map_type, map_flags, 1000 * nr_cpus);
else
map_fd = create_map(map_type, map_flags, 1000);
assert(map_fd != -1);
value[0] = 1234;
for (key = 1; key <= 1000; key++)
assert(!bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST));
for (key = 1; key <= 1000; key++) {
if (bpf_map_lookup_elem(map_fd, &key, value))
nr_losses++;
}
close(map_fd);
printf("nr_losses:%d(/1000)\n", nr_losses);
}
static void do_test_parallel_lru_loss(int task, void *data)
{
const unsigned int nr_stable_elems = 1000;
const unsigned int nr_repeats = 100000;
int map_fd = *(int *)data;
unsigned long long stable_base;
unsigned long long key, value[nr_cpus];
unsigned long long next_ins_key;
unsigned int nr_losses = 0;
unsigned int i;
stable_base = task * nr_repeats * 2 + 1;
next_ins_key = stable_base;
value[0] = 1234;
for (i = 0; i < nr_stable_elems; i++) {
assert(bpf_map_update_elem(map_fd, &next_ins_key, value,
BPF_NOEXIST) == 0);
next_ins_key++;
}
for (i = 0; i < nr_repeats; i++) {
int rn;
rn = rand();
if (rn % 10) {
key = rn % nr_stable_elems + stable_base;
bpf_map_lookup_elem(map_fd, &key, value);
} else {
bpf_map_update_elem(map_fd, &next_ins_key, value,
BPF_NOEXIST);
next_ins_key++;
}
}
key = stable_base;
for (i = 0; i < nr_stable_elems; i++) {
if (bpf_map_lookup_elem(map_fd, &key, value))
nr_losses++;
key++;
}
printf(" task:%d nr_losses:%u\n", task, nr_losses);
}
static void test_parallel_lru_loss(int map_type, int map_flags, int nr_tasks)
{
int map_fd;
printf("%s (map_type:%d map_flags:0x%X):\n", __func__, map_type,
map_flags);
/* Give 20% more than the active working set */
if (map_flags & BPF_F_NO_COMMON_LRU)
map_fd = create_map(map_type, map_flags,
nr_cpus * (1000 + 200));
else
map_fd = create_map(map_type, map_flags,
nr_tasks * (1000 + 200));
assert(map_fd != -1);
run_parallel(nr_tasks, do_test_parallel_lru_loss, &map_fd);
close(map_fd);
}
int main(int argc, char **argv)
{
int map_flags[] = {0, BPF_F_NO_COMMON_LRU};
const char *dist_file;
int nr_tasks = 1;
int lru_size;
int f;
if (argc < 4) {
printf("Usage: %s <dist-file> <lru-size> <nr-tasks>\n",
argv[0]);
return -1;
}
dist_file = argv[1];
lru_size = atoi(argv[2]);
nr_tasks = atoi(argv[3]);
setbuf(stdout, NULL);
srand(time(NULL));
nr_cpus = bpf_num_possible_cpus();
assert(nr_cpus != -1);
printf("nr_cpus:%d\n\n", nr_cpus);
nr_tasks = min(nr_tasks, nr_cpus);
dist_key_counts = read_keys(dist_file, &dist_keys);
if (!dist_key_counts) {
printf("%s has no key\n", dist_file);
return -1;
}
for (f = 0; f < ARRAY_SIZE(map_flags); f++) {
test_lru_loss0(BPF_MAP_TYPE_LRU_HASH, map_flags[f]);
test_lru_loss1(BPF_MAP_TYPE_LRU_HASH, map_flags[f]);
test_parallel_lru_loss(BPF_MAP_TYPE_LRU_HASH, map_flags[f],
nr_tasks);
test_parallel_lru_dist(BPF_MAP_TYPE_LRU_HASH, map_flags[f],
nr_tasks, lru_size);
printf("\n");
}
free(dist_keys);
return 0;
}
+245
View File
@@ -0,0 +1,245 @@
/* Copyright (c) 2016 Thomas Graf <tgraf@tgraf.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License 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.
*/
#include "vmlinux.h"
#include "net_shared.h"
#include <bpf/bpf_helpers.h>
#include <string.h>
# define printk(fmt, ...) \
({ \
char ____fmt[] = fmt; \
bpf_trace_printk(____fmt, sizeof(____fmt), \
##__VA_ARGS__); \
})
#define CB_MAGIC 1234
/* Test: Pass all packets through */
SEC("nop")
int do_nop(struct __sk_buff *skb)
{
return BPF_OK;
}
/* Test: Verify context information can be accessed */
SEC("test_ctx")
int do_test_ctx(struct __sk_buff *skb)
{
skb->cb[0] = CB_MAGIC;
printk("len %d hash %d protocol %d", skb->len, skb->hash,
skb->protocol);
printk("cb %d ingress_ifindex %d ifindex %d", skb->cb[0],
skb->ingress_ifindex, skb->ifindex);
return BPF_OK;
}
/* Test: Ensure skb->cb[] buffer is cleared */
SEC("test_cb")
int do_test_cb(struct __sk_buff *skb)
{
printk("cb0: %x cb1: %x cb2: %x", skb->cb[0], skb->cb[1],
skb->cb[2]);
printk("cb3: %x cb4: %x", skb->cb[3], skb->cb[4]);
return BPF_OK;
}
/* Test: Verify skb data can be read */
SEC("test_data")
int do_test_data(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct iphdr *iph = data;
if (data + sizeof(*iph) > data_end) {
printk("packet truncated");
return BPF_DROP;
}
printk("src: %x dst: %x", iph->saddr, iph->daddr);
return BPF_OK;
}
#define IP_CSUM_OFF offsetof(struct iphdr, check)
#define IP_DST_OFF offsetof(struct iphdr, daddr)
#define IP_SRC_OFF offsetof(struct iphdr, saddr)
#define IP_PROTO_OFF offsetof(struct iphdr, protocol)
#define TCP_CSUM_OFF offsetof(struct tcphdr, check)
#define UDP_CSUM_OFF offsetof(struct udphdr, check)
#define IS_PSEUDO 0x10
static inline int rewrite(struct __sk_buff *skb, uint32_t old_ip,
uint32_t new_ip, int rw_daddr)
{
int ret, off = 0, flags = IS_PSEUDO;
uint8_t proto;
ret = bpf_skb_load_bytes(skb, IP_PROTO_OFF, &proto, 1);
if (ret < 0) {
printk("bpf_l4_csum_replace failed: %d", ret);
return BPF_DROP;
}
switch (proto) {
case IPPROTO_TCP:
off = TCP_CSUM_OFF;
break;
case IPPROTO_UDP:
off = UDP_CSUM_OFF;
flags |= BPF_F_MARK_MANGLED_0;
break;
case IPPROTO_ICMPV6:
off = offsetof(struct icmp6hdr, icmp6_cksum);
break;
}
if (off) {
ret = bpf_l4_csum_replace(skb, off, old_ip, new_ip,
flags | sizeof(new_ip));
if (ret < 0) {
printk("bpf_l4_csum_replace failed: %d");
return BPF_DROP;
}
}
ret = bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
if (ret < 0) {
printk("bpf_l3_csum_replace failed: %d", ret);
return BPF_DROP;
}
if (rw_daddr)
ret = bpf_skb_store_bytes(skb, IP_DST_OFF, &new_ip, sizeof(new_ip), 0);
else
ret = bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
if (ret < 0) {
printk("bpf_skb_store_bytes() failed: %d", ret);
return BPF_DROP;
}
return BPF_OK;
}
/* Test: Verify skb data can be modified */
SEC("test_rewrite")
int do_test_rewrite(struct __sk_buff *skb)
{
uint32_t old_ip, new_ip = 0x3fea8c0;
int ret;
ret = bpf_skb_load_bytes(skb, IP_DST_OFF, &old_ip, 4);
if (ret < 0) {
printk("bpf_skb_load_bytes failed: %d", ret);
return BPF_DROP;
}
if (old_ip == 0x2fea8c0) {
printk("out: rewriting from %x to %x", old_ip, new_ip);
return rewrite(skb, old_ip, new_ip, 1);
}
return BPF_OK;
}
static inline int __do_push_ll_and_redirect(struct __sk_buff *skb)
{
uint64_t smac = SRC_MAC, dmac = DST_MAC;
int ret, ifindex = DST_IFINDEX;
struct ethhdr ehdr;
ret = bpf_skb_change_head(skb, 14, 0);
if (ret < 0) {
printk("skb_change_head() failed: %d", ret);
}
ehdr.h_proto = bpf_htons(ETH_P_IP);
memcpy(&ehdr.h_source, &smac, 6);
memcpy(&ehdr.h_dest, &dmac, 6);
ret = bpf_skb_store_bytes(skb, 0, &ehdr, sizeof(ehdr), 0);
if (ret < 0) {
printk("skb_store_bytes() failed: %d", ret);
return BPF_DROP;
}
return bpf_redirect(ifindex, 0);
}
SEC("push_ll_and_redirect_silent")
int do_push_ll_and_redirect_silent(struct __sk_buff *skb)
{
return __do_push_ll_and_redirect(skb);
}
SEC("push_ll_and_redirect")
int do_push_ll_and_redirect(struct __sk_buff *skb)
{
int ret, ifindex = DST_IFINDEX;
ret = __do_push_ll_and_redirect(skb);
if (ret >= 0)
printk("redirected to %d", ifindex);
return ret;
}
static inline void __fill_garbage(struct __sk_buff *skb)
{
uint64_t f = 0xFFFFFFFFFFFFFFFF;
bpf_skb_store_bytes(skb, 0, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 8, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 16, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 24, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 32, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 40, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 48, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 56, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 64, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 72, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 80, &f, sizeof(f), 0);
bpf_skb_store_bytes(skb, 88, &f, sizeof(f), 0);
}
SEC("fill_garbage")
int do_fill_garbage(struct __sk_buff *skb)
{
__fill_garbage(skb);
printk("Set initial 96 bytes of header to FF");
return BPF_OK;
}
SEC("fill_garbage_and_redirect")
int do_fill_garbage_and_redirect(struct __sk_buff *skb)
{
int ifindex = DST_IFINDEX;
__fill_garbage(skb);
printk("redirected to %d", ifindex);
return bpf_redirect(ifindex, 0);
}
/* Drop all packets */
SEC("drop_all")
int do_drop_all(struct __sk_buff *skb)
{
printk("dropping with: %d", BPF_DROP);
return BPF_DROP;
}
char _license[] SEC("license") = "GPL";
+405
View File
@@ -0,0 +1,405 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Uncomment to see generated bytecode
#VERBOSE=verbose
NS1=lwt_ns1
NS2=lwt_ns2
VETH0=tst_lwt1a
VETH1=tst_lwt1b
VETH2=tst_lwt2a
VETH3=tst_lwt2b
IPVETH0="192.168.254.1"
IPVETH1="192.168.254.2"
IPVETH1b="192.168.254.3"
IPVETH2="192.168.111.1"
IPVETH3="192.168.111.2"
IP_LOCAL="192.168.99.1"
PROG_SRC="test_lwt_bpf.c"
BPF_PROG="test_lwt_bpf.o"
TRACE_ROOT=/sys/kernel/tracing
CONTEXT_INFO=$(cat ${TRACE_ROOT}/trace_options | grep context)
function lookup_mac()
{
set +x
if [ ! -z "$2" ]; then
MAC=$(ip netns exec $2 ip link show $1 | grep ether | awk '{print $2}')
else
MAC=$(ip link show $1 | grep ether | awk '{print $2}')
fi
MAC="${MAC//:/}"
echo "0x${MAC:10:2}${MAC:8:2}${MAC:6:2}${MAC:4:2}${MAC:2:2}${MAC:0:2}"
set -x
}
function cleanup {
set +ex
rm $BPF_PROG 2> /dev/null
ip link del $VETH0 2> /dev/null
ip link del $VETH1 2> /dev/null
ip link del $VETH2 2> /dev/null
ip link del $VETH3 2> /dev/null
ip netns exec $NS1 killall netserver
ip netns delete $NS1 2> /dev/null
ip netns delete $NS2 2> /dev/null
set -ex
}
function setup_one_veth {
ip netns add $1
ip link add $2 type veth peer name $3
ip link set dev $2 up
ip addr add $4/24 dev $2
ip link set $3 netns $1
ip netns exec $1 ip link set dev $3 up
ip netns exec $1 ip addr add $5/24 dev $3
if [ "$6" ]; then
ip netns exec $1 ip addr add $6/32 dev $3
fi
}
function get_trace {
set +x
cat ${TRACE_ROOT}/trace | grep -v '^#'
set -x
}
function cleanup_routes {
ip route del ${IPVETH1}/32 dev $VETH0 2> /dev/null || true
ip route del table local local ${IP_LOCAL}/32 dev lo 2> /dev/null || true
}
function install_test {
cleanup_routes
cp /dev/null ${TRACE_ROOT}/trace
OPTS="encap bpf headroom 14 $1 obj $BPF_PROG section $2 $VERBOSE"
if [ "$1" == "in" ]; then
ip route add table local local ${IP_LOCAL}/32 $OPTS dev lo
else
ip route add ${IPVETH1}/32 $OPTS dev $VETH0
fi
}
function remove_prog {
if [ "$1" == "in" ]; then
ip route del table local local ${IP_LOCAL}/32 dev lo
else
ip route del ${IPVETH1}/32 dev $VETH0
fi
}
function filter_trace {
# Add newline to allow starting EXPECT= variables on newline
NL=$'\n'
echo "${NL}$*" | sed -e 's/bpf_trace_printk: //g'
}
function expect_fail {
set +x
echo "FAIL:"
echo "Expected: $1"
echo "Got: $2"
set -x
exit 1
}
function match_trace {
set +x
RET=0
TRACE=$1
EXPECT=$2
GOT="$(filter_trace "$TRACE")"
[ "$GOT" != "$EXPECT" ] && {
expect_fail "$EXPECT" "$GOT"
RET=1
}
set -x
return $RET
}
function test_start {
set +x
echo "----------------------------------------------------------------"
echo "Starting test: $*"
echo "----------------------------------------------------------------"
set -x
}
function failure {
get_trace
echo "FAIL: $*"
exit 1
}
function test_ctx_xmit {
test_start "test_ctx on lwt xmit"
install_test xmit test_ctx
ping -c 3 $IPVETH1 || {
failure "test_ctx xmit: packets are dropped"
}
match_trace "$(get_trace)" "
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX" || exit 1
remove_prog xmit
}
function test_ctx_out {
test_start "test_ctx on lwt out"
install_test out test_ctx
ping -c 3 $IPVETH1 || {
failure "test_ctx out: packets are dropped"
}
match_trace "$(get_trace)" "
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex 0
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex 0
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 0 ifindex 0" || exit 1
remove_prog out
}
function test_ctx_in {
test_start "test_ctx on lwt in"
install_test in test_ctx
ping -c 3 $IP_LOCAL || {
failure "test_ctx out: packets are dropped"
}
# We will both request & reply packets as the packets will
# be from $IP_LOCAL => $IP_LOCAL
match_trace "$(get_trace)" "
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1
len 84 hash 0 protocol 8
cb 1234 ingress_ifindex 1 ifindex 1" || exit 1
remove_prog in
}
function test_data {
test_start "test_data on lwt $1"
install_test $1 test_data
ping -c 3 $IPVETH1 || {
failure "test_data ${1}: packets are dropped"
}
match_trace "$(get_trace)" "
src: 1fea8c0 dst: 2fea8c0
src: 1fea8c0 dst: 2fea8c0
src: 1fea8c0 dst: 2fea8c0" || exit 1
remove_prog $1
}
function test_data_in {
test_start "test_data on lwt in"
install_test in test_data
ping -c 3 $IP_LOCAL || {
failure "test_data in: packets are dropped"
}
# We will both request & reply packets as the packets will
# be from $IP_LOCAL => $IP_LOCAL
match_trace "$(get_trace)" "
src: 163a8c0 dst: 163a8c0
src: 163a8c0 dst: 163a8c0
src: 163a8c0 dst: 163a8c0
src: 163a8c0 dst: 163a8c0
src: 163a8c0 dst: 163a8c0
src: 163a8c0 dst: 163a8c0" || exit 1
remove_prog in
}
function test_cb {
test_start "test_cb on lwt $1"
install_test $1 test_cb
ping -c 3 $IPVETH1 || {
failure "test_cb ${1}: packets are dropped"
}
match_trace "$(get_trace)" "
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0" || exit 1
remove_prog $1
}
function test_cb_in {
test_start "test_cb on lwt in"
install_test in test_cb
ping -c 3 $IP_LOCAL || {
failure "test_cb in: packets are dropped"
}
# We will both request & reply packets as the packets will
# be from $IP_LOCAL => $IP_LOCAL
match_trace "$(get_trace)" "
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0
cb0: 0 cb1: 0 cb2: 0
cb3: 0 cb4: 0" || exit 1
remove_prog in
}
function test_drop_all {
test_start "test_drop_all on lwt $1"
install_test $1 drop_all
ping -c 3 $IPVETH1 && {
failure "test_drop_all ${1}: Unexpected success of ping"
}
match_trace "$(get_trace)" "
dropping with: 2
dropping with: 2
dropping with: 2" || exit 1
remove_prog $1
}
function test_drop_all_in {
test_start "test_drop_all on lwt in"
install_test in drop_all
ping -c 3 $IP_LOCAL && {
failure "test_drop_all in: Unexpected success of ping"
}
match_trace "$(get_trace)" "
dropping with: 2
dropping with: 2
dropping with: 2" || exit 1
remove_prog in
}
function test_push_ll_and_redirect {
test_start "test_push_ll_and_redirect on lwt xmit"
install_test xmit push_ll_and_redirect
ping -c 3 $IPVETH1 || {
failure "Redirected packets appear to be dropped"
}
match_trace "$(get_trace)" "
redirected to $DST_IFINDEX
redirected to $DST_IFINDEX
redirected to $DST_IFINDEX" || exit 1
remove_prog xmit
}
function test_no_l2_and_redirect {
test_start "test_no_l2_and_redirect on lwt xmit"
install_test xmit fill_garbage_and_redirect
ping -c 3 $IPVETH1 && {
failure "Unexpected success despite lack of L2 header"
}
match_trace "$(get_trace)" "
redirected to $DST_IFINDEX
redirected to $DST_IFINDEX
redirected to $DST_IFINDEX" || exit 1
remove_prog xmit
}
function test_rewrite {
test_start "test_rewrite on lwt xmit"
install_test xmit test_rewrite
ping -c 3 $IPVETH1 || {
failure "Rewritten packets appear to be dropped"
}
match_trace "$(get_trace)" "
out: rewriting from 2fea8c0 to 3fea8c0
out: rewriting from 2fea8c0 to 3fea8c0
out: rewriting from 2fea8c0 to 3fea8c0" || exit 1
remove_prog out
}
function test_fill_garbage {
test_start "test_fill_garbage on lwt xmit"
install_test xmit fill_garbage
ping -c 3 $IPVETH1 && {
failure "test_drop_all ${1}: Unexpected success of ping"
}
match_trace "$(get_trace)" "
Set initial 96 bytes of header to FF
Set initial 96 bytes of header to FF
Set initial 96 bytes of header to FF" || exit 1
remove_prog xmit
}
function test_netperf_nop {
test_start "test_netperf_nop on lwt xmit"
install_test xmit nop
netperf -H $IPVETH1 -t TCP_STREAM || {
failure "packets appear to be dropped"
}
match_trace "$(get_trace)" ""|| exit 1
remove_prog xmit
}
function test_netperf_redirect {
test_start "test_netperf_redirect on lwt xmit"
install_test xmit push_ll_and_redirect_silent
netperf -H $IPVETH1 -t TCP_STREAM || {
failure "Rewritten packets appear to be dropped"
}
match_trace "$(get_trace)" ""|| exit 1
remove_prog xmit
}
cleanup
setup_one_veth $NS1 $VETH0 $VETH1 $IPVETH0 $IPVETH1 $IPVETH1b
setup_one_veth $NS2 $VETH2 $VETH3 $IPVETH2 $IPVETH3
ip netns exec $NS1 netserver
echo 1 > ${TRACE_ROOT}/tracing_on
echo nocontext-info > ${TRACE_ROOT}/trace_options
DST_MAC=$(lookup_mac $VETH1 $NS1)
SRC_MAC=$(lookup_mac $VETH0)
DST_IFINDEX=$(cat /sys/class/net/$VETH0/ifindex)
CLANG_OPTS="-O2 --target=bpf -I ../include/"
CLANG_OPTS+=" -DSRC_MAC=$SRC_MAC -DDST_MAC=$DST_MAC -DDST_IFINDEX=$DST_IFINDEX"
clang $CLANG_OPTS -c $PROG_SRC -o $BPF_PROG
test_ctx_xmit
test_ctx_out
test_ctx_in
test_data "xmit"
test_data "out"
test_data_in
test_cb "xmit"
test_cb "out"
test_cb_in
test_drop_all "xmit"
test_drop_all "out"
test_drop_all_in
test_rewrite
test_push_ll_and_redirect
test_no_l2_and_redirect
test_fill_garbage
test_netperf_nop
test_netperf_redirect
cleanup
echo 0 > ${TRACE_ROOT}/tracing_on
echo $CONTEXT_INFO > ${TRACE_ROOT}/trace_options
exit 0
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2017 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#define KBUILD_MODNAME "foo"
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#define MAX_NR_PORTS 65536
#define EINVAL 22
#define ENOENT 2
/* map #0 */
struct inner_a {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, int);
__uint(max_entries, MAX_NR_PORTS);
} port_a SEC(".maps");
/* map #1 */
struct inner_h {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, int);
__uint(max_entries, 1);
} port_h SEC(".maps");
/* map #2 */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, int);
__uint(max_entries, 1);
} reg_result_h SEC(".maps");
/* map #3 */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, int);
__uint(max_entries, 1);
} inline_result_h SEC(".maps");
/* map #4 */ /* Test case #0 */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
__uint(max_entries, MAX_NR_PORTS);
__uint(key_size, sizeof(u32));
__array(values, struct inner_a); /* use inner_a as inner map */
} a_of_port_a SEC(".maps");
/* map #5 */ /* Test case #1 */
struct {
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
__uint(max_entries, 1);
__uint(key_size, sizeof(u32));
__array(values, struct inner_a); /* use inner_a as inner map */
} h_of_port_a SEC(".maps");
/* map #6 */ /* Test case #2 */
struct {
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
__uint(max_entries, 1);
__uint(key_size, sizeof(u32));
__array(values, struct inner_h); /* use inner_h as inner map */
} h_of_port_h SEC(".maps");
static __always_inline int do_reg_lookup(void *inner_map, u32 port)
{
int *result;
result = bpf_map_lookup_elem(inner_map, &port);
return result ? *result : -ENOENT;
}
static __always_inline int do_inline_array_lookup(void *inner_map, u32 port)
{
int *result;
if (inner_map != &port_a)
return -EINVAL;
result = bpf_map_lookup_elem(&port_a, &port);
return result ? *result : -ENOENT;
}
static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port)
{
int *result;
if (inner_map != &port_h)
return -EINVAL;
result = bpf_map_lookup_elem(&port_h, &port);
return result ? *result : -ENOENT;
}
SEC("ksyscall/connect")
int BPF_KSYSCALL(trace_sys_connect, unsigned int fd, struct sockaddr_in6 *in6, int addrlen)
{
u16 test_case, port, dst6[8];
int ret, inline_ret, ret_key = 0;
u32 port_key;
void *outer_map, *inner_map;
bool inline_hash = false;
if (addrlen != sizeof(*in6))
return 0;
ret = bpf_probe_read_user(dst6, sizeof(dst6), &in6->sin6_addr);
if (ret) {
inline_ret = ret;
goto done;
}
if (dst6[0] != 0xdead || dst6[1] != 0xbeef)
return 0;
test_case = dst6[7];
ret = bpf_probe_read_user(&port, sizeof(port), &in6->sin6_port);
if (ret) {
inline_ret = ret;
goto done;
}
port_key = port;
ret = -ENOENT;
if (test_case == 0) {
outer_map = &a_of_port_a;
} else if (test_case == 1) {
outer_map = &h_of_port_a;
} else if (test_case == 2) {
outer_map = &h_of_port_h;
} else {
ret = __LINE__;
inline_ret = ret;
goto done;
}
inner_map = bpf_map_lookup_elem(outer_map, &port_key);
if (!inner_map) {
ret = __LINE__;
inline_ret = ret;
goto done;
}
ret = do_reg_lookup(inner_map, port_key);
if (test_case == 0 || test_case == 1)
inline_ret = do_inline_array_lookup(inner_map, port_key);
else
inline_ret = do_inline_hash_lookup(inner_map, port_key);
done:
bpf_map_update_elem(&reg_result_h, &ret_key, &ret, BPF_ANY);
bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Facebook
*/
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_util.h"
static int map_fd[7];
#define PORT_A (map_fd[0])
#define PORT_H (map_fd[1])
#define REG_RESULT_H (map_fd[2])
#define INLINE_RESULT_H (map_fd[3])
#define A_OF_PORT_A (map_fd[4]) /* Test case #0 */
#define H_OF_PORT_A (map_fd[5]) /* Test case #1 */
#define H_OF_PORT_H (map_fd[6]) /* Test case #2 */
static const char * const test_names[] = {
"Array of Array",
"Hash of Array",
"Hash of Hash",
};
#define NR_TESTS ARRAY_SIZE(test_names)
static void check_map_id(int inner_map_fd, int map_in_map_fd, uint32_t key)
{
struct bpf_map_info info = {};
uint32_t info_len = sizeof(info);
int ret, id;
ret = bpf_map_get_info_by_fd(inner_map_fd, &info, &info_len);
assert(!ret);
ret = bpf_map_lookup_elem(map_in_map_fd, &key, &id);
assert(!ret);
assert(id == info.id);
}
static void populate_map(uint32_t port_key, int magic_result)
{
int ret;
ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY);
assert(!ret);
ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result,
BPF_NOEXIST);
assert(!ret);
ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY);
assert(!ret);
check_map_id(PORT_A, A_OF_PORT_A, port_key);
ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST);
assert(!ret);
check_map_id(PORT_A, H_OF_PORT_A, port_key);
ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST);
assert(!ret);
check_map_id(PORT_H, H_OF_PORT_H, port_key);
}
static void test_map_in_map(void)
{
struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 };
uint32_t result_key = 0, port_key;
int result, inline_result;
int magic_result = 0xfaceb00c;
int ret;
int i;
port_key = rand() & 0x00FF;
populate_map(port_key, magic_result);
in6.sin6_addr.s6_addr16[0] = 0xdead;
in6.sin6_addr.s6_addr16[1] = 0xbeef;
in6.sin6_port = port_key;
for (i = 0; i < NR_TESTS; i++) {
printf("%s: ", test_names[i]);
in6.sin6_addr.s6_addr16[7] = i;
ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6));
assert(ret == -1 && errno == EBADF);
ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result);
assert(!ret);
ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key,
&inline_result);
assert(!ret);
if (result != magic_result || inline_result != magic_result) {
printf("Error. result:%d inline_result:%d\n",
result, inline_result);
exit(1);
}
bpf_map_delete_elem(REG_RESULT_H, &result_key);
bpf_map_delete_elem(INLINE_RESULT_H, &result_key);
printf("Pass\n");
}
}
int main(int argc, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "trace_sys_connect");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "port_a");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "port_h");
map_fd[2] = bpf_object__find_map_fd_by_name(obj, "reg_result_h");
map_fd[3] = bpf_object__find_map_fd_by_name(obj, "inline_result_h");
map_fd[4] = bpf_object__find_map_fd_by_name(obj, "a_of_port_a");
map_fd[5] = bpf_object__find_map_fd_by_name(obj, "h_of_port_a");
map_fd[6] = bpf_object__find_map_fd_by_name(obj, "h_of_port_h");
if (map_fd[0] < 0 || map_fd[1] < 0 || map_fd[2] < 0 ||
map_fd[3] < 0 || map_fd[4] < 0 || map_fd[5] < 0 || map_fd[6] < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
test_map_in_map();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
@@ -0,0 +1,79 @@
/* Copyright (c) 2016 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <linux/ptrace.h>
#include <uapi/linux/bpf.h>
#include <uapi/linux/bpf_perf_event.h>
#include <uapi/linux/perf_event.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct key_t {
char comm[TASK_COMM_LEN];
u32 kernstack;
u32 userstack;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct key_t);
__type(value, u64);
__uint(max_entries, 10000);
} counts SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
__uint(max_entries, 10000);
} stackmap SEC(".maps");
#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK)
SEC("perf_event")
int bpf_prog1(struct bpf_perf_event_data *ctx)
{
char time_fmt1[] = "Time Enabled: %llu, Time Running: %llu";
char time_fmt2[] = "Get Time Failed, ErrCode: %d";
char addr_fmt[] = "Address recorded on event: %llx";
char fmt[] = "CPU-%d period %lld ip %llx";
u32 cpu = bpf_get_smp_processor_id();
struct bpf_perf_event_value value_buf;
struct key_t key;
u64 *val, one = 1;
int ret;
if (ctx->sample_period < 10000)
/* ignore warmup */
return 0;
bpf_get_current_comm(&key.comm, sizeof(key.comm));
key.kernstack = bpf_get_stackid(ctx, &stackmap, KERN_STACKID_FLAGS);
key.userstack = bpf_get_stackid(ctx, &stackmap, USER_STACKID_FLAGS);
if ((int)key.kernstack < 0 && (int)key.userstack < 0) {
bpf_trace_printk(fmt, sizeof(fmt), cpu, ctx->sample_period,
PT_REGS_IP(&ctx->regs));
return 0;
}
ret = bpf_perf_prog_read_value(ctx, (void *)&value_buf, sizeof(struct bpf_perf_event_value));
if (!ret)
bpf_trace_printk(time_fmt1, sizeof(time_fmt1), value_buf.enabled, value_buf.running);
else
bpf_trace_printk(time_fmt2, sizeof(time_fmt2), ret);
if (ctx->addr != 0)
bpf_trace_printk(addr_fmt, sizeof(addr_fmt), ctx->addr);
val = bpf_map_lookup_elem(&counts, &key);
if (val)
(*val)++;
else
bpf_map_update_elem(&counts, &key, &one, BPF_NOEXIST);
return 0;
}
char _license[] SEC("license") = "GPL";
+352
View File
@@ -0,0 +1,352 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016 Facebook
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <linux/perf_event.h>
#include <linux/bpf.h>
#include <signal.h>
#include <errno.h>
#include <sys/resource.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "perf-sys.h"
#include "trace_helpers.h"
#define SAMPLE_FREQ 50
static int pid;
/* counts, stackmap */
static int map_fd[2];
struct bpf_program *prog;
static bool sys_read_seen, sys_write_seen;
static void print_ksym(__u64 addr)
{
struct ksym *sym;
if (!addr)
return;
sym = ksym_search(addr);
if (!sym) {
printf("ksym not found. Is kallsyms loaded?\n");
return;
}
printf("%s;", sym->name);
if (!strstr(sym->name, "sys_read"))
sys_read_seen = true;
else if (!strstr(sym->name, "sys_write"))
sys_write_seen = true;
}
static void print_addr(__u64 addr)
{
if (!addr)
return;
printf("%llx;", addr);
}
#define TASK_COMM_LEN 16
struct key_t {
char comm[TASK_COMM_LEN];
__u32 kernstack;
__u32 userstack;
};
static void print_stack(struct key_t *key, __u64 count)
{
__u64 ip[PERF_MAX_STACK_DEPTH] = {};
static bool warned;
int i;
printf("%3lld %s;", count, key->comm);
if (bpf_map_lookup_elem(map_fd[1], &key->kernstack, ip) != 0) {
printf("---;");
} else {
for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
print_ksym(ip[i]);
}
printf("-;");
if (bpf_map_lookup_elem(map_fd[1], &key->userstack, ip) != 0) {
printf("---;");
} else {
for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
print_addr(ip[i]);
}
if (count < 6)
printf("\r");
else
printf("\n");
if (key->kernstack == -EEXIST && !warned) {
printf("stackmap collisions seen. Consider increasing size\n");
warned = true;
} else if ((int)key->kernstack < 0 && (int)key->userstack < 0) {
printf("err stackid %d %d\n", key->kernstack, key->userstack);
}
}
static void err_exit(int err)
{
kill(pid, SIGKILL);
exit(err);
}
static void print_stacks(void)
{
struct key_t key = {}, next_key;
__u64 value;
__u32 stackid = 0, next_id;
int error = 1, fd = map_fd[0], stack_map = map_fd[1];
sys_read_seen = sys_write_seen = false;
while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(fd, &next_key, &value);
print_stack(&next_key, value);
bpf_map_delete_elem(fd, &next_key);
key = next_key;
}
printf("\n");
if (!sys_read_seen || !sys_write_seen) {
printf("BUG kernel stack doesn't contain sys_read() and sys_write()\n");
err_exit(error);
}
/* clear stack map */
while (bpf_map_get_next_key(stack_map, &stackid, &next_id) == 0) {
bpf_map_delete_elem(stack_map, &next_id);
stackid = next_id;
}
}
static inline int generate_load(void)
{
if (system("dd if=/dev/zero of=/dev/null count=5000k status=none") < 0) {
printf("failed to generate some load with dd: %s\n", strerror(errno));
return -1;
}
return 0;
}
static void test_perf_event_all_cpu(struct perf_event_attr *attr)
{
int nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
struct bpf_link **links = calloc(nr_cpus, sizeof(struct bpf_link *));
int i, pmu_fd, error = 1;
if (!links) {
printf("malloc of links failed\n");
goto err;
}
/* system wide perf event, no need to inherit */
attr->inherit = 0;
/* open perf_event on all cpus */
for (i = 0; i < nr_cpus; i++) {
pmu_fd = sys_perf_event_open(attr, -1, i, -1, 0);
if (pmu_fd < 0) {
printf("sys_perf_event_open failed\n");
goto all_cpu_err;
}
links[i] = bpf_program__attach_perf_event(prog, pmu_fd);
if (libbpf_get_error(links[i])) {
printf("bpf_program__attach_perf_event failed\n");
links[i] = NULL;
close(pmu_fd);
goto all_cpu_err;
}
}
if (generate_load() < 0)
goto all_cpu_err;
print_stacks();
error = 0;
all_cpu_err:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
err:
free(links);
if (error)
err_exit(error);
}
static void test_perf_event_task(struct perf_event_attr *attr)
{
struct bpf_link *link = NULL;
int pmu_fd, error = 1;
/* per task perf event, enable inherit so the "dd ..." command can be traced properly.
* Enabling inherit will cause bpf_perf_prog_read_time helper failure.
*/
attr->inherit = 1;
/* open task bound event */
pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0);
if (pmu_fd < 0) {
printf("sys_perf_event_open failed\n");
goto err;
}
link = bpf_program__attach_perf_event(prog, pmu_fd);
if (libbpf_get_error(link)) {
printf("bpf_program__attach_perf_event failed\n");
link = NULL;
close(pmu_fd);
goto err;
}
if (generate_load() < 0)
goto err;
print_stacks();
error = 0;
err:
bpf_link__destroy(link);
if (error)
err_exit(error);
}
static void test_bpf_perf_event(void)
{
struct perf_event_attr attr_type_hw = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_CPU_CYCLES,
};
struct perf_event_attr attr_type_sw = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_CPU_CLOCK,
};
struct perf_event_attr attr_hw_cache_l1d = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_HW_CACHE,
.config =
PERF_COUNT_HW_CACHE_L1D |
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
};
struct perf_event_attr attr_hw_cache_branch_miss = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_HW_CACHE,
.config =
PERF_COUNT_HW_CACHE_BPU |
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
};
struct perf_event_attr attr_type_raw = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_RAW,
/* Intel Instruction Retired */
.config = 0xc0,
};
struct perf_event_attr attr_type_raw_lock_load = {
.sample_freq = SAMPLE_FREQ,
.freq = 1,
.type = PERF_TYPE_RAW,
/* Intel MEM_UOPS_RETIRED.LOCK_LOADS */
.config = 0x21d0,
/* Request to record lock address from PEBS */
.sample_type = PERF_SAMPLE_ADDR,
/* Record address value requires precise event */
.precise_ip = 2,
};
printf("Test HW_CPU_CYCLES\n");
test_perf_event_all_cpu(&attr_type_hw);
test_perf_event_task(&attr_type_hw);
printf("Test SW_CPU_CLOCK\n");
test_perf_event_all_cpu(&attr_type_sw);
test_perf_event_task(&attr_type_sw);
printf("Test HW_CACHE_L1D\n");
test_perf_event_all_cpu(&attr_hw_cache_l1d);
test_perf_event_task(&attr_hw_cache_l1d);
printf("Test HW_CACHE_BPU\n");
test_perf_event_all_cpu(&attr_hw_cache_branch_miss);
test_perf_event_task(&attr_hw_cache_branch_miss);
printf("Test Instruction Retired\n");
test_perf_event_all_cpu(&attr_type_raw);
test_perf_event_task(&attr_type_raw);
printf("Test Lock Load\n");
test_perf_event_all_cpu(&attr_type_raw_lock_load);
test_perf_event_task(&attr_type_raw_lock_load);
printf("*** PASS ***\n");
}
int main(int argc, char **argv)
{
struct bpf_object *obj = NULL;
char filename[256];
int error = 1;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
signal(SIGINT, err_exit);
signal(SIGTERM, err_exit);
if (load_kallsyms()) {
printf("failed to process /proc/kallsyms\n");
goto cleanup;
}
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
printf("opening BPF object file failed\n");
obj = NULL;
goto cleanup;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
printf("loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counts");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "stackmap");
if (map_fd[0] < 0 || map_fd[1] < 0) {
printf("finding a counts/stackmap map in obj file failed\n");
goto cleanup;
}
pid = fork();
if (pid == 0) {
read_trace_pipe();
return 0;
} else if (pid == -1) {
printf("couldn't spawn process\n");
goto cleanup;
}
test_bpf_perf_event();
error = 0;
cleanup:
bpf_object__close(obj);
err_exit(error);
}
@@ -0,0 +1,29 @@
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(u32));
__uint(max_entries, 2);
} my_map SEC(".maps");
SEC("ksyscall/write")
int bpf_prog1(struct pt_regs *ctx)
{
struct S {
u64 pid;
u64 cookie;
} data;
data.pid = bpf_get_current_pid_tgid();
data.cookie = 0x12345678;
bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+105
View File
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdio.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <signal.h>
#include <bpf/libbpf.h>
static __u64 time_get_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000ull + ts.tv_nsec;
}
static __u64 start_time;
static __u64 cnt;
#define MAX_CNT 100000ll
static void print_bpf_output(void *ctx, int cpu, void *data, __u32 size)
{
struct {
__u64 pid;
__u64 cookie;
} *e = data;
if (e->cookie != 0x12345678) {
printf("BUG pid %llx cookie %llx sized %d\n",
e->pid, e->cookie, size);
return;
}
cnt++;
if (cnt == MAX_CNT) {
printf("recv %lld events per sec\n",
MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
return;
}
}
int main(int argc, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct perf_buffer *pb;
struct bpf_object *obj;
int map_fd, ret = 0;
char filename[256];
FILE *f;
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (libbpf_get_error(prog)) {
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
pb = perf_buffer__new(map_fd, 8, print_bpf_output, NULL, NULL, NULL);
ret = libbpf_get_error(pb);
if (ret) {
printf("failed to setup perf_buffer: %d\n", ret);
return 1;
}
f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");
(void) f;
start_time = time_get_ns();
while ((ret = perf_buffer__poll(pb, 1000)) >= 0 && cnt < MAX_CNT) {
}
kill(0, SIGINT);
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return ret;
}
+47
View File
@@ -0,0 +1,47 @@
/* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include "net_shared.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
/* kprobe is NOT a stable ABI
* kernel functions can be removed, renamed or completely change semantics.
* Number of arguments and their positions can change, etc.
* In such case this bpf+kprobe example will no longer be meaningful
*/
SEC("kprobe.multi/__netif_receive_skb_core*")
int bpf_prog1(struct pt_regs *ctx)
{
/* attaches to kprobe __netif_receive_skb_core,
* looks for packets on loopback device and prints them
* (wildcard is used for avoiding symbol mismatch due to optimization)
*/
char devname[IFNAMSIZ];
struct net_device *dev;
struct sk_buff *skb;
int len;
bpf_core_read(&skb, sizeof(skb), (void *)PT_REGS_PARM1(ctx));
dev = BPF_CORE_READ(skb, dev);
len = BPF_CORE_READ(skb, len);
BPF_CORE_READ_STR_INTO(&devname, dev, name);
if (devname[0] == 'l' && devname[1] == 'o') {
char fmt[] = "skb %p len %d\n";
/* using bpf_trace_printk() for DEBUG ONLY */
bpf_trace_printk(fmt, sizeof(fmt), skb, len);
}
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+50
View File
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include "trace_helpers.h"
int main(int ac, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
FILE *f;
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (!prog) {
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
f = popen("taskset 1 ping -c5 localhost", "r");
(void) f;
read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
+100
View File
@@ -0,0 +1,100 @@
/* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct start_key {
dev_t dev;
u32 _pad;
sector_t sector;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, long);
__type(value, u64);
__uint(max_entries, 4096);
} my_map SEC(".maps");
/* from /sys/kernel/tracing/events/block/block_io_start/format */
SEC("tracepoint/block/block_io_start")
int bpf_prog1(struct trace_event_raw_block_rq *ctx)
{
u64 val = bpf_ktime_get_ns();
struct start_key key = {
.dev = ctx->dev,
.sector = ctx->sector
};
bpf_map_update_elem(&my_map, &key, &val, BPF_ANY);
return 0;
}
static unsigned int log2l(unsigned long long n)
{
#define S(k) if (n >= (1ull << k)) { i += k; n >>= k; }
int i = -(n == 0);
S(32); S(16); S(8); S(4); S(2); S(1);
return i;
#undef S
}
#define SLOTS 100
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u64));
__uint(max_entries, SLOTS);
} lat_map SEC(".maps");
/* from /sys/kernel/tracing/events/block/block_io_done/format */
SEC("tracepoint/block/block_io_done")
int bpf_prog2(struct trace_event_raw_block_rq *ctx)
{
struct start_key key = {
.dev = ctx->dev,
.sector = ctx->sector
};
u64 *value, l, base;
u32 index;
value = bpf_map_lookup_elem(&my_map, &key);
if (!value)
return 0;
u64 cur_time = bpf_ktime_get_ns();
u64 delta = cur_time - *value;
bpf_map_delete_elem(&my_map, &key);
/* the lines below are computing index = log10(delta)*10
* using integer arithmetic
* index = 29 ~ 1 usec
* index = 59 ~ 1 msec
* index = 89 ~ 1 sec
* index = 99 ~ 10sec or more
* log10(x)*10 = log2(x)*10/log2(10) = log2(x)*3
*/
l = log2l(delta);
base = 1ll << l;
index = (l * 64 + (delta - base) * 64 / base) * 3 / 64;
if (index >= SLOTS)
index = SLOTS - 1;
value = bpf_map_lookup_elem(&lat_map, &index);
if (value)
*value += 1;
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+183
View File
@@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_util.h"
#define SLOTS 100
static void clear_stats(int fd)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
__u64 values[nr_cpus];
__u32 key;
memset(values, 0, sizeof(values));
for (key = 0; key < SLOTS; key++)
bpf_map_update_elem(fd, &key, values, BPF_ANY);
}
const char *color[] = {
"\033[48;5;255m",
"\033[48;5;252m",
"\033[48;5;250m",
"\033[48;5;248m",
"\033[48;5;246m",
"\033[48;5;244m",
"\033[48;5;242m",
"\033[48;5;240m",
"\033[48;5;238m",
"\033[48;5;236m",
"\033[48;5;234m",
"\033[48;5;232m",
};
const int num_colors = ARRAY_SIZE(color);
const char nocolor[] = "\033[00m";
const char *sym[] = {
" ",
" ",
".",
".",
"*",
"*",
"o",
"o",
"O",
"O",
"#",
"#",
};
bool full_range = false;
bool text_only = false;
static void print_banner(void)
{
if (full_range)
printf("|1ns |10ns |100ns |1us |10us |100us"
" |1ms |10ms |100ms |1s |10s\n");
else
printf("|1us |10us |100us |1ms |10ms "
"|100ms |1s |10s\n");
}
static void print_hist(int fd)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
__u64 total_events = 0;
long values[nr_cpus];
__u64 max_cnt = 0;
__u64 cnt[SLOTS];
__u64 value;
__u32 key;
int i;
for (key = 0; key < SLOTS; key++) {
bpf_map_lookup_elem(fd, &key, values);
value = 0;
for (i = 0; i < nr_cpus; i++)
value += values[i];
cnt[key] = value;
total_events += value;
if (value > max_cnt)
max_cnt = value;
}
clear_stats(fd);
for (key = full_range ? 0 : 29; key < SLOTS; key++) {
int c = num_colors * cnt[key] / (max_cnt + 1);
if (text_only)
printf("%s", sym[c]);
else
printf("%s %s", color[c], nocolor);
}
printf(" # %lld\n", total_events);
}
int main(int ac, char **argv)
{
struct bpf_link *links[2];
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int map_fd, i, j = 0;
for (i = 1; i < ac; i++) {
if (strcmp(argv[i], "-a") == 0) {
full_range = true;
} else if (strcmp(argv[i], "-t") == 0) {
text_only = true;
} else if (strcmp(argv[i], "-h") == 0) {
printf("Usage:\n"
" -a display wider latency range\n"
" -t text only\n");
return 1;
}
}
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "lat_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[j] = bpf_program__attach(prog);
if (libbpf_get_error(links[j])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[j] = NULL;
goto cleanup;
}
j++;
}
printf(" heatmap of IO latency\n");
if (text_only)
printf(" %s", sym[num_colors - 1]);
else
printf(" %s %s", color[num_colors - 1], nocolor);
printf(" - many events with this latency\n");
if (text_only)
printf(" %s", sym[0]);
else
printf(" %s %s", color[0], nocolor);
printf(" - few events\n");
for (i = 0; ; i++) {
if (i % 20 == 0)
print_banner();
print_hist(map_fd);
sleep(2);
}
cleanup:
for (j--; j >= 0; j--)
bpf_link__destroy(links[j]);
bpf_object__close(obj);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct pair {
u64 val;
u64 ip;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, long);
__type(value, struct pair);
__uint(max_entries, 1000000);
} my_map SEC(".maps");
/* kprobe is NOT a stable ABI. If kernel internals change this bpf+kprobe
* example will no longer be meaningful
*/
SEC("kprobe/kmem_cache_free")
int bpf_prog1(struct pt_regs *ctx)
{
long ptr = PT_REGS_PARM2(ctx);
bpf_map_delete_elem(&my_map, &ptr);
return 0;
}
SEC("kretprobe/kmem_cache_alloc_node_noprof")
int bpf_prog2(struct pt_regs *ctx)
{
long ptr = PT_REGS_RC(ctx);
long ip = 0;
/* get ip address of kmem_cache_alloc_node_noprof() caller */
BPF_KRETPROBE_READ_RET_IP(ip, ctx);
struct pair v = {
.val = bpf_ktime_get_ns(),
.ip = ip,
};
bpf_map_update_elem(&my_map, &ptr, &v, BPF_ANY);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+96
View File
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
struct pair {
long long val;
__u64 ip;
};
static __u64 time_get_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000ull + ts.tv_nsec;
}
static void print_old_objects(int fd)
{
long long val = time_get_ns();
__u64 key, next_key;
struct pair v;
key = write(1, "\e[1;1H\e[2J", 11); /* clear screen */
key = -1;
while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
bpf_map_lookup_elem(fd, &next_key, &v);
key = next_key;
if (val - v.val < 1000000000ll)
/* object was allocated more then 1 sec ago */
continue;
printf("obj 0x%llx is %2lldsec old was allocated at ip %llx\n",
next_key, (val - v.val) / 1000000000ll, v.ip);
}
}
int main(int ac, char **argv)
{
struct bpf_link *links[2];
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int map_fd, j = 0;
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
if (map_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[j] = bpf_program__attach(prog);
if (libbpf_get_error(links[j])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[j] = NULL;
goto cleanup;
}
j++;
}
while (1) {
print_old_objects(map_fd);
sleep(1);
}
cleanup:
for (j--; j >= 0; j--)
bpf_link__destroy(links[j]);
bpf_object__close(obj);
return 0;
}
+93
View File
@@ -0,0 +1,93 @@
/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include "vmlinux.h"
#include "syscall_nrs.h"
#include <linux/version.h>
#include <uapi/linux/unistd.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#define __stringify(x) #x
#define PROG(F) SEC("kprobe/"__stringify(F)) int bpf_func_##F
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
#ifdef __mips__
__uint(max_entries, 6000); /* MIPS n64 syscalls start at 5000 */
#else
__uint(max_entries, 1024);
#endif
} progs SEC(".maps");
SEC("kprobe/__seccomp_filter")
int bpf_prog1(struct pt_regs *ctx)
{
int sc_nr = (int)PT_REGS_PARM1(ctx);
/* dispatch into next BPF program depending on syscall number */
bpf_tail_call(ctx, &progs, sc_nr);
/* fall through -> unknown syscall */
if (sc_nr >= __NR_getuid && sc_nr <= __NR_getsid) {
char fmt[] = "syscall=%d (one of get/set uid/pid/gid)\n";
bpf_trace_printk(fmt, sizeof(fmt), sc_nr);
}
return 0;
}
/* we jump here when syscall number == __NR_write */
PROG(SYS__NR_write)(struct pt_regs *ctx)
{
struct seccomp_data sd;
bpf_core_read(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] == 512) {
char fmt[] = "write(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
}
return 0;
}
PROG(SYS__NR_read)(struct pt_regs *ctx)
{
struct seccomp_data sd;
bpf_core_read(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] > 128 && sd.args[2] <= 1024) {
char fmt[] = "read(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
}
return 0;
}
#ifdef __NR_mmap2
PROG(SYS__NR_mmap2)(struct pt_regs *ctx)
{
char fmt[] = "mmap2\n";
bpf_trace_printk(fmt, sizeof(fmt));
return 0;
}
#endif
#ifdef __NR_mmap
PROG(SYS__NR_mmap)(struct pt_regs *ctx)
{
char fmt[] = "mmap\n";
bpf_trace_printk(fmt, sizeof(fmt));
return 0;
}
#endif
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+98
View File
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "trace_helpers.h"
#include "bpf_util.h"
#ifdef __mips__
#define MAX_ENTRIES 6000 /* MIPS n64 syscalls start at 5000 */
#else
#define MAX_ENTRIES 1024
#endif
/* install fake seccomp program to enable seccomp code path inside the kernel,
* so that our kprobe attached to seccomp_phase1() can be triggered
*/
static void install_accept_all_seccomp(void)
{
struct sock_filter filter[] = {
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short)ARRAY_SIZE(filter),
.filter = filter,
};
if (prctl(PR_SET_SECCOMP, 2, &prog))
perror("prctl");
}
int main(int ac, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
int key, fd, progs_fd;
const char *section;
char filename[256];
FILE *f;
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
progs_fd = bpf_object__find_map_fd_by_name(obj, "progs");
if (progs_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
section = bpf_program__section_name(prog);
/* register only syscalls to PROG_ARRAY */
if (sscanf(section, "kprobe/%d", &key) != 1)
continue;
fd = bpf_program__fd(prog);
bpf_map_update_elem(progs_fd, &key, &fd, BPF_ANY);
}
install_accept_all_seccomp();
f = popen("dd if=/dev/zero of=/dev/null count=5", "r");
(void) f;
read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
+81
View File
@@ -0,0 +1,81 @@
#include "vmlinux.h"
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(u32));
__uint(max_entries, 64);
} counters SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, int);
__type(value, u64);
__uint(max_entries, 64);
} values SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, int);
__type(value, struct bpf_perf_event_value);
__uint(max_entries, 64);
} values2 SEC(".maps");
SEC("kprobe/htab_map_get_next_key")
int bpf_prog1(struct pt_regs *ctx)
{
u32 key = bpf_get_smp_processor_id();
u64 count, *val;
s64 error;
count = bpf_perf_event_read(&counters, key);
error = (s64)count;
if (error <= -2 && error >= -22)
return 0;
val = bpf_map_lookup_elem(&values, &key);
if (val)
*val = count;
else
bpf_map_update_elem(&values, &key, &count, BPF_NOEXIST);
return 0;
}
/*
* Since *_map_lookup_elem can't be expected to trigger bpf programs
* due to potential deadlocks (bpf_disable_instrumentation), this bpf
* program will be attached to bpf_map_copy_value (which is called
* from map_lookup_elem) and will only filter the hashtable type.
*/
SEC("kprobe/bpf_map_copy_value")
int BPF_KPROBE(bpf_prog2, struct bpf_map *map)
{
u32 key = bpf_get_smp_processor_id();
struct bpf_perf_event_value *val, buf;
enum bpf_map_type type;
int error;
type = BPF_CORE_READ(map, map_type);
if (type != BPF_MAP_TYPE_HASH)
return 0;
error = bpf_perf_event_read_value(&counters, key, &buf, sizeof(buf));
if (error)
return 0;
val = bpf_map_lookup_elem(&values2, &key);
if (val)
*val = buf;
else
bpf_map_update_elem(&values2, &key, &buf, BPF_NOEXIST);
return 0;
}
char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
+222
View File
@@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/perf_event.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "perf-sys.h"
#define SAMPLE_PERIOD 0x7fffffffffffffffULL
/* counters, values, values2 */
static int map_fd[3];
static void check_on_cpu(int cpu, struct perf_event_attr *attr)
{
struct bpf_perf_event_value value2;
int pmu_fd, error = 0;
cpu_set_t set;
__u64 value;
/* Move to target CPU */
CPU_ZERO(&set);
CPU_SET(cpu, &set);
assert(sched_setaffinity(0, sizeof(set), &set) == 0);
/* Open perf event and attach to the perf_event_array */
pmu_fd = sys_perf_event_open(attr, -1/*pid*/, cpu/*cpu*/, -1/*group_fd*/, 0);
if (pmu_fd < 0) {
fprintf(stderr, "sys_perf_event_open failed on CPU %d\n", cpu);
error = 1;
goto on_exit;
}
assert(bpf_map_update_elem(map_fd[0], &cpu, &pmu_fd, BPF_ANY) == 0);
assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0) == 0);
/* Trigger the kprobe */
bpf_map_get_next_key(map_fd[1], &cpu, NULL);
/* Check the value */
if (bpf_map_lookup_elem(map_fd[1], &cpu, &value)) {
fprintf(stderr, "Value missing for CPU %d\n", cpu);
error = 1;
goto on_exit;
} else {
fprintf(stderr, "CPU %d: %llu\n", cpu, value);
}
/* The above bpf_map_lookup_elem should trigger the second kprobe */
if (bpf_map_lookup_elem(map_fd[2], &cpu, &value2)) {
fprintf(stderr, "Value2 missing for CPU %d\n", cpu);
error = 1;
goto on_exit;
} else {
fprintf(stderr, "CPU %d: counter: %llu, enabled: %llu, running: %llu\n", cpu,
value2.counter, value2.enabled, value2.running);
}
on_exit:
assert(bpf_map_delete_elem(map_fd[0], &cpu) == 0 || error);
assert(ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE, 0) == 0 || error);
assert(close(pmu_fd) == 0 || error);
assert(bpf_map_delete_elem(map_fd[1], &cpu) == 0 || error);
exit(error);
}
static void test_perf_event_array(struct perf_event_attr *attr,
const char *name)
{
int i, status, nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
pid_t pid[nr_cpus];
int err = 0;
printf("Test reading %s counters\n", name);
for (i = 0; i < nr_cpus; i++) {
pid[i] = fork();
assert(pid[i] >= 0);
if (pid[i] == 0) {
check_on_cpu(i, attr);
exit(1);
}
}
for (i = 0; i < nr_cpus; i++) {
assert(waitpid(pid[i], &status, 0) == pid[i]);
err |= status;
}
if (err)
printf("Test: %s FAILED\n", name);
}
static void test_bpf_perf_event(void)
{
struct perf_event_attr attr_cycles = {
.freq = 0,
.sample_period = SAMPLE_PERIOD,
.inherit = 0,
.type = PERF_TYPE_HARDWARE,
.read_format = 0,
.sample_type = 0,
.config = PERF_COUNT_HW_CPU_CYCLES,
};
struct perf_event_attr attr_clock = {
.freq = 0,
.sample_period = SAMPLE_PERIOD,
.inherit = 0,
.type = PERF_TYPE_SOFTWARE,
.read_format = 0,
.sample_type = 0,
.config = PERF_COUNT_SW_CPU_CLOCK,
};
struct perf_event_attr attr_raw = {
.freq = 0,
.sample_period = SAMPLE_PERIOD,
.inherit = 0,
.type = PERF_TYPE_RAW,
.read_format = 0,
.sample_type = 0,
/* Intel Instruction Retired */
.config = 0xc0,
};
struct perf_event_attr attr_l1d_load = {
.freq = 0,
.sample_period = SAMPLE_PERIOD,
.inherit = 0,
.type = PERF_TYPE_HW_CACHE,
.read_format = 0,
.sample_type = 0,
.config =
PERF_COUNT_HW_CACHE_L1D |
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
};
struct perf_event_attr attr_llc_miss = {
.freq = 0,
.sample_period = SAMPLE_PERIOD,
.inherit = 0,
.type = PERF_TYPE_HW_CACHE,
.read_format = 0,
.sample_type = 0,
.config =
PERF_COUNT_HW_CACHE_LL |
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
};
struct perf_event_attr attr_msr_tsc = {
.freq = 0,
.sample_period = 0,
.inherit = 0,
/* From /sys/bus/event_source/devices/msr/ */
.type = 7,
.read_format = 0,
.sample_type = 0,
.config = 0,
};
test_perf_event_array(&attr_cycles, "HARDWARE-cycles");
test_perf_event_array(&attr_clock, "SOFTWARE-clock");
test_perf_event_array(&attr_raw, "RAW-instruction-retired");
test_perf_event_array(&attr_l1d_load, "HW_CACHE-L1D-load");
/* below tests may fail in qemu */
test_perf_event_array(&attr_llc_miss, "HW_CACHE-LLC-miss");
test_perf_event_array(&attr_msr_tsc, "Dynamic-msr-tsc");
}
int main(int argc, char **argv)
{
struct bpf_link *links[2];
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256];
int i = 0;
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counters");
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "values");
map_fd[2] = bpf_object__find_map_fd_by_name(obj, "values2");
if (map_fd[0] < 0 || map_fd[1] < 0 || map_fd[2] < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
links[i] = bpf_program__attach(prog);
if (libbpf_get_error(links[i])) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
links[i] = NULL;
goto cleanup;
}
i++;
}
test_bpf_perf_event();
cleanup:
for (i--; i >= 0; i--)
bpf_link__destroy(links[i]);
bpf_object__close(obj);
return 0;
}
+220
View File
@@ -0,0 +1,220 @@
#!/bin/bash
#
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
#
# Bash-shell example on using iproute2 tools 'tc' and 'ip' to load
# eBPF programs, both for XDP and clsbpf. Shell script function
# wrappers and even long options parsing is illustrated, for ease of
# use.
#
# Related to sample/bpf/xdp2skb_meta_kern.c, which contains BPF-progs
# that need to collaborate between XDP and TC hooks. Thus, it is
# convenient that the same tool load both programs that need to work
# together.
#
BPF_FILE=xdp2skb_meta_kern.o
DIR=$(dirname $0)
[ -z "$TC" ] && TC=tc
[ -z "$IP" ] && IP=ip
function usage() {
echo ""
echo "Usage: $0 [-vfh] --dev ethX"
echo " -d | --dev : Network device (required)"
echo " --flush : Cleanup flush TC and XDP progs"
echo " --list : (\$LIST) List TC and XDP progs"
echo " -v | --verbose : (\$VERBOSE) Verbose"
echo " --dry-run : (\$DRYRUN) Dry-run only (echo commands)"
echo ""
}
## -- General shell logging cmds --
function err() {
local exitcode=$1
shift
echo "ERROR: $@" >&2
exit $exitcode
}
function info() {
if [[ -n "$VERBOSE" ]]; then
echo "# $@"
fi
}
## -- Helper function calls --
# Wrapper call for TC and IP
# - Will display the offending command on failure
function _call_cmd() {
local cmd="$1"
local allow_fail="$2"
shift 2
if [[ -n "$VERBOSE" ]]; then
echo "$cmd $@"
fi
if [[ -n "$DRYRUN" ]]; then
return
fi
$cmd "$@"
local status=$?
if (( $status != 0 )); then
if [[ "$allow_fail" == "" ]]; then
err 2 "Exec error($status) occurred cmd: \"$cmd $@\""
fi
fi
}
function call_tc() {
_call_cmd "$TC" "" "$@"
}
function call_tc_allow_fail() {
_call_cmd "$TC" "allow_fail" "$@"
}
function call_ip() {
_call_cmd "$IP" "" "$@"
}
## --- Parse command line arguments / parameters ---
# Using external program "getopt" to get --long-options
OPTIONS=$(getopt -o vfhd: \
--long verbose,flush,help,list,dev:,dry-run -- "$@")
if (( $? != 0 )); then
err 4 "Error calling getopt"
fi
eval set -- "$OPTIONS"
unset DEV
unset FLUSH
while true; do
case "$1" in
-d | --dev ) # device
DEV=$2
info "Device set to: DEV=$DEV" >&2
shift 2
;;
-v | --verbose)
VERBOSE=yes
# info "Verbose mode: VERBOSE=$VERBOSE" >&2
shift
;;
--dry-run )
DRYRUN=yes
VERBOSE=yes
info "Dry-run mode: enable VERBOSE and don't call TC+IP" >&2
shift
;;
-f | --flush )
FLUSH=yes
shift
;;
--list )
LIST=yes
shift
;;
-- )
shift
break
;;
-h | --help )
usage;
exit 0
;;
* )
shift
break
;;
esac
done
FILE="$DIR/$BPF_FILE"
if [[ ! -e $FILE ]]; then
err 3 "Missing BPF object file ($FILE)"
fi
if [[ -z $DEV ]]; then
usage
err 2 "Please specify network device -- required option --dev"
fi
## -- Function calls --
function list_tc()
{
local device="$1"
shift
info "Listing current TC ingress rules"
call_tc filter show dev $device ingress
}
function list_xdp()
{
local device="$1"
shift
info "Listing current XDP device($device) setting"
call_ip link show dev $device | grep --color=auto xdp
}
function flush_tc()
{
local device="$1"
shift
info "Flush TC on device: $device"
call_tc_allow_fail filter del dev $device ingress
call_tc_allow_fail qdisc del dev $device clsact
}
function flush_xdp()
{
local device="$1"
shift
info "Flush XDP on device: $device"
call_ip link set dev $device xdp off
}
function attach_tc_mark()
{
local device="$1"
local file="$2"
local prog="tc_mark"
shift 2
# Re-attach clsact to clear/flush existing role
call_tc_allow_fail qdisc del dev $device clsact 2> /dev/null
call_tc qdisc add dev $device clsact
# Attach BPF prog
call_tc filter add dev $device ingress \
prio 1 handle 1 bpf da obj $file sec $prog
}
function attach_xdp_mark()
{
local device="$1"
local file="$2"
local prog="xdp_mark"
shift 2
# Remove XDP prog in-case it's already loaded
# TODO: Need ip-link option to override/replace existing XDP prog
flush_xdp $device
# Attach XDP/BPF prog
call_ip link set dev $device xdp obj $file sec $prog
}
if [[ -n $FLUSH ]]; then
flush_tc $DEV
flush_xdp $DEV
exit 0
fi
if [[ -n $LIST ]]; then
list_tc $DEV
list_xdp $DEV
exit 0
fi
attach_tc_mark $DEV $FILE
attach_xdp_mark $DEV $FILE
+104
View File
@@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
*
* Example howto transfer info from XDP to SKB, e.g. skb->mark
* -----------------------------------------------------------
* This uses the XDP data_meta infrastructure, and is a cooperation
* between two bpf-programs (1) XDP and (2) clsact at TC-ingress hook.
*
* Notice: This example does not use the BPF C-loader,
* but instead rely on the iproute2 TC tool for loading BPF-objects.
*/
#include <uapi/linux/bpf.h>
#include <uapi/linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
/*
* This struct is stored in the XDP 'data_meta' area, which is located
* just in-front-of the raw packet payload data. The meaning is
* specific to these two BPF programs that use it as a communication
* channel. XDP adjust/increase the area via a bpf-helper, and TC use
* boundary checks to see if data have been provided.
*
* The struct must be 4 byte aligned, which here is enforced by the
* struct __attribute__((aligned(4))).
*/
struct meta_info {
__u32 mark;
} __attribute__((aligned(4)));
SEC("xdp_mark")
int _xdp_mark(struct xdp_md *ctx)
{
struct meta_info *meta;
void *data;
int ret;
/* Reserve space in-front of data pointer for our meta info.
* (Notice drivers not supporting data_meta will fail here!)
*/
ret = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta));
if (ret < 0)
return XDP_ABORTED;
/* Notice: Kernel-side verifier requires that loading of
* ctx->data MUST happen _after_ helper bpf_xdp_adjust_meta(),
* as pkt-data pointers are invalidated. Helpers that require
* this are determined/marked by bpf_helper_changes_pkt_data()
*/
data = (void *)(unsigned long)ctx->data;
/* Check data_meta have room for meta_info struct */
meta = (void *)(unsigned long)ctx->data_meta;
if (meta + 1 > data)
return XDP_ABORTED;
meta->mark = 42;
return XDP_PASS;
}
SEC("tc_mark")
int _tc_mark(struct __sk_buff *ctx)
{
void *data = (void *)(unsigned long)ctx->data;
void *data_meta = (void *)(unsigned long)ctx->data_meta;
struct meta_info *meta = data_meta;
/* Check XDP gave us some data_meta */
if (meta + 1 > data) {
ctx->mark = 41;
/* Skip "accept" if no data_meta is avail */
return TC_ACT_OK;
}
/* Hint: See func tc_cls_act_is_valid_access() for BPF_WRITE access */
ctx->mark = meta->mark; /* Transfer XDP-mark to SKB-mark */
return TC_ACT_OK;
}
/* Manually attaching these programs:
export DEV=ixgbe2
export FILE=xdp2skb_meta_kern.o
# via TC command
tc qdisc del dev $DEV clsact 2> /dev/null
tc qdisc add dev $DEV clsact
tc filter add dev $DEV ingress prio 1 handle 1 bpf da obj $FILE sec tc_mark
tc filter show dev $DEV ingress
# XDP via IP command:
ip link set dev $DEV xdp off
ip link set dev $DEV xdp obj $FILE sec xdp_mark
# Use iptable to "see" if SKBs are marked
iptables -I INPUT -p icmp -m mark --mark 41 # == 0x29
iptables -I INPUT -p icmp -m mark --mark 42 # == 0x2a
# Hint: catch XDP_ABORTED errors via
perf record -e xdp:*
perf script
*/
@@ -0,0 +1,156 @@
/* SPDX-License-Identifier: GPL-2.0
* Copyright (c) 2018 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program shows how to use bpf_xdp_adjust_tail() by
* generating ICMPv4 "packet to big" (unreachable/ df bit set frag needed
* to be more preice in case of v4)" where receiving packets bigger then
* 600 bytes.
*/
#define KBUILD_MODNAME "foo"
#include <uapi/linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <bpf/bpf_helpers.h>
#define DEFAULT_TTL 64
#define MAX_PCKT_SIZE 600
#define ICMP_TOOBIG_SIZE 98
#define ICMP_TOOBIG_PAYLOAD_SIZE 92
/* volatile to prevent compiler optimizations */
static volatile __u32 max_pcktsz = MAX_PCKT_SIZE;
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} icmpcnt SEC(".maps");
static __always_inline void count_icmp(void)
{
u64 key = 0;
u64 *icmp_count;
icmp_count = bpf_map_lookup_elem(&icmpcnt, &key);
if (icmp_count)
*icmp_count += 1;
}
static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth)
{
struct ethhdr *eth;
eth = data;
memcpy(eth->h_source, orig_eth->h_dest, ETH_ALEN);
memcpy(eth->h_dest, orig_eth->h_source, ETH_ALEN);
eth->h_proto = orig_eth->h_proto;
}
static __always_inline __u16 csum_fold_helper(__u32 csum)
{
csum = (csum & 0xffff) + (csum >> 16);
return ~((csum & 0xffff) + (csum >> 16));
}
static __always_inline void ipv4_csum(void *data_start, int data_size,
__u32 *csum)
{
*csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
*csum = csum_fold_helper(*csum);
}
static __always_inline int send_icmp4_too_big(struct xdp_md *xdp)
{
int headroom = (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr);
if (bpf_xdp_adjust_head(xdp, 0 - headroom))
return XDP_DROP;
void *data = (void *)(long)xdp->data;
void *data_end = (void *)(long)xdp->data_end;
if (data + (ICMP_TOOBIG_SIZE + headroom) > data_end)
return XDP_DROP;
struct iphdr *iph, *orig_iph;
struct icmphdr *icmp_hdr;
struct ethhdr *orig_eth;
__u32 csum = 0;
__u64 off = 0;
orig_eth = data + headroom;
swap_mac(data, orig_eth);
off += sizeof(struct ethhdr);
iph = data + off;
off += sizeof(struct iphdr);
icmp_hdr = data + off;
off += sizeof(struct icmphdr);
orig_iph = data + off;
icmp_hdr->type = ICMP_DEST_UNREACH;
icmp_hdr->code = ICMP_FRAG_NEEDED;
icmp_hdr->un.frag.mtu = htons(max_pcktsz - sizeof(struct ethhdr));
icmp_hdr->checksum = 0;
ipv4_csum(icmp_hdr, ICMP_TOOBIG_PAYLOAD_SIZE, &csum);
icmp_hdr->checksum = csum;
iph->ttl = DEFAULT_TTL;
iph->daddr = orig_iph->saddr;
iph->saddr = orig_iph->daddr;
iph->version = 4;
iph->ihl = 5;
iph->protocol = IPPROTO_ICMP;
iph->tos = 0;
iph->tot_len = htons(
ICMP_TOOBIG_SIZE + headroom - sizeof(struct ethhdr));
iph->check = 0;
csum = 0;
ipv4_csum(iph, sizeof(struct iphdr), &csum);
iph->check = csum;
count_icmp();
return XDP_TX;
}
static __always_inline int handle_ipv4(struct xdp_md *xdp)
{
void *data_end = (void *)(long)xdp->data_end;
void *data = (void *)(long)xdp->data;
int pckt_size = data_end - data;
int offset;
if (pckt_size > max(max_pcktsz, ICMP_TOOBIG_SIZE)) {
offset = pckt_size - ICMP_TOOBIG_SIZE;
if (bpf_xdp_adjust_tail(xdp, 0 - offset))
return XDP_PASS;
return send_icmp4_too_big(xdp);
}
return XDP_PASS;
}
SEC("xdp_icmp")
int _xdp_icmp(struct xdp_md *xdp)
{
void *data_end = (void *)(long)xdp->data_end;
void *data = (void *)(long)xdp->data;
struct ethhdr *eth = data;
__u16 h_proto;
if (eth + 1 > data_end)
return XDP_DROP;
h_proto = eth->h_proto;
if (h_proto == htons(ETH_P_IP))
return handle_ipv4(xdp);
else
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";

Some files were not shown because too many files have changed in this diff Show More