Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71bb428f authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

tools: bpf: add bpftool



Add a simple tool for querying and updating BPF objects on the system.

Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: default avatarSimon Horman <simon.horman@netronome.com>
Acked-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a92bb546
Loading
Loading
Loading
Loading
+15 −3
Original line number Original line Diff line number Diff line
@@ -3,6 +3,7 @@ prefix = /usr
CC = gcc
CC = gcc
LEX = flex
LEX = flex
YACC = bison
YACC = bison
MAKE = make


CFLAGS += -Wall -O2
CFLAGS += -Wall -O2
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
@@ -13,7 +14,7 @@ CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
%.lex.c: %.l
%.lex.c: %.l
	$(LEX) -o $@ $<
	$(LEX) -o $@ $<


all : bpf_jit_disasm bpf_dbg bpf_asm
all: bpf_jit_disasm bpf_dbg bpf_asm bpftool


bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl
@@ -26,10 +27,21 @@ bpf_asm : LDLIBS =
bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o
bpf_exp.lex.o : bpf_exp.yacc.c
bpf_exp.lex.o : bpf_exp.yacc.c


clean :
clean: bpftool_clean
	rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*
	rm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*


install :
install: bpftool_install
	install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
	install bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm
	install bpf_dbg $(prefix)/bin/bpf_dbg
	install bpf_dbg $(prefix)/bin/bpf_dbg
	install bpf_asm $(prefix)/bin/bpf_asm
	install bpf_asm $(prefix)/bin/bpf_asm

bpftool:
	$(MAKE) -C bpftool

bpftool_install:
	$(MAKE) -C bpftool install

bpftool_clean:
	$(MAKE) -C bpftool clean

.PHONY: bpftool FORCE
+80 −0
Original line number Original line Diff line number Diff line
include ../../scripts/Makefile.include

include ../../scripts/utilities.mak

ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
#$(info Determined 'srctree' to be $(srctree))
endif

ifneq ($(objtree),)
#$(info Determined 'objtree' to be $(objtree))
endif

ifneq ($(OUTPUT),)
#$(info Determined 'OUTPUT' to be $(OUTPUT))
# Adding $(OUTPUT) as a directory to look for source files,
# because use generated output files as sources dependency
# for flex/bison parsers.
VPATH += $(OUTPUT)
export VPATH
endif

ifeq ($(V),1)
  Q =
else
  Q = @
endif

BPF_DIR	= $(srctree)/tools/lib/bpf/

ifneq ($(OUTPUT),)
  BPF_PATH=$(OUTPUT)
else
  BPF_PATH=$(BPF_DIR)
endif

LIBBPF = $(BPF_PATH)libbpf.a

$(LIBBPF): FORCE
	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)

$(LIBBPF)-clean:
	$(call QUIET_CLEAN, libbpf)
	$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null

prefix = /usr

CC = gcc

CFLAGS += -O2
CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow
CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf
LIBS = -lelf -lbfd -lopcodes $(LIBBPF)

include $(wildcard *.d)

all: $(OUTPUT)bpftool

SRCS=$(wildcard *.c)
OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS))

$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
	$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)

$(OUTPUT)%.o: %.c
	$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<

