Netdev List
 help / color / mirror / Atom feed
* [PATCH 10/14] ipvs: use IPVS_CMD_ATTR_MAX for family.maxattr
From: Pablo Neira Ayuso @ 2016-11-10  0:23 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1478737427-1574-1-git-send-email-pablo@netfilter.org>

From: WANG Cong <xiyou.wangcong@gmail.com>

family.maxattr is the max index for policy[], the size of
ops[] is determined with ARRAY_SIZE().

Reported-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Andrey Konovalov <andreyknvl@google.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipvs/ip_vs_ctl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index c3c809b2e712..a6e44ef2ec9a 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2845,7 +2845,7 @@ static struct genl_family ip_vs_genl_family = {
 	.hdrsize	= 0,
 	.name		= IPVS_GENL_NAME,
 	.version	= IPVS_GENL_VERSION,
-	.maxattr	= IPVS_CMD_MAX,
+	.maxattr	= IPVS_CMD_ATTR_MAX,
 	.netnsok        = true,         /* Make ipvsadm to work on netns */
 };
 
-- 
2.1.4


^ permalink raw reply related

* [PATCH 07/14] netfilter: ip_vs_sync: fix bogus maybe-uninitialized warning
From: Pablo Neira Ayuso @ 2016-11-10  0:23 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1478737427-1574-1-git-send-email-pablo@netfilter.org>

From: Arnd Bergmann <arnd@arndb.de>

Building the ip_vs_sync code with CONFIG_OPTIMIZE_INLINING on x86
confuses the compiler to the point where it produces a rather
dubious warning message:

net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘opt.init_seq’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
  struct ip_vs_sync_conn_options opt;
                                 ^~~
net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘opt.delta’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘opt.previous_delta’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘*((void *)&opt+12).init_seq’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘*((void *)&opt+12).delta’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
net/netfilter/ipvs/ip_vs_sync.c:1073:33: error: ‘*((void *)&opt+12).previous_delta’ may be used uninitialized in this function [-Werror=maybe-uninitialized]

The problem appears to be a combination of a number of factors, including
the __builtin_bswap32 compiler builtin being slightly odd, having a large
amount of code inlined into a single function, and the way that some
functions only get partially inlined here.

I've spent way too much time trying to work out a way to improve the
code, but the best I've come up with is to add an explicit memset
right before the ip_vs_seq structure is first initialized here. When
the compiler works correctly, this has absolutely no effect, but in the
case that produces the warning, the warning disappears.

In the process of analysing this warning, I also noticed that
we use memcpy to copy the larger ip_vs_sync_conn_options structure
over two members of the ip_vs_conn structure. This works because
the layout is identical, but seems error-prone, so I'm changing
this in the process to directly copy the two members. This change
seemed to have no effect on the object code or the warning, but
it deals with the same data, so I kept the two changes together.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipvs/ip_vs_sync.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 1b07578bedf3..9350530c16c1 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -283,6 +283,7 @@ struct ip_vs_sync_buff {
  */
 static void ntoh_seq(struct ip_vs_seq *no, struct ip_vs_seq *ho)
 {
+	memset(ho, 0, sizeof(*ho));
 	ho->init_seq       = get_unaligned_be32(&no->init_seq);
 	ho->delta          = get_unaligned_be32(&no->delta);
 	ho->previous_delta = get_unaligned_be32(&no->previous_delta);
@@ -917,8 +918,10 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa
 			kfree(param->pe_data);
 	}
 
-	if (opt)
-		memcpy(&cp->in_seq, opt, sizeof(*opt));
+	if (opt) {
+		cp->in_seq = opt->in_seq;
+		cp->out_seq = opt->out_seq;
+	}
 	atomic_set(&cp->in_pkts, sysctl_sync_threshold(ipvs));
 	cp->state = state;
 	cp->old_state = cp->state;
-- 
2.1.4


^ permalink raw reply related

* [PATCH 04/14] netfilter: nf_conntrack_sip: extend request line validation
From: Pablo Neira Ayuso @ 2016-11-10  0:23 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1478737427-1574-1-git-send-email-pablo@netfilter.org>

From: Ulrich Weber <ulrich.weber@riverbed.com>

on SIP requests, so a fragmented TCP SIP packet from an allow header starting with
 INVITE,NOTIFY,OPTIONS,REFER,REGISTER,UPDATE,SUBSCRIBE
 Content-Length: 0

will not bet interpreted as an INVITE request. Also Request-URI must start with an alphabetic character.

Confirm with RFC 3261
 Request-Line   =  Method SP Request-URI SP SIP-Version CRLF

Fixes: 30f33e6dee80 ("[NETFILTER]: nf_conntrack_sip: support method specific request/response handling")
Signed-off-by: Ulrich Weber <ulrich.weber@riverbed.com>
Acked-by: Marco Angaroni <marcoangaroni@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_sip.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 621b81c7bddc..c3fc14e021ec 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -1436,9 +1436,12 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
 		handler = &sip_handlers[i];
 		if (handler->request == NULL)
 			continue;
-		if (*datalen < handler->len ||
+		if (*datalen < handler->len + 2 ||
 		    strncasecmp(*dptr, handler->method, handler->len))
 			continue;
+		if ((*dptr)[handler->len] != ' ' ||
+		    !isalpha((*dptr)[handler->len+1]))
+			continue;
 
 		if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
 				      &matchoff, &matchlen) <= 0) {
-- 
2.1.4


^ permalink raw reply related

* [PATCH 01/14] netfilter: nft_dynset: fix panic if NFT_SET_HASH is not enabled
From: Pablo Neira Ayuso @ 2016-11-10  0:23 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1478737427-1574-1-git-send-email-pablo@netfilter.org>

From: Liping Zhang <zlpnobody@gmail.com>

When CONFIG_NFT_SET_HASH is not enabled and I input the following rule:
"nft add rule filter output flow table test {ip daddr counter }", kernel
panic happened on my system:
 BUG: unable to handle kernel NULL pointer dereference at (null)
 IP: [<          (null)>]           (null)
 [...]
 Call Trace:
 [<ffffffffa0590466>] ? nft_dynset_eval+0x56/0x100 [nf_tables]
 [<ffffffffa05851bb>] nft_do_chain+0xfb/0x4e0 [nf_tables]
 [<ffffffffa0432f01>] ? nf_conntrack_tuple_taken+0x61/0x210 [nf_conntrack]
 [<ffffffffa0459ea6>] ? get_unique_tuple+0x136/0x560 [nf_nat]
 [<ffffffffa043bca1>] ? __nf_ct_ext_add_length+0x111/0x130 [nf_conntrack]
 [<ffffffffa045a357>] ? nf_nat_setup_info+0x87/0x3b0 [nf_nat]
 [<ffffffff81761e27>] ? ipt_do_table+0x327/0x610
 [<ffffffffa045a6d7>] ? __nf_nat_alloc_null_binding+0x57/0x80 [nf_nat]
 [<ffffffffa059f21f>] nft_ipv4_output+0xaf/0xd0 [nf_tables_ipv4]
 [<ffffffff81702515>] nf_iterate+0x55/0x60
 [<ffffffff81702593>] nf_hook_slow+0x73/0xd0

Because in rbtree type set, ops->update is not implemented. So just keep
it simple, in such case, report -EOPNOTSUPP to the user space.

Fixes: 22fe54d5fefc ("netfilter: nf_tables: add support for dynamic set updates")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nft_dynset.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 517f08767a3c..bfdb689664b0 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -139,6 +139,9 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
 			return PTR_ERR(set);
 	}
 
+	if (set->ops->update == NULL)
+		return -EOPNOTSUPP;
+
 	if (set->flags & NFT_SET_CONSTANT)
 		return -EBUSY;
 
-- 
2.1.4


^ permalink raw reply related

* [PATCH iproute2 -net-next] bpf: make tc's bpf loader generic and move into lib
From: Daniel Borkmann @ 2016-11-10  0:20 UTC (permalink / raw)
  To: stephen; +Cc: tgraf, alexei.starovoitov, netdev, Daniel Borkmann

This work moves the bpf loader into the iproute2 library and reworks
the tc specific parts into generic code. It's useful as we can then
more easily support new program types by just having the same ELF
loader backend. Joint work with Thomas Graf. I hacked a rough start
of a test suite to make sure nothing breaks [1] and looks all good.

  [1] https://github.com/borkmann/clsact/blob/master/test_bpf.sh

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Thomas Graf <tgraf@suug.ch>
---
 Makefile           |   13 +
 configure          |    2 +-
 include/bpf_api.h  |   26 +-
 include/bpf_util.h |   95 +++
 lib/Makefile       |    2 +-
 lib/bpf.c          | 2262 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tc/Makefile        |    7 +-
 tc/e_bpf.c         |    2 +-
 tc/f_bpf.c         |   58 +-
 tc/m_bpf.c         |   47 +-
 tc/tc_bpf.c        | 2010 ----------------------------------------------
 tc/tc_bpf.h        |   82 --
 12 files changed, 2467 insertions(+), 2139 deletions(-)
 create mode 100644 include/bpf_util.h
 create mode 100644 lib/bpf.c
 delete mode 100644 tc/tc_bpf.c
 delete mode 100644 tc/tc_bpf.h

diff --git a/Makefile b/Makefile
index fa200dd..37b68ad 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,8 @@
+# Include "Config" if already generated
+ifneq ($(wildcard Config),)
+include Config
+endif
+
 ifndef VERBOSE
 MAKEFLAGS += --no-print-directory
 endif
@@ -7,6 +12,7 @@ LIBDIR?=$(PREFIX)/lib
 SBINDIR?=/sbin
 CONFDIR?=/etc/iproute2
 DATADIR?=$(PREFIX)/share
+HDRDIR?=$(PREFIX)/include/iproute2
 DOCDIR?=$(DATADIR)/doc/iproute2
 MANDIR?=$(DATADIR)/man
 ARPDDIR?=/var/lib/arpd
@@ -51,6 +57,11 @@ SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man
 LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a
 LDLIBS += $(LIBNETLINK)
 
+ifeq ($(HAVE_ELF),y)
+CFLAGS += -DHAVE_ELF
+LDLIBS += -lelf
+endif
+
 all: Config
 	@set -e; \
 	for i in $(SUBDIRS); \
@@ -63,6 +74,7 @@ install: all
 	install -m 0755 -d $(DESTDIR)$(SBINDIR)
 	install -m 0755 -d $(DESTDIR)$(CONFDIR)
 	install -m 0755 -d $(DESTDIR)$(ARPDDIR)
+	install -m 0755 -d $(DESTDIR)$(HDRDIR)
 	install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples
 	install -m 0755 -d $(DESTDIR)$(DOCDIR)/examples/diffserv
 	install -m 0644 README.iproute2+tc $(shell find examples -maxdepth 1 -type f) \
@@ -73,6 +85,7 @@ install: all
 	install -m 0644 $(shell find etc/iproute2 -maxdepth 1 -type f) $(DESTDIR)$(CONFDIR)
 	install -m 0755 -d $(DESTDIR)$(BASH_COMPDIR)
 	install -m 0644 bash-completion/tc $(DESTDIR)$(BASH_COMPDIR)
+	install -m 0644 include/bpf_elf.h $(DESTDIR)$(HDRDIR)
 
 snapshot:
 	echo "static const char SNAPSHOT[] = \""`date +%y%m%d`"\";" \
diff --git a/configure b/configure
index c978da3..6c431c3 100755
--- a/configure
+++ b/configure
@@ -272,7 +272,7 @@ EOF
 
     if $CC -I$INCLUDE -o $TMPDIR/elftest $TMPDIR/elftest.c -lelf >/dev/null 2>&1
     then
-	echo "TC_CONFIG_ELF:=y" >>Config
+	echo "HAVE_ELF:=y" >>Config
 	echo "yes"
     else
 	echo "no"
diff --git a/include/bpf_api.h b/include/bpf_api.h
index 1b250d2..7642623 100644
--- a/include/bpf_api.h
+++ b/include/bpf_api.h
@@ -107,9 +107,14 @@
 
 /** BPF helper functions for tc. Individual flags are in linux/bpf.h */
 
+#ifndef __BPF_FUNC
+# define __BPF_FUNC(NAME, ...)						\
+	(* NAME)(__VA_ARGS__) __maybe_unused
+#endif
+
 #ifndef BPF_FUNC
 # define BPF_FUNC(NAME, ...)						\
-	(* NAME)(__VA_ARGS__) __maybe_unused = (void *) BPF_FUNC_##NAME
+	__BPF_FUNC(NAME, __VA_ARGS__) = (void *) BPF_FUNC_##NAME
 #endif
 
 /* Map access/manipulation */
@@ -147,10 +152,15 @@ static void BPF_FUNC(tail_call, struct __sk_buff *skb, void *map,
 
 /* System helpers */
 static uint32_t BPF_FUNC(get_smp_processor_id);
+static uint32_t BPF_FUNC(get_numa_node_id);
 
 /* Packet misc meta data */
 static uint32_t BPF_FUNC(get_cgroup_classid, struct __sk_buff *skb);
+static int BPF_FUNC(skb_under_cgroup, void *map, uint32_t index);
+
 static uint32_t BPF_FUNC(get_route_realm, struct __sk_buff *skb);
+static uint32_t BPF_FUNC(get_hash_recalc, struct __sk_buff *skb);
+static uint32_t BPF_FUNC(set_hash_invalid, struct __sk_buff *skb);
 
 /* Packet redirection */
 static int BPF_FUNC(redirect, int ifindex, uint32_t flags);
@@ -169,6 +179,20 @@ static int BPF_FUNC(l4_csum_replace, struct __sk_buff *skb, uint32_t off,
 		    uint32_t from, uint32_t to, uint32_t flags);
 static int BPF_FUNC(csum_diff, const void *from, uint32_t from_size,
 		    const void *to, uint32_t to_size, uint32_t seed);
+static int BPF_FUNC(csum_update, struct __sk_buff *skb, uint32_t wsum);
+
+static int BPF_FUNC(skb_change_type, struct __sk_buff *skb, uint32_t type);
+static int BPF_FUNC(skb_change_proto, struct __sk_buff *skb, uint32_t proto,
+		    uint32_t flags);
+static int BPF_FUNC(skb_change_tail, struct __sk_buff *skb, uint32_t nlen,
+		    uint32_t flags);
+
+static int BPF_FUNC(skb_pull_data, struct __sk_buff *skb, uint32_t len);
+
+/* Event notification */
+static int __BPF_FUNC(skb_event_output, struct __sk_buff *skb, void *map,
+		      uint64_t index, const void *data, uint32_t size) =
+		      (void *) BPF_FUNC_perf_event_output;
 
 /* Packet vlan encap/decap */
 static int BPF_FUNC(skb_vlan_push, struct __sk_buff *skb, uint16_t proto,
diff --git a/include/bpf_util.h b/include/bpf_util.h
new file mode 100644
index 0000000..05baeec
--- /dev/null
+++ b/include/bpf_util.h
@@ -0,0 +1,95 @@
+/*
+ * bpf_util.h	BPF common code
+ *
+ *		This program is free software; you can distribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Daniel Borkmann <daniel@iogearbox.net>
+ *		Jiri Pirko <jiri@resnulli.us>
+ */
+
+#ifndef __BPF_UTIL__
+#define __BPF_UTIL__
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include <linux/magic.h>
+#include <linux/elf-em.h>
+#include <linux/if_alg.h>
+
+#include "utils.h"
+#include "bpf_scm.h"
+
+#define BPF_ENV_UDS	"TC_BPF_UDS"
+#define BPF_ENV_MNT	"TC_BPF_MNT"
+
+#ifndef BPF_MAX_LOG
+# define BPF_MAX_LOG	4096
+#endif
+
+#define BPF_DIR_GLOBALS	"globals"
+
+#ifndef BPF_FS_MAGIC
+# define BPF_FS_MAGIC	0xcafe4a11
+#endif
+
+#define BPF_DIR_MNT	"/sys/fs/bpf"
+
+#ifndef TRACEFS_MAGIC
+# define TRACEFS_MAGIC	0x74726163
+#endif
+
+#define TRACE_DIR_MNT	"/sys/kernel/tracing"
+
+#ifndef AF_ALG
+# define AF_ALG		38
+#endif
+
+#ifndef EM_BPF
+# define EM_BPF		247
+#endif
+
+struct bpf_cfg_ops {
+	void (*cbpf_cb)(void *nl, const struct sock_filter *ops, int ops_len);
+	void (*ebpf_cb)(void *nl, int fd, const char *annotation);
+};
+
+struct bpf_cfg_in {
+	const char *object;
+	const char *section;
+	const char *uds;
+	int argc;
+	char **argv;
+	struct sock_filter *ops;
+};
+
+int bpf_parse_common(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+		     const struct bpf_cfg_ops *ops, void *nl);
+
+const char *bpf_prog_to_default_section(enum bpf_prog_type type);
+
+int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv);
+int bpf_trace_pipe(void);
+
+void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len);
+
+#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,
+		     unsigned int entries);
+#else
+static inline int bpf_send_map_fds(const char *path, const char *obj)
+{
+	return 0;
+}
+
+static inline int bpf_recv_map_fds(const char *path, int *fds,
+				   struct bpf_map_aux *aux,
+				   unsigned int entries)
+{
+	return -1;
+}
+#endif /* HAVE_ELF */
+#endif /* __BPF_UTIL__ */
diff --git a/lib/Makefile b/lib/Makefile
index 52e016d..5b7ec16 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -8,7 +8,7 @@ CFLAGS += -fPIC
 
 UTILOBJ = utils.o rt_names.o ll_types.o ll_proto.o ll_addr.o \
 	inet_proto.o namespace.o json_writer.o \
-	names.o color.o
+	names.o color.o bpf.o
 
 NLOBJ=libgenl.o ll_map.o libnetlink.o
 
