Skip to contentSkip to conversation

BPF updates

PATCH Applied Daniel Borkmann 2 participants 5 messages Jul 17, 2017 – Jul 19, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover BPF updates

Couple of misc updates related to BPF. First removes a BPF sample that is long legacy (pre BPF fs times), then we add support for the loader to be able to take care of map in map, and last but not least we dump id and whether prog was jited in tc filter show for cls/act BPF programs.

The set is intended for -master branch _after_ the -net-next branch got merged into -master branch.

Thanks!

Daniel Borkmann (3):
bpf: remove obsolete samples
bpf: support loading map in map from obj
bpf: dump id/jited info for cls/act programs

 examples/bpf/bpf_agent.c      | 258 ----------------------
 examples/bpf/bpf_map_in_map.c |  56 +++++
 examples/bpf/bpf_prog.c       | 501 ------------------------------------------
 examples/bpf/bpf_shared.h     |  22 --
 examples/bpf/bpf_sys.h        |  23 --
 include/bpf_elf.h             |   2 +
 include/bpf_util.h            |   2 +
 lib/bpf.c                     | 205 ++++++++++++++++-
 tc/f_bpf.c                    |   3 +
 tc/m_bpf.c                    |   3 +
 10 files changed, 261 insertions(+), 814 deletions(-)
 delete mode 100644 examples/bpf/bpf_agent.c
 create mode 100644 examples/bpf/bpf_map_in_map.c
 delete mode 100644 examples/bpf/bpf_prog.c
 delete mode 100644 examples/bpf/bpf_shared.h
 delete mode 100644 examples/bpf/bpf_sys.h
Signature · 1 line
1.9.3
#1 lore raw

PATCH 1/3 bpf: remove obsolete samples

Remove old samples that have been added in pre BPF fs days which were using file descriptor passing. It's long obsolete and not encouraged to use this method given BPF fs is the default way like in the other samples.

---
 examples/bpf/bpf_agent.c  | 258 ------------------------
 examples/bpf/bpf_prog.c   | 501 ----------------------------------------------
 examples/bpf/bpf_shared.h |  22 --
 examples/bpf/bpf_sys.h    |  23 ---
 4 files changed, 804 deletions(-)
 delete mode 100644 examples/bpf/bpf_agent.c
 delete mode 100644 examples/bpf/bpf_prog.c
 delete mode 100644 examples/bpf/bpf_shared.h
 delete mode 100644 examples/bpf/bpf_sys.h