clean: $(LIBBPF)-clean
	$(call QUIET_CLEAN, bpftool)
	$(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d

install:
	install $(OUTPUT)bpftool $(prefix)/sbin/bpftool

FORCE:

.PHONY: all clean FORCE
.DEFAULT_GOAL := all
+216 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 Netronome Systems, Inc.
 *
 * This software is dual licensed under the GNU General License Version 2,
 * June 1991 as shown in the file COPYING in the top-level directory of this
 * source tree or the BSD 2-Clause License provided below.  You have the
 * option to license this software under the complete terms of either license.
 *
 * The BSD 2-Clause License:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      1. Redistributions of source code must retain the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer.
 *
 *      2. Redistributions in binary form must reproduce the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer in the documentation and/or other materials
 *         provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* Author: Jakub Kicinski <kubakici@wp.pl> */

#include <errno.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/limits.h>
#include <linux/magic.h>
#include <sys/types.h>
#include <sys/vfs.h>

#include <bpf.h>

#include "main.h"

static bool is_bpffs(char *path)
{
	struct statfs st_fs;

	if (statfs(path, &st_fs) < 0)
		return false;

	return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
}

int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
{
	enum bpf_obj_type type;
	int fd;

	fd = bpf_obj_get(path);
	if (fd < 0) {
		err("bpf obj get (%s): %s\n", path,
		    errno == EACCES && !is_bpffs(dirname(path)) ?
		    "directory not in bpf file system (bpffs)" :
		    strerror(errno));
		return -1;
	}

	type = get_fd_type(fd);
	if (type < 0) {
		close(fd);
		return type;
	}
	if (type != exp_type) {
		err("incorrect object type: %s\n", get_fd_type_name(type));
		close(fd);
		return -1;
	}

	return fd;
}

int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
{
	unsigned int id;
	char *endptr;
	int err;
	int fd;

	if (!is_prefix(*argv, "id")) {
		err("expected 'id' got %s\n", *argv);
		return -1;
	}
	NEXT_ARG();

	id = strtoul(*argv, &endptr, 0);
	if (*endptr) {
		err("can't parse %s as ID\n", *argv);
		return -1;
	}
	NEXT_ARG();

	if (argc != 1)
		usage();

	fd = get_fd_by_id(id);
	if (fd < 0) {
		err("can't get prog by id (%u): %s\n", id, strerror(errno));
		return -1;
	}

	err = bpf_obj_pin(fd, *argv);
	close(fd);
	if (err) {
		err("can't pin the object (%s): %s\n", *argv,
		    errno == EACCES && !is_bpffs(dirname(*argv)) ?
		    "directory not in bpf file system (bpffs)" :
		    strerror(errno));
		return -1;
	}

	return 0;
}

const char *get_fd_type_name(enum bpf_obj_type type)
{
	static const char * const names[] = {
		[BPF_OBJ_UNKNOWN]	= "unknown",
		[BPF_OBJ_PROG]		= "prog",
		[BPF_OBJ_MAP]		= "map",
	};

	if (type < 0 || type >= ARRAY_SIZE(names) || !names[type])
		return names[BPF_OBJ_UNKNOWN];

	return names[type];
}

int get_fd_type(int fd)
{
	char path[PATH_MAX];
	char buf[512];
	ssize_t n;

	snprintf(path, sizeof(path), "/proc/%d/fd/%d", getpid(), fd);

	n = readlink(path, buf, sizeof(buf));
	if (n < 0) {
		err("can't read link type: %s\n", strerror(errno));
		return -1;
	}
	if (n == sizeof(path)) {
		err("can't read link type: path too long!\n");
		return -1;
	}

	if (strstr(buf, "bpf-map"))
		return BPF_OBJ_MAP;
	else if (strstr(buf, "bpf-prog"))
		return BPF_OBJ_PROG;

	return BPF_OBJ_UNKNOWN;
}

char *get_fdinfo(int fd, const char *key)
{
	char path[PATH_MAX];
	char *line = NULL;
	size_t line_n = 0;
	ssize_t n;
	FILE *fdi;

	snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd);

	fdi = fopen(path, "r");
	if (!fdi) {
		err("can't open fdinfo: %s\n", strerror(errno));
		return NULL;
	}

	while ((n = getline(&line, &line_n, fdi))) {
		char *value;
		int len;

		if (!strstr(line, key))
			continue;

		fclose(fdi);

		value = strchr(line, '\t');
		if (!value || !value[1]) {
			err("malformed fdinfo!?\n");
			free(line);
			return NULL;
		}
		value++;

		len = strlen(value);
		memmove(line, value, len);
		line[len - 1] = '\0';

		return line;
	}

	err("key '%s' not found in fdinfo\n", key);
	free(line);
	fclose(fdi);
	return NULL;
}
+87 −0
Original line number Original line Diff line number Diff line
/*
 * Based on:
 *
 * Minimal BPF JIT image disassembler
 *
 * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
 * debugging or verification purposes.
 *
 * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>
 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <bfd.h>
#include <dis-asm.h>
#include <sys/types.h>
#include <sys/stat.h>

static void get_exec_path(char *tpath, size_t size)
{
	ssize_t len;
	char *path;

	snprintf(tpath, size, "/proc/%d/exe", (int) getpid());
	tpath[size - 1] = 0;

	path = strdup(tpath);
	assert(path);

	len = readlink(path, tpath, size - 1);
	assert(len > 0);
	tpath[len] = 0;

	free(path);
}

void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
{
	disassembler_ftype disassemble;
	struct disassemble_info info;
	int count, i, pc = 0;
	char tpath[256];
	bfd *bfdf;

	if (!len)
		return;

	memset(tpath, 0, sizeof(tpath));
	get_exec_path(tpath, sizeof(tpath));

	bfdf = bfd_openr(tpath, NULL);
	assert(bfdf);
	assert(bfd_check_format(bfdf, bfd_object));

	init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
	info.arch = bfd_get_arch(bfdf);
	info.mach = bfd_get_mach(bfdf);
	info.buffer = image;
	info.buffer_length = len;

	disassemble_init_for_target(&info);

	disassemble = disassembler(bfdf);
	assert(disassemble);

	do {
		printf("%4x:\t", pc);

		count = disassemble(pc, &info);

		if (opcodes) {
			printf("\n\t");
			for (i = 0; i < count; ++i)
				printf("%02x ", (uint8_t) image[pc + i]);
		}
		printf("\n");

		pc += count;
	} while (count > 0 && pc < len);

	bfd_close(bfdf);
}
+212 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 Netronome Systems, Inc.
 *
 * This software is dual licensed under the GNU General License Version 2,
 * June 1991 as shown in the file COPYING in the top-level directory of this
 * source tree or the BSD 2-Clause License provided below.  You have the
 * option to license this software under the complete terms of either license.
 *
 * The BSD 2-Clause License:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      1. Redistributions of source code must retain the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer.
 *
 *      2. Redistributions in binary form must reproduce the above
 *         copyright notice, this list of conditions and the following
 *         disclaimer in the documentation and/or other materials
 *         provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* Author: Jakub Kicinski <kubakici@wp.pl> */