diff --git a/lib/bpf.c b/lib/bpf.c
new file mode 100644
index 0000000..8a5b84b
--- /dev/null
+++ b/lib/bpf.c
@@ -0,0 +1,2262 @@
+/*
+ * bpf.c	BPF common code
+ *
+ *		This program is free software; you can distribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Daniel Borkmann <daniel@iogearbox.net>
+ *		Jiri Pirko <jiri@resnulli.us>
+ *		Alexei Starovoitov <ast@kernel.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <assert.h>
+
+#ifdef HAVE_ELF
+#include <libelf.h>
+#include <gelf.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/vfs.h>
+#include <sys/mount.h>
+#include <sys/syscall.h>
+#include <sys/sendfile.h>
+#include <sys/resource.h>
+
+#include <arpa/inet.h>
+
+#include "utils.h"
+
+#include "bpf_util.h"
+#include "bpf_elf.h"
+#include "bpf_scm.h"
+
+struct bpf_prog_meta {
+	const char *type;
+	const char *subdir;
+	const char *section;
+	bool may_uds_export;
+};
+
+static const enum bpf_prog_type __bpf_types[] = {
+	BPF_PROG_TYPE_SCHED_CLS,
+	BPF_PROG_TYPE_SCHED_ACT,
+};
+
+static const struct bpf_prog_meta __bpf_prog_meta[] = {
+	[BPF_PROG_TYPE_SCHED_CLS] = {
+		.type		= "cls",
+		.subdir		= "tc",
+		.section	= ELF_SECTION_CLASSIFIER,
+		.may_uds_export	= true,
+	},
+	[BPF_PROG_TYPE_SCHED_ACT] = {
+		.type		= "act",
+		.subdir		= "tc",
+		.section	= ELF_SECTION_ACTION,
+		.may_uds_export	= true,
+	},
+};
+
+static const char *bpf_prog_to_subdir(enum bpf_prog_type type)
+{
+	assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
+	       __bpf_prog_meta[type].subdir);
+	return __bpf_prog_meta[type].subdir;
+}
+
+const char *bpf_prog_to_default_section(enum bpf_prog_type type)
+{
+	assert(type < ARRAY_SIZE(__bpf_prog_meta) &&
+	       __bpf_prog_meta[type].section);
+	return __bpf_prog_meta[type].section;
+}
+
+#ifdef HAVE_ELF
+static int bpf_obj_open(const char *path, enum bpf_prog_type type,
+			const char *sec, bool verbose);
+#else
+static int bpf_obj_open(const char *path, enum bpf_prog_type type,
+			const char *sec, bool verbose)
+{
+	fprintf(stderr, "No ELF library support compiled in.\n");
+	errno = ENOSYS;
+	return -1;
+}
+#endif
+
+static inline __u64 bpf_ptr_to_u64(const void *ptr)
+{
+	return (__u64)(unsigned long)ptr;
+}
+
+static int bpf(int cmd, union bpf_attr *attr, unsigned int size)
+{
+#ifdef __NR_bpf
+	return syscall(__NR_bpf, cmd, attr, size);
+#else
+	fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
+static int bpf_map_update(int fd, const void *key, const void *value,
+			  uint64_t flags)
+{
+	union bpf_attr attr = {};
+
+	attr.map_fd = fd;
+	attr.key = bpf_ptr_to_u64(key);
+	attr.value = bpf_ptr_to_u64(value);
+	attr.flags = flags;
+
+	return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
+
+static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
+			    char **bpf_string, bool *need_release,
+			    const char separator)
+{
+	char sp;
+
+	if (from_file) {
+		size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
+		char *tmp_string, *last;
+		FILE *fp;
+
+		tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
+		tmp_string = calloc(1, tmp_len);
+		if (tmp_string == NULL)
+			return -ENOMEM;
+
+		fp = fopen(arg, "r");
+		if (fp == NULL) {
+			perror("Cannot fopen");
+			free(tmp_string);
+			return -ENOENT;
+		}
+
+		if (!fgets(tmp_string, tmp_len, fp)) {
+			free(tmp_string);
+			fclose(fp);
+			return -EIO;
+		}
+
+		fclose(fp);
+
+		last = &tmp_string[strlen(tmp_string) - 1];
+		if (*last == '\n')
+			*last = 0;
+
+		*need_release = true;
+		*bpf_string = tmp_string;
+	} else {
+		*need_release = false;
+		*bpf_string = arg;
+	}
+
+	if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 ||
+	    sp != separator) {
+		if (*need_release)
+			free(*bpf_string);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bpf_ops_parse(int argc, char **argv, struct sock_filter *bpf_ops,
+			 bool from_file)
+{
+	char *bpf_string, *token, separator = ',';
+	int ret = 0, i = 0;
+	bool need_release;
+	__u16 bpf_len = 0;
+
+	if (argc < 1)
+		return -EINVAL;
+	if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string,
+			     &need_release, separator))
+		return -EINVAL;
+	if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	token = bpf_string;
+	while ((token = strchr(token, separator)) && (++token)[0]) {
+		if (i >= bpf_len) {
+			fprintf(stderr, "Real program length exceeds encoded length parameter!\n");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		if (sscanf(token, "%hu %hhu %hhu %u,",
+			   &bpf_ops[i].code, &bpf_ops[i].jt,
+			   &bpf_ops[i].jf, &bpf_ops[i].k) != 4) {
+			fprintf(stderr, "Error at instruction %d!\n", i);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		i++;
+	}
+
+	if (i != bpf_len) {
+		fprintf(stderr, "Parsed program length is less than encoded length parameter!\n");
+		ret = -EINVAL;
+		goto out;
+	}
+	ret = bpf_len;
+out:
+	if (need_release)
+		free(bpf_string);
+
+	return ret;
+}
+
+void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len)
+{
+	struct sock_filter *ops = (struct sock_filter *) RTA_DATA(bpf_ops);
+	int i;
+
+	if (len == 0)
+		return;
+
+	fprintf(f, "bytecode \'%u,", len);
+
+	for (i = 0; i < len - 1; i++)
+		fprintf(f, "%hu %hhu %hhu %u,", ops[i].code, ops[i].jt,
+			ops[i].jf, ops[i].k);
+
+	fprintf(f, "%hu %hhu %hhu %u\'", ops[i].code, ops[i].jt,
+		ops[i].jf, ops[i].k);
+}
+
+static void bpf_map_pin_report(const struct bpf_elf_map *pin,
+			       const struct bpf_elf_map *obj)
+{
+	fprintf(stderr, "Map specification differs from pinned file!\n");
+
+	if (obj->type != pin->type)
+		fprintf(stderr, " - Type:         %u (obj) != %u (pin)\n",
+			obj->type, pin->type);
+	if (obj->size_key != pin->size_key)
+		fprintf(stderr, " - Size key:     %u (obj) != %u (pin)\n",
+			obj->size_key, pin->size_key);
+	if (obj->size_value != pin->size_value)
+		fprintf(stderr, " - Size value:   %u (obj) != %u (pin)\n",
+			obj->size_value, pin->size_value);
+	if (obj->max_elem != pin->max_elem)
+		fprintf(stderr, " - Max elems:    %u (obj) != %u (pin)\n",
+			obj->max_elem, pin->max_elem);
+	if (obj->flags != pin->flags)
+		fprintf(stderr, " - Flags:        %#x (obj) != %#x (pin)\n",
+			obj->flags, pin->flags);
+
+	fprintf(stderr, "\n");
+}
+
+static int bpf_map_selfcheck_pinned(int fd, const struct bpf_elf_map *map,
+				    int length)
+{
+	char file[PATH_MAX], buff[4096];
+	struct bpf_elf_map tmp = {}, zero = {};
+	unsigned int val;
+	FILE *fp;
+
+	snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
+
+	fp = fopen(file, "r");
+	if (!fp) {
+		fprintf(stderr, "No procfs support?!\n");
+		return -EIO;
+	}
+
+	while (fgets(buff, sizeof(buff), fp)) {
+		if (sscanf(buff, "map_type:\t%u", &val) == 1)
+			tmp.type = val;
+		else if (sscanf(buff, "key_size:\t%u", &val) == 1)
+			tmp.size_key = val;
+		else if (sscanf(buff, "value_size:\t%u", &val) == 1)
+			tmp.size_value = val;
+		else if (sscanf(buff, "max_entries:\t%u", &val) == 1)
+			tmp.max_elem = val;
+		else if (sscanf(buff, "map_flags:\t%i", &val) == 1)
+			tmp.flags = val;
+	}
+
+	fclose(fp);
+
+	if (!memcmp(&tmp, map, length)) {
+		return 0;
+	} else {
+		/* If kernel doesn't have eBPF-related fdinfo, we cannot do much,
+		 * so just accept it. We know we do have an eBPF fd and in this
+		 * case, everything is 0. It is guaranteed that no such map exists
+		 * since map type of 0 is unloadable BPF_MAP_TYPE_UNSPEC.
+		 */
+		if (!memcmp(&tmp, &zero, length))
+			return 0;
+
+		bpf_map_pin_report(&tmp, map);
+		return -EINVAL;
+	}
+}
+
+static int bpf_mnt_fs(const char *target)
+{
+	bool bind_done = false;
+
+	while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
+		if (errno != EINVAL || bind_done) {
+			fprintf(stderr, "mount --make-private %s failed: %s\n",
+				target,	strerror(errno));
+			return -1;
+		}
+
+		if (mount(target, target, "none", MS_BIND, NULL)) {
+			fprintf(stderr, "mount --bind %s %s failed: %s\n",
+				target,	target, strerror(errno));
+			return -1;
+		}
+
+		bind_done = true;
+	}
+
+	if (mount("bpf", target, "bpf", 0, "mode=0700")) {
+		fprintf(stderr, "mount -t bpf bpf %s failed: %s\n",
+			target,	strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int bpf_valid_mntpt(const char *mnt, unsigned long magic)
+{
+	struct statfs st_fs;
+
+	if (statfs(mnt, &st_fs) < 0)
+		return -ENOENT;
+	if ((unsigned long)st_fs.f_type != magic)
+		return -ENOENT;
+
+	return 0;
+}
+
+static const char *bpf_find_mntpt(const char *fstype, unsigned long magic,
+				  char *mnt, int len,
+				  const char * const *known_mnts)
+{
+	const char * const *ptr;
+	char type[100];
+	FILE *fp;
+
+	if (known_mnts) {
+		ptr = known_mnts;
+		while (*ptr) {
+			if (bpf_valid_mntpt(*ptr, magic) == 0) {
+				strncpy(mnt, *ptr, len - 1);
+				mnt[len - 1] = 0;
+				return mnt;
+			}
+			ptr++;
+		}
+	}
+
+	fp = fopen("/proc/mounts", "r");
+	if (fp == NULL || len != PATH_MAX)
+		return NULL;
+
+	while (fscanf(fp, "%*s %" textify(PATH_MAX) "s %99s %*s %*d %*d\n",
+		      mnt, type) == 2) {
+		if (strcmp(type, fstype) == 0)
+			break;
+	}
+
+	fclose(fp);
+	if (strcmp(type, fstype) != 0)
+		return NULL;
+
+	return mnt;
+}
+
+int bpf_trace_pipe(void)
+{
+	char tracefs_mnt[PATH_MAX] = TRACE_DIR_MNT;
+	static const char * const tracefs_known_mnts[] = {
+		TRACE_DIR_MNT,
+		"/sys/kernel/debug/tracing",
+		"/tracing",
+		"/trace",
+		0,
+	};
+	char tpipe[PATH_MAX];
+	const char *mnt;
+	int fd;
+
+	mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt,
+			     sizeof(tracefs_mnt), tracefs_known_mnts);
+	if (!mnt) {
+		fprintf(stderr, "tracefs not mounted?\n");
+		return -1;
+	}
+
+	snprintf(tpipe, sizeof(tpipe), "%s/trace_pipe", mnt);
+
+	fd = open(tpipe, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	fprintf(stderr, "Running! Hang up with ^C!\n\n");
+	while (1) {
+		static char buff[4096];
+		ssize_t ret;
+
+		ret = read(fd, buff, sizeof(buff) - 1);
+		if (ret > 0) {
+			write(2, buff, ret);
+			fflush(stderr);
+		}
+	}
+
+	return 0;
+}
+
+static int bpf_gen_global(const char *bpf_sub_dir)
+{
+	char bpf_glo_dir[PATH_MAX];
+	int ret;
+
+	snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s/",
+		 bpf_sub_dir, BPF_DIR_GLOBALS);
+
+	ret = mkdir(bpf_glo_dir, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
+			strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bpf_gen_master(const char *base, const char *name)
+{
+	char bpf_sub_dir[PATH_MAX];
+	int ret;
+
+	snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s/", base, name);
+
+	ret = mkdir(bpf_sub_dir, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", bpf_sub_dir,
+			strerror(errno));
+		return ret;
+	}
+
+	return bpf_gen_global(bpf_sub_dir);
+}
+
+static int bpf_slave_via_bind_mnt(const char *full_name,
+				  const char *full_link)
+{
+	int ret;
+
+	ret = mkdir(full_name, S_IRWXU);
+	if (ret) {
+		assert(errno != EEXIST);
+		fprintf(stderr, "mkdir %s failed: %s\n", full_name,
+			strerror(errno));
+		return ret;
+	}
+
+	ret = mount(full_link, full_name, "none", MS_BIND, NULL);
+	if (ret) {
+		rmdir(full_name);
+		fprintf(stderr, "mount --bind %s %s failed: %s\n",
+			full_link, full_name, strerror(errno));
+	}
+
+	return ret;
+}
+
+static int bpf_gen_slave(const char *base, const char *name,
+			 const char *link)
+{
+	char bpf_lnk_dir[PATH_MAX];
+	char bpf_sub_dir[PATH_MAX];
+	struct stat sb = {};
+	int ret;
+
+	snprintf(bpf_lnk_dir, sizeof(bpf_lnk_dir), "%s%s/", base, link);
+	snprintf(bpf_sub_dir, sizeof(bpf_sub_dir), "%s%s",  base, name);
+
+	ret = symlink(bpf_lnk_dir, bpf_sub_dir);
+	if (ret) {
+		if (errno != EEXIST) {
+			if (errno != EPERM) {
+				fprintf(stderr, "symlink %s failed: %s\n",
+					bpf_sub_dir, strerror(errno));
+				return ret;
+			}
+
+			return bpf_slave_via_bind_mnt(bpf_sub_dir,
+						      bpf_lnk_dir);
+		}
+
+		ret = lstat(bpf_sub_dir, &sb);
+		if (ret) {
+			fprintf(stderr, "lstat %s failed: %s\n",
+				bpf_sub_dir, strerror(errno));
+			return ret;
+		}
+
+		if ((sb.st_mode & S_IFMT) != S_IFLNK)
+			return bpf_gen_global(bpf_sub_dir);
+	}
+
+	return 0;
+}
+
+static int bpf_gen_hierarchy(const char *base)
+{
+	int ret, i;
+
+	ret = bpf_gen_master(base, bpf_prog_to_subdir(__bpf_types[0]));
+	for (i = 1; i < ARRAY_SIZE(__bpf_types) && !ret; i++)
+		ret = bpf_gen_slave(base,
+				    bpf_prog_to_subdir(__bpf_types[i]),
+				    bpf_prog_to_subdir(__bpf_types[0]));
+	return ret;
+}
+
+static const char *bpf_get_work_dir(enum bpf_prog_type type)
+{
+	static char bpf_tmp[PATH_MAX] = BPF_DIR_MNT;
+	static char bpf_wrk_dir[PATH_MAX];
+	static const char *mnt;
+	static bool bpf_mnt_cached;
+	static const char * const bpf_known_mnts[] = {
+		BPF_DIR_MNT,
+		"/bpf",
+		0,
+	};
+	int ret;
+
+	if (bpf_mnt_cached) {
+		const char *out = mnt;
+
+		if (out) {
+			snprintf(bpf_tmp, sizeof(bpf_tmp), "%s%s/",
+				 out, bpf_prog_to_subdir(type));
+			out = bpf_tmp;
+		}
+		return out;
+	}
+
+	mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_tmp, sizeof(bpf_tmp),
+			     bpf_known_mnts);
+	if (!mnt) {
+		mnt = getenv(BPF_ENV_MNT);
+		if (!mnt)
+			mnt = BPF_DIR_MNT;
+		ret = bpf_mnt_fs(mnt);
+		if (ret) {
+			mnt = NULL;
+			goto out;
+		}
+	}
+
+	snprintf(bpf_wrk_dir, sizeof(bpf_wrk_dir), "%s/", mnt);
+
+	ret = bpf_gen_hierarchy(bpf_wrk_dir);
+	if (ret) {
+		mnt = NULL;
+		goto out;
+	}
+
+	mnt = bpf_wrk_dir;
+out:
+	bpf_mnt_cached = true;
+	return mnt;
+}
+
+static int bpf_obj_get(const char *pathname, enum bpf_prog_type type)
+{
+	union bpf_attr attr = {};
+	char tmp[PATH_MAX];
+
+	if (strlen(pathname) > 2 && pathname[0] == 'm' &&
+	    pathname[1] == ':' && bpf_get_work_dir(type)) {
+		snprintf(tmp, sizeof(tmp), "%s/%s",
+			 bpf_get_work_dir(type), pathname + 2);
+		pathname = tmp;
+	}
+
+	attr.pathname = bpf_ptr_to_u64(pathname);
+
+	return bpf(BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
+enum bpf_mode {
+	CBPF_BYTECODE,
+	CBPF_FILE,
+	EBPF_OBJECT,
+	EBPF_PINNED,
+	BPF_MODE_MAX,
+};
+
+static int bpf_parse(enum bpf_prog_type *type, enum bpf_mode *mode,
+		     struct bpf_cfg_in *cfg, const bool *opt_tbl)
+{
+	const char *file, *section, *uds_name;
+	bool verbose = false;
+	int i, ret, argc;
+	char **argv;
+
+	argv = cfg->argv;
+	argc = cfg->argc;
+
+	if (opt_tbl[CBPF_BYTECODE] &&
+	    (matches(*argv, "bytecode") == 0 ||
+	     strcmp(*argv, "bc") == 0)) {
+		*mode = CBPF_BYTECODE;
+	} else if (opt_tbl[CBPF_FILE] &&
+		   (matches(*argv, "bytecode-file") == 0 ||
+		    strcmp(*argv, "bcf") == 0)) {
+		*mode = CBPF_FILE;
+	} else if (opt_tbl[EBPF_OBJECT] &&
+		   (matches(*argv, "object-file") == 0 ||
+		    strcmp(*argv, "obj") == 0)) {
+		*mode = EBPF_OBJECT;
+	} else if (opt_tbl[EBPF_PINNED] &&
+		   (matches(*argv, "object-pinned") == 0 ||
+		    matches(*argv, "pinned") == 0 ||
+		    matches(*argv, "fd") == 0)) {
+		*mode = EBPF_PINNED;
+	} else {
+		fprintf(stderr, "What mode is \"%s\"?\n", *argv);
+		return -1;
+	}
+
+	NEXT_ARG();
+	file = section = uds_name = NULL;
+	if (*mode == EBPF_OBJECT || *mode == EBPF_PINNED) {
+		file = *argv;
+		NEXT_ARG_FWD();
+
+		if (*type == BPF_PROG_TYPE_UNSPEC) {
+			if (argc > 0 && matches(*argv, "type") == 0) {
+				NEXT_ARG();
+				for (i = 0; i < ARRAY_SIZE(__bpf_prog_meta);
+				     i++) {
+					if (!__bpf_prog_meta[i].type)
+						continue;
+					if (!matches(*argv,
+						     __bpf_prog_meta[i].type)) {
+						*type = i;
+						break;
+					}
+				}
+
+				if (*type == BPF_PROG_TYPE_UNSPEC) {
+					fprintf(stderr, "What type is \"%s\"?\n",
+						*argv);
+					return -1;
+				}
+				NEXT_ARG_FWD();
+			} else {
+				*type = BPF_PROG_TYPE_SCHED_CLS;
+			}
+		}
+
+		section = bpf_prog_to_default_section(*type);
+		if (argc > 0 && matches(*argv, "section") == 0) {
+			NEXT_ARG();
+			section = *argv;
+			NEXT_ARG_FWD();
+		}
+
+		if (__bpf_prog_meta[*type].may_uds_export) {
+			uds_name = getenv(BPF_ENV_UDS);
+			if (argc > 0 && !uds_name &&
+			    matches(*argv, "export") == 0) {
+				NEXT_ARG();
+				uds_name = *argv;
+				NEXT_ARG_FWD();
+			}
+		}
+
+		if (argc > 0 && matches(*argv, "verbose") == 0) {
+			verbose = true;
+			NEXT_ARG_FWD();
+		}
+
+		PREV_ARG();
+	}
+
+	if (*mode == CBPF_BYTECODE || *mode == CBPF_FILE)
+		ret = bpf_ops_parse(argc, argv, cfg->ops, *mode == CBPF_FILE);
+	else if (*mode == EBPF_OBJECT)
+		ret = bpf_obj_open(file, *type, section, verbose);
+	else if (*mode == EBPF_PINNED)
+		ret = bpf_obj_get(file, *type);
+	else
+		return -1;
+
+	cfg->object  = file;
+	cfg->section = section;
+	cfg->uds     = uds_name;
+	cfg->argc    = argc;
+	cfg->argv    = argv;
+
+	return ret;
+}
+
+static int bpf_parse_opt_tbl(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+			     const struct bpf_cfg_ops *ops, void *nl,
+			     const bool *opt_tbl)
+{
+	struct sock_filter opcodes[BPF_MAXINSNS];
+	char annotation[256];
+	enum bpf_mode mode;
+	int ret;
+
+	cfg->ops = opcodes;
+	ret = bpf_parse(&type, &mode, cfg, opt_tbl);
+	cfg->ops = NULL;
+	if (ret < 0)
+		return ret;
+
+	if (mode == CBPF_BYTECODE || mode == CBPF_FILE)
+		ops->cbpf_cb(nl, opcodes, ret);
+	if (mode == EBPF_OBJECT || mode == EBPF_PINNED) {
+		snprintf(annotation, sizeof(annotation), "%s:[%s]",
+			 basename(cfg->object), mode == EBPF_PINNED ?
+			 "*fsobj" : cfg->section);
+		ops->ebpf_cb(nl, ret, annotation);
+	}
+
+	return 0;
+}
+
+int bpf_parse_common(enum bpf_prog_type type, struct bpf_cfg_in *cfg,
+		     const struct bpf_cfg_ops *ops, void *nl)
+{
+	bool opt_tbl[BPF_MODE_MAX] = {};
+
+	if (ops->cbpf_cb) {
+		opt_tbl[CBPF_BYTECODE] = true;
+		opt_tbl[CBPF_FILE]     = true;
+	}
+
+	if (ops->ebpf_cb) {
+		opt_tbl[EBPF_OBJECT]   = true;
+		opt_tbl[EBPF_PINNED]   = true;
+	}
+
+	return bpf_parse_opt_tbl(type, cfg, ops, nl, opt_tbl);
+}
+
+int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
+{
+	enum bpf_prog_type type = BPF_PROG_TYPE_UNSPEC;
+	const bool opt_tbl[BPF_MODE_MAX] = {
+		[EBPF_OBJECT]	= true,
+		[EBPF_PINNED]	= true,
+	};
+	const struct bpf_elf_map test = {
+		.type		= BPF_MAP_TYPE_PROG_ARRAY,
+		.size_key	= sizeof(int),
+		.size_value	= sizeof(int),
+	};
+	struct bpf_cfg_in cfg = {
+		.argc		= argc,
+		.argv		= argv,
+	};
+	int ret, prog_fd, map_fd;
+	enum bpf_mode mode;
+	uint32_t map_key;
+
+	prog_fd = bpf_parse(&type, &mode, &cfg, opt_tbl);
+	if (prog_fd < 0)
+		return prog_fd;
+	if (key) {
+		map_key = *key;
+	} else {
+		ret = sscanf(cfg.section, "%*i/%i", &map_key);
+		if (ret != 1) {
+			fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n");
+			ret = -EINVAL;
+			goto out_prog;
+		}
+	}
+
+	map_fd = bpf_obj_get(map_path, type);
+	if (map_fd < 0) {
+		fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n",
+			map_path, strerror(errno));
+		ret = map_fd;
+		goto out_prog;
+	}
+
+	ret = bpf_map_selfcheck_pinned(map_fd, &test,
+				       offsetof(struct bpf_elf_map, max_elem));
+	if (ret < 0) {
+		fprintf(stderr, "Map \'%s\' self-check failed!\n", map_path);
+		goto out_map;
+	}
+
+	ret = bpf_map_update(map_fd, &map_key, &prog_fd, BPF_ANY);
+	if (ret < 0)
+		fprintf(stderr, "Map update failed: %s\n", strerror(errno));
+out_map:
+	close(map_fd);
+out_prog:
+	close(prog_fd);
+	return ret;
+}
+
+#ifdef HAVE_ELF
+struct bpf_elf_prog {
+	enum bpf_prog_type	type;
+	const struct bpf_insn	*insns;
+	size_t			size;
+	const char		*license;
+};
+
+struct bpf_hash_entry {
+	unsigned int		pinning;
+	const char		*subpath;
+	struct bpf_hash_entry	*next;
+};
+
+struct bpf_elf_ctx {
+	Elf			*elf_fd;
+	GElf_Ehdr		elf_hdr;
+	Elf_Data		*sym_tab;
+	Elf_Data		*str_tab;
+	int			obj_fd;
+	int			map_fds[ELF_MAX_MAPS];
+	struct bpf_elf_map	maps[ELF_MAX_MAPS];
+	int			sym_num;
+	int			map_num;
+	int			map_len;
+	bool			*sec_done;
+	int			sec_maps;
+	char			license[ELF_MAX_LICENSE_LEN];
+	enum bpf_prog_type	type;
+	bool			verbose;
+	struct bpf_elf_st	stat;
+	struct bpf_hash_entry	*ht[256];
+	char			*log;
+	size_t			log_size;
+};
+
+struct bpf_elf_sec_data {
+	GElf_Shdr		sec_hdr;
+	Elf_Data		*sec_data;
+	const char		*sec_name;
+};
+
+struct bpf_map_data {
+	int			*fds;
+	const char		*obj;
+	struct bpf_elf_st	*st;
+	struct bpf_elf_map	*ent;
+};
+
+static __check_format_string(2, 3) void
+bpf_dump_error(struct bpf_elf_ctx *ctx, const char *format, ...)
+{
+	va_list vl;
+
+	va_start(vl, format);
+	vfprintf(stderr, format, vl);
+	va_end(vl);
+
+	if (ctx->log && ctx->log[0]) {
+		if (ctx->verbose) {
+			fprintf(stderr, "%s\n", ctx->log);
+		} else {
+			unsigned int off = 0, len = strlen(ctx->log);
+
+			if (len > BPF_MAX_LOG) {
+				off = len - BPF_MAX_LOG;
+				fprintf(stderr, "Skipped %u bytes, use \'verb\' option for the full verbose log.\n[...]\n",
+					off);
+			}
+			fprintf(stderr, "%s\n", ctx->log + off);
+		}
+
+		memset(ctx->log, 0, ctx->log_size);
+	}
+}
+
+static int bpf_log_realloc(struct bpf_elf_ctx *ctx)
+{
+	size_t log_size = ctx->log_size;
+	void *ptr;
+
+	if (!ctx->log) {
+		log_size = 65536;
+	} else {
+		log_size <<= 1;
+		if (log_size > (UINT_MAX >> 8))
+			return -EINVAL;
+	}
+
+	ptr = realloc(ctx->log, log_size);
+	if (!ptr)
+		return -ENOMEM;
+
+	ctx->log = ptr;
+	ctx->log_size = log_size;
+
+	return 0;
+}
+
+static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
+			  uint32_t size_value, uint32_t max_elem,
+			  uint32_t flags)
+{
+	union bpf_attr attr = {};
+
+	attr.map_type = type;
+	attr.key_size = size_key;
+	attr.value_size = size_value;
+	attr.max_entries = max_elem;
+	attr.map_flags = flags;
+
+	return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+static int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
+			 size_t size_insns, const char *license, char *log,
+			 size_t size_log)
+{
+	union bpf_attr attr = {};
+
+	attr.prog_type = type;
+	attr.insns = bpf_ptr_to_u64(insns);
+	attr.insn_cnt = size_insns / sizeof(struct bpf_insn);
+	attr.license = bpf_ptr_to_u64(license);
+
+	if (size_log > 0) {
+		attr.log_buf = bpf_ptr_to_u64(log);
+		attr.log_size = size_log;
+		attr.log_level = 1;
+	}
+
+	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
+
+static int bpf_obj_pin(int fd, const char *pathname)
+{
+	union bpf_attr attr = {};
+
+	attr.pathname = bpf_ptr_to_u64(pathname);
+	attr.bpf_fd = fd;
+
+	return bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
+}
+
+static int bpf_obj_hash(const char *object, uint8_t *out, size_t len)
+{
+	struct sockaddr_alg alg = {
+		.salg_family	= AF_ALG,
+		.salg_type	= "hash",
+		.salg_name	= "sha1",
+	};
+	int ret, cfd, ofd, ffd;
+	struct stat stbuff;
+	ssize_t size;
+
+	if (!object || len != 20)
+		return -EINVAL;
+
+	cfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+	if (cfd < 0) {
+		fprintf(stderr, "Cannot get AF_ALG socket: %s\n",
+			strerror(errno));
+		return cfd;
+	}
+
+	ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg));
+	if (ret < 0) {
+		fprintf(stderr, "Error binding socket: %s\n", strerror(errno));
+		goto out_cfd;
+	}
+
+	ofd = accept(cfd, NULL, 0);
+	if (ofd < 0) {
+		fprintf(stderr, "Error accepting socket: %s\n",
+			strerror(errno));
+		ret = ofd;
+		goto out_cfd;
+	}
+
+	ffd = open(object, O_RDONLY);
+	if (ffd < 0) {
+		fprintf(stderr, "Error opening object %s: %s\n",
+			object, strerror(errno));
+		ret = ffd;
+		goto out_ofd;
+	}
+
+	ret = fstat(ffd, &stbuff);
+	if (ret < 0) {
+		fprintf(stderr, "Error doing fstat: %s\n",
+			strerror(errno));
+		goto out_ffd;
+	}
+
+	size = sendfile(ofd, ffd, NULL, stbuff.st_size);
+	if (size != stbuff.st_size) {
+		fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n",
+			size, stbuff.st_size, strerror(errno));
+		ret = -1;
+		goto out_ffd;
+	}
+
+	size = read(ofd, out, len);
+	if (size != len) {
+		fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n",
+			size, len, strerror(errno));
+		ret = -1;
+	} else {
+		ret = 0;
+	}
+out_ffd:
+	close(ffd);
+out_ofd:
+	close(ofd);
+out_cfd:
+	close(cfd);
+	return ret;
+}
+
+static const char *bpf_get_obj_uid(const char *pathname)
+{
+	static bool bpf_uid_cached;
+	static char bpf_uid[64];
+	uint8_t tmp[20];
+	int ret;
+
+	if (bpf_uid_cached)
+		goto done;
+
+	ret = bpf_obj_hash(pathname, tmp, sizeof(tmp));
+	if (ret) {
+		fprintf(stderr, "Object hashing failed!\n");
+		return NULL;
+	}
+
+	hexstring_n2a(tmp, sizeof(tmp), bpf_uid, sizeof(bpf_uid));
+	bpf_uid_cached = true;
+done:
+	return bpf_uid;
+}
+
+static int bpf_init_env(const char *pathname)
+{
+	struct rlimit limit = {
+		.rlim_cur = RLIM_INFINITY,
+		.rlim_max = RLIM_INFINITY,
+	};
+
+	/* Don't bother in case we fail! */
+	setrlimit(RLIMIT_MEMLOCK, &limit);
+
+	if (!bpf_get_work_dir(BPF_PROG_TYPE_UNSPEC)) {
+		fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n");
+		return 0;
+	}
+
+	if (!bpf_get_obj_uid(pathname))
+		return -1;
+
+	return 0;
+}
+
+static const char *bpf_custom_pinning(const struct bpf_elf_ctx *ctx,
+				      uint32_t pinning)
+{
+	struct bpf_hash_entry *entry;
+
+	entry = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
+	while (entry && entry->pinning != pinning)
+		entry = entry->next;
+
+	return entry ? entry->subpath : NULL;
+}
+
+static bool bpf_no_pinning(const struct bpf_elf_ctx *ctx,
+			   uint32_t pinning)
+{
+	switch (pinning) {
+	case PIN_OBJECT_NS:
+	case PIN_GLOBAL_NS:
+		return false;
+	case PIN_NONE:
+		return true;
+	default:
+		return !bpf_custom_pinning(ctx, pinning);
+	}
+}
+
+static void bpf_make_pathname(char *pathname, size_t len, const char *name,
+			      const struct bpf_elf_ctx *ctx, uint32_t pinning)
+{
+	switch (pinning) {
+	case PIN_OBJECT_NS:
+		snprintf(pathname, len, "%s/%s/%s",
+			 bpf_get_work_dir(ctx->type),
+			 bpf_get_obj_uid(NULL), name);
+		break;
+	case PIN_GLOBAL_NS:
+		snprintf(pathname, len, "%s/%s/%s",
+			 bpf_get_work_dir(ctx->type),
+			 BPF_DIR_GLOBALS, name);
+		break;
+	default:
+		snprintf(pathname, len, "%s/../%s/%s",
+			 bpf_get_work_dir(ctx->type),
+			 bpf_custom_pinning(ctx, pinning), name);
+		break;
+	}
+}
+
+static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx,
+			    uint32_t pinning)
+{
+	char pathname[PATH_MAX];
+
+	if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+		return 0;
+
+	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
+	return bpf_obj_get(pathname, ctx->type);
+}
+
+static int bpf_make_obj_path(const struct bpf_elf_ctx *ctx)
+{
+	char tmp[PATH_MAX];
+	int ret;
+
+	snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_work_dir(ctx->type),
+		 bpf_get_obj_uid(NULL));
+
+	ret = mkdir(tmp, S_IRWXU);
+	if (ret && errno != EEXIST) {
+		fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bpf_make_custom_path(const struct bpf_elf_ctx *ctx,
+				const char *todo)
+{
+	char tmp[PATH_MAX], rem[PATH_MAX], *sub;
+	int ret;
+
+	snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_work_dir(ctx->type));
+	snprintf(rem, sizeof(rem), "%s/", todo);
+	sub = strtok(rem, "/");
+
+	while (sub) {
+		if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX)
+			return -EINVAL;
+
+		strcat(tmp, sub);
+		strcat(tmp, "/");
+
+		ret = mkdir(tmp, S_IRWXU);
+		if (ret && errno != EEXIST) {
+			fprintf(stderr, "mkdir %s failed: %s\n", tmp,
+				strerror(errno));
+			return ret;
+		}
+
+		sub = strtok(NULL, "/");
+	}
+
+	return 0;
+}
+
+static int bpf_place_pinned(int fd, const char *name,
+			    const struct bpf_elf_ctx *ctx, uint32_t pinning)
+{
+	char pathname[PATH_MAX];
+	const char *tmp;
+	int ret = 0;
+
+	if (bpf_no_pinning(ctx, pinning) || !bpf_get_work_dir(ctx->type))
+		return 0;
+
+	if (pinning == PIN_OBJECT_NS)
+		ret = bpf_make_obj_path(ctx);
+	else if ((tmp = bpf_custom_pinning(ctx, pinning)))
+		ret = bpf_make_custom_path(ctx, tmp);
+	if (ret < 0)
+		return ret;
+
+	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
+	return bpf_obj_pin(fd, pathname);
+}
+
+static void bpf_prog_report(int fd, const char *section,
+			    const struct bpf_elf_prog *prog,
+			    struct bpf_elf_ctx *ctx)
+{
+	unsigned int insns = prog->size / sizeof(struct bpf_insn);
+
+	fprintf(stderr, "\nProg section \'%s\' %s%s (%d)!\n", section,
+		fd < 0 ? "rejected: " : "loaded",
+		fd < 0 ? strerror(errno) : "",
+		fd < 0 ? errno : fd);
+
+	fprintf(stderr, " - Type:         %u\n", prog->type);
+	fprintf(stderr, " - Instructions: %u (%u over limit)\n",
+		insns, insns > BPF_MAXINSNS ? insns - BPF_MAXINSNS : 0);
+	fprintf(stderr, " - License:      %s\n\n", prog->license);
+
+	bpf_dump_error(ctx, "Verifier analysis:\n\n");
+}
+
+static int bpf_prog_attach(const char *section,
+			   const struct bpf_elf_prog *prog,
+			   struct bpf_elf_ctx *ctx)
+{
+	int tries = 0, fd;
+retry:
+	errno = 0;
+	fd = bpf_prog_load(prog->type, prog->insns, prog->size,
+			   prog->license, ctx->log, ctx->log_size);
+	if (fd < 0 || ctx->verbose) {
+		/* The verifier log is pretty chatty, sometimes so chatty
+		 * on larger programs, that we could fail to dump everything
+		 * into our buffer. Still, try to give a debuggable error
+		 * log for the user, so enlarge it and re-fail.
+		 */
+		if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) {
+			if (tries++ < 6 && !bpf_log_realloc(ctx))
+				goto retry;
+
+			fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n",
+				ctx->log_size, tries);
+			return fd;
+		}
+
+		bpf_prog_report(fd, section, prog, ctx);
+	}
+
+	return fd;
+}
+
+static void bpf_map_report(int fd, const char *name,
+			   const struct bpf_elf_map *map,
+			   struct bpf_elf_ctx *ctx)
+{
+	fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name,
+		fd < 0 ? "rejected: " : "loaded",
+		fd < 0 ? strerror(errno) : "",
+		fd < 0 ? errno : fd);
+
+	fprintf(stderr, " - Type:         %u\n", map->type);
+	fprintf(stderr, " - Identifier:   %u\n", map->id);
+	fprintf(stderr, " - Pinning:      %u\n", map->pinning);
+	fprintf(stderr, " - Size key:     %u\n", map->size_key);
+	fprintf(stderr, " - Size value:   %u\n", map->size_value);
+	fprintf(stderr, " - Max elems:    %u\n", map->max_elem);
+	fprintf(stderr, " - Flags:        %#x\n\n", map->flags);
+}
+
+static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
+			  struct bpf_elf_ctx *ctx)
+{
+	int fd, ret;
+
+	fd = bpf_probe_pinned(name, ctx, map->pinning);
+	if (fd > 0) {
+		ret = bpf_map_selfcheck_pinned(fd, map,
+					       offsetof(struct bpf_elf_map,
+							id));
+		if (ret < 0) {
+			close(fd);
+			fprintf(stderr, "Map \'%s\' self-check failed!\n",
+				name);
+			return ret;
+		}
+		if (ctx->verbose)
+			fprintf(stderr, "Map \'%s\' loaded as pinned!\n",
+				name);
+		return fd;
+	}
+
+	errno = 0;
+	fd = bpf_map_create(map->type, map->size_key, map->size_value,
+			    map->max_elem, map->flags);
+	if (fd < 0 || ctx->verbose) {
+		bpf_map_report(fd, name, map, ctx);
+		if (fd < 0)
+			return fd;
+	}
+
+	ret = bpf_place_pinned(fd, name, ctx, map->pinning);
+	if (ret < 0 && errno != EEXIST) {
+		fprintf(stderr, "Could not pin %s map: %s\n", name,
+			strerror(errno));
+		close(fd);
+		return ret;
+	}
+
+	return fd;
+}
+
+static const char *bpf_str_tab_name(const struct bpf_elf_ctx *ctx,
+				    const GElf_Sym *sym)
+{
+	return ctx->str_tab->d_buf + sym->st_name;
+}
+
+static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
+{
+	GElf_Sym sym;
+	int i;
+
+	for (i = 0; i < ctx->sym_num; i++) {
+		if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
+			continue;
+
+		if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+		    GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
+		    sym.st_shndx != ctx->sec_maps ||
+		    sym.st_value / ctx->map_len != which)
+			continue;
+
+		return bpf_str_tab_name(ctx, &sym);
+	}
+
+	return NULL;
+}
+
+static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx)
+{
+	const char *map_name;
+	int i, fd;
+
+	for (i = 0; i < ctx->map_num; i++) {
+		map_name = bpf_map_fetch_name(ctx, i);
+		if (!map_name)
+			return -EIO;
+
+		fd = bpf_map_attach(map_name, &ctx->maps[i], ctx);
+		if (fd < 0)
+			return fd;
+
+		ctx->map_fds[i] = fd;
+	}
+
+	return 0;
+}
+
+static int bpf_map_num_sym(struct bpf_elf_ctx *ctx)
+{
+	int i, num = 0;
+	GElf_Sym sym;
+
+	for (i = 0; i < ctx->sym_num; i++) {
+		if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
+			continue;
+
+		if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+		    GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
+		    sym.st_shndx != ctx->sec_maps)
+			continue;
+		num++;
+	}
+
+	return num;
+}
+
+static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section,
+				 struct bpf_elf_sec_data *data)
+{
+	Elf_Data *sec_edata;
+	GElf_Shdr sec_hdr;
+	Elf_Scn *sec_fd;
+	char *sec_name;
+
+	memset(data, 0, sizeof(*data));
+
+	sec_fd = elf_getscn(ctx->elf_fd, section);
+	if (!sec_fd)
+		return -EINVAL;
+	if (gelf_getshdr(sec_fd, &sec_hdr) != &sec_hdr)
+		return -EIO;
+
+	sec_name = elf_strptr(ctx->elf_fd, ctx->elf_hdr.e_shstrndx,
+			      sec_hdr.sh_name);
+	if (!sec_name || !sec_hdr.sh_size)
+		return -ENOENT;
+
+	sec_edata = elf_getdata(sec_fd, NULL);
+	if (!sec_edata || elf_getdata(sec_fd, sec_edata))
+		return -EIO;
+
+	memcpy(&data->sec_hdr, &sec_hdr, sizeof(sec_hdr));
+
+	data->sec_name = sec_name;
+	data->sec_data = sec_edata;
+	return 0;
+}
+
+struct bpf_elf_map_min {
+	__u32 type;
+	__u32 size_key;
+	__u32 size_value;
+	__u32 max_elem;
+};
+
+static int bpf_fetch_maps_begin(struct bpf_elf_ctx *ctx, int section,
+				struct bpf_elf_sec_data *data)
+{
+	ctx->map_num = data->sec_data->d_size;
+	ctx->sec_maps = section;
+	ctx->sec_done[section] = true;
+
+	if (ctx->map_num > sizeof(ctx->maps)) {
+		fprintf(stderr, "Too many BPF maps in ELF section!\n");
+		return -ENOMEM;
+	}
+
+	memcpy(ctx->maps, data->sec_data->d_buf, ctx->map_num);
+	return 0;
+}
+
+static int bpf_map_verify_all_offs(struct bpf_elf_ctx *ctx, int end)
+{
+	GElf_Sym sym;
+	int off, i;
+
+	for (off = 0; off < end; off += ctx->map_len) {
+		/* Order doesn't need to be linear here, hence we walk
+		 * the table again.
+		 */
+		for (i = 0; i < ctx->sym_num; i++) {
+			if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
+				continue;
+			if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+			    GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
+			    sym.st_shndx != ctx->sec_maps)
+				continue;
+			if (sym.st_value == off)
+				break;
+			if (i == ctx->sym_num - 1)
+				return -1;
+		}
+	}
+
+	return off == end ? 0 : -1;
+}
+
+static int bpf_fetch_maps_end(struct bpf_elf_ctx *ctx)
+{
+	struct bpf_elf_map fixup[ARRAY_SIZE(ctx->maps)] = {};
+	int i, sym_num = bpf_map_num_sym(ctx);
+	__u8 *buff;
+
+	if (sym_num == 0 || sym_num > ARRAY_SIZE(ctx->maps)) {
+		fprintf(stderr, "%u maps not supported in current map section!\n",
+			sym_num);
+		return -EINVAL;
+	}
+
+	if (ctx->map_num % sym_num != 0 ||
+	    ctx->map_num % sizeof(__u32) != 0) {
+		fprintf(stderr, "Number BPF map symbols are not multiple of struct bpf_elf_map!\n");
+		return -EINVAL;
+	}
+
+	ctx->map_len = ctx->map_num / sym_num;
+	if (bpf_map_verify_all_offs(ctx, ctx->map_num)) {
+		fprintf(stderr, "Different struct bpf_elf_map in use!\n");
+		return -EINVAL;
+	}
+
+	if (ctx->map_len == sizeof(struct bpf_elf_map)) {
+		ctx->map_num = sym_num;
+		return 0;
+	} else if (ctx->map_len > sizeof(struct bpf_elf_map)) {
+		fprintf(stderr, "struct bpf_elf_map not supported, coming from future version?\n");
+		return -EINVAL;
+	} else if (ctx->map_len < sizeof(struct bpf_elf_map_min)) {
+		fprintf(stderr, "struct bpf_elf_map too small, not supported!\n");
+		return -EINVAL;
+	}
+
+	ctx->map_num = sym_num;
+	for (i = 0, buff = (void *)ctx->maps; i < ctx->map_num;
+	     i++, buff += ctx->map_len) {
+		/* The fixup leaves the rest of the members as zero, which
+		 * is fine currently, but option exist to set some other
+		 * default value as well when needed in future.
+		 */
+		memcpy(&fixup[i], buff, ctx->map_len);
+	}
+
+	memcpy(ctx->maps, fixup, sizeof(fixup));
+
+	printf("Note: %zu bytes struct bpf_elf_map fixup performed due to size mismatch!\n",
+	       sizeof(struct bpf_elf_map) - ctx->map_len);
+	return 0;
+}
+
+static int bpf_fetch_license(struct bpf_elf_ctx *ctx, int section,
+			     struct bpf_elf_sec_data *data)
+{
+	if (data->sec_data->d_size > sizeof(ctx->license))
+		return -ENOMEM;
+
+	memcpy(ctx->license, data->sec_data->d_buf, data->sec_data->d_size);
+	ctx->sec_done[section] = true;
+	return 0;
+}
+
+static int bpf_fetch_symtab(struct bpf_elf_ctx *ctx, int section,
+			    struct bpf_elf_sec_data *data)
+{
+	ctx->sym_tab = data->sec_data;
+	ctx->sym_num = data->sec_hdr.sh_size / data->sec_hdr.sh_entsize;
+	ctx->sec_done[section] = true;
+	return 0;
+}
+
+static int bpf_fetch_strtab(struct bpf_elf_ctx *ctx, int section,
+			    struct bpf_elf_sec_data *data)
+{
+	ctx->str_tab = data->sec_data;
+	ctx->sec_done[section] = true;
+	return 0;
+}
+
+static bool bpf_has_map_data(const struct bpf_elf_ctx *ctx)
+{
+	return ctx->sym_tab && ctx->str_tab && ctx->sec_maps;
+}
+
+static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx)
+{
+	struct bpf_elf_sec_data data;
+	int i, ret = -1;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0)
+			continue;
+
+		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
+		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
+			ret = bpf_fetch_maps_begin(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
+			 !strcmp(data.sec_name, ELF_SECTION_LICENSE))
+			ret = bpf_fetch_license(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
+			 !strcmp(data.sec_name, ".symtab"))
+			ret = bpf_fetch_symtab(ctx, i, &data);
+		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
+			 !strcmp(data.sec_name, ".strtab"))
+			ret = bpf_fetch_strtab(ctx, i, &data);
+		if (ret < 0) {
+			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
+				i);
+			return ret;
+		}
+	}
+
+	if (bpf_has_map_data(ctx)) {
+		ret = bpf_fetch_maps_end(ctx);
+		if (ret < 0) {
+			fprintf(stderr, "Error fixing up map structure, incompatible struct bpf_elf_map used?\n");
+			return ret;
+		}
+
+		ret = bpf_maps_attach_all(ctx);
+		if (ret < 0) {
+			fprintf(stderr, "Error loading maps into kernel!\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section,
+			  bool *sseen)
+{
+	struct bpf_elf_sec_data data;
+	struct bpf_elf_prog prog;
+	int ret, i, fd = -1;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		if (ctx->sec_done[i])
+			continue;
+
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0 ||
+		    !(data.sec_hdr.sh_type == SHT_PROGBITS &&
+		      data.sec_hdr.sh_flags & SHF_EXECINSTR &&
+		      !strcmp(data.sec_name, section)))
+			continue;
+
+		*sseen = true;
+
+		memset(&prog, 0, sizeof(prog));
+		prog.type    = ctx->type;
+		prog.insns   = data.sec_data->d_buf;
+		prog.size    = data.sec_data->d_size;
+		prog.license = ctx->license;
+
+		fd = bpf_prog_attach(section, &prog, ctx);
+		if (fd < 0)
+			return fd;
+
+		ctx->sec_done[i] = true;
+		break;
+	}
+
+	return fd;
+}
+
+static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
+			       struct bpf_elf_sec_data *data_relo,
+			       struct bpf_elf_sec_data *data_insn)
+{
+	Elf_Data *idata = data_insn->sec_data;
+	GElf_Shdr *rhdr = &data_relo->sec_hdr;
+	int relo_ent, relo_num = rhdr->sh_size / rhdr->sh_entsize;
+	struct bpf_insn *insns = idata->d_buf;
+	unsigned int num_insns = idata->d_size / sizeof(*insns);
+
+	for (relo_ent = 0; relo_ent < relo_num; relo_ent++) {
+		unsigned int ioff, rmap;
+		GElf_Rel relo;
+		GElf_Sym sym;
+
+		if (gelf_getrel(data_relo->sec_data, relo_ent, &relo) != &relo)
+			return -EIO;
+
+		ioff = relo.r_offset / sizeof(struct bpf_insn);
+		if (ioff >= num_insns ||
+		    insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+			fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n",
+				ioff);
+			if (ioff < num_insns &&
+			    insns[ioff].code == (BPF_JMP | BPF_CALL))
+				fprintf(stderr, " - Try to annotate functions with always_inline attribute!\n");
+			return -EINVAL;
+		}
+
+		if (gelf_getsym(ctx->sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym)
+			return -EIO;
+		if (sym.st_shndx != ctx->sec_maps) {
+			fprintf(stderr, "ELF contains non-map related relo data in entry %u pointing to section %u! Compiler bug?!\n",
+				relo_ent, sym.st_shndx);
+			return -EIO;
+		}
+
+		rmap = sym.st_value / ctx->map_len;
+		if (rmap >= ARRAY_SIZE(ctx->map_fds))
+			return -EINVAL;
+		if (!ctx->map_fds[rmap])
+			return -EINVAL;
+
+		if (ctx->verbose)
+			fprintf(stderr, "Map \'%s\' (%d) injected into prog section \'%s\' at offset %u!\n",
+				bpf_str_tab_name(ctx, &sym), ctx->map_fds[rmap],
+				data_insn->sec_name, ioff);
+
+		insns[ioff].src_reg = BPF_PSEUDO_MAP_FD;
+		insns[ioff].imm     = ctx->map_fds[rmap];
+	}
+
+	return 0;
+}
+
+static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
+			       bool *lderr, bool *sseen)
+{
+	struct bpf_elf_sec_data data_relo, data_insn;
+	struct bpf_elf_prog prog;
+	int ret, idx, i, fd = -1;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		ret = bpf_fill_section_data(ctx, i, &data_relo);
+		if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL)
+			continue;
+
+		idx = data_relo.sec_hdr.sh_info;
+
+		ret = bpf_fill_section_data(ctx, idx, &data_insn);
+		if (ret < 0 ||
+		    !(data_insn.sec_hdr.sh_type == SHT_PROGBITS &&
+		      data_insn.sec_hdr.sh_flags & SHF_EXECINSTR &&
+		      !strcmp(data_insn.sec_name, section)))
+			continue;
+
+		*sseen = true;
+
+		ret = bpf_apply_relo_data(ctx, &data_relo, &data_insn);
+		if (ret < 0)
+			return ret;
+
+		memset(&prog, 0, sizeof(prog));
+		prog.type    = ctx->type;
+		prog.insns   = data_insn.sec_data->d_buf;
+		prog.size    = data_insn.sec_data->d_size;
+		prog.license = ctx->license;
+
+		fd = bpf_prog_attach(section, &prog, ctx);
+		if (fd < 0) {
+			*lderr = true;
+			return fd;
+		}
+
+		ctx->sec_done[i]   = true;
+		ctx->sec_done[idx] = true;
+		break;
+	}
+
+	return fd;
+}
+
+static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section)
+{
+	bool lderr = false, sseen = false;
+	int ret = -1;
+
+	if (bpf_has_map_data(ctx))
+		ret = bpf_fetch_prog_relo(ctx, section, &lderr, &sseen);
+	if (ret < 0 && !lderr)
+		ret = bpf_fetch_prog(ctx, section, &sseen);
+	if (ret < 0 && !sseen)
+		fprintf(stderr, "Program section \'%s\' not found in ELF file!\n",
+			section);
+	return ret;
+}
+
+static int bpf_find_map_by_id(struct bpf_elf_ctx *ctx, uint32_t id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++)
+		if (ctx->map_fds[i] && ctx->maps[i].id == id &&
+		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY)
+			return i;
+	return -1;
+}
+
+static int bpf_fill_prog_arrays(struct bpf_elf_ctx *ctx)
+{
+	struct bpf_elf_sec_data data;
+	uint32_t map_id, key_id;
+	int fd, i, ret, idx;
+
+	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
+		if (ctx->sec_done[i])
+			continue;
+
+		ret = bpf_fill_section_data(ctx, i, &data);
+		if (ret < 0)
+			continue;
+
+		ret = sscanf(data.sec_name, "%i/%i", &map_id, &key_id);
+		if (ret != 2)
+			continue;
+
+		idx = bpf_find_map_by_id(ctx, map_id);
+		if (idx < 0)
+			continue;
+
+		fd = bpf_fetch_prog_sec(ctx, data.sec_name);
+		if (fd < 0)
+			return -EIO;
+
+		ret = bpf_map_update(ctx->map_fds[idx], &key_id,
+				     &fd, BPF_ANY);
+		if (ret < 0) {
+			if (errno == E2BIG)
+				fprintf(stderr, "Tail call key %u for map %u out of bounds?\n",
+					key_id, map_id);
+			return -errno;
+		}
+
+		ctx->sec_done[i] = true;
+	}
+
+	return 0;
+}
+
+static void bpf_save_finfo(struct bpf_elf_ctx *ctx)
+{
+	struct stat st;
+	int ret;
+
+	memset(&ctx->stat, 0, sizeof(ctx->stat));
+
+	ret = fstat(ctx->obj_fd, &st);
+	if (ret < 0) {
+		fprintf(stderr, "Stat of elf file failed: %s\n",
+			strerror(errno));
+		return;
+	}
+
+	ctx->stat.st_dev = st.st_dev;
+	ctx->stat.st_ino = st.st_ino;
+}
+
+static int bpf_read_pin_mapping(FILE *fp, uint32_t *id, char *path)
+{
+	char buff[PATH_MAX];
+
+	while (fgets(buff, sizeof(buff), fp)) {
+		char *ptr = buff;
+
+		while (*ptr == ' ' || *ptr == '\t')
+			ptr++;
+
+		if (*ptr == '#' || *ptr == '\n' || *ptr == 0)
+			continue;
+
+		if (sscanf(ptr, "%i %s\n", id, path) != 2 &&
+		    sscanf(ptr, "%i %s #", id, path) != 2) {
+			strcpy(path, ptr);
+			return -1;
+		}
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static bool bpf_pinning_reserved(uint32_t pinning)
+{
+	switch (pinning) {
+	case PIN_NONE:
+	case PIN_OBJECT_NS:
+	case PIN_GLOBAL_NS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void bpf_hash_init(struct bpf_elf_ctx *ctx, const char *db_file)
+{
+	struct bpf_hash_entry *entry;
+	char subpath[PATH_MAX] = {};
+	uint32_t pinning;
+	FILE *fp;
+	int ret;
+
+	fp = fopen(db_file, "r");
+	if (!fp)
+		return;
+
+	while ((ret = bpf_read_pin_mapping(fp, &pinning, subpath))) {
+		if (ret == -1) {
+			fprintf(stderr, "Database %s is corrupted at: %s\n",
+				db_file, subpath);
+			fclose(fp);
+			return;
+		}
+
+		if (bpf_pinning_reserved(pinning)) {
+			fprintf(stderr, "Database %s, id %u is reserved - ignoring!\n",
+				db_file, pinning);
+			continue;
+		}
+
+		entry = malloc(sizeof(*entry));
+		if (!entry) {
+			fprintf(stderr, "No memory left for db entry!\n");
+			continue;
+		}
+
+		entry->pinning = pinning;
+		entry->subpath = strdup(subpath);
+		if (!entry->subpath) {
+			fprintf(stderr, "No memory left for db entry!\n");
+			free(entry);
+			continue;
+		}
+
+		entry->next = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
+		ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)] = entry;
+	}
+
+	fclose(fp);
+}
+
+static void bpf_hash_destroy(struct bpf_elf_ctx *ctx)
+{
+	struct bpf_hash_entry *entry;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->ht); i++) {
+		while ((entry = ctx->ht[i]) != NULL) {
+			ctx->ht[i] = entry->next;
+			free((char *)entry->subpath);
+			free(entry);
+		}
+	}
+}
+
+static int bpf_elf_check_ehdr(const struct bpf_elf_ctx *ctx)
+{
+	if (ctx->elf_hdr.e_type != ET_REL ||
+	    (ctx->elf_hdr.e_machine != EM_NONE &&
+	     ctx->elf_hdr.e_machine != EM_BPF) ||
+	    ctx->elf_hdr.e_version != EV_CURRENT) {
+		fprintf(stderr, "ELF format error, ELF file not for eBPF?\n");
+		return -EINVAL;
+	}
+
+	switch (ctx->elf_hdr.e_ident[EI_DATA]) {
+	default:
+		fprintf(stderr, "ELF format error, wrong endianness info?\n");
+		return -EINVAL;
+	case ELFDATA2LSB:
+		if (htons(1) == 1) {
+			fprintf(stderr,
+				"We are big endian, eBPF object is little endian!\n");
+			return -EIO;
+		}
+		break;
+	case ELFDATA2MSB:
+		if (htons(1) != 1) {
+			fprintf(stderr,
+				"We are little endian, eBPF object is big endian!\n");
+			return -EIO;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname,
+			    enum bpf_prog_type type, bool verbose)
+{
+	int ret = -EINVAL;
+
+	if (elf_version(EV_CURRENT) == EV_NONE ||
+	    bpf_init_env(pathname))
+		return ret;
+
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->verbose = verbose;
+	ctx->type    = type;
+
+	ctx->obj_fd = open(pathname, O_RDONLY);
+	if (ctx->obj_fd < 0)
+		return ctx->obj_fd;
+
+	ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL);
+	if (!ctx->elf_fd) {
+		ret = -EINVAL;
+		goto out_fd;
+	}
+
+	if (elf_kind(ctx->elf_fd) != ELF_K_ELF) {
+		ret = -EINVAL;
+		goto out_fd;
+	}
+
+	if (gelf_getehdr(ctx->elf_fd, &ctx->elf_hdr) !=
+	    &ctx->elf_hdr) {
+		ret = -EIO;
+		goto out_elf;
+	}
+
+	ret = bpf_elf_check_ehdr(ctx);
+	if (ret < 0)
+		goto out_elf;
+
+	ctx->sec_done = calloc(ctx->elf_hdr.e_shnum,
+			       sizeof(*(ctx->sec_done)));
+	if (!ctx->sec_done) {
+		ret = -ENOMEM;
+		goto out_elf;
+	}
+
+	if (ctx->verbose && bpf_log_realloc(ctx)) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	bpf_save_finfo(ctx);
+	bpf_hash_init(ctx, CONFDIR "/bpf_pinning");
+
+	return 0;
+out_free:
+	free(ctx->sec_done);
+out_elf:
+	elf_end(ctx->elf_fd);
+out_fd:
+	close(ctx->obj_fd);
+	return ret;
+}
+
+static int bpf_maps_count(struct bpf_elf_ctx *ctx)
+{
+	int i, count = 0;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
+		if (!ctx->map_fds[i])
+			break;
+		count++;
+	}
+
+	return count;
+}
+
+static void bpf_maps_teardown(struct bpf_elf_ctx *ctx)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
+		if (ctx->map_fds[i])
+			close(ctx->map_fds[i]);
+	}
+}
+
+static void bpf_elf_ctx_destroy(struct bpf_elf_ctx *ctx, bool failure)
+{
+	if (failure)
+		bpf_maps_teardown(ctx);
+
+	bpf_hash_destroy(ctx);
+
+	free(ctx->sec_done);
+	free(ctx->log);
+
+	elf_end(ctx->elf_fd);
+	close(ctx->obj_fd);
+}
+
+static struct bpf_elf_ctx __ctx;
+
+static int bpf_obj_open(const char *pathname, enum bpf_prog_type type,
+			const char *section, bool verbose)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	int fd = 0, ret;
+
+	ret = bpf_elf_ctx_init(ctx, pathname, type, verbose);
+	if (ret < 0) {
+		fprintf(stderr, "Cannot initialize ELF context!\n");
+		return ret;
+	}
+
+	ret = bpf_fetch_ancillary(ctx);
+	if (ret < 0) {
+		fprintf(stderr, "Error fetching ELF ancillary data!\n");
+		goto out;
+	}
+
+	fd = bpf_fetch_prog_sec(ctx, section);
+	if (fd < 0) {
+		fprintf(stderr, "Error fetching program/map!\n");
+		ret = fd;
+		goto out;
+	}
+
+	ret = bpf_fill_prog_arrays(ctx);
+	if (ret < 0)
+		fprintf(stderr, "Error filling program arrays!\n");
+out:
+	bpf_elf_ctx_destroy(ctx, ret < 0);
+	if (ret < 0) {
+		if (fd)
+			close(fd);
+		return ret;
+	}
+
+	return fd;
+}
+
+static int
+bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len,
+		 const struct bpf_map_data *aux, unsigned int entries)
+{
+	struct bpf_map_set_msg msg = {
+		.aux.uds_ver = BPF_SCM_AUX_VER,
+		.aux.num_ent = entries,
+	};
+	int *cmsg_buf, min_fd;
+	char *amsg_buf;
+	int i;
+
+	strncpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name));
+	memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st));
+
+	cmsg_buf = bpf_map_set_init(&msg, addr, addr_len);
+	amsg_buf = (char *)msg.aux.ent;
+
+	for (i = 0; i < entries; i += min_fd) {
+		int ret;
+
+		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
+		bpf_map_set_init_single(&msg, min_fd);
+
+		memcpy(cmsg_buf, &aux->fds[i], sizeof(aux->fds[0]) * min_fd);
+		memcpy(amsg_buf, &aux->ent[i], sizeof(aux->ent[0]) * min_fd);
+
+		ret = sendmsg(fd, &msg.hdr, 0);
+		if (ret <= 0)
+			return ret ? : -1;
+	}
+
+	return 0;
+}
+
+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;
+	char *amsg_buf, *mmsg_buf;
+	unsigned int needed = 1;
+	int i;
+
+	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
+	amsg_buf = (char *)msg.aux.ent;
+	mmsg_buf = (char *)&msg.aux;
+
+	for (i = 0; i < min(entries, needed); i += min_fd) {
+		struct cmsghdr *cmsg;
+		int ret;
+
+		min_fd = min(entries, 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;
+		if (msg.aux.uds_ver != BPF_SCM_AUX_VER)
+			return -ENOSYS;
+
+		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
+		if (min_fd > entries || min_fd <= 0)
+			return -EINVAL;
+
+		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));
+
+		needed = aux->num_ent;
+	}
+
+	return 0;
+}
+
+int bpf_send_map_fds(const char *path, const char *obj)
+{
+	struct bpf_elf_ctx *ctx = &__ctx;
+	struct sockaddr_un addr = { .sun_family = AF_UNIX };
+	struct bpf_map_data bpf_aux = {
+		.fds = ctx->map_fds,
+		.ent = ctx->maps,
+		.st  = &ctx->stat,
+		.obj = obj,
+	};
+	int fd, ret;
+
+	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open socket: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+	if (ret < 0) {
+		fprintf(stderr, "Cannot connect to %s: %s\n",
+			path, strerror(errno));
+		return -1;
+	}
+
+	ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux,
+			       bpf_maps_count(ctx));
+	if (ret < 0)
+		fprintf(stderr, "Cannot send fds to %s: %s\n",
+			path, strerror(errno));
+
+	bpf_maps_teardown(ctx);
+	close(fd);
+	return ret;
+}
+
+int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
+		     unsigned int entries)
+{
+	struct sockaddr_un addr = { .sun_family = AF_UNIX };
+	int fd, ret;
+
+	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Cannot open socket: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	strncpy(addr.sun_path, path, 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));
+		return -1;
+	}
+
+	ret = bpf_map_set_recv(fd, fds, aux, entries);
+	if (ret < 0)
+		fprintf(stderr, "Cannot recv fds from %s: %s\n",
+			path, strerror(errno));
+
+	unlink(addr.sun_path);
+	close(fd);
+	return ret;
+}
+#endif /* HAVE_ELF */
diff --git a/tc/Makefile b/tc/Makefile
index dfa875b..f986fcb 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -1,5 +1,5 @@
 TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o tc_monitor.o \