examples/bpf/bpf_agent.c deleted +0 -258
deleted file mode 100644
index f9b9ce3..0000000
@@ -1,258 +0,0 @@
-/*
- * eBPF user space agent part
- *
- * Simple, _self-contained_ user space agent for the eBPF kernel
- * ebpf_prog.c program, which gets all map fds passed from tc via unix
- * domain socket in one transaction and can thus keep referencing
- * them from user space in order to read out (or possibly modify)
- * map data. Here, just as a minimal example to display counters.
- *
- * The agent only uses the bpf(2) syscall API to read or possibly
- * write to eBPF maps, it doesn't need to be aware of the low-level
- * bytecode parts and/or ELF parsing bits.
- *
- * ! For more details, see header comment in bpf_prog.c !
- *
- * gcc bpf_agent.c -o bpf_agent -Wall -O2
- *
- * For example, a more complex user space agent could run on each
- * host, reading and writing into eBPF maps used by tc classifier
- * and actions. It would thus allow for implementing a distributed
- * tc architecture, for example, which would push down central
- * policies into eBPF maps, and thus altering run-time behaviour.
- *
- *   -- Happy eBPF hacking! ;)
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <assert.h>
-
-#include <sys/un.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-
-/* Just some misc macros as min(), offsetof(), etc. */
-#include "../../include/utils.h"
-/* Common code from fd passing. */
-#include "../../include/bpf_scm.h"
-/* Common, shared definitions with ebpf_prog.c */
-#include "bpf_shared.h"
-/* Mini syscall wrapper */
-#include "bpf_sys.h"
-
-static void bpf_dump_drops(int fd)
-{
-	int cpu, max;
-
-	max = sysconf(_SC_NPROCESSORS_ONLN);
-
-	printf(" `- number of drops:");
-	for (cpu = 0; cpu < max; cpu++) {
-		long drops;
-
-		assert(bpf_lookup_elem(fd, &cpu, &drops) == 0);
-		printf("\tcpu%d: %5ld", cpu, drops);
-	}
-	printf("\n");
-}
-
-static void bpf_dump_queue(int fd)
-{
-	/* Just for the same of the example. */
-	int max_queue = 4, i;
-
-	printf("  | nic queues:");
-	for (i = 0; i < max_queue; i++) {
-		struct count_queue cq;
-		int ret;
-
-		memset(&cq, 0, sizeof(cq));
-		ret = bpf_lookup_elem(fd, &i, &cq);
-		assert(ret == 0 || (ret < 0 && errno == ENOENT));
-
-		printf("\tq%d:[pkts: %ld, mis: %ld]",
-		       i, cq.total, cq.mismatch);
-	}
-	printf("\n");
-}
-
-static void bpf_dump_proto(int fd)
-{
-	uint8_t protos[] = { IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP };
-	char *names[] = { "tcp", "udp", "icmp" };
-	int i;
-
-	printf("  ` protos:");
-	for (i = 0; i < ARRAY_SIZE(protos); i++) {
-		struct count_tuple ct;
-		int ret;
-
-		memset(&ct, 0, sizeof(ct));
-		ret = bpf_lookup_elem(fd, &protos[i], &ct);
-		assert(ret == 0 || (ret < 0 && errno == ENOENT));
-
-		printf("\t%s:[pkts: %ld, bytes: %ld]",
-		       names[i], ct.packets, ct.bytes);
-	}
-	printf("\n");
-}
-
-static void bpf_dump_map_data(int *tfd)
-{
-	int i;
-
-	for (i = 0; i < 30; i++) {
-		const int period = 5;
-
-		printf("data, period: %dsec\n", period);
-
-		bpf_dump_drops(tfd[BPF_MAP_ID_DROPS]);
-		bpf_dump_queue(tfd[BPF_MAP_ID_QUEUE]);
-		bpf_dump_proto(tfd[BPF_MAP_ID_PROTO]);
-
-		sleep(period);
-	}
-}
-
-static void bpf_info_loop(int *fds, struct bpf_map_aux *aux)
-{
-	int i, tfd[BPF_MAP_ID_MAX];
-
-	printf("ver: %d\nobj: %s\ndev: %lu\nino: %lu\nmaps: %u\n",
-	       aux->uds_ver, aux->obj_name, aux->obj_st.st_dev,
-	       aux->obj_st.st_ino, aux->num_ent);
-
-	for (i = 0; i < aux->num_ent; i++) {
-		printf("map%d:\n", i);
-		printf(" `- fd: %u\n", fds[i]);
-		printf("  | serial: %u\n", aux->ent[i].id);
-		printf("  | type: %u\n", aux->ent[i].type);
-		printf("  | max elem: %u\n", aux->ent[i].max_elem);
-		printf("  | size key: %u\n", aux->ent[i].size_key);
-		printf("  ` size val: %u\n", aux->ent[i].size_value);
-
-		tfd[aux->ent[i].id] = fds[i];
-	}
-
-	bpf_dump_map_data(tfd);
-}
-
-static void bpf_map_get_from_env(int *tfd)
-{
-	char key[64], *val;
-	int i;
-
-	for (i = 0; i < BPF_MAP_ID_MAX; i++) {
-		memset(key, 0, sizeof(key));
-		snprintf(key, sizeof(key), "BPF_MAP%d", i);
-
-		val = getenv(key);
-		assert(val != NULL);
-
-		tfd[i] = atoi(val);
-	}
-}
-
-static int bpf_map_set_recv(int fd, int *fds,  struct bpf_map_aux *aux,
-			    unsigned int entries)
-{
-	struct bpf_map_set_msg msg;
-	int *cmsg_buf, min_fd, i;
-	char *amsg_buf, *mmsg_buf;
-
-	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
-	amsg_buf = (char *)msg.aux.ent;
-	mmsg_buf = (char *)&msg.aux;
-
-	for (i = 0; i < entries; i += min_fd) {
-		struct cmsghdr *cmsg;
-		int ret;
-
-		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
-
-		bpf_map_set_init_single(&msg, min_fd);
-
-		ret = recvmsg(fd, &msg.hdr, 0);
-		if (ret <= 0)
-			return ret ? : -1;
-
-		cmsg = CMSG_FIRSTHDR(&msg.hdr);
-		if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS)
-			return -EINVAL;
-		if (msg.hdr.msg_flags & MSG_CTRUNC)
-			return -EIO;
-
-		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
-		if (min_fd > entries || min_fd <= 0)
-			return -1;
-
-		memcpy(&fds[i], cmsg_buf, sizeof(fds[0]) * min_fd);
-		memcpy(&aux->ent[i], amsg_buf, sizeof(aux->ent[0]) * min_fd);
-		memcpy(aux, mmsg_buf, offsetof(struct bpf_map_aux, ent));
-
-		if (i + min_fd == aux->num_ent)
-			break;
-	}
-
-	return 0;
-}
-
-int main(int argc, char **argv)
-{
-	int fds[BPF_SCM_MAX_FDS];
-	struct bpf_map_aux aux;
-	struct sockaddr_un addr;
-	int fd, ret, i;
-
-	/* When arguments are being passed, we take it as a path
-	 * to a Unix domain socket, otherwise we grab the fds
-	 * from the environment to demonstrate both possibilities.
-	 */
-	if (argc == 1) {
-		int tfd[BPF_MAP_ID_MAX];
-
-		bpf_map_get_from_env(tfd);
-		bpf_dump_map_data(tfd);
-
-		return 0;
-	}
-
-	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open socket: %s\n",
-			strerror(errno));
-		exit(1);
-	}
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sun_family = AF_UNIX;
-	strncpy(addr.sun_path, argv[argc - 1], sizeof(addr.sun_path));
-
-	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
-	if (ret < 0) {
-		fprintf(stderr, "Cannot bind to socket: %s\n",
-			strerror(errno));
-		exit(1);
-	}
-
-	memset(fds, 0, sizeof(fds));
-	memset(&aux, 0, sizeof(aux));
-
-	ret = bpf_map_set_recv(fd, fds, &aux, BPF_SCM_MAX_FDS);
-	if (ret >= 0)
-		bpf_info_loop(fds, &aux);
-
-	for (i = 0; i < aux.num_ent; i++)
-		close(fds[i]);
-
-	close(fd);
-	return 0;
-}
examples/bpf/bpf_prog.c deleted +0 -501