#include <bfd.h>
#include <ctype.h>
#include <errno.h>
#include <linux/bpf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <bpf.h>

#include "main.h"

const char *bin_name;
static int last_argc;
static char **last_argv;
static int (*last_do_help)(int argc, char **argv);

void usage(void)
{
	last_do_help(last_argc - 1, last_argv + 1);

	exit(-1);
}

static int do_help(int argc, char **argv)
{
	fprintf(stderr,
		"Usage: %s OBJECT { COMMAND | help }\n"
		"       %s batch file FILE\n"
		"\n"
		"       OBJECT := { prog | map }\n",
		bin_name, bin_name);

	return 0;
}

int cmd_select(const struct cmd *cmds, int argc, char **argv,
	       int (*help)(int argc, char **argv))
{
	unsigned int i;

	last_argc = argc;
	last_argv = argv;
	last_do_help = help;

	if (argc < 1 && cmds[0].func)
		return cmds[0].func(argc, argv);

	for (i = 0; cmds[i].func; i++)
		if (is_prefix(*argv, cmds[i].cmd))
			return cmds[i].func(argc - 1, argv + 1);

	help(argc - 1, argv + 1);

	return -1;
}

bool is_prefix(const char *pfx, const char *str)
{
	if (!pfx)
		return false;
	if (strlen(str) < strlen(pfx))
		return false;

	return !memcmp(str, pfx, strlen(pfx));
}

void print_hex(void *arg, unsigned int n, const char *sep)
{
	unsigned char *data = arg;
	unsigned int i;

	for (i = 0; i < n; i++) {
		const char *pfx = "";

		if (!i)
			/* nothing */;
		else if (!(i % 16))
			printf("\n");
		else if (!(i % 8))
			printf("  ");
		else
			pfx = sep;

		printf("%s%02hhx", i ? pfx : "", data[i]);
	}
}

static int do_batch(int argc, char **argv);

static const struct cmd cmds[] = {
	{ "help",	do_help },
	{ "batch",	do_batch },
	{ "prog",	do_prog },
	{ "map",	do_map },
	{ 0 }
};

static int do_batch(int argc, char **argv)
{
	unsigned int lines = 0;
	char *n_argv[4096];
	char buf[65536];
	int n_argc;
	FILE *fp;
	int err;

	if (argc < 2) {
		err("too few parameters for batch\n");
		return -1;
	} else if (!is_prefix(*argv, "file")) {
		err("expected 'file', got: %s\n", *argv);
		return -1;
	} else if (argc > 2) {
		err("too many parameters for batch\n");
		return -1;
	}
	NEXT_ARG();

	fp = fopen(*argv, "r");
	if (!fp) {
		err("Can't open file (%s): %s\n", *argv, strerror(errno));
		return -1;
	}

	while (fgets(buf, sizeof(buf), fp)) {
		if (strlen(buf) == sizeof(buf) - 1) {
			errno = E2BIG;
			break;
		}

		n_argc = 0;
		n_argv[n_argc] = strtok(buf, " \t\n");

		while (n_argv[n_argc]) {
			n_argc++;
			if (n_argc == ARRAY_SIZE(n_argv)) {
				err("line %d has too many arguments, skip\n",
				    lines);
				n_argc = 0;
				break;
			}
			n_argv[n_argc] = strtok(NULL, " \t\n");
		}

		if (!n_argc)
			continue;

		err = cmd_select(cmds, n_argc, n_argv, do_help);
		if (err)
			goto err_close;

		lines++;
	}

	if (errno && errno != ENOENT) {
		perror("reading batch file failed");
		err = -1;
	} else {
		info("processed %d lines\n", lines);
		err = 0;
	}
err_close:
	fclose(fp);

	return err;
}

int main(int argc, char **argv)
{
	bin_name = argv[0];
	NEXT_ARG();

	bfd_init();

	return cmd_select(cmds, argc, argv, do_help);
}
Loading