-       tc_exec.o tc_bpf.o m_police.o m_estimator.o m_action.o m_ematch.o \
+       tc_exec.o m_police.o m_estimator.o m_action.o m_ematch.o \
        emp_ematch.yacc.o emp_ematch.lex.o
 
 include ../Config
@@ -94,11 +94,6 @@ ifneq ($(TC_CONFIG_NO_XT),y)
   endif
 endif
 
-ifeq ($(TC_CONFIG_ELF),y)
-  CFLAGS += -DHAVE_ELF
-  LDLIBS += -lelf
-endif
-
 TCOBJ += $(TCMODULES)
 LDLIBS += -L. -ltc -lm
 
diff --git a/tc/e_bpf.c b/tc/e_bpf.c
index d1f5d87..84f43e6 100644
--- a/tc/e_bpf.c
+++ b/tc/e_bpf.c
@@ -15,8 +15,8 @@
 #include "utils.h"
 
 #include "tc_util.h"
-#include "tc_bpf.h"
 
+#include "bpf_util.h"
 #include "bpf_elf.h"
 #include "bpf_scm.h"
 
diff --git a/tc/f_bpf.c b/tc/f_bpf.c
index 665bc66..c4764d8 100644
--- a/tc/f_bpf.c
+++ b/tc/f_bpf.c
@@ -6,7 +6,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Authors:	Daniel Borkmann <dborkman@redhat.com>
+ * Authors:	Daniel Borkmann <daniel@iogearbox.net>
  */
 
 #include <stdio.h>