501 lines not on this page · raw message

examples/bpf/bpf_shared.h deleted +0 -22
deleted file mode 100644
index a24038d..0000000
@@ -1,22 +0,0 @@
-#ifndef __BPF_SHARED__
-#define __BPF_SHARED__
-
-enum {
-	BPF_MAP_ID_PROTO,
-	BPF_MAP_ID_QUEUE,
-	BPF_MAP_ID_DROPS,
-	__BPF_MAP_ID_MAX,
-#define BPF_MAP_ID_MAX	__BPF_MAP_ID_MAX
-};
-
-struct count_tuple {
-	long packets; /* type long for lock_xadd() */
-	long bytes;
-};
-
-struct count_queue {
-	long total;
-	long mismatch;
-};
-
-#endif /* __BPF_SHARED__ */
examples/bpf/bpf_sys.h deleted +0 -23
deleted file mode 100644
index 6e4f09e..0000000
@@ -1,23 +0,0 @@
-#ifndef __BPF_SYS__
-#define __BPF_SYS__
-
-#include <sys/syscall.h>
-#include <linux/bpf.h>
-
-static inline __u64 bpf_ptr_to_u64(const void *ptr)
-{
-	return (__u64) (unsigned long) ptr;
-}
-
-static inline int bpf_lookup_elem(int fd, void *key, void *value)
-{
-	union bpf_attr attr = {
-		.map_fd		= fd,
-		.key		= bpf_ptr_to_u64(key),
-		.value		= bpf_ptr_to_u64(value),
-	};
-
-	return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
-}
-
-#endif /* __BPF_SYS__ */
Signature · 1 line
1.9.3
#2 lore raw