@@ -15,18 +15,12 @@
 #include <linux/bpf.h>
 
 #include "utils.h"
+
 #include "tc_util.h"
-#include "tc_bpf.h"
+#include "bpf_util.h"
 
 static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_CLS;
 
-static const int nla_tbl[BPF_NLA_MAX] = {
-	[BPF_NLA_OPS_LEN]	= TCA_BPF_OPS_LEN,
-	[BPF_NLA_OPS]		= TCA_BPF_OPS,
-	[BPF_NLA_FD]		= TCA_BPF_FD,
-	[BPF_NLA_NAME]		= TCA_BPF_NAME,
-};
-
 static void explain(void)
 {
 	fprintf(stderr, "Usage: ... bpf ...\n");
@@ -52,7 +46,7 @@ static void explain(void)
 	fprintf(stderr, "pinned eBPF program.\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where CLS_NAME refers to the section name containing the\n");
-	fprintf(stderr, "classifier (default \'%s\').\n", bpf_default_section(bpf_type));
+	fprintf(stderr, "classifier (default \'%s\').\n", bpf_prog_to_default_section(bpf_type));
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where UDS_FILE points to a unix domain socket file in order\n");
 	fprintf(stderr, "to hand off control of all created eBPF maps to an agent.\n");
@@ -61,6 +55,24 @@ static void explain(void)
 	fprintf(stderr, "NOTE: CLASSID is parsed as hexadecimal input.\n");
 }
 
+static void bpf_cbpf_cb(void *nl, const struct sock_filter *ops, int ops_len)
+{
+	addattr16(nl, MAX_MSG, TCA_BPF_OPS_LEN, ops_len);
+	addattr_l(nl, MAX_MSG, TCA_BPF_OPS, ops,
+		  ops_len * sizeof(struct sock_filter));
+}
+
+static void bpf_ebpf_cb(void *nl, int fd, const char *annotation)
+{
+	addattr32(nl, MAX_MSG, TCA_BPF_FD, fd);
+	addattrstrz(nl, MAX_MSG, TCA_BPF_NAME, annotation);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+	.cbpf_cb = bpf_cbpf_cb,
+	.ebpf_cb = bpf_ebpf_cb,
+};
+
 static int bpf_parse_opt(struct filter_util *qu, char *handle,
 			 int argc, char **argv, struct nlmsghdr *n)
 {
@@ -68,6 +80,7 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
 	struct tcmsg *t = NLMSG_DATA(n);
 	unsigned int bpf_gen_flags = 0;
 	unsigned int bpf_flags = 0;
+	struct bpf_cfg_in cfg = {};
 	bool seen_run = false;
 	struct rtattr *tail;
 	int ret = 0;
@@ -90,11 +103,17 @@ static int bpf_parse_opt(struct filter_util *qu, char *handle,
 			NEXT_ARG();
 opt_bpf:
 			seen_run = true;
-			if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type,
-					     &bpf_obj, &bpf_uds_name, n)) {
-				fprintf(stderr, "Failed to retrieve (e)BPF data!\n");
+			cfg.argc = argc;
+			cfg.argv = argv;
+
+			if (bpf_parse_common(bpf_type, &cfg, &bpf_cb_ops, n))
 				return -1;
-			}
+
+			argc = cfg.argc;
+			argv = cfg.argv;
+
+			bpf_obj = cfg.object;
+			bpf_uds_name = cfg.uds;
 		} else if (matches(*argv, "classid") == 0 ||
 			   matches(*argv, "flowid") == 0) {
 			unsigned int handle;
@@ -143,7 +162,7 @@ opt_bpf:
 
 	if (bpf_gen_flags)
 		addattr32(n, MAX_MSG, TCA_BPF_FLAGS_GEN, bpf_gen_flags);
-	if (bpf_obj && bpf_flags)
+	if (bpf_flags)
 		addattr32(n, MAX_MSG, TCA_BPF_FLAGS, bpf_flags);
 
 	tail->rta_len = (((void *)n) + n->nlmsg_len) - (void *)tail;
@@ -175,8 +194,6 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
 
 	if (tb[TCA_BPF_NAME])
 		fprintf(f, "%s ", rta_getattr_str(tb[TCA_BPF_NAME]));
-	else if (tb[TCA_BPF_FD])
-		fprintf(f, "pfd %u ", rta_getattr_u32(tb[TCA_BPF_FD]));
 
 	if (tb[TCA_BPF_FLAGS]) {
 		unsigned int flags = rta_getattr_u32(tb[TCA_BPF_FLAGS]);
@@ -195,20 +212,17 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f,
 			fprintf(f, "skip_sw ");
 	}
 
-	if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN]) {
+	if (tb[TCA_BPF_OPS] && tb[TCA_BPF_OPS_LEN])
 		bpf_print_ops(f, tb[TCA_BPF_OPS],
 			      rta_getattr_u16(tb[TCA_BPF_OPS_LEN]));
-		fprintf(f, "\n");
-	}
 
 	if (tb[TCA_BPF_POLICE]) {
 		fprintf(f, "\n");
 		tc_print_police(f, tb[TCA_BPF_POLICE]);
 	}
 
-	if (tb[TCA_BPF_ACT]) {
+	if (tb[TCA_BPF_ACT])
 		tc_print_action(f, tb[TCA_BPF_ACT]);
-	}
 
 	return 0;
 }
diff --git a/tc/m_bpf.c b/tc/m_bpf.c
index 9bf2a85..e26b85d 100644
--- a/tc/m_bpf.c
+++ b/tc/m_bpf.c
@@ -17,18 +17,12 @@
 #include <linux/tc_act/tc_bpf.h>
 
 #include "utils.h"
+
 #include "tc_util.h"
-#include "tc_bpf.h"
+#include "bpf_util.h"
 
 static const enum bpf_prog_type bpf_type = BPF_PROG_TYPE_SCHED_ACT;
 
-static const int nla_tbl[BPF_NLA_MAX] = {
-	[BPF_NLA_OPS_LEN]	= TCA_ACT_BPF_OPS_LEN,
-	[BPF_NLA_OPS]		= TCA_ACT_BPF_OPS,
-	[BPF_NLA_FD]		= TCA_ACT_BPF_FD,
-	[BPF_NLA_NAME]		= TCA_ACT_BPF_NAME,
-};
-
 static void explain(void)
 {
 	fprintf(stderr, "Usage: ... bpf ... [ index INDEX ]\n");
@@ -50,7 +44,7 @@ static void explain(void)
 	fprintf(stderr, "pinned eBPF program.\n");
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where ACT_NAME refers to the section name containing the\n");
-	fprintf(stderr, "action (default \'%s\').\n", bpf_default_section(bpf_type));
+	fprintf(stderr, "action (default \'%s\').\n", bpf_prog_to_default_section(bpf_type));
 	fprintf(stderr, "\n");
 	fprintf(stderr, "Where UDS_FILE points to a unix domain socket file in order\n");
 	fprintf(stderr, "to hand off control of all created eBPF maps to an agent.\n");
@@ -59,11 +53,30 @@ static void explain(void)
 	fprintf(stderr, "explicitly specifies an action index upon creation.\n");
 }
 
+static void bpf_cbpf_cb(void *nl, const struct sock_filter *ops, int ops_len)
+{
+	addattr16(nl, MAX_MSG, TCA_ACT_BPF_OPS_LEN, ops_len);
+	addattr_l(nl, MAX_MSG, TCA_ACT_BPF_OPS, ops,
+		  ops_len * sizeof(struct sock_filter));
+}
+
+static void bpf_ebpf_cb(void *nl, int fd, const char *annotation)
+{
+	addattr32(nl, MAX_MSG, TCA_ACT_BPF_FD, fd);
+	addattrstrz(nl, MAX_MSG, TCA_ACT_BPF_NAME, annotation);
+}
+
+static const struct bpf_cfg_ops bpf_cb_ops = {
+	.cbpf_cb = bpf_cbpf_cb,
+	.ebpf_cb = bpf_ebpf_cb,
+};
+
 static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv,
 			 int tca_id, struct nlmsghdr *n)
 {
 	const char *bpf_obj = NULL, *bpf_uds_name = NULL;
 	struct tc_act_bpf parm = { .action = TC_ACT_PIPE };
+	struct bpf_cfg_in cfg = {};
 	bool seen_run = false;
 	struct rtattr *tail;
 	int argc, ret = 0;
@@ -85,11 +98,17 @@ static int bpf_parse_opt(struct action_util *a, int *ptr_argc, char ***ptr_argv,
 			NEXT_ARG();
 opt_bpf:
 			seen_run = true;
-			if (bpf_parse_common(&argc, &argv, nla_tbl, bpf_type,
-					     &bpf_obj, &bpf_uds_name, n)) {
-				fprintf(stderr, "Failed to retrieve (e)BPF data!\n");
+			cfg.argc = argc;
+			cfg.argv = argv;
+
+			if (bpf_parse_common(bpf_type, &cfg, &bpf_cb_ops, n))
 				return -1;
-			}
+
+			argc = cfg.argc;
+			argv = cfg.argv;
+
+			bpf_obj = cfg.object;
+			bpf_uds_name = cfg.uds;
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -151,8 +170,6 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
 
 	if (tb[TCA_ACT_BPF_NAME])
 		fprintf(f, "%s ", rta_getattr_str(tb[TCA_ACT_BPF_NAME]));
-	else if (tb[TCA_ACT_BPF_FD])
-		fprintf(f, "pfd %u ", rta_getattr_u32(tb[TCA_ACT_BPF_FD]));
 
 	if (tb[TCA_ACT_BPF_OPS] && tb[TCA_ACT_BPF_OPS_LEN]) {
 		bpf_print_ops(f, tb[TCA_ACT_BPF_OPS],
diff --git a/tc/tc_bpf.c b/tc/tc_bpf.c
deleted file mode 100644
index b390f7e..0000000
--- a/tc/tc_bpf.c
+++ /dev/null
@@ -1,2010 +0,0 @@
-/*
- * tc_bpf.c	BPF common code
- *
- *		This program is free software; you can distribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- *
- * Authors:	Daniel Borkmann <dborkman@redhat.com>
- *		Jiri Pirko <jiri@resnulli.us>
- *		Alexei Starovoitov <ast@plumgrid.com>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <limits.h>
-
-#ifdef HAVE_ELF
-#include <libelf.h>
-#include <gelf.h>
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/un.h>
-#include <sys/vfs.h>
-#include <sys/mount.h>
-#include <sys/syscall.h>
-#include <sys/sendfile.h>
-#include <sys/resource.h>
-
-#include <linux/bpf.h>
-#include <linux/filter.h>
-#include <linux/if_alg.h>
-
-#include <arpa/inet.h>
-
-#include "utils.h"
-
-#include "bpf_elf.h"
-#include "bpf_scm.h"
-
-#include "tc_util.h"
-#include "tc_bpf.h"
-
-#ifndef AF_ALG
-#define AF_ALG 38
-#endif
-
-#ifndef EM_BPF
-#define EM_BPF 247
-#endif
-
-#ifdef HAVE_ELF
-static int bpf_obj_open(const char *path, enum bpf_prog_type type,
-			const char *sec, bool verbose);
-#else
-static int bpf_obj_open(const char *path, enum bpf_prog_type type,
-			const char *sec, bool verbose)
-{
-	fprintf(stderr, "No ELF library support compiled in.\n");
-	errno = ENOSYS;
-	return -1;
-}
-#endif
-
-static inline __u64 bpf_ptr_to_u64(const void *ptr)
-{
-	return (__u64)(unsigned long)ptr;
-}
-
-static int bpf(int cmd, union bpf_attr *attr, unsigned int size)
-{
-#ifdef __NR_bpf
-	return syscall(__NR_bpf, cmd, attr, size);
-#else
-	fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
-	errno = ENOSYS;
-	return -1;
-#endif
-}
-
-static int bpf_map_update(int fd, const void *key, const void *value,
-			  uint64_t flags)
-{
-	union bpf_attr attr = {};
-
-	attr.map_fd = fd;
-	attr.key = bpf_ptr_to_u64(key);
-	attr.value = bpf_ptr_to_u64(value);
-	attr.flags = flags;
-
-	return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
-}
-
-static int bpf_parse_string(char *arg, bool from_file, __u16 *bpf_len,
-			    char **bpf_string, bool *need_release,
-			    const char separator)
-{
-	char sp;
-
-	if (from_file) {
-		size_t tmp_len, op_len = sizeof("65535 255 255 4294967295,");
-		char *tmp_string;
-		FILE *fp;
-
-		tmp_len = sizeof("4096,") + BPF_MAXINSNS * op_len;
-		tmp_string = calloc(1, tmp_len);
-		if (tmp_string == NULL)
-			return -ENOMEM;
-
-		fp = fopen(arg, "r");
-		if (fp == NULL) {
-			perror("Cannot fopen");
-			free(tmp_string);
-			return -ENOENT;
-		}
-
-		if (!fgets(tmp_string, tmp_len, fp)) {
-			free(tmp_string);
-			fclose(fp);
-			return -EIO;
-		}
-
-		fclose(fp);
-
-		*need_release = true;
-		*bpf_string = tmp_string;
-	} else {
-		*need_release = false;
-		*bpf_string = arg;
-	}
-
-	if (sscanf(*bpf_string, "%hu%c", bpf_len, &sp) != 2 ||
-	    sp != separator) {
-		if (*need_release)
-			free(*bpf_string);
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int bpf_ops_parse(int argc, char **argv, struct sock_filter *bpf_ops,
-			 bool from_file)
-{
-	char *bpf_string, *token, separator = ',';
-	int ret = 0, i = 0;
-	bool need_release;
-	__u16 bpf_len = 0;
-
-	if (argc < 1)
-		return -EINVAL;
-	if (bpf_parse_string(argv[0], from_file, &bpf_len, &bpf_string,
-			     &need_release, separator))
-		return -EINVAL;
-	if (bpf_len == 0 || bpf_len > BPF_MAXINSNS) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	token = bpf_string;
-	while ((token = strchr(token, separator)) && (++token)[0]) {
-		if (i >= bpf_len) {
-			fprintf(stderr, "Real program length exceeds encoded length parameter!\n");
-			ret = -EINVAL;
-			goto out;
-		}
-
-		if (sscanf(token, "%hu %hhu %hhu %u,",
-			   &bpf_ops[i].code, &bpf_ops[i].jt,
-			   &bpf_ops[i].jf, &bpf_ops[i].k) != 4) {
-			fprintf(stderr, "Error at instruction %d!\n", i);
-			ret = -EINVAL;
-			goto out;
-		}
-
-		i++;
-	}
-
-	if (i != bpf_len) {
-		fprintf(stderr, "Parsed program length is less than encoded length parameter!\n");
-		ret = -EINVAL;
-		goto out;
-	}
-	ret = bpf_len;
-out:
-	if (need_release)
-		free(bpf_string);
-
-	return ret;
-}
-
-void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len)
-{
-	struct sock_filter *ops = (struct sock_filter *) RTA_DATA(bpf_ops);
-	int i;
-
-	if (len == 0)
-		return;
-
-	fprintf(f, "bytecode \'%u,", len);
-
-	for (i = 0; i < len - 1; i++)
-		fprintf(f, "%hu %hhu %hhu %u,", ops[i].code, ops[i].jt,
-			ops[i].jf, ops[i].k);
-
-	fprintf(f, "%hu %hhu %hhu %u\'", ops[i].code, ops[i].jt,
-		ops[i].jf, ops[i].k);
-}
-
-static void bpf_map_pin_report(const struct bpf_elf_map *pin,
-			       const struct bpf_elf_map *obj)
-{
-	fprintf(stderr, "Map specification differs from pinned file!\n");
-
-	if (obj->type != pin->type)
-		fprintf(stderr, " - Type:         %u (obj) != %u (pin)\n",
-			obj->type, pin->type);
-	if (obj->size_key != pin->size_key)
-		fprintf(stderr, " - Size key:     %u (obj) != %u (pin)\n",
-			obj->size_key, pin->size_key);
-	if (obj->size_value != pin->size_value)
-		fprintf(stderr, " - Size value:   %u (obj) != %u (pin)\n",
-			obj->size_value, pin->size_value);
-	if (obj->max_elem != pin->max_elem)
-		fprintf(stderr, " - Max elems:    %u (obj) != %u (pin)\n",
-			obj->max_elem, pin->max_elem);
-	if (obj->flags != pin->flags)
-		fprintf(stderr, " - Flags:        %#x (obj) != %#x (pin)\n",
-			obj->flags, pin->flags);
-
-	fprintf(stderr, "\n");
-}
-
-static int bpf_map_selfcheck_pinned(int fd, const struct bpf_elf_map *map,
-				    int length)
-{
-	char file[PATH_MAX], buff[4096];
-	struct bpf_elf_map tmp = {}, zero = {};
-	unsigned int val;
-	FILE *fp;
-
-	snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
-
-	fp = fopen(file, "r");
-	if (!fp) {
-		fprintf(stderr, "No procfs support?!\n");
-		return -EIO;
-	}
-
-	while (fgets(buff, sizeof(buff), fp)) {
-		if (sscanf(buff, "map_type:\t%u", &val) == 1)
-			tmp.type = val;
-		else if (sscanf(buff, "key_size:\t%u", &val) == 1)
-			tmp.size_key = val;
-		else if (sscanf(buff, "value_size:\t%u", &val) == 1)
-			tmp.size_value = val;
-		else if (sscanf(buff, "max_entries:\t%u", &val) == 1)
-			tmp.max_elem = val;
-		else if (sscanf(buff, "map_flags:\t%i", &val) == 1)
-			tmp.flags = val;
-	}
-
-	fclose(fp);
-
-	if (!memcmp(&tmp, map, length)) {
-		return 0;
-	} else {
-		/* If kernel doesn't have eBPF-related fdinfo, we cannot do much,
-		 * so just accept it. We know we do have an eBPF fd and in this
-		 * case, everything is 0. It is guaranteed that no such map exists
-		 * since map type of 0 is unloadable BPF_MAP_TYPE_UNSPEC.
-		 */
-		if (!memcmp(&tmp, &zero, length))
-			return 0;
-
-		bpf_map_pin_report(&tmp, map);
-		return -EINVAL;
-	}
-}
-
-static int bpf_mnt_fs(const char *target)
-{
-	bool bind_done = false;
-
-	while (mount("", target, "none", MS_PRIVATE | MS_REC, NULL)) {
-		if (errno != EINVAL || bind_done) {
-			fprintf(stderr, "mount --make-private %s failed: %s\n",
-				target,	strerror(errno));
-			return -1;
-		}
-
-		if (mount(target, target, "none", MS_BIND, NULL)) {
-			fprintf(stderr, "mount --bind %s %s failed: %s\n",
-				target,	target, strerror(errno));
-			return -1;
-		}
-
-		bind_done = true;
-	}
-
-	if (mount("bpf", target, "bpf", 0, NULL)) {
-		fprintf(stderr, "mount -t bpf bpf %s failed: %s\n",
-			target,	strerror(errno));
-		return -1;
-	}
-
-	return 0;
-}
-
-static int bpf_valid_mntpt(const char *mnt, unsigned long magic)
-{
-	struct statfs st_fs;
-
-	if (statfs(mnt, &st_fs) < 0)
-		return -ENOENT;
-	if ((unsigned long)st_fs.f_type != magic)
-		return -ENOENT;
-
-	return 0;
-}
-
-static const char *bpf_find_mntpt(const char *fstype, unsigned long magic,
-				  char *mnt, int len,
-				  const char * const *known_mnts)
-{
-	const char * const *ptr;
-	char type[100];
-	FILE *fp;
-
-	if (known_mnts) {
-		ptr = known_mnts;
-		while (*ptr) {
-			if (bpf_valid_mntpt(*ptr, magic) == 0) {
-				strncpy(mnt, *ptr, len - 1);
-				mnt[len - 1] = 0;
-				return mnt;
-			}
-			ptr++;
-		}
-	}
-
-	fp = fopen("/proc/mounts", "r");
-	if (fp == NULL || len != PATH_MAX)
-		return NULL;
-
-	while (fscanf(fp, "%*s %" textify(PATH_MAX) "s %99s %*s %*d %*d\n",
-		      mnt, type) == 2) {
-		if (strcmp(type, fstype) == 0)
-			break;
-	}
-
-	fclose(fp);
-	if (strcmp(type, fstype) != 0)
-		return NULL;
-
-	return mnt;
-}
-
-int bpf_trace_pipe(void)
-{
-	char tracefs_mnt[PATH_MAX] = TRACE_DIR_MNT;
-	static const char * const tracefs_known_mnts[] = {
-		TRACE_DIR_MNT,
-		"/sys/kernel/debug/tracing",
-		"/tracing",
-		"/trace",
-		0,
-	};
-	char tpipe[PATH_MAX];
-	const char *mnt;
-	int fd;
-
-	mnt = bpf_find_mntpt("tracefs", TRACEFS_MAGIC, tracefs_mnt,
-			     sizeof(tracefs_mnt), tracefs_known_mnts);
-	if (!mnt) {
-		fprintf(stderr, "tracefs not mounted?\n");
-		return -1;
-	}
-
-	snprintf(tpipe, sizeof(tpipe), "%s/trace_pipe", mnt);
-
-	fd = open(tpipe, O_RDONLY);
-	if (fd < 0)
-		return -1;
-
-	fprintf(stderr, "Running! Hang up with ^C!\n\n");
-	while (1) {
-		static char buff[4096];
-		ssize_t ret;
-
-		ret = read(fd, buff, sizeof(buff) - 1);
-		if (ret > 0) {
-			write(2, buff, ret);
-			fflush(stderr);
-		}
-	}
-
-	return 0;
-}
-
-static const char *bpf_get_tc_dir(void)
-{
-	static bool bpf_mnt_cached;
-	static char bpf_tc_dir[PATH_MAX];
-	static const char *mnt;
-	static const char * const bpf_known_mnts[] = {
-		BPF_DIR_MNT,
-		0,
-	};
-	char bpf_mnt[PATH_MAX] = BPF_DIR_MNT;
-	char bpf_glo_dir[PATH_MAX];
-	int ret;
-
-	if (bpf_mnt_cached)
-		goto done;
-
-	mnt = bpf_find_mntpt("bpf", BPF_FS_MAGIC, bpf_mnt, sizeof(bpf_mnt),
-			     bpf_known_mnts);
-	if (!mnt) {
-		mnt = getenv(BPF_ENV_MNT);
-		if (!mnt)
-			mnt = BPF_DIR_MNT;
-		ret = bpf_mnt_fs(mnt);
-		if (ret) {
-			mnt = NULL;
-			goto out;
-		}
-	}
-
-	snprintf(bpf_tc_dir, sizeof(bpf_tc_dir), "%s/%s", mnt, BPF_DIR_TC);
-	ret = mkdir(bpf_tc_dir, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", bpf_tc_dir,
-			strerror(errno));
-		mnt = NULL;
-		goto out;
-	}
-
-	snprintf(bpf_glo_dir, sizeof(bpf_glo_dir), "%s/%s",
-		 bpf_tc_dir, BPF_DIR_GLOBALS);
-	ret = mkdir(bpf_glo_dir, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", bpf_glo_dir,
-			strerror(errno));
-		mnt = NULL;
-		goto out;
-	}
-
-	mnt = bpf_tc_dir;
-out:
-	bpf_mnt_cached = true;
-done:
-	return mnt;
-}
-
-static int bpf_obj_get(const char *pathname)
-{
-	union bpf_attr attr = {};
-	char tmp[PATH_MAX];
-
-	if (strlen(pathname) > 2 && pathname[0] == 'm' &&
-	    pathname[1] == ':' && bpf_get_tc_dir()) {
-		snprintf(tmp, sizeof(tmp), "%s/%s",
-			 bpf_get_tc_dir(), pathname + 2);
-		pathname = tmp;
-	}
-
-	attr.pathname = bpf_ptr_to_u64(pathname);
-
-	return bpf(BPF_OBJ_GET, &attr, sizeof(attr));
-}
-
-const char *bpf_default_section(const enum bpf_prog_type type)
-{
-	switch (type) {
-	case BPF_PROG_TYPE_SCHED_CLS:
-		return ELF_SECTION_CLASSIFIER;
-	case BPF_PROG_TYPE_SCHED_ACT:
-		return ELF_SECTION_ACTION;
-	default:
-		return NULL;
-	}
-}
-
-enum bpf_mode {
-	CBPF_BYTECODE = 0,
-	CBPF_FILE,
-	EBPF_OBJECT,
-	EBPF_PINNED,
-	__BPF_MODE_MAX,
-#define BPF_MODE_MAX	__BPF_MODE_MAX
-};
-
-static int bpf_parse(int *ptr_argc, char ***ptr_argv, const bool *opt_tbl,
-		     enum bpf_prog_type *type, enum bpf_mode *mode,
-		     const char **ptr_object, const char **ptr_section,
-		     const char **ptr_uds_name, struct sock_filter *opcodes)
-{
-	const char *file, *section, *uds_name;
-	bool verbose = false;
-	int ret, argc;
-	char **argv;
-
-	argv = *ptr_argv;
-	argc = *ptr_argc;
-
-	if (opt_tbl[CBPF_BYTECODE] &&
-	    (matches(*argv, "bytecode") == 0 ||
-	     strcmp(*argv, "bc") == 0)) {
-		*mode = CBPF_BYTECODE;
-	} else if (opt_tbl[CBPF_FILE] &&
-		   (matches(*argv, "bytecode-file") == 0 ||
-		    strcmp(*argv, "bcf") == 0)) {
-		*mode = CBPF_FILE;
-	} else if (opt_tbl[EBPF_OBJECT] &&
-		   (matches(*argv, "object-file") == 0 ||
-		    strcmp(*argv, "obj") == 0)) {
-		*mode = EBPF_OBJECT;
-	} else if (opt_tbl[EBPF_PINNED] &&
-		   (matches(*argv, "object-pinned") == 0 ||
-		    matches(*argv, "pinned") == 0 ||
-		    matches(*argv, "fd") == 0)) {
-		*mode = EBPF_PINNED;
-	} else {
-		fprintf(stderr, "What mode is \"%s\"?\n", *argv);
-		return -1;
-	}
-
-	NEXT_ARG();
-	file = section = uds_name = NULL;
-	if (*mode == EBPF_OBJECT || *mode == EBPF_PINNED) {
-		file = *argv;
-		NEXT_ARG_FWD();
-
-		if (*type == BPF_PROG_TYPE_UNSPEC) {
-			if (argc > 0 && matches(*argv, "type") == 0) {
-				NEXT_ARG();
-				if (matches(*argv, "cls") == 0) {
-					*type = BPF_PROG_TYPE_SCHED_CLS;
-				} else if (matches(*argv, "act") == 0) {
-					*type = BPF_PROG_TYPE_SCHED_ACT;
-				} else {
-					fprintf(stderr, "What type is \"%s\"?\n",
-						*argv);
-					return -1;
-				}
-				NEXT_ARG_FWD();
-			} else {
-				*type = BPF_PROG_TYPE_SCHED_CLS;
-			}
-		}
-
-		section = bpf_default_section(*type);
-		if (argc > 0 && matches(*argv, "section") == 0) {
-			NEXT_ARG();
-			section = *argv;
-			NEXT_ARG_FWD();
-		}
-
-		uds_name = getenv(BPF_ENV_UDS);
-		if (argc > 0 && !uds_name &&
-		    matches(*argv, "export") == 0) {
-			NEXT_ARG();
-			uds_name = *argv;
-			NEXT_ARG_FWD();
-		}
-
-		if (argc > 0 && matches(*argv, "verbose") == 0) {
-			verbose = true;
-			NEXT_ARG_FWD();
-		}
-
-		PREV_ARG();
-	}
-
-	if (*mode == CBPF_BYTECODE || *mode == CBPF_FILE)
-		ret = bpf_ops_parse(argc, argv, opcodes, *mode == CBPF_FILE);
-	else if (*mode == EBPF_OBJECT)
-		ret = bpf_obj_open(file, *type, section, verbose);
-	else if (*mode == EBPF_PINNED)
-		ret = bpf_obj_get(file);
-	else
-		return -1;
-
-	if (ptr_object)
-		*ptr_object = file;
-	if (ptr_section)
-		*ptr_section = section;
-	if (ptr_uds_name)
-		*ptr_uds_name = uds_name;
-
-	*ptr_argc = argc;
-	*ptr_argv = argv;
-
-	return ret;
-}
-
-int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl,
-		     enum bpf_prog_type type, const char **ptr_object,
-		     const char **ptr_uds_name, struct nlmsghdr *n)
-{
-	struct sock_filter opcodes[BPF_MAXINSNS];
-	const bool opt_tbl[BPF_MODE_MAX] = {
-		[CBPF_BYTECODE]	= true,
-		[CBPF_FILE]	= true,
-		[EBPF_OBJECT]	= true,
-		[EBPF_PINNED]	= true,
-	};
-	char annotation[256];
-	const char *section;
-	enum bpf_mode mode;
-	int ret;
-
-	ret = bpf_parse(ptr_argc, ptr_argv, opt_tbl, &type, &mode,
-			ptr_object, &section, ptr_uds_name, opcodes);
-	if (ret < 0)
-		return ret;
-
-	if (mode == CBPF_BYTECODE || mode == CBPF_FILE) {
-		addattr16(n, MAX_MSG, nla_tbl[BPF_NLA_OPS_LEN], ret);
-		addattr_l(n, MAX_MSG, nla_tbl[BPF_NLA_OPS], opcodes,
-			  ret * sizeof(struct sock_filter));
-	}
-
-	if (mode == EBPF_OBJECT || mode == EBPF_PINNED) {
-		snprintf(annotation, sizeof(annotation), "%s:[%s]",
-			 basename(*ptr_object), mode == EBPF_PINNED ?
-			 "*fsobj" : section);
-
-		addattr32(n, MAX_MSG, nla_tbl[BPF_NLA_FD], ret);
-		addattrstrz(n, MAX_MSG, nla_tbl[BPF_NLA_NAME], annotation);
-	}
-
-	return 0;
-}
-
-int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv)
-{
-	enum bpf_prog_type type = BPF_PROG_TYPE_UNSPEC;
-	const bool opt_tbl[BPF_MODE_MAX] = {
-		[CBPF_BYTECODE]	= false,
-		[CBPF_FILE]	= false,
-		[EBPF_OBJECT]	= true,
-		[EBPF_PINNED]	= true,
-	};
-	const struct bpf_elf_map test = {
-		.type		= BPF_MAP_TYPE_PROG_ARRAY,
-		.size_key	= sizeof(int),
-		.size_value	= sizeof(int),
-	};
-	int ret, prog_fd, map_fd;
-	const char *section;
-	enum bpf_mode mode;
-	uint32_t map_key;
-
-	prog_fd = bpf_parse(&argc, &argv, opt_tbl, &type, &mode,
-			    NULL, &section, NULL, NULL);
-	if (prog_fd < 0)
-		return prog_fd;
-	if (key) {
-		map_key = *key;
-	} else {
-		ret = sscanf(section, "%*i/%i", &map_key);
-		if (ret != 1) {
-			fprintf(stderr, "Couldn\'t infer map key from section name! Please provide \'key\' argument!\n");
-			ret = -EINVAL;
-			goto out_prog;
-		}
-	}
-
-	map_fd = bpf_obj_get(map_path);
-	if (map_fd < 0) {
-		fprintf(stderr, "Couldn\'t retrieve pinned map \'%s\': %s\n",
-			map_path, strerror(errno));
-		ret = map_fd;
-		goto out_prog;
-	}
-
-	ret = bpf_map_selfcheck_pinned(map_fd, &test,
-				       offsetof(struct bpf_elf_map, max_elem));
-	if (ret < 0) {
-		fprintf(stderr, "Map \'%s\' self-check failed!\n", map_path);
-		goto out_map;
-	}
-
-	ret = bpf_map_update(map_fd, &map_key, &prog_fd, BPF_ANY);
-	if (ret < 0)
-		fprintf(stderr, "Map update failed: %s\n", strerror(errno));
-out_map:
-	close(map_fd);
-out_prog:
-	close(prog_fd);
-	return ret;
-}
-
-#ifdef HAVE_ELF
-struct bpf_elf_prog {
-	enum bpf_prog_type	type;
-	const struct bpf_insn	*insns;
-	size_t			size;
-	const char		*license;
-};
-
-struct bpf_hash_entry {
-	unsigned int		pinning;
-	const char		*subpath;
-	struct bpf_hash_entry	*next;
-};
-
-struct bpf_elf_ctx {
-	Elf			*elf_fd;
-	GElf_Ehdr		elf_hdr;
-	Elf_Data		*sym_tab;
-	Elf_Data		*str_tab;
-	int			obj_fd;
-	int			map_fds[ELF_MAX_MAPS];
-	struct bpf_elf_map	maps[ELF_MAX_MAPS];
-	int			sym_num;
-	int			map_num;
-	bool			*sec_done;
-	int			sec_maps;
-	char			license[ELF_MAX_LICENSE_LEN];
-	enum bpf_prog_type	type;
-	bool			verbose;
-	struct bpf_elf_st	stat;
-	struct bpf_hash_entry	*ht[256];
-	char			*log;
-	size_t			log_size;
-};
-
-struct bpf_elf_sec_data {
-	GElf_Shdr		sec_hdr;
-	Elf_Data		*sec_data;
-	const char		*sec_name;
-};
-
-struct bpf_map_data {
-	int			*fds;
-	const char		*obj;
-	struct bpf_elf_st	*st;
-	struct bpf_elf_map	*ent;
-};
-
-static __check_format_string(2, 3) void
-bpf_dump_error(struct bpf_elf_ctx *ctx, const char *format, ...)
-{
-	va_list vl;
-
-	va_start(vl, format);
-	vfprintf(stderr, format, vl);
-	va_end(vl);
-
-	if (ctx->log && ctx->log[0]) {
-		if (ctx->verbose) {
-			fprintf(stderr, "%s\n", ctx->log);
-		} else {
-			unsigned int off = 0, len = strlen(ctx->log);
-
-			if (len > BPF_MAX_LOG) {
-				off = len - BPF_MAX_LOG;
-				fprintf(stderr, "Skipped %u bytes, use \'verb\' option for the full verbose log.\n[...]\n",
-					off);
-			}
-			fprintf(stderr, "%s\n", ctx->log + off);
-		}
-
-		memset(ctx->log, 0, ctx->log_size);
-	}
-}
-
-static int bpf_log_realloc(struct bpf_elf_ctx *ctx)
-{
-	size_t log_size = ctx->log_size;
-	void *ptr;
-
-	if (!ctx->log) {
-		log_size = 65536;
-	} else {
-		log_size <<= 1;
-		if (log_size > (UINT_MAX >> 8))
-			return -EINVAL;
-	}
-
-	ptr = realloc(ctx->log, log_size);
-	if (!ptr)
-		return -ENOMEM;
-
-	ctx->log = ptr;
-	ctx->log_size = log_size;
-
-	return 0;
-}
-
-static int bpf_map_create(enum bpf_map_type type, uint32_t size_key,
-			  uint32_t size_value, uint32_t max_elem,
-			  uint32_t flags)
-{
-	union bpf_attr attr = {};
-
-	attr.map_type = type;
-	attr.key_size = size_key;
-	attr.value_size = size_value;
-	attr.max_entries = max_elem;
-	attr.map_flags = flags;
-
-	return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
-}
-
-static int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-			 size_t size_insns, const char *license, char *log,
-			 size_t size_log)
-{
-	union bpf_attr attr = {};
-
-	attr.prog_type = type;
-	attr.insns = bpf_ptr_to_u64(insns);
-	attr.insn_cnt = size_insns / sizeof(struct bpf_insn);
-	attr.license = bpf_ptr_to_u64(license);
-
-	if (size_log > 0) {
-		attr.log_buf = bpf_ptr_to_u64(log);
-		attr.log_size = size_log;
-		attr.log_level = 1;
-	}
-
-	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
-}
-
-static int bpf_obj_pin(int fd, const char *pathname)
-{
-	union bpf_attr attr = {};
-
-	attr.pathname = bpf_ptr_to_u64(pathname);
-	attr.bpf_fd = fd;
-
-	return bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
-}
-
-static int bpf_obj_hash(const char *object, uint8_t *out, size_t len)
-{
-	struct sockaddr_alg alg = {
-		.salg_family	= AF_ALG,
-		.salg_type	= "hash",
-		.salg_name	= "sha1",
-	};
-	int ret, cfd, ofd, ffd;
-	struct stat stbuff;
-	ssize_t size;
-
-	if (!object || len != 20)
-		return -EINVAL;
-
-	cfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
-	if (cfd < 0) {
-		fprintf(stderr, "Cannot get AF_ALG socket: %s\n",
-			strerror(errno));
-		return cfd;
-	}
-
-	ret = bind(cfd, (struct sockaddr *)&alg, sizeof(alg));
-	if (ret < 0) {
-		fprintf(stderr, "Error binding socket: %s\n", strerror(errno));
-		goto out_cfd;
-	}
-
-	ofd = accept(cfd, NULL, 0);
-	if (ofd < 0) {
-		fprintf(stderr, "Error accepting socket: %s\n",
-			strerror(errno));
-		ret = ofd;
-		goto out_cfd;
-	}
-
-	ffd = open(object, O_RDONLY);
-	if (ffd < 0) {
-		fprintf(stderr, "Error opening object %s: %s\n",
-			object, strerror(errno));
-		ret = ffd;
-		goto out_ofd;
-	}
-
-	ret = fstat(ffd, &stbuff);
-	if (ret < 0) {
-		fprintf(stderr, "Error doing fstat: %s\n",
-			strerror(errno));
-		goto out_ffd;
-	}
-
-	size = sendfile(ofd, ffd, NULL, stbuff.st_size);
-	if (size != stbuff.st_size) {
-		fprintf(stderr, "Error from sendfile (%zd vs %zu bytes): %s\n",
-			size, stbuff.st_size, strerror(errno));
-		ret = -1;
-		goto out_ffd;
-	}
-
-	size = read(ofd, out, len);
-	if (size != len) {
-		fprintf(stderr, "Error from read (%zd vs %zu bytes): %s\n",
-			size, len, strerror(errno));
-		ret = -1;
-	} else {
-		ret = 0;
-	}
-out_ffd:
-	close(ffd);
-out_ofd:
-	close(ofd);
-out_cfd:
-	close(cfd);
-	return ret;
-}
-
-static const char *bpf_get_obj_uid(const char *pathname)
-{
-	static bool bpf_uid_cached;
-	static char bpf_uid[64];
-	uint8_t tmp[20];
-	int ret;
-
-	if (bpf_uid_cached)
-		goto done;
-
-	ret = bpf_obj_hash(pathname, tmp, sizeof(tmp));
-	if (ret) {
-		fprintf(stderr, "Object hashing failed!\n");
-		return NULL;
-	}
-
-	hexstring_n2a(tmp, sizeof(tmp), bpf_uid, sizeof(bpf_uid));
-	bpf_uid_cached = true;
-done:
-	return bpf_uid;
-}
-
-static int bpf_init_env(const char *pathname)
-{
-	struct rlimit limit = {
-		.rlim_cur = RLIM_INFINITY,
-		.rlim_max = RLIM_INFINITY,
-	};
-
-	/* Don't bother in case we fail! */
-	setrlimit(RLIMIT_MEMLOCK, &limit);
-
-	if (!bpf_get_tc_dir()) {
-		fprintf(stderr, "Continuing without mounted eBPF fs. Too old kernel?\n");
-		return 0;
-	}
-
-	if (!bpf_get_obj_uid(pathname))
-		return -1;
-
-	return 0;
-}
-
-static const char *bpf_custom_pinning(const struct bpf_elf_ctx *ctx,
-				      uint32_t pinning)
-{
-	struct bpf_hash_entry *entry;
-
-	entry = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
-	while (entry && entry->pinning != pinning)
-		entry = entry->next;
-
-	return entry ? entry->subpath : NULL;
-}
-
-static bool bpf_no_pinning(const struct bpf_elf_ctx *ctx,
-			   uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_OBJECT_NS:
-	case PIN_GLOBAL_NS:
-		return false;
-	case PIN_NONE:
-		return true;
-	default:
-		return !bpf_custom_pinning(ctx, pinning);
-	}
-}
-
-static void bpf_make_pathname(char *pathname, size_t len, const char *name,
-			      const struct bpf_elf_ctx *ctx, uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_OBJECT_NS:
-		snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(),
-			 bpf_get_obj_uid(NULL), name);
-		break;
-	case PIN_GLOBAL_NS:
-		snprintf(pathname, len, "%s/%s/%s", bpf_get_tc_dir(),
-			 BPF_DIR_GLOBALS, name);
-		break;
-	default:
-		snprintf(pathname, len, "%s/../%s/%s", bpf_get_tc_dir(),
-			 bpf_custom_pinning(ctx, pinning), name);
-		break;
-	}
-}
-
-static int bpf_probe_pinned(const char *name, const struct bpf_elf_ctx *ctx,
-			    uint32_t pinning)
-{
-	char pathname[PATH_MAX];
-
-	if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir())
-		return 0;
-
-	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
-	return bpf_obj_get(pathname);
-}
-
-static int bpf_make_obj_path(void)
-{
-	char tmp[PATH_MAX];
-	int ret;
-
-	snprintf(tmp, sizeof(tmp), "%s/%s", bpf_get_tc_dir(),
-		 bpf_get_obj_uid(NULL));
-
-	ret = mkdir(tmp, S_IRWXU);
-	if (ret && errno != EEXIST) {
-		fprintf(stderr, "mkdir %s failed: %s\n", tmp, strerror(errno));
-		return ret;
-	}
-
-	return 0;
-}
-
-static int bpf_make_custom_path(const char *todo)
-{
-	char tmp[PATH_MAX], rem[PATH_MAX], *sub;
-	int ret;
-
-	snprintf(tmp, sizeof(tmp), "%s/../", bpf_get_tc_dir());
-	snprintf(rem, sizeof(rem), "%s/", todo);
-	sub = strtok(rem, "/");
-
-	while (sub) {
-		if (strlen(tmp) + strlen(sub) + 2 > PATH_MAX)
-			return -EINVAL;
-
-		strcat(tmp, sub);
-		strcat(tmp, "/");
-
-		ret = mkdir(tmp, S_IRWXU);
-		if (ret && errno != EEXIST) {
-			fprintf(stderr, "mkdir %s failed: %s\n", tmp,
-				strerror(errno));
-			return ret;
-		}
-
-		sub = strtok(NULL, "/");
-	}
-
-	return 0;
-}
-
-static int bpf_place_pinned(int fd, const char *name,
-			    const struct bpf_elf_ctx *ctx, uint32_t pinning)
-{
-	char pathname[PATH_MAX];
-	const char *tmp;
-	int ret = 0;
-
-	if (bpf_no_pinning(ctx, pinning) || !bpf_get_tc_dir())
-		return 0;
-
-	if (pinning == PIN_OBJECT_NS)
-		ret = bpf_make_obj_path();
-	else if ((tmp = bpf_custom_pinning(ctx, pinning)))
-		ret = bpf_make_custom_path(tmp);
-	if (ret < 0)
-		return ret;
-
-	bpf_make_pathname(pathname, sizeof(pathname), name, ctx, pinning);
-	return bpf_obj_pin(fd, pathname);
-}
-
-static void bpf_prog_report(int fd, const char *section,
-			    const struct bpf_elf_prog *prog,
-			    struct bpf_elf_ctx *ctx)
-{
-	unsigned int insns = prog->size / sizeof(struct bpf_insn);
-
-	fprintf(stderr, "\nProg section \'%s\' %s%s (%d)!\n", section,
-		fd < 0 ? "rejected: " : "loaded",
-		fd < 0 ? strerror(errno) : "",
-		fd < 0 ? errno : fd);
-
-	fprintf(stderr, " - Type:         %u\n", prog->type);
-	fprintf(stderr, " - Instructions: %u (%u over limit)\n",
-		insns, insns > BPF_MAXINSNS ? insns - BPF_MAXINSNS : 0);
-	fprintf(stderr, " - License:      %s\n\n", prog->license);
-
-	bpf_dump_error(ctx, "Verifier analysis:\n\n");
-}
-
-static int bpf_prog_attach(const char *section,
-			   const struct bpf_elf_prog *prog,
-			   struct bpf_elf_ctx *ctx)
-{
-	int tries = 0, fd;
-retry:
-	errno = 0;
-	fd = bpf_prog_load(prog->type, prog->insns, prog->size,
-			   prog->license, ctx->log, ctx->log_size);
-	if (fd < 0 || ctx->verbose) {
-		/* The verifier log is pretty chatty, sometimes so chatty
-		 * on larger programs, that we could fail to dump everything
-		 * into our buffer. Still, try to give a debuggable error
-		 * log for the user, so enlarge it and re-fail.
-		 */
-		if (fd < 0 && (errno == ENOSPC || !ctx->log_size)) {
-			if (tries++ < 6 && !bpf_log_realloc(ctx))
-				goto retry;
-
-			fprintf(stderr, "Log buffer too small to dump verifier log %zu bytes (%d tries)!\n",
-				ctx->log_size, tries);
-			return fd;
-		}
-
-		bpf_prog_report(fd, section, prog, ctx);
-	}
-
-	return fd;
-}
-
-static void bpf_map_report(int fd, const char *name,
-			   const struct bpf_elf_map *map,
-			   struct bpf_elf_ctx *ctx)
-{
-	fprintf(stderr, "Map object \'%s\' %s%s (%d)!\n", name,
-		fd < 0 ? "rejected: " : "loaded",
-		fd < 0 ? strerror(errno) : "",
-		fd < 0 ? errno : fd);
-
-	fprintf(stderr, " - Type:         %u\n", map->type);
-	fprintf(stderr, " - Identifier:   %u\n", map->id);
-	fprintf(stderr, " - Pinning:      %u\n", map->pinning);
-	fprintf(stderr, " - Size key:     %u\n", map->size_key);
-	fprintf(stderr, " - Size value:   %u\n", map->size_value);
-	fprintf(stderr, " - Max elems:    %u\n", map->max_elem);
-	fprintf(stderr, " - Flags:        %#x\n\n", map->flags);
-}
-
-static int bpf_map_attach(const char *name, const struct bpf_elf_map *map,
-			  struct bpf_elf_ctx *ctx)
-{
-	int fd, ret;
-
-	fd = bpf_probe_pinned(name, ctx, map->pinning);
-	if (fd > 0) {
-		ret = bpf_map_selfcheck_pinned(fd, map,
-					       offsetof(struct bpf_elf_map,
-							id));
-		if (ret < 0) {
-			close(fd);
-			fprintf(stderr, "Map \'%s\' self-check failed!\n",
-				name);
-			return ret;
-		}
-		if (ctx->verbose)
-			fprintf(stderr, "Map \'%s\' loaded as pinned!\n",
-				name);
-		return fd;
-	}
-
-	errno = 0;
-	fd = bpf_map_create(map->type, map->size_key, map->size_value,
-			    map->max_elem, map->flags);
-	if (fd < 0 || ctx->verbose) {
-		bpf_map_report(fd, name, map, ctx);
-		if (fd < 0)
-			return fd;
-	}
-
-	ret = bpf_place_pinned(fd, name, ctx, map->pinning);
-	if (ret < 0 && errno != EEXIST) {
-		fprintf(stderr, "Could not pin %s map: %s\n", name,
-			strerror(errno));
-		close(fd);
-		return ret;
-	}
-
-	return fd;
-}
-
-static const char *bpf_str_tab_name(const struct bpf_elf_ctx *ctx,
-				    const GElf_Sym *sym)
-{
-	return ctx->str_tab->d_buf + sym->st_name;
-}
-
-static const char *bpf_map_fetch_name(struct bpf_elf_ctx *ctx, int which)
-{
-	GElf_Sym sym;
-	int i;
-
-	for (i = 0; i < ctx->sym_num; i++) {
-		if (gelf_getsym(ctx->sym_tab, i, &sym) != &sym)
-			continue;
-
-		if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
-		    GELF_ST_TYPE(sym.st_info) != STT_NOTYPE ||
-		    sym.st_shndx != ctx->sec_maps ||
-		    sym.st_value / sizeof(struct bpf_elf_map) != which)
-			continue;
-
-		return bpf_str_tab_name(ctx, &sym);
-	}
-
-	return NULL;
-}
-
-static int bpf_maps_attach_all(struct bpf_elf_ctx *ctx)
-{
-	const char *map_name;
-	int i, fd;
-
-	for (i = 0; i < ctx->map_num; i++) {
-		map_name = bpf_map_fetch_name(ctx, i);
-		if (!map_name)
-			return -EIO;
-
-		fd = bpf_map_attach(map_name, &ctx->maps[i], ctx);
-		if (fd < 0)
-			return fd;
-
-		ctx->map_fds[i] = fd;
-	}
-
-	return 0;
-}
-
-static int bpf_fill_section_data(struct bpf_elf_ctx *ctx, int section,
-				 struct bpf_elf_sec_data *data)
-{
-	Elf_Data *sec_edata;
-	GElf_Shdr sec_hdr;
-	Elf_Scn *sec_fd;
-	char *sec_name;
-
-	memset(data, 0, sizeof(*data));
-
-	sec_fd = elf_getscn(ctx->elf_fd, section);
-	if (!sec_fd)
-		return -EINVAL;
-	if (gelf_getshdr(sec_fd, &sec_hdr) != &sec_hdr)
-		return -EIO;
-
-	sec_name = elf_strptr(ctx->elf_fd, ctx->elf_hdr.e_shstrndx,
-			      sec_hdr.sh_name);
-	if (!sec_name || !sec_hdr.sh_size)
-		return -ENOENT;
-
-	sec_edata = elf_getdata(sec_fd, NULL);
-	if (!sec_edata || elf_getdata(sec_fd, sec_edata))
-		return -EIO;
-
-	memcpy(&data->sec_hdr, &sec_hdr, sizeof(sec_hdr));
-
-	data->sec_name = sec_name;
-	data->sec_data = sec_edata;
-	return 0;
-}
-
-static int bpf_fetch_maps(struct bpf_elf_ctx *ctx, int section,
-			  struct bpf_elf_sec_data *data)
-{
-	if (data->sec_data->d_size % sizeof(struct bpf_elf_map) != 0)
-		return -EINVAL;
-
-	ctx->map_num = data->sec_data->d_size / sizeof(struct bpf_elf_map);
-	ctx->sec_maps = section;
-	ctx->sec_done[section] = true;
-
-	if (ctx->map_num > ARRAY_SIZE(ctx->map_fds)) {
-		fprintf(stderr, "Too many BPF maps in ELF section!\n");
-		return -ENOMEM;
-	}
-
-	memcpy(ctx->maps, data->sec_data->d_buf, data->sec_data->d_size);
-	return 0;
-}
-
-static int bpf_fetch_license(struct bpf_elf_ctx *ctx, int section,
-			     struct bpf_elf_sec_data *data)
-{
-	if (data->sec_data->d_size > sizeof(ctx->license))
-		return -ENOMEM;
-
-	memcpy(ctx->license, data->sec_data->d_buf, data->sec_data->d_size);
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_fetch_symtab(struct bpf_elf_ctx *ctx, int section,
-			    struct bpf_elf_sec_data *data)
-{
-	ctx->sym_tab = data->sec_data;
-	ctx->sym_num = data->sec_hdr.sh_size / data->sec_hdr.sh_entsize;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static int bpf_fetch_strtab(struct bpf_elf_ctx *ctx, int section,
-			    struct bpf_elf_sec_data *data)
-{
-	ctx->str_tab = data->sec_data;
-	ctx->sec_done[section] = true;
-	return 0;
-}
-
-static bool bpf_has_map_data(const struct bpf_elf_ctx *ctx)
-{
-	return ctx->sym_tab && ctx->str_tab && ctx->sec_maps;
-}
-
-static int bpf_fetch_ancillary(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_elf_sec_data data;
-	int i, ret = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0)
-			continue;
-
-		if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-		    !strcmp(data.sec_name, ELF_SECTION_MAPS))
-			ret = bpf_fetch_maps(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_PROGBITS &&
-			 !strcmp(data.sec_name, ELF_SECTION_LICENSE))
-			ret = bpf_fetch_license(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_SYMTAB &&
-			 !strcmp(data.sec_name, ".symtab"))
-			ret = bpf_fetch_symtab(ctx, i, &data);
-		else if (data.sec_hdr.sh_type == SHT_STRTAB &&
-			 !strcmp(data.sec_name, ".strtab"))
-			ret = bpf_fetch_strtab(ctx, i, &data);
-		if (ret < 0) {
-			fprintf(stderr, "Error parsing section %d! Perhaps check with readelf -a?\n",
-				i);
-			break;
-		}
-	}
-
-	if (bpf_has_map_data(ctx)) {
-		ret = bpf_maps_attach_all(ctx);
-		if (ret < 0) {
-			fprintf(stderr, "Error loading maps into kernel!\n");
-			return ret;
-		}
-	}
-
-	return ret;
-}
-
-static int bpf_fetch_prog(struct bpf_elf_ctx *ctx, const char *section)
-{
-	struct bpf_elf_sec_data data;
-	struct bpf_elf_prog prog;
-	int ret, i, fd = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		if (ctx->sec_done[i])
-			continue;
-
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0 ||
-		    !(data.sec_hdr.sh_type == SHT_PROGBITS &&
-		      data.sec_hdr.sh_flags & SHF_EXECINSTR &&
-		      !strcmp(data.sec_name, section)))
-			continue;
-
-		memset(&prog, 0, sizeof(prog));
-		prog.type    = ctx->type;
-		prog.insns   = data.sec_data->d_buf;
-		prog.size    = data.sec_data->d_size;
-		prog.license = ctx->license;
-
-		fd = bpf_prog_attach(section, &prog, ctx);
-		if (fd < 0)
-			break;
-
-		ctx->sec_done[i] = true;
-		break;
-	}
-
-	return fd;
-}
-
-static int bpf_apply_relo_data(struct bpf_elf_ctx *ctx,
-			       struct bpf_elf_sec_data *data_relo,
-			       struct bpf_elf_sec_data *data_insn)
-{
-	Elf_Data *idata = data_insn->sec_data;
-	GElf_Shdr *rhdr = &data_relo->sec_hdr;
-	int relo_ent, relo_num = rhdr->sh_size / rhdr->sh_entsize;
-	struct bpf_insn *insns = idata->d_buf;
-	unsigned int num_insns = idata->d_size / sizeof(*insns);
-
-	for (relo_ent = 0; relo_ent < relo_num; relo_ent++) {
-		unsigned int ioff, rmap;
-		GElf_Rel relo;
-		GElf_Sym sym;
-
-		if (gelf_getrel(data_relo->sec_data, relo_ent, &relo) != &relo)
-			return -EIO;
-
-		ioff = relo.r_offset / sizeof(struct bpf_insn);
-		if (ioff >= num_insns ||
-		    insns[ioff].code != (BPF_LD | BPF_IMM | BPF_DW)) {
-			fprintf(stderr, "ELF contains relo data for non ld64 instruction at offset %u! Compiler bug?!\n",
-				ioff);
-			if (ioff < num_insns &&
-			    insns[ioff].code == (BPF_JMP | BPF_CALL))
-				fprintf(stderr, " - Try to annotate functions with always_inline attribute!\n");
-			return -EINVAL;
-		}
-
-		if (gelf_getsym(ctx->sym_tab, GELF_R_SYM(relo.r_info), &sym) != &sym)
-			return -EIO;
-		if (sym.st_shndx != ctx->sec_maps) {
-			fprintf(stderr, "ELF contains non-map related relo data in entry %u pointing to section %u! Compiler bug?!\n",
-				relo_ent, sym.st_shndx);
-			return -EIO;
-		}
-
-		rmap = sym.st_value / sizeof(struct bpf_elf_map);
-		if (rmap >= ARRAY_SIZE(ctx->map_fds))
-			return -EINVAL;
-		if (!ctx->map_fds[rmap])
-			return -EINVAL;
-
-		if (ctx->verbose)
-			fprintf(stderr, "Map \'%s\' (%d) injected into prog section \'%s\' at offset %u!\n",
-				bpf_str_tab_name(ctx, &sym), ctx->map_fds[rmap],
-				data_insn->sec_name, ioff);
-
-		insns[ioff].src_reg = BPF_PSEUDO_MAP_FD;
-		insns[ioff].imm     = ctx->map_fds[rmap];
-	}
-
-	return 0;
-}
-
-static int bpf_fetch_prog_relo(struct bpf_elf_ctx *ctx, const char *section,
-			       bool *lderr)
-{
-	struct bpf_elf_sec_data data_relo, data_insn;
-	struct bpf_elf_prog prog;
-	int ret, idx, i, fd = -1;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		ret = bpf_fill_section_data(ctx, i, &data_relo);
-		if (ret < 0 || data_relo.sec_hdr.sh_type != SHT_REL)
-			continue;
-
-		idx = data_relo.sec_hdr.sh_info;
-		ret = bpf_fill_section_data(ctx, idx, &data_insn);
-		if (ret < 0 ||
-		    !(data_insn.sec_hdr.sh_type == SHT_PROGBITS &&
-		      data_insn.sec_hdr.sh_flags & SHF_EXECINSTR &&
-		      !strcmp(data_insn.sec_name, section)))
-			continue;
-
-		ret = bpf_apply_relo_data(ctx, &data_relo, &data_insn);
-		if (ret < 0)
-			continue;
-
-		memset(&prog, 0, sizeof(prog));
-		prog.type    = ctx->type;
-		prog.insns   = data_insn.sec_data->d_buf;
-		prog.size    = data_insn.sec_data->d_size;
-		prog.license = ctx->license;
-
-		fd = bpf_prog_attach(section, &prog, ctx);
-		if (fd < 0) {
-			*lderr = true;
-			break;
-		}
-
-		ctx->sec_done[i]   = true;
-		ctx->sec_done[idx] = true;
-		break;
-	}
-
-	return fd;
-}
-
-static int bpf_fetch_prog_sec(struct bpf_elf_ctx *ctx, const char *section)
-{
-	bool lderr = false;
-	int ret = -1;
-
-	if (bpf_has_map_data(ctx))
-		ret = bpf_fetch_prog_relo(ctx, section, &lderr);
-	if (ret < 0 && !lderr)
-		ret = bpf_fetch_prog(ctx, section);
-
-	return ret;
-}
-
-static int bpf_find_map_by_id(struct bpf_elf_ctx *ctx, uint32_t id)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++)
-		if (ctx->map_fds[i] && ctx->maps[i].id == id &&
-		    ctx->maps[i].type == BPF_MAP_TYPE_PROG_ARRAY)
-			return i;
-	return -1;
-}
-
-static int bpf_fill_prog_arrays(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_elf_sec_data data;
-	uint32_t map_id, key_id;
-	int fd, i, ret, idx;
-
-	for (i = 1; i < ctx->elf_hdr.e_shnum; i++) {
-		if (ctx->sec_done[i])
-			continue;
-
-		ret = bpf_fill_section_data(ctx, i, &data);
-		if (ret < 0)
-			continue;
-
-		ret = sscanf(data.sec_name, "%i/%i", &map_id, &key_id);
-		if (ret != 2)
-			continue;
-
-		idx = bpf_find_map_by_id(ctx, map_id);
-		if (idx < 0)
-			continue;
-
-		fd = bpf_fetch_prog_sec(ctx, data.sec_name);
-		if (fd < 0)
-			return -EIO;
-
-		ret = bpf_map_update(ctx->map_fds[idx], &key_id,
-				     &fd, BPF_ANY);
-		if (ret < 0) {
-			if (errno == E2BIG)
-				fprintf(stderr, "Tail call key %u for map %u out of bounds?\n",
-					key_id, map_id);
-			return -errno;
-		}
-
-		ctx->sec_done[i] = true;
-	}
-
-	return 0;
-}
-
-static void bpf_save_finfo(struct bpf_elf_ctx *ctx)
-{
-	struct stat st;
-	int ret;
-
-	memset(&ctx->stat, 0, sizeof(ctx->stat));
-
-	ret = fstat(ctx->obj_fd, &st);
-	if (ret < 0) {
-		fprintf(stderr, "Stat of elf file failed: %s\n",
-			strerror(errno));
-		return;
-	}
-
-	ctx->stat.st_dev = st.st_dev;
-	ctx->stat.st_ino = st.st_ino;
-}
-
-static int bpf_read_pin_mapping(FILE *fp, uint32_t *id, char *path)
-{
-	char buff[PATH_MAX];
-
-	while (fgets(buff, sizeof(buff), fp)) {
-		char *ptr = buff;
-
-		while (*ptr == ' ' || *ptr == '\t')
-			ptr++;
-
-		if (*ptr == '#' || *ptr == '\n' || *ptr == 0)
-			continue;
-
-		if (sscanf(ptr, "%i %s\n", id, path) != 2 &&
-		    sscanf(ptr, "%i %s #", id, path) != 2) {
-			strcpy(path, ptr);
-			return -1;
-		}
-
-		return 1;
-	}
-
-	return 0;
-}
-
-static bool bpf_pinning_reserved(uint32_t pinning)
-{
-	switch (pinning) {
-	case PIN_NONE:
-	case PIN_OBJECT_NS:
-	case PIN_GLOBAL_NS:
-		return true;
-	default:
-		return false;
-	}
-}
-
-static void bpf_hash_init(struct bpf_elf_ctx *ctx, const char *db_file)
-{
-	struct bpf_hash_entry *entry;
-	char subpath[PATH_MAX] = {};
-	uint32_t pinning;
-	FILE *fp;
-	int ret;
-
-	fp = fopen(db_file, "r");
-	if (!fp)
-		return;
-
-	while ((ret = bpf_read_pin_mapping(fp, &pinning, subpath))) {
-		if (ret == -1) {
-			fprintf(stderr, "Database %s is corrupted at: %s\n",
-				db_file, subpath);
-			fclose(fp);
-			return;
-		}
-
-		if (bpf_pinning_reserved(pinning)) {
-			fprintf(stderr, "Database %s, id %u is reserved - ignoring!\n",
-				db_file, pinning);
-			continue;
-		}
-
-		entry = malloc(sizeof(*entry));
-		if (!entry) {
-			fprintf(stderr, "No memory left for db entry!\n");
-			continue;
-		}
-
-		entry->pinning = pinning;
-		entry->subpath = strdup(subpath);
-		if (!entry->subpath) {
-			fprintf(stderr, "No memory left for db entry!\n");
-			free(entry);
-			continue;
-		}
-
-		entry->next = ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)];
-		ctx->ht[pinning & (ARRAY_SIZE(ctx->ht) - 1)] = entry;
-	}
-
-	fclose(fp);
-}
-
-static void bpf_hash_destroy(struct bpf_elf_ctx *ctx)
-{
-	struct bpf_hash_entry *entry;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->ht); i++) {
-		while ((entry = ctx->ht[i]) != NULL) {
-			ctx->ht[i] = entry->next;
-			free((char *)entry->subpath);
-			free(entry);
-		}
-	}
-}
-
-static int bpf_elf_check_ehdr(const struct bpf_elf_ctx *ctx)
-{
-	if (ctx->elf_hdr.e_type != ET_REL ||
-	    (ctx->elf_hdr.e_machine != EM_NONE &&
-	     ctx->elf_hdr.e_machine != EM_BPF) ||
-	    ctx->elf_hdr.e_version != EV_CURRENT) {
-		fprintf(stderr, "ELF format error, ELF file not for eBPF?\n");
-		return -EINVAL;
-	}
-
-	switch (ctx->elf_hdr.e_ident[EI_DATA]) {
-	default:
-		fprintf(stderr, "ELF format error, wrong endianness info?\n");
-		return -EINVAL;
-	case ELFDATA2LSB:
-		if (htons(1) == 1) {
-			fprintf(stderr,
-				"We are big endian, eBPF object is little endian!\n");
-			return -EIO;
-		}
-		break;
-	case ELFDATA2MSB:
-		if (htons(1) != 1) {
-			fprintf(stderr,
-				"We are little endian, eBPF object is big endian!\n");
-			return -EIO;
-		}
-		break;
-	}
-
-	return 0;
-}
-
-static int bpf_elf_ctx_init(struct bpf_elf_ctx *ctx, const char *pathname,
-			    enum bpf_prog_type type, bool verbose)
-{
-	int ret = -EINVAL;
-
-	if (elf_version(EV_CURRENT) == EV_NONE ||
-	    bpf_init_env(pathname))
-		return ret;
-
-	memset(ctx, 0, sizeof(*ctx));
-	ctx->verbose = verbose;
-	ctx->type    = type;
-
-	ctx->obj_fd = open(pathname, O_RDONLY);
-	if (ctx->obj_fd < 0)
-		return ctx->obj_fd;
-
-	ctx->elf_fd = elf_begin(ctx->obj_fd, ELF_C_READ, NULL);
-	if (!ctx->elf_fd) {
-		ret = -EINVAL;
-		goto out_fd;
-	}
-
-	if (elf_kind(ctx->elf_fd) != ELF_K_ELF) {
-		ret = -EINVAL;
-		goto out_fd;
-	}
-
-	if (gelf_getehdr(ctx->elf_fd, &ctx->elf_hdr) !=
-	    &ctx->elf_hdr) {
-		ret = -EIO;
-		goto out_elf;
-	}
-
-	ret = bpf_elf_check_ehdr(ctx);
-	if (ret < 0)
-		goto out_elf;
-
-	ctx->sec_done = calloc(ctx->elf_hdr.e_shnum,
-			       sizeof(*(ctx->sec_done)));
-	if (!ctx->sec_done) {
-		ret = -ENOMEM;
-		goto out_elf;
-	}
-
-	if (ctx->verbose && bpf_log_realloc(ctx)) {
-		ret = -ENOMEM;
-		goto out_free;
-	}
-
-	bpf_save_finfo(ctx);
-	bpf_hash_init(ctx, CONFDIR "/bpf_pinning");
-
-	return 0;
-out_free:
-	free(ctx->sec_done);
-out_elf:
-	elf_end(ctx->elf_fd);
-out_fd:
-	close(ctx->obj_fd);
-	return ret;
-}
-
-static int bpf_maps_count(struct bpf_elf_ctx *ctx)
-{
-	int i, count = 0;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
-		if (!ctx->map_fds[i])
-			break;
-		count++;
-	}
-
-	return count;
-}
-
-static void bpf_maps_teardown(struct bpf_elf_ctx *ctx)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(ctx->map_fds); i++) {
-		if (ctx->map_fds[i])
-			close(ctx->map_fds[i]);
-	}
-}
-
-static void bpf_elf_ctx_destroy(struct bpf_elf_ctx *ctx, bool failure)
-{
-	if (failure)
-		bpf_maps_teardown(ctx);
-
-	bpf_hash_destroy(ctx);
-
-	free(ctx->sec_done);
-	free(ctx->log);
-
-	elf_end(ctx->elf_fd);
-	close(ctx->obj_fd);
-}
-
-static struct bpf_elf_ctx __ctx;
-
-static int bpf_obj_open(const char *pathname, enum bpf_prog_type type,
-			const char *section, bool verbose)
-{
-	struct bpf_elf_ctx *ctx = &__ctx;
-	int fd = 0, ret;
-
-	ret = bpf_elf_ctx_init(ctx, pathname, type, verbose);
-	if (ret < 0) {
-		fprintf(stderr, "Cannot initialize ELF context!\n");
-		return ret;
-	}
-
-	ret = bpf_fetch_ancillary(ctx);
-	if (ret < 0) {
-		fprintf(stderr, "Error fetching ELF ancillary data!\n");
-		goto out;
-	}
-
-	fd = bpf_fetch_prog_sec(ctx, section);
-	if (fd < 0) {
-		fprintf(stderr, "Error fetching program/map!\n");
-		ret = fd;
-		goto out;
-	}
-
-	ret = bpf_fill_prog_arrays(ctx);
-	if (ret < 0)
-		fprintf(stderr, "Error filling program arrays!\n");
-out:
-	bpf_elf_ctx_destroy(ctx, ret < 0);
-	if (ret < 0) {
-		if (fd)
-			close(fd);
-		return ret;
-	}
-
-	return fd;
-}
-
-static int
-bpf_map_set_send(int fd, struct sockaddr_un *addr, unsigned int addr_len,
-		 const struct bpf_map_data *aux, unsigned int entries)
-{
-	struct bpf_map_set_msg msg = {
-		.aux.uds_ver = BPF_SCM_AUX_VER,
-		.aux.num_ent = entries,
-	};
-	int *cmsg_buf, min_fd;
-	char *amsg_buf;
-	int i;
-
-	strncpy(msg.aux.obj_name, aux->obj, sizeof(msg.aux.obj_name));
-	memcpy(&msg.aux.obj_st, aux->st, sizeof(msg.aux.obj_st));
-
-	cmsg_buf = bpf_map_set_init(&msg, addr, addr_len);
-	amsg_buf = (char *)msg.aux.ent;
-
-	for (i = 0; i < entries; i += min_fd) {
-		int ret;
-
-		min_fd = min(BPF_SCM_MAX_FDS * 1U, entries - i);
-		bpf_map_set_init_single(&msg, min_fd);
-
-		memcpy(cmsg_buf, &aux->fds[i], sizeof(aux->fds[0]) * min_fd);
-		memcpy(amsg_buf, &aux->ent[i], sizeof(aux->ent[0]) * min_fd);
-
-		ret = sendmsg(fd, &msg.hdr, 0);
-		if (ret <= 0)
-			return ret ? : -1;
-	}
-
-	return 0;
-}
-
-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;
-	char *amsg_buf, *mmsg_buf;
-	unsigned int needed = 1;
-	int i;
-
-	cmsg_buf = bpf_map_set_init(&msg, NULL, 0);
-	amsg_buf = (char *)msg.aux.ent;
-	mmsg_buf = (char *)&msg.aux;
-
-	for (i = 0; i < min(entries, needed); i += min_fd) {
-		struct cmsghdr *cmsg;
-		int ret;
-
-		min_fd = min(entries, 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;
-		if (msg.aux.uds_ver != BPF_SCM_AUX_VER)
-			return -ENOSYS;
-
-		min_fd = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(fd);
-		if (min_fd > entries || min_fd <= 0)
-			return -EINVAL;
-
-		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));
-
-		needed = aux->num_ent;
-	}
-
-	return 0;
-}
-
-int bpf_send_map_fds(const char *path, const char *obj)
-{
-	struct bpf_elf_ctx *ctx = &__ctx;
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	struct bpf_map_data bpf_aux = {
-		.fds = ctx->map_fds,
-		.ent = ctx->maps,
-		.st  = &ctx->stat,
-		.obj = obj,
-	};
-	int fd, ret;
-
-	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open socket: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	strncpy(addr.sun_path, path, sizeof(addr.sun_path));
-
-	ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
-	if (ret < 0) {
-		fprintf(stderr, "Cannot connect to %s: %s\n",
-			path, strerror(errno));
-		return -1;
-	}
-
-	ret = bpf_map_set_send(fd, &addr, sizeof(addr), &bpf_aux,
-			       bpf_maps_count(ctx));
-	if (ret < 0)
-		fprintf(stderr, "Cannot send fds to %s: %s\n",
-			path, strerror(errno));
-
-	bpf_maps_teardown(ctx);
-	close(fd);
-	return ret;
-}
-
-int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
-		     unsigned int entries)
-{
-	struct sockaddr_un addr = { .sun_family = AF_UNIX };
-	int fd, ret;
-
-	fd = socket(AF_UNIX, SOCK_DGRAM, 0);
-	if (fd < 0) {
-		fprintf(stderr, "Cannot open socket: %s\n",
-			strerror(errno));
-		return -1;
-	}
-
-	strncpy(addr.sun_path, path, 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));
-		return -1;
-	}
-
-	ret = bpf_map_set_recv(fd, fds, aux, entries);
-	if (ret < 0)
-		fprintf(stderr, "Cannot recv fds from %s: %s\n",
-			path, strerror(errno));
-
-	unlink(addr.sun_path);
-	close(fd);
-	return ret;
-}
-#endif /* HAVE_ELF */
diff --git a/tc/tc_bpf.h b/tc/tc_bpf.h
deleted file mode 100644
index 30306de..0000000
--- a/tc/tc_bpf.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * tc_bpf.h	BPF common code
- *
- *		This program is free software; you can distribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- *
- * Authors:	Daniel Borkmann <dborkman@redhat.com>
- *		Jiri Pirko <jiri@resnulli.us>
- */
-
-#ifndef _TC_BPF_H_
-#define _TC_BPF_H_ 1
-
-#include <linux/netlink.h>
-#include <linux/bpf.h>
-#include <linux/magic.h>
-
-#include "utils.h"
-#include "bpf_scm.h"
-
-enum {
-	BPF_NLA_OPS_LEN = 0,
-	BPF_NLA_OPS,
-	BPF_NLA_FD,
-	BPF_NLA_NAME,
-	__BPF_NLA_MAX,
-};
-
-#define BPF_NLA_MAX	__BPF_NLA_MAX
-
-#define BPF_ENV_UDS	"TC_BPF_UDS"
-#define BPF_ENV_MNT	"TC_BPF_MNT"
-
-#ifndef BPF_MAX_LOG
-# define BPF_MAX_LOG	4096
-#endif
-
-#ifndef BPF_FS_MAGIC
-# define BPF_FS_MAGIC	0xcafe4a11
-#endif
-
-#define BPF_DIR_MNT	"/sys/fs/bpf"
-
-#define BPF_DIR_TC	"tc"
-#define BPF_DIR_GLOBALS	"globals"
-
-#ifndef TRACEFS_MAGIC
-# define TRACEFS_MAGIC	0x74726163
-#endif
-
-#define TRACE_DIR_MNT	"/sys/kernel/tracing"
-
-int bpf_trace_pipe(void);
-const char *bpf_default_section(const enum bpf_prog_type type);
-
-int bpf_parse_common(int *ptr_argc, char ***ptr_argv, const int *nla_tbl,
-		     enum bpf_prog_type type, const char **ptr_object,
-		     const char **ptr_uds_name, struct nlmsghdr *n);
-int bpf_graft_map(const char *map_path, uint32_t *key, int argc, char **argv);
-
-void bpf_print_ops(FILE *f, struct rtattr *bpf_ops, __u16 len);
-
-#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,
-		     unsigned int entries);
-#else
-static inline int bpf_send_map_fds(const char *path, const char *obj)
-{
-	return 0;
-}
-
-static inline int bpf_recv_map_fds(const char *path, int *fds,
-				   struct bpf_map_aux *aux,
-				   unsigned int entries)
-{
-	return -1;
-}
-#endif /* HAVE_ELF */
-#endif /* _TC_BPF_H_ */
-- 
1.9.3