PATCH 2/3 bpf: support loading map in map from obj

In reply to Daniel Borkmann

Add support for map in map in the loader and add a small example program. The outer map uses inner_id to reference a bpf_elf_map with a given ID as the inner type. Loading maps is done in three passes, i) all non-map in map maps are loaded, ii) all map in map maps are loaded based on the inner_id map spec of a non-map in map with corresponding id, and iii) related inner maps are attached to the map in map with given inner_idx key. Pinned objetcs are assumed to be managed externally, so they are only retrieved from BPF fs.

---
 examples/bpf/bpf_map_in_map.c |  56 +++++++++++++++
 include/bpf_elf.h             |   2 +
 lib/bpf.c                     | 157 +++++++++++++++++++++++++++++++++++++++---
 3 files changed, 205 insertions(+), 10 deletions(-)
 create mode 100644 examples/bpf/bpf_map_in_map.c
examples/bpf/bpf_map_in_map.c new file +56 -0
new file mode 100644
index 0000000..ff0e623
@@ -0,0 +1,56 @@
+#include "../../include/bpf_api.h"
+
+#define MAP_INNER_ID	42
+
+struct bpf_elf_map __section_maps map_inner = {
+	.type		= BPF_MAP_TYPE_ARRAY,
+	.size_key	= sizeof(uint32_t),
+	.size_value	= sizeof(uint32_t),
+	.id		= MAP_INNER_ID,
+	.inner_idx	= 0,
+	.pinning	= PIN_GLOBAL_NS,
+	.max_elem	= 1,
+};
+
+struct bpf_elf_map __section_maps map_outer = {
+	.type		= BPF_MAP_TYPE_ARRAY_OF_MAPS,
+	.size_key	= sizeof(uint32_t),
+	.size_value	= sizeof(uint32_t),
+	.inner_id	= MAP_INNER_ID,
+	.pinning	= PIN_GLOBAL_NS,
+	.max_elem	= 1,
+};
+
+__section("egress")
+int emain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			lock_xadd(val, 1);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+__section("ingress")
+int imain(struct __sk_buff *skb)
+{
+	struct bpf_elf_map *map_inner;
+	int key = 0, *val;
+
+	map_inner = map_lookup_elem(&map_outer, &key);
+	if (map_inner) {
+		val = map_lookup_elem(map_inner, &key);
+		if (val)
+			printt("map val: %d\n", *val);
+	}
+
+	return BPF_H_DEFAULT;
+}
+
+BPF_LICENSE("GPL");
include/bpf_elf.h +2 -0
index 239a0f3..406c308 100644
@@ -36,6 +36,8 @@ struct bpf_elf_map {
 	__u32 flags;
 	__u32 id;
 	__u32 pinning;
+	__u32 inner_id;
+	__u32 inner_idx;
 };
 
 #endif /* __BPF_ELF__ */
lib/bpf.c +147 -10

216 lines not on this page · raw message

Signature · 1 line
1.9.3
#3 lore raw