^ permalink raw reply related

* [PATCH net] net: __skb_flow_dissect() must cap its return value
From: Eric Dumazet @ 2016-11-10  0:04 UTC (permalink / raw)
  To: David Miller
  Cc: Yibin Yang, Tom Herbert, Jojy Varghese, Alexander Duyck,
	Alexei Starovoitov, Willem de Bruijn, netdev
In-Reply-To: <1478718427.16809.7.camel@edumazet-glaptop3.roam.corp.google.com>

From: Eric Dumazet <edumazet@google.com>

After Tom patch, thoff field could point past the end of the buffer,
this could fool some callers.

If an skb was provided, skb->len should be the upper limit.
If not, hlen is supposed to be the upper limit.

Fixes: a6e544b0a88b ("flow_dissector: Jump to exit code in __skb_flow_dissect")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Yibin Yang <yibyang@cisco.com
Acked-by: Alexander Duyck <alexander.h.duyck@intel.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 net/core/flow_dissector.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 44e6ba9d3a6b..5a908c534bec 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -122,7 +122,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
 	struct flow_dissector_key_keyid *key_keyid;
 	bool skip_vlan = false;
 	u8 ip_proto = 0;
-	bool ret = false;
+	bool ret;
 
 	if (!data) {
 		data = skb->data;
@@ -549,12 +549,17 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
 out_good:
 	ret = true;
 
-out_bad:
+	key_control->thoff = (u16)nhoff;
+out:
 	key_basic->n_proto = proto;
 	key_basic->ip_proto = ip_proto;
-	key_control->thoff = (u16)nhoff;
 
 	return ret;
+
+out_bad:
+	ret = false;
+	key_control->thoff = min_t(u16, nhoff, skb ? skb->len : hlen);
+	goto out;
 }
 EXPORT_SYMBOL(__skb_flow_dissect);
 

^ permalink raw reply related

* Re: [PATCH] vxlan: hide unused local variable
From: David Miller @ 2016-11-10  0:00 UTC (permalink / raw)
  To: arnd; +Cc: jbenc, hannes, aduyck, pshelar, netdev, linux-kernel
In-Reply-To: <20161107211017.857340-1-arnd@arndb.de>

From: Arnd Bergmann <arnd@arndb.de>
Date: Mon,  7 Nov 2016 22:09:07 +0100

> A bugfix introduced a harmless warning in v4.9-rc4:
> 
> drivers/net/vxlan.c: In function 'vxlan_group_used':
> drivers/net/vxlan.c:947:21: error: unused variable 'sock6' [-Werror=unused-variable]
> 
> This hides the variable inside of the same #ifdef that is
> around its user. The extraneous initialization is removed
> at the same time, it was accidentally introduced in the
> same commit.
> 
> Fixes: c6fcc4fc5f8b ("vxlan: avoid using stale vxlan socket.")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

Applied.

^ permalink raw reply

* Re: [PATCH net-next v2 5/5] net: l2tp: fix negative assignment to unsigned int
From: David Miller @ 2016-11-09 23:56 UTC (permalink / raw)
  To: asbjorn; +Cc: jchapman, netdev, linux-kernel
In-Reply-To: <20161107203928.30111-5-asbjorn@asbjorn.st>

From: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
Date: Mon,  7 Nov 2016 20:39:28 +0000

> recv_seq, send_seq and lns_mode mode are all defined as
> unsigned int foo:1;
> 
> Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>

Applied.

^ permalink raw reply

* Re: [PATCH] net: ethernet: ti: davinci_cpdma: fix fixed prio cpdma ctlr configuration
From: Ivan Khoronzhuk @ 2016-11-09 23:56 UTC (permalink / raw)
  To: Grygorii Strashko, mugunthanvnm, netdev; +Cc: linux-omap, linux-kernel
In-Reply-To: <a20ab87c-4f9d-0eaa-e486-156d9f359866@ti.com>



On 09.11.16 23:09, Grygorii Strashko wrote:
>
>
> On 11/08/2016 07:10 AM, Ivan Khoronzhuk wrote:
>> The dma ctlr is reseted to 0 while cpdma start, thus cpdma ctlr
>
> I assume this is because cpdma_ctlr_start() does soft reset. Is it correct?
Probably not. I've seen this register doesn't hold any previous settings (just trash)
after cpdma_ctlr_stop(), actually after last channel is stopped (inside of cpdma_ctlr_stop()).
Then cpdma_ctlr_start() just reset it to 0.

>
>> cannot be configured after cpdma is stopped. So, restore content
>> of cpdma ctlr while off/on procedure.
>>
>> Based on net-next/master
>
> ^ remove it
sure

>
>>
>> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
>> ---
>>  drivers/net/ethernet/ti/cpsw.c          |   6 +-
>>  drivers/net/ethernet/ti/davinci_cpdma.c | 103 +++++++++++++++++---------------
>>  drivers/net/ethernet/ti/davinci_cpdma.h |   2 +
>>  3 files changed, 58 insertions(+), 53 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
>> index b1ddf89..4d04b8e 100644
>> --- a/drivers/net/ethernet/ti/cpsw.c
>> +++ b/drivers/net/ethernet/ti/cpsw.c
>> @@ -1376,10 +1376,6 @@ static int cpsw_ndo_open(struct net_device *ndev)
>>  				  ALE_ALL_PORTS, ALE_ALL_PORTS, 0, 0);
>>
>>  	if (!cpsw_common_res_usage_state(cpsw)) {
>> -		/* setup tx dma to fixed prio and zero offset */
>> -		cpdma_control_set(cpsw->dma, CPDMA_TX_PRIO_FIXED, 1);
>> -		cpdma_control_set(cpsw->dma, CPDMA_RX_BUFFER_OFFSET, 0);
>> -
>>  		/* disable priority elevation */
>>  		__raw_writel(0, &cpsw->regs->ptype);
>>
>> @@ -2710,6 +2706,8 @@ static int cpsw_probe(struct platform_device *pdev)
>>  	dma_params.desc_align		= 16;
>>  	dma_params.has_ext_regs		= true;
>>  	dma_params.desc_hw_addr         = dma_params.desc_mem_phys;
>> +	dma_params.rxbuf_offset		= 0;
>> +	dma_params.fixed_prio		= 1;
>
> Do we really need this new parameters? Do you have plans to use other values?
I'm ok if this is static (equally as a bunch of rest in dma_params), no see reason to use other values,
it at least now. But the issue is not only in these two parameters and not only in cpsw_ndo_open().
It touches cpsw_set_channels() also, where ctlr stop/start is present.
In order to not copy cpdma_control_set(cpsw->dma, CPDMA_TX_PRIO_FIXED, 1)...
in all such kind places in eth drivers, better to allow the cpdma to control it's parameters...

The cpdma ctlr register holds a little more parameters (but only two of them are set in cpsw)
Maybe there is reason to save them also. Actually I'd seen this problem when playing with
on/off channel shapers, unfortunately the cpdma ctlr holds this info also, and it was lost
while on/off (but I'm going to restore it in chan_start()).


>

-- 
Regards,
Ivan Khoronzhuk

^ permalink raw reply

* Re: [PATCH net-next v2 4/5] net: l2tp: cleanup: remove redundant condition
From: David Miller @ 2016-11-09 23:56 UTC (permalink / raw)
  To: asbjorn; +Cc: jchapman, netdev, linux-kernel
In-Reply-To: <20161107203928.30111-4-asbjorn@asbjorn.st>

From: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
Date: Mon,  7 Nov 2016 20:39:27 +0000

> These assignments follow this pattern:
> 
> 	unsigned int foo:1;
> 	struct nlattr *nla = info->attrs[bar];
> 
> 	if (nla)
> 		foo = nla_get_flag(nla); /* expands to: foo = !!nla */
> 
> This could be simplified to: if (nla) foo = 1;
> but lets just remove the condition and use the macro,
> 
> 	foo = nla_get_flag(nla);
> 
> Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>

Applied.

^ permalink raw reply

* Re: [PATCH net-next v2 3/5] net: l2tp: netlink: l2tp_nl_tunnel_send: set UDP6 checksum flags
From: David Miller @ 2016-11-09 23:56 UTC (permalink / raw)
  To: asbjorn; +Cc: jchapman, netdev, linux-kernel
In-Reply-To: <20161107203928.30111-3-asbjorn@asbjorn.st>

From: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
Date: Mon,  7 Nov 2016 20:39:26 +0000

> This patch causes the proper attribute flags to be set,
> in the case that IPv6 UDP checksums are disabled, so that
> userspace ie. `ip l2tp show tunnel` knows about it.
> 
> Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>

Applied.

^ permalink raw reply

* linux-next: manual merge of the net-next tree with the netfilter tree
From: Stephen Rothwell @ 2016-11-09 23:56 UTC (permalink / raw)
  To: David Miller, Networking, Pablo Neira Ayuso, NetFilter
  Cc: linux-next, linux-kernel, WANG Cong, Johannes Berg

Hi all,

Today's linux-next merge of the net-next tree got a conflict in:

  net/netfilter/ipvs/ip_vs_ctl.c

between commit:

  8fbfef7f505b ("ipvs: use IPVS_CMD_ATTR_MAX for family.maxattr")

from the netfilter tree and commit:

  489111e5c25b ("genetlink: statically initialize families")

from the net-next tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging.  You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.

-- 
Cheers,
Stephen Rothwell

diff --cc net/netfilter/ipvs/ip_vs_ctl.c
index a6e44ef2ec9a,6b85ded4f91d..000000000000
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@@ -3872,10 -3865,20 +3865,20 @@@ static const struct genl_ops ip_vs_genl
  	},
  };
  
+ static struct genl_family ip_vs_genl_family __ro_after_init = {
+ 	.hdrsize	= 0,
+ 	.name		= IPVS_GENL_NAME,
+ 	.version	= IPVS_GENL_VERSION,
 -	.maxattr	= IPVS_CMD_MAX,
++	.maxattr	= IPVS_CMD_ATTR_MAX,
+ 	.netnsok        = true,         /* Make ipvsadm to work on netns */
+ 	.module		= THIS_MODULE,
+ 	.ops		= ip_vs_genl_ops,
+ 	.n_ops		= ARRAY_SIZE(ip_vs_genl_ops),
+ };
+ 
  static int __init ip_vs_genl_register(void)
  {
- 	return genl_register_family_with_ops(&ip_vs_genl_family,
- 					     ip_vs_genl_ops);
+ 	return genl_register_family(&ip_vs_genl_family);
  }
  
  static void ip_vs_genl_unregister(void)

^ permalink raw reply

* Re: [PATCH net-next v2 2/5] net: l2tp: only set L2TP_ATTR_UDP_CSUM if AF_INET
From: David Miller @ 2016-11-09 23:56 UTC (permalink / raw)
  To: asbjorn; +Cc: jchapman, netdev, linux-kernel
In-Reply-To: <20161107203928.30111-2-asbjorn@asbjorn.st>

From: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
Date: Mon,  7 Nov 2016 20:39:25 +0000

> Only set L2TP_ATTR_UDP_CSUM in l2tp_nl_tunnel_send()
> when it's running over IPv4.
> 
> This prepares the code to also have IPv6 specific attributes.
> 
> Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>

Applied.

^ permalink raw reply

* Re: [PATCH net-next v2 1/5] net: l2tp: change L2TP_ATTR_UDP_ZERO_CSUM6_{RX,TX} attribute types
From: David Miller @ 2016-11-09 23:56 UTC (permalink / raw)
  To: asbjorn; +Cc: jchapman, netdev, linux-kernel, therbert
In-Reply-To: <20161107203928.30111-1-asbjorn@asbjorn.st>

From: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
Date: Mon,  7 Nov 2016 20:39:24 +0000

> The attributes L2TP_ATTR_UDP_ZERO_CSUM6_RX and
> L2TP_ATTR_UDP_ZERO_CSUM6_TX are used as flags,
> but is defined as a u8 in a comment.
> 
> This patch redocuments them as flags.
> 
> Adding nla_policy entries would break API, so not doing that.
> 
> CC: Tom Herbert <therbert@google.com>
> Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>

Applied.

^ permalink raw reply

* Re: [PATCH net] ibmvnic: Start completion queue negotiation at server-provided optimum values
From: David Miller @ 2016-11-09 23:52 UTC (permalink / raw)
  To: jallen; +Cc: netdev, tlfalcon
In-Reply-To: <7d5c1364-7167-8d69-d9d4-bc89d19f8457@linux.vnet.ibm.com>

From: John Allen <jallen@linux.vnet.ibm.com>
Date: Mon, 7 Nov 2016 14:27:28 -0600

> Use the opt_* fields to determine the starting point for negotiating the
> number of tx/rx completion queues with the vnic server. These contain the
> number of queues that the vnic server estimates that it will be able to
> allocate. While renegotiation may still occur, using the opt_* fields will
> reduce the number of times this needs to happen and will prevent driver
> probe timeout on systems using large numbers of ibmvnic client devices per
> vnic port.
> 
> Signed-off-by: John Allen <jallen@linux.vnet.ibm.com>

Applied, thanks.

^ permalink raw reply

* Re: [PATCH v2] net: icmp_route_lookup should use rt dev to determine L3 domain
From: David Miller @ 2016-11-09 23:50 UTC (permalink / raw)
  To: dsa; +Cc: netdev
In-Reply-To: <1478548989-1832-1-git-send-email-dsa@cumulusnetworks.com>

From: David Ahern <dsa@cumulusnetworks.com>
Date: Mon,  7 Nov 2016 12:03:09 -0800

> icmp_send is called in response to some event. The skb may not have
> the device set (skb->dev is NULL), but it is expected to have an rt.
> Update icmp_route_lookup to use the rt on the skb to determine L3
> domain.
> 
> Fixes: 613d09b30f8b ("net: Use VRF device index for lookups on TX")
> Signed-off-by: David Ahern <dsa@cumulusnetworks.com>

Applied and queued up for -stable, thanks David.

^ permalink raw reply

* linux-next: manual merge of the net-next tree with the net tree
From: Stephen Rothwell @ 2016-11-09 23:50 UTC (permalink / raw)
  To: David Miller, Networking
  Cc: linux-next, linux-kernel, Or Gerlitz, Saeed Mahameed,
	Hadar Hen Zion

Hi all,

Today's linux-next merge of the net-next tree got a conflict in:

  drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c

between commit:

  ee39fbc4447d ("net/mlx5: E-Switch, Set the actions for offloaded rules properly")

from the net tree and commit:

  66958ed906b8 ("net/mlx5: Support encap id when setting new steering entry")

from the net-next tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging.  You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.

-- 
Cheers,
Stephen Rothwell

diff --cc drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index d239f5d0ea36,50fe8e8861bb..000000000000
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@@ -57,14 -58,14 +58,15 @@@ mlx5_eswitch_add_offloaded_rule(struct 
  	if (esw->mode != SRIOV_OFFLOADS)
  		return ERR_PTR(-EOPNOTSUPP);
  
 -	flow_act.action = attr->action;
 +	/* per flow vlan pop/push is emulated, don't set that into the firmware */
- 	action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
++	flow_act.action = attr->action & ~(MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH | MLX5_FLOW_CONTEXT_ACTION_VLAN_POP);
  
- 	if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
- 		dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
- 		dest.vport_num = attr->out_rep->vport;
- 		action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
- 	} else if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
+ 	if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
+ 		dest[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
+ 		dest[i].vport_num = attr->out_rep->vport;
+ 		i++;
+ 	}
+ 	if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
  		counter = mlx5_fc_create(esw->dev, true);
  		if (IS_ERR(counter))
  			return ERR_CAST(counter);

^ permalink raw reply

* Re: [PATCH net-next] net-gro: avoid reorders
From: David Miller @ 2016-11-09 23:50 UTC (permalink / raw)
  To: eric.dumazet; +Cc: netdev
In-Reply-To: <1478545947.17367.10.camel@edumazet-glaptop3.roam.corp.google.com>

From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 07 Nov 2016 11:12:27 -0800

> From: Eric Dumazet <edumazet@google.com>
> 
> Receiving a GSO packet in dev_gro_receive() is not uncommon
> in stacked devices, or devices partially implementing LRO/GRO
> like bnx2x. GRO is implementing the aggregation the device
> was not able to do itself.
> 
> Current code causes reorders, like in following case :
> 
> For a given flow where sender sent 3 packets P1,P2,P3,P4
> 
> Receiver might receive P1 as a single packet, stored in GRO engine.
> 
> Then P2-P4 are received as a single GSO packet, immediately given to
> upper stack, while P1 is held in GRO engine.
> 
> This patch will make sure P1 is given to upper stack, then P2-P4
> immediately after.
> 
> Signed-off-by: Eric Dumazet <edumazet@google.com>

Applied.

^ permalink raw reply

* Re: [PATCH 0/2] net: qcom/emac: ensure that pause frames are enabled
From: David Miller @ 2016-11-09 23:45 UTC (permalink / raw)
  To: timur; +Cc: f.fainelli, alokc, netdev
In-Reply-To: <1478537501-23454-1-git-send-email-timur@codeaurora.org>

From: Timur Tabi <timur@codeaurora.org>
Date: Mon,  7 Nov 2016 10:51:39 -0600

> The qcom emac driver experiences significant packet loss (through frame
> check sequence errors) if flow control is not enabled and the phy is
> not configured to allow pause frames to pass through it.  Therefore, we
> need to enable flow control and force the phy to pass pause frames.

Series applied, thanks.

^ permalink raw reply

* [PATCH net 2/2] bpf: Add test for bpf_redirect to ipip/ip6tnl
From: Martin KaFai Lau @ 2016-11-09 23:36 UTC (permalink / raw)
  To: netdev; +Cc: Alexei Starovoitov, Daniel Borkmann, Kernel Team
In-Reply-To: <1478734594-337666-1-git-send-email-kafai@fb.com>

The test creates two netns, ns1 and ns2.  The host (the default netns)
has an ipip or ip6tnl dev configured for tunneling traffic to the ns2.

    ping VIPS from ns1 <----> host <--tunnel--> ns2 (VIPs at loopback)

The test is to have ns1 pinging VIPs configured at the loopback
interface in ns2.

The VIPs are 10.10.1.102 and 2401:face::66 (which are configured
at lo@ns2). [Note: 0x66 => 102].

At ns1, the VIPs are routed _via_ the host.

At the host, bpf programs are installed at the veth to redirect packets
from a veth to the ipip/ip6tnl.  The test is configured in a way so
that both ingress and egress can be tested.

At ns2, the ipip/ip6tnl dev is configured with the local and remote address
specified.  The return path is routed to the dev ipip/ip6tnl.

During egress test, the host also locally tests pinging the VIPs to ensure
that bpf_redirect at egress also works for the direct egress (i.e. not
forwarding from dev ve1 to ve2).

Acked-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
---
 samples/bpf/Makefile              |   4 +
 samples/bpf/tc_l2_redirect.sh     | 173 ++++++++++++++++++++++++++++
 samples/bpf/tc_l2_redirect_kern.c | 236 ++++++++++++++++++++++++++++++++++++++
 samples/bpf/tc_l2_redirect_user.c |  73 ++++++++++++
 4 files changed, 486 insertions(+)
 create mode 100755 samples/bpf/tc_l2_redirect.sh
 create mode 100644 samples/bpf/tc_l2_redirect_kern.c
 create mode 100644 samples/bpf/tc_l2_redirect_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 12b7304..72c5867 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -27,6 +27,7 @@ hostprogs-y += xdp2
 hostprogs-y += test_current_task_under_cgroup
 hostprogs-y += trace_event
 hostprogs-y += sampleip
+hostprogs-y += tc_l2_redirect
 
 test_verifier-objs := test_verifier.o libbpf.o
 test_maps-objs := test_maps.o libbpf.o
@@ -56,6 +57,7 @@ test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \
 				       test_current_task_under_cgroup_user.o
 trace_event-objs := bpf_load.o libbpf.o trace_event_user.o
 sampleip-objs := bpf_load.o libbpf.o sampleip_user.o
+tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
@@ -72,6 +74,7 @@ always += test_probe_write_user_kern.o
 always += trace_output_kern.o
 always += tcbpf1_kern.o
 always += tcbpf2_kern.o
+always += tc_l2_redirect_kern.o
 always += lathist_kern.o
 always += offwaketime_kern.o
 always += spintest_kern.o
@@ -111,6 +114,7 @@ HOSTLOADLIBES_xdp2 += -lelf
 HOSTLOADLIBES_test_current_task_under_cgroup += -lelf
 HOSTLOADLIBES_trace_event += -lelf
 HOSTLOADLIBES_sampleip += -lelf
+HOSTLOADLIBES_tc_l2_redirect += -l elf
 
 # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
 #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/tc_l2_redirect.sh b/samples/bpf/tc_l2_redirect.sh
new file mode 100755
index 0000000..b0e1f09
--- /dev/null
+++ b/samples/bpf/tc_l2_redirect.sh
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+[[ -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_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
+}
+
+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
+	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
diff --git a/samples/bpf/tc_l2_redirect_kern.c b/samples/bpf/tc_l2_redirect_kern.c
new file mode 100644
index 0000000..92a4472
--- /dev/null
+++ b/samples/bpf/tc_l2_redirect_kern.c
@@ -0,0 +1,236 @@
+/* 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 <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_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)
+{
+	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;
+
+	int ret;
+
+	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;
+
+	int ret;
+
+	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)
+{
+	struct bpf_tunnel_key tkey = {};
+	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";
diff --git a/samples/bpf/tc_l2_redirect_user.c b/samples/bpf/tc_l2_redirect_user.c
new file mode 100644
index 0000000..4013c53
--- /dev/null
+++ b/samples/bpf/tc_l2_redirect_user.c
@@ -0,0 +1,73 @@
+/* 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/unistd.h>
+#include <linux/bpf.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "libbpf.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_update_elem(array_fd, &array_key, &ifindex, 0);
+	if (ret) {
+		perror("bpf_update_elem");
+		goto out;
+	}
+
+out:
+	if (array_fd != -1)
+		close(array_fd);
+	return ret;
+}
-- 
2.5.1

^ permalink raw reply related

* [PATCH net 1/2] bpf: Fix bpf_redirect to an ipip/ip6tnl dev
From: Martin KaFai Lau @ 2016-11-09 23:36 UTC (permalink / raw)
  To: netdev; +Cc: Alexei Starovoitov, Daniel Borkmann, Kernel Team
In-Reply-To: <1478734594-337666-1-git-send-email-kafai@fb.com>

If the bpf program calls bpf_redirect(dev, 0) and dev is
an ipip/ip6tnl, it currently includes the mac header.
e.g. If dev is ipip, the end result is IP-EthHdr-IP instead
of IP-IP.

The fix is to pull the mac header.  At ingress, skb_postpull_rcsum()
is not needed because the ethhdr should have been pulled once already
and then got pushed back just before calling the bpf_prog.
At egress, this patch calls skb_postpull_rcsum().

If bpf_redirect(dev, BPF_F_INGRESS) is called,
it also fails now because it calls dev_forward_skb() which
eventually calls eth_type_trans(skb, dev).  The eth_type_trans()
will set skb->type = PACKET_OTHERHOST because the mac address
does not match the redirecting dev->dev_addr.  The PACKET_OTHERHOST
will eventually cause the ip_rcv() errors out.  To fix this,
____dev_forward_skb() is added.

Joint work with Daniel Borkmann.

Fixes: cfc7381b3002 ("ip_tunnel: add collect_md mode to IPIP tunnel")
Fixes: 8d79266bc48c ("ip6_tunnel: add collect_md mode to IPv6 tunnels")
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
---
 include/linux/netdevice.h | 15 +++++++++++
 net/core/dev.c            | 17 +++++-------
 net/core/filter.c         | 68 +++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 81 insertions(+), 19 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 91ee364..bf04a46 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3354,6 +3354,21 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 bool is_skb_forwardable(const struct net_device *dev,
 			const struct sk_buff *skb);
 
+static __always_inline int ____dev_forward_skb(struct net_device *dev,
+					       struct sk_buff *skb)
+{
+	if (skb_orphan_frags(skb, GFP_ATOMIC) ||
+	    unlikely(!is_skb_forwardable(dev, skb))) {
+		atomic_long_inc(&dev->rx_dropped);
+		kfree_skb(skb);
+		return NET_RX_DROP;
+	}
+
+	skb_scrub_packet(skb, true);
+	skb->priority = 0;
+	return 0;
+}
+
 void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev);
 
 extern int		netdev_budget;
diff --git a/net/core/dev.c b/net/core/dev.c
index eaad4c2..6666b28 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1766,19 +1766,14 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
 
 int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
 {
-	if (skb_orphan_frags(skb, GFP_ATOMIC) ||
-	    unlikely(!is_skb_forwardable(dev, skb))) {
-		atomic_long_inc(&dev->rx_dropped);
-		kfree_skb(skb);
-		return NET_RX_DROP;
-	}
+	int ret = ____dev_forward_skb(dev, skb);
 
-	skb_scrub_packet(skb, true);
-	skb->priority = 0;
-	skb->protocol = eth_type_trans(skb, dev);
-	skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+	if (likely(!ret)) {
+		skb->protocol = eth_type_trans(skb, dev);
+		skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+	}
 
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(__dev_forward_skb);
 
diff --git a/net/core/filter.c b/net/core/filter.c
index 00351cd..b391209 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1628,6 +1628,19 @@ static inline int __bpf_rx_skb(struct net_device *dev, struct sk_buff *skb)
 	return dev_forward_skb(dev, skb);
 }
 
+static inline int __bpf_rx_skb_no_mac(struct net_device *dev,
+				      struct sk_buff *skb)
+{
+	int ret = ____dev_forward_skb(dev, skb);
+
+	if (likely(!ret)) {
+		skb->dev = dev;
+		ret = netif_rx(skb);
+	}
+
+	return ret;
+}
+
 static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb)
 {
 	int ret;
@@ -1647,6 +1660,51 @@ static inline int __bpf_tx_skb(struct net_device *dev, struct sk_buff *skb)
 	return ret;
 }
 
+static int __bpf_redirect_no_mac(struct sk_buff *skb, struct net_device *dev,
+				 u32 flags)
+{
+	/* skb->mac_len is not set on normal egress */
+	unsigned int mlen = skb->network_header - skb->mac_header;
+
+	__skb_pull(skb, mlen);
+
+	/* At ingress, the mac header has already been pulled once.
+	 * At egress, skb_pospull_rcsum has to be done in case that
+	 * the skb is originated from ingress (i.e. a forwarded skb)
+	 * to ensure that rcsum starts at net header.
+	 */
+	if (!skb_at_tc_ingress(skb))
+		skb_postpull_rcsum(skb, skb_mac_header(skb), mlen);
+	skb_pop_mac_header(skb);
+	skb_reset_mac_len(skb);
+	return flags & BPF_F_INGRESS ?
+	       __bpf_rx_skb_no_mac(dev, skb) : __bpf_tx_skb(dev, skb);
+}
+
+static int __bpf_redirect_common(struct sk_buff *skb, struct net_device *dev,
+				 u32 flags)
+{
+	bpf_push_mac_rcsum(skb);
+	return flags & BPF_F_INGRESS ?
+	       __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);
+}
+
+static int __bpf_redirect(struct sk_buff *skb, struct net_device *dev,
+			  u32 flags)
+{
+	switch (dev->type) {
+	case ARPHRD_TUNNEL:
+	case ARPHRD_TUNNEL6:
+	case ARPHRD_SIT:
+	case ARPHRD_IPGRE:
+	case ARPHRD_VOID:
+	case ARPHRD_NONE:
+		return __bpf_redirect_no_mac(skb, dev, flags);
+	default:
+		return __bpf_redirect_common(skb, dev, flags);
+	}
+}
+
 BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags)
 {
 	struct net_device *dev;
@@ -1675,10 +1733,7 @@ BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags)
 		return -ENOMEM;
 	}
 