PATCH 3/3 bpf: dump id/jited info for cls/act programs

In reply to Daniel Borkmann

Make use of TCA_BPF_ID/TCA_ACT_BPF_ID that we exposed and print the ID of the programs loaded and use the new BPF_OBJ_GET_INFO_BY_FD command for dumping further information about the program, currently whether the attached program is jited.

---
 include/bpf_util.h |  2 ++
 lib/bpf.c          | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 tc/f_bpf.c         |  3 +++
 tc/m_bpf.c         |  3 +++
 4 files changed, 56 insertions(+)
include/bpf_util.h +2 -0
index 5361dab..6582ec8 100644
@@ -261,6 +261,8 @@ int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
 int bpf_prog_attach_fd(int prog_fd, int target_fd, enum bpf_attach_type type);
 int bpf_prog_detach_fd(int target_fd, enum bpf_attach_type type);
 
+void bpf_dump_prog_info(FILE *f, uint32_t id);
+
 #ifdef HAVE_ELF
 int bpf_send_map_fds(const char *path, const char *obj);
 int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
lib/bpf.c +48 -0

54 lines not on this page · raw message

tc/f_bpf.c +3 -0
index 75c44c0..2f8d12a 100644
@@ -230,6 +230,9 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
 				      b, sizeof(b)));
 	}
 
+	if (tb[TCA_BPF_ID])
+		bpf_dump_prog_info(f, rta_getattr_u32(tb[TCA_BPF_ID]));
+
 	if (tb[TCA_BPF_POLICE]) {
 		fprintf(f, "\n");
 		tc_print_police(f, tb[TCA_BPF_POLICE]);
tc/m_bpf.c +3 -0
index 5728303..df559bc 100644
@@ -186,6 +186,9 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
 				      b, sizeof(b)));
 	}
 
+        if (tb[TCA_ACT_BPF_ID])
+                bpf_dump_prog_info(f, rta_getattr_u32(tb[TCA_ACT_BPF_ID]));
+
 	print_action_control(f, "default-action ", parm->action, "\n");
 	fprintf(f, "\tindex %u ref %d bind %d", parm->index, parm->refcnt,
 		parm->bindcnt);
Signature · 1 line
1.9.3
#4 lore raw
In reply to Daniel Borkmann
Quoted 16 lines · On Mon, 17 Jul 2017 17:18:49 +0200 Daniel Borkmann <[email protected]> wrote:

Couple of misc updates related to BPF. First removes a BPF sample that is long legacy (pre BPF fs times), then we add support for the loader to be able to take care of map in map, and last but not least we dump id and whether prog was jited in tc filter show for cls/act BPF programs.

The set is intended for -master branch _after_ the -net-next branch got merged into -master branch.

Thanks!

Daniel Borkmann (3):
bpf: remove obsolete samples
bpf: support loading map in map from obj
bpf: dump id/jited info for cls/act programs

Quoted 17 lines
 examples/bpf/bpf_agent.c      | 258 ----------------------
 examples/bpf/bpf_map_in_map.c |  56 +++++
 examples/bpf/bpf_prog.c       | 501 ------------------------------------------
 examples/bpf/bpf_shared.h     |  22 --
 examples/bpf/bpf_sys.h        |  23 --
 include/bpf_elf.h             |   2 +
 include/bpf_util.h            |   2 +
 lib/bpf.c                     | 205 ++++++++++++++++-
 tc/f_bpf.c                    |   3 +
 tc/m_bpf.c                    |   3 +
 10 files changed, 261 insertions(+), 814 deletions(-)
 delete mode 100644 examples/bpf/bpf_agent.c
 create mode 100644 examples/bpf/bpf_map_in_map.c
 delete mode 100644 examples/bpf/bpf_prog.c
 delete mode 100644 examples/bpf/bpf_shared.h
 delete mode 100644 examples/bpf/bpf_sys.h

Applied.