-	bpf_push_mac_rcsum(clone);
-
-	return flags & BPF_F_INGRESS ?
-	       __bpf_rx_skb(dev, clone) : __bpf_tx_skb(dev, clone);
+	return __bpf_redirect(clone, dev, flags);
 }
 
 static const struct bpf_func_proto bpf_clone_redirect_proto = {
@@ -1722,10 +1777,7 @@ int skb_do_redirect(struct sk_buff *skb)
 		return -EINVAL;
 	}
 
-	bpf_push_mac_rcsum(skb);
-
-	return ri->flags & BPF_F_INGRESS ?
-	       __bpf_rx_skb(dev, skb) : __bpf_tx_skb(dev, skb);
+	return __bpf_redirect(skb, dev, ri->flags);
 }
 
 static const struct bpf_func_proto bpf_redirect_proto = {
-- 
2.5.1

^ permalink raw reply related

* [PATCH net 0/2] bpf: Fix bpf_redirect to an ipip/ip6tnl dev
From: Martin KaFai Lau @ 2016-11-09 23:36 UTC (permalink / raw)
  To: netdev; +Cc: Alexei Starovoitov, Daniel Borkmann, Kernel Team

Hi,

This patch set fixes a bug in bpf_redirect(dev, flags) when dev is an
ipip/ip6tnl.  The current problem is IP-EthHdr-IP is sent out instead of
IP-IP.

Patch 1 adds a dev->type test similar to dev_is_mac_header_xmit()
in act_mirred.c which is only available in net-next.  We can consider to
refactor it once this patch is pulled into net-next from net.

Thanks,
-- Martin

^ permalink raw reply

* Re: [PATCH] mwifiex: fix memory leak in mwifiex_save_hidden_ssid_channels()
From: Brian Norris @ 2016-11-09 23:27 UTC (permalink / raw)
  To: Ricky Liang
  Cc: Amitkumar Karwar, Nishant Sarmukadam, Kalle Valo,
	open list:MARVELL MWIFIEX WIRELESS DRIVER,
	open list:NETWORKING DRIVERS, open list
In-Reply-To: <1478662648-70698-1-git-send-email-jcliang@chromium.org>

On Wed, Nov 09, 2016 at 11:37:28AM +0800, Ricky Liang wrote:
> kmemleak reports memory leak in mwifiex_save_hidden_ssid_channels():
> 
> unreferenced object 0xffffffc0a2914780 (size 192):
>   comm "ksdioirqd/mmc2", pid 2004, jiffies 4307182506 (age 820.684s)
>   hex dump (first 32 bytes):
>     00 06 47 49 4e 2d 32 67 01 03 c8 60 6c 03 01 40  ..GIN-2g...`l..@
>     07 10 54 57 20 34 04 1e 64 05 24 84 03 24 95 04  ..TW 4..d.$..$..
>   backtrace:
>     [<ffffffc0003375f4>] create_object+0x164/0x2b4
>     [<ffffffc0008e3530>] kmemleak_alloc+0x50/0x88
>     [<ffffffc000335120>] __kmalloc_track_caller+0x1bc/0x264
>     [<ffffffc00030899c>] kmemdup+0x38/0x64
>     [<ffffffbffc2311cc>] mwifiex_fill_new_bss_desc+0x3c/0x130 [mwifiex]
>     [<ffffffbffc22ee9c>] mwifiex_save_curr_bcn+0x4ec/0x640 [mwifiex]
>     [<ffffffbffc22f45c>] mwifiex_handle_event_ext_scan_report+0x1d4/0x268 [mwifiex]
>     [<ffffffbffc2375d0>] mwifiex_process_sta_event+0x378/0x898 [mwifiex]
>     [<ffffffbffc224dc8>] mwifiex_process_event+0x1a8/0x1e8 [mwifiex]
>     [<ffffffbffc2228f0>] mwifiex_main_process+0x258/0x534 [mwifiex]
>     [<ffffffbffc258858>] 0xffffffbffc258858
>     [<ffffffc00071ee90>] process_sdio_pending_irqs+0xf8/0x160
>     [<ffffffc00071efdc>] sdio_irq_thread+0x9c/0x1a4
>     [<ffffffc000240d08>] kthread+0xf4/0x100
>     [<ffffffc0002043fc>] ret_from_fork+0xc/0x50
>     [<ffffffffffffffff>] 0xffffffffffffffff
> 
> Signed-off-by: Ricky Liang <jcliang@chromium.org>
> ---
>  drivers/net/wireless/marvell/mwifiex/scan.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
> index 97c9765..98ce072 100644
> --- a/drivers/net/wireless/marvell/mwifiex/scan.c
> +++ b/drivers/net/wireless/marvell/mwifiex/scan.c
> @@ -1671,6 +1671,10 @@ static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv,
>  	}
>  
>  done:
> +	/* beacon_ie buffer was allocated in function
> +	 * mwifiex_fill_new_bss_desc(). Free it now.
> +	 */
> +	kfree(bss_desc->beacon_buf);

For a bit, I thought this was possibly a sort of double-free, since
mwifiex_fill_new_bss_desc() might actually fail to allocate
->beacon_buf, but kfree(NULL) is safe, so:

Reviewed-by: Brian Norris <briannorris@chromium.org>

>  	kfree(bss_desc);
>  	return 0;
>  }
> -- 
> 2.6.6
> 

^ permalink raw reply

* Re: [PATCH 2/2] [nf-next] netfilter: fix NF_REPEAT handling
From: Pablo Neira Ayuso @ 2016-11-09 23:15 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: netfilter-devel, coreteam, netdev, Patrick McHardy,
	Jozsef Kadlecsik, David S. Miller, Florian Westphal, Eric Dumazet,
	linux-kernel
In-Reply-To: <20161108132914.1495283-2-arnd@arndb.de>

[-- Attachment #1: Type: text/plain, Size: 2250 bytes --]

On Tue, Nov 08, 2016 at 02:28:19PM +0100, Arnd Bergmann wrote:
> gcc correctly identified a theoretical uninitialized variable use:
> 
> net/netfilter/nf_conntrack_core.c: In function 'nf_conntrack_in':
> net/netfilter/nf_conntrack_core.c:1125:14: error: 'l4proto' may be used uninitialized in this function [-Werror=maybe-uninitialized]
> 
> This could only happen when we 'goto out' before looking up l4proto,
> and then enter the retry, implying that l3proto->get_l4proto()
> returned NF_REPEAT. This does not currently get returned in any
> code path and probably won't ever happen, but is not good to
> rely on.
> 
> Moving the repeat handling up a little should have the same
> behavior as today but avoids the warning by making that case
> impossible to enter.
> 
> Fixes: 08733a0cb7de ("netfilter: handle NF_REPEAT from nf_conntrack_in()")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> The patch causing this is currently only in nf-next, and not yet
> in net-next.
> ---
>  net/netfilter/nf_conntrack_core.c | 7 +++----
>  1 file changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
> index de4b8a75f30b..610c9de0ce18 100644
> --- a/net/netfilter/nf_conntrack_core.c
> +++ b/net/netfilter/nf_conntrack_core.c
> @@ -1337,6 +1337,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
>  		NF_CT_STAT_INC_ATOMIC(net, invalid);
>  		if (ret == -NF_DROP)
>  			NF_CT_STAT_INC_ATOMIC(net, drop);
> +		if (ret == -NF_REPEAT && tmpl)
> +			goto repeat;

This is my fault, I'm going to mangle this patch since 08733a0cb7de
really broke the NF_REPEAT handling. We should inconditionally jump
back to repeat if we get NF_REPEAT, no matter if the template is set
or not. I'll include a side node on this mangling.

>  		ret = -ret;
>  		goto out;
>  	}
> @@ -1349,10 +1351,7 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
>  		 * closed/aborted connection. We have to go back and create a
>  		 * fresh conntrack.
>  		 */

I'm going to move the comment above on top of the NF_REPEAT check, so
it still keeps around as context.

BTW, the revamped patch looks like the one attached.

Thanks a lot for addressing this fallout.

[-- Attachment #2: x --]
[-- Type: text/plain, Size: 1140 bytes --]

diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index de4b8a75f30b..e9ffe33dc0ca 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1337,6 +1337,12 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
 		NF_CT_STAT_INC_ATOMIC(net, invalid);
 		if (ret == -NF_DROP)
 			NF_CT_STAT_INC_ATOMIC(net, drop);
+		/* Special case: TCP tracker reports an attempt to reopen a
+		 * closed/aborted connection. We have to go back and create a
+		 * fresh conntrack.
+		 */
+		if (ret == -NF_REPEAT)
+			goto repeat;
 		ret = -ret;
 		goto out;
 	}
@@ -1344,16 +1350,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
 	if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
 		nf_conntrack_event_cache(IPCT_REPLY, ct);
 out:
-	if (tmpl) {
-		/* Special case: TCP tracker reports an attempt to reopen a
-		 * closed/aborted connection. We have to go back and create a
-		 * fresh conntrack.
-		 */
-		if (ret == NF_REPEAT)
-			goto repeat;
-		else
-			nf_ct_put(tmpl);
-	}
+	if (tmpl)
+		nf_ct_put(tmpl);
 
 	return ret;
 }

^ permalink raw reply related

* [PATCH net-next V5 8/9] liquidio CN23XX: copyrights changes and alignment
From: Raghu Vatsavayi @ 2016-11-09 23:13 UTC (permalink / raw)
  To: davem
  Cc: netdev, Raghu Vatsavayi, Raghu Vatsavayi, Derek Chickles,
	Satanand Burla, Felix Manlunas
In-Reply-To: <1478733232-15739-1-git-send-email-rvatsavayi@caviumnetworks.com>

Updated copyrights comments and also changed some other comments
alignments.

Signed-off-by: Raghu Vatsavayi <raghu.vatsavayi@caviumnetworks.com>
Signed-off-by: Derek Chickles <derek.chickles@caviumnetworks.com>
Signed-off-by: Satanand Burla <satananda.burla@caviumnetworks.com>
Signed-off-by: Felix Manlunas <felix.manlunas@caviumnetworks.com>
---
 .../ethernet/cavium/liquidio/cn23xx_pf_device.c    | 53 ++++++++++------------
 .../ethernet/cavium/liquidio/cn23xx_pf_device.h    | 39 +++++++---------
 .../net/ethernet/cavium/liquidio/cn23xx_pf_regs.h  | 39 +++++++---------
 .../net/ethernet/cavium/liquidio/cn66xx_device.c   | 36 +++++++--------
 .../net/ethernet/cavium/liquidio/cn66xx_device.h   | 37 +++++++--------
 drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h | 37 +++++++--------
 .../net/ethernet/cavium/liquidio/cn68xx_device.c   | 36 +++++++--------
 .../net/ethernet/cavium/liquidio/cn68xx_device.h   | 37 +++++++--------
 drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h | 37 +++++++--------
 drivers/net/ethernet/cavium/liquidio/lio_core.c    | 36 +++++++--------
 drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 42 ++++++++---------
 drivers/net/ethernet/cavium/liquidio/lio_main.c    | 36 +++++++--------
 .../net/ethernet/cavium/liquidio/liquidio_common.h | 37 +++++++--------
 .../net/ethernet/cavium/liquidio/liquidio_image.h  | 36 +++++++--------
 .../net/ethernet/cavium/liquidio/octeon_config.h   | 37 +++++++--------
 .../net/ethernet/cavium/liquidio/octeon_console.c  | 43 ++++++++----------
 .../net/ethernet/cavium/liquidio/octeon_device.c   | 36 +++++++--------
 .../net/ethernet/cavium/liquidio/octeon_device.h   | 45 ++++++++----------
 drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 36 +++++++--------
 drivers/net/ethernet/cavium/liquidio/octeon_droq.h | 17 +++----
 drivers/net/ethernet/cavium/liquidio/octeon_iq.h   | 21 ++++-----
 drivers/net/ethernet/cavium/liquidio/octeon_main.h | 19 +++-----
 .../net/ethernet/cavium/liquidio/octeon_mem_ops.c  |  5 +-
 .../net/ethernet/cavium/liquidio/octeon_mem_ops.h  |  5 +-
 .../net/ethernet/cavium/liquidio/octeon_network.h  |  5 +-
 drivers/net/ethernet/cavium/liquidio/octeon_nic.c  |  5 +-
 drivers/net/ethernet/cavium/liquidio/octeon_nic.h  |  5 +-
 .../net/ethernet/cavium/liquidio/request_manager.c |  5 +-
 .../ethernet/cavium/liquidio/response_manager.c    |  5 +-
 .../ethernet/cavium/liquidio/response_manager.h    |  5 +-
 30 files changed, 352 insertions(+), 480 deletions(-)

diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
index d01b00b..962dcbc 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
@@ -1,27 +1,21 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
-#include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/etherdevice.h>
 #include "liquidio_common.h"
@@ -421,10 +415,10 @@ static int cn23xx_pf_setup_global_input_regs(struct octeon_device *oct)
 		return -1;
 
 	/** Set the MAC_NUM and PVF_NUM in IQ_PKT_CONTROL reg
-	* for all queues.Only PF can set these bits.
-	* bits 29:30 indicate the MAC num.
-	* bits 32:47 indicate the PVF num.
-	*/
+	 * for all queues.Only PF can set these bits.
+	 * bits 29:30 indicate the MAC num.
+	 * bits 32:47 indicate the PVF num.
+	 */
 	for (q_no = 0; q_no < ern; q_no++) {
 		reg_val = oct->pcie_port << CN23XX_PKT_INPUT_CTL_MAC_NUM_POS;
 
@@ -547,8 +541,8 @@ static void cn23xx_pf_setup_global_output_regs(struct octeon_device *oct)
 	writeq(0x40, (u8 *)oct->mmio[0].hw_addr + CN23XX_SLI_OQ_WMARK);
 
 	/** Disabling setting OQs in reset when ring has no dorebells
-	  * enabling this will cause of head of line blocking
-	  */
+	 * enabling this will cause of head of line blocking
+	 */
 	/* Do it only for pass1.1. and pass1.2 */
 	if ((oct->rev_id == OCTEON_CN23XX_REV_1_0) ||
 	    (oct->rev_id == OCTEON_CN23XX_REV_1_1))
@@ -1391,8 +1385,7 @@ void cn23xx_dump_iq_regs(struct octeon_device *oct)
 		dev_dbg(&oct->pci_dev->dev, "SLI_PKT[%d]_INPUT_CTL [0x%x]: 0x%016llx\n",
 			q_no, CN23XX_SLI_IQ_PKT_CONTROL64(q_no),
 			CVM_CAST64(octeon_read_csr64
-				(oct,
-					CN23XX_SLI_IQ_PKT_CONTROL64(q_no))));
+				(oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no))));
 	}
 
 	pci_read_config_dword(oct->pci_dev, CN23XX_CONFIG_PCIE_DEVCTL, &regval);
diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h
index cee346a..2fedd91 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h
@@ -1,28 +1,23 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file  cn23xx_device.h
  * \brief Host Driver: Routines that perform CN23XX specific operations.
-*/
+ */
 
 #ifndef __CN23XX_PF_DEVICE_H__
 #define __CN23XX_PF_DEVICE_H__
diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h
index 03d79d9..680a405 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_regs.h
@@ -1,29 +1,24 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file cn23xx_regs.h
  * \brief Host Driver: Register Address and Register Mask values for
  * Octeon CN23XX devices.
-*/
+ */
 
 #ifndef __CN23XX_PF_REGS_H__
 #define __CN23XX_PF_REGS_H__
diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c
index 1ebc225..bdec051 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include "liquidio_common.h"
diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h
index 32fbbb2..8ed5713 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_device.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file  cn66xx_device.h
  *  \brief Host Driver: Routines that perform CN66XX specific operations.
  */
diff --git a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h
index 5e3aff2..23152c0 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn66xx_regs.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file cn66xx_regs.h
  *  \brief Host Driver: Register Address and Register Mask values for
  *  Octeon CN66XX devices.
diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c
index dbf3566..1e40f1a 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include "liquidio_common.h"
diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h
index ea7bdcc..66b8d6b 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_device.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file  cn68xx_device.h
  *  \brief Host Driver: Routines that perform CN68XX specific operations.
  */
diff --git a/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h b/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h
index d45a0f4..0b742f0 100644
--- a/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h
+++ b/drivers/net/ethernet/cavium/liquidio/cn68xx_regs.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file cn68xx_regs.h
  *  \brief Host Driver: Register Address and Register Mask values for
  *  Octeon CN68XX devices. The register map for CN66XX is the same
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c
index e6026df..b28e740 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_core.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/if_vlan.h>
 #include "liquidio_common.h"
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
index 1674522..c7c39a0 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/netdevice.h>
 #include <linux/net_tstamp.h>
 #include <linux/pci.h>
@@ -87,9 +83,9 @@ enum {
 	"tx_bytes",
 	"rx_errors",	/*jabber_err+l2_err+frame_err */
 	"tx_errors",	/*fw_err_pko+fw_err_link+fw_err_drop */
-	"rx_dropped",   /*st->fromwire.total_rcvd - st->fromwire.fw_total_rcvd
-			*+st->fromwire.dmac_drop + st->fromwire.fw_err_drop
-			*/
+	"rx_dropped",   /*st->fromwire.total_rcvd - st->fromwire.fw_total_rcvd +
+			 *st->fromwire.dmac_drop + st->fromwire.fw_err_drop
+			 */
 	"tx_dropped",
 
 	"tx_total_sent",
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 7e0bdbe..61b7b36 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/firmware.h>
 #include <net/vxlan.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
index 1d2eba7..82a4eed 100644
--- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
+++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*!  \file  liquidio_common.h
  *   \brief Common: Structures and macros used in PCI-NIC package by core and
  *   host driver.
diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_image.h b/drivers/net/ethernet/cavium/liquidio/liquidio_image.h
index 93819bd..78a3685 100644
--- a/drivers/net/ethernet/cavium/liquidio/liquidio_image.h
+++ b/drivers/net/ethernet/cavium/liquidio/liquidio_image.h
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #ifndef _LIQUIDIO_IMAGE_H_
 #define _LIQUIDIO_IMAGE_H_
 
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h
index 71daf82..1cb3514 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file  octeon_config.h
  *  \brief Host Driver: Configuration data structures for the host driver.
  */
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c
index 01a50f3..c0089ed 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /**
  * @file octeon_console.c
  */
@@ -76,9 +71,9 @@ static int octeon_console_read(struct octeon_device *oct, u32 console_num,
 #define OCTEON_CONSOLE_POLL_INTERVAL_MS  100    /* 10 times per second */
 
 /* First three members of cvmx_bootmem_desc are left in original
-** positions for backwards compatibility.
-** Assumes big endian target
-*/
+ * positions for backwards compatibility.
+ * Assumes big endian target
+ */
 struct cvmx_bootmem_desc {
 	/** spinlock to control access to list */
 	u32 lock;
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c
index 278c880..4a30d7c 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
index 6c18baa..c59b77e 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h
@@ -1,25 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
-
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file octeon_device.h
  *  \brief Host Driver: This file defines the octeon device structure.
  */
@@ -93,9 +88,9 @@ enum octeon_pci_swap_mode {
 #define    OCT_DEV_STATES                 OCT_DEV_STATE_INVALID
 
 /** Octeon Device interrupts
-  *  These interrupt bits are set in int_status filed of
-  *  octeon_device structure
-  */
+ * These interrupt bits are set in int_status filed of
+ * octeon_device structure
+ */
 #define	   OCT_DEV_INTR_DMA0_FORCE	  0x01
 #define	   OCT_DEV_INTR_DMA1_FORCE	  0x02
 #define	   OCT_DEV_INTR_PKT_DATA	  0x04
@@ -548,7 +543,7 @@ struct oct_intrmod_cmd {
 void octeon_init_device_list(int conf_type);
 
 /** Free memory for Input and Output queue structures for a octeon device */
-void octeon_free_device_mem(struct octeon_device *);
+void octeon_free_device_mem(struct octeon_device *oct);
 
 /* Look up a free entry in the octeon_device table and allocate resources
  * for the octeon_device structure for an octeon device. Called at init
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
index 8f6fc95..d7ba000 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c
@@ -1,24 +1,20 @@
 /**********************************************************************
-* Author: Cavium, Inc.
-*
-* Contact: support@cavium.com
-*          Please include "LiquidIO" in the subject.
-*
-* Copyright (c) 2003-2015 Cavium, Inc.
-*
-* This file is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License, Version 2, as
-* published by the Free Software Foundation.
-*
-* This file is distributed in the hope that it will be useful, but
-* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
-* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
-* NONINFRINGEMENT.  See the GNU General Public License for more
-* details.
-*
-* This file may also be available under a different license from Cavium.
-* Contact Cavium, Inc. for more information
-**********************************************************************/
+ * Author: Cavium, Inc.
+ *
+ * Contact: support@cavium.com
+ *          Please include "LiquidIO" in the subject.
+ *
+ * Copyright (c) 2003-2016 Cavium, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
index d591f5f..e620740 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -13,13 +13,8 @@
  * This file is distributed in the hope that it will be useful, but
  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
- **********************************************************************/
-
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*!  \file  octeon_droq.h
  *   \brief Implementation of Octeon Output queues. "Output" is with
  *   respect to the Octeon device on the NIC. From this driver's point of
@@ -81,7 +76,7 @@ struct octeon_skb_page_info {
  *  the Octeon device. Since the descriptor ring keeps physical (bus)
  *  addresses, this field is required for the driver to keep track of
  *  the virtual address pointers.
-*/
+ */
 struct octeon_recv_buffer {
 	/** Packet buffer, including metadata. */
 	void *buffer;
@@ -358,7 +353,7 @@ struct octeon_droq {
  * @param  q_no       - droq no. ranges from 0 - 3.
  * @param app_ctx     - pointer to application context
  * @return Success: 0    Failure: 1
-*/
+ */
 int octeon_init_droq(struct octeon_device *oct_dev,
 		     u32 q_no,
 		     u32 num_descs,
@@ -371,7 +366,7 @@ int octeon_init_droq(struct octeon_device *oct_dev,
  *  @param oct_dev - pointer to the octeon device structure
  *  @param q_no    - droq no. ranges from 0 - 3.
  *  @return:    Success: 0    Failure: 1
-*/
+ */
 int octeon_delete_droq(struct octeon_device *oct_dev, u32 q_no);
 
 /** Register a change in droq operations. The ops field has a pointer to a
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
index b493e67..c67152a 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -13,13 +13,8 @@
  * This file is distributed in the hope that it will be useful, but
  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
- **********************************************************************/
-
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*!  \file  octeon_iq.h
  *   \brief Host Driver: Implementation of Octeon input queues. "Input" is
  *   with respect to the Octeon device on the NIC. From this driver's
@@ -77,7 +72,7 @@ struct oct_iq_stats {
  *  The input queue is used to post raw (instruction) mode data or packet
  *  data to Octeon device from the host. Each input queue (upto 4) for
  *  a Octeon device has one such structure to represent it.
-*/
+ */
 struct octeon_instr_queue {
 	struct octeon_device *oct_dev;
 
@@ -117,8 +112,8 @@ struct octeon_instr_queue {
 	u32 octeon_read_index;
 
 	/** This index aids in finding the window in the queue where Octeon
-	  * has read the commands.
-	  */
+	 *  has read the commands.
+	 */
 	u32 flush_index;
 
 	/** This field keeps track of the instructions pending in this queue. */
@@ -149,8 +144,8 @@ struct octeon_instr_queue {
 	u64 last_db_time;
 
 	/** The doorbell timeout. If the doorbell was not rung for this time and
-	  * fill_cnt is non-zero, ring the doorbell again.
-	  */
+	 * fill_cnt is non-zero, ring the doorbell again.
+	 */
 	u32 db_timeout;
 
 	/** Statistics for this input queue. */
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h
index db86c63..8cd3891 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -13,13 +13,8 @@
  * This file is distributed in the hope that it will be useful, but
  * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
- **********************************************************************/
-
+ * NONINFRINGEMENT.  See the GNU General Public License for more details.
+ ***********************************************************************/
 /*! \file octeon_main.h
  *  \brief Host Driver: This file is included by all host driver source files
  *  to include common definitions.
@@ -78,10 +73,10 @@ static inline void octeon_swap_8B_data(u64 *data, u32 blocks)
 }
 
 /**
-  * \brief unmaps a PCI BAR
-  * @param oct Pointer to Octeon device
-  * @param baridx bar index
-  */
+ * \brief unmaps a PCI BAR
+ * @param oct Pointer to Octeon device
+ * @param baridx bar index
+ */
 static inline void octeon_unmap_pci_barx(struct octeon_device *oct, int baridx)
 {
 	dev_dbg(&oct->pci_dev->dev, "Freeing PCI mapped regions for Bar%d\n",
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c
index 0dc081a..98d8066d 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.c
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 #include <linux/netdevice.h>
 #include "liquidio_common.h"
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h
index 11b1833..bae2fdd 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_mem_ops.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 
 /*!  \file octeon_mem_ops.h
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h
index 54b9665..172b70e 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 
 /*!  \file  octeon_network.h
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c
index 40ac1fe..c3d6a82 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h
index 4b8da67b..b059979 100644
--- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h
+++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 
 /*!  \file octeon_nic.h
diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c
index f6e1616..0cc1583 100644
--- a/drivers/net/ethernet/cavium/liquidio/request_manager.c
+++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c
index 9e496c1..fdaf742 100644
--- a/drivers/net/ethernet/cavium/liquidio/response_manager.c
+++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 #include <linux/pci.h>
 #include <linux/netdevice.h>
diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.h b/drivers/net/ethernet/cavium/liquidio/response_manager.h
index d377faf..cbb2d84 100644
--- a/drivers/net/ethernet/cavium/liquidio/response_manager.h
+++ b/drivers/net/ethernet/cavium/liquidio/response_manager.h
@@ -4,7 +4,7 @@
  * Contact: support@cavium.com
  *          Please include "LiquidIO" in the subject.
  *
- * Copyright (c) 2003-2015 Cavium, Inc.
+ * Copyright (c) 2003-2016 Cavium, Inc.
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
@@ -15,9 +15,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
  * NONINFRINGEMENT.  See the GNU General Public License for more
  * details.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium, Inc. for more information
  **********************************************************************/
 
 /*! \file response_manager.h
-- 
1.8.3.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox