Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/818718/?format=api
{ "id": 818718, "url": "http://patchwork.ozlabs.org/api/patches/818718/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20170926153522.31500-3-jakub.kicinski@netronome.com/", "project": { "id": 7, "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api", "name": "Linux network development", "link_name": "netdev", "list_id": "netdev.vger.kernel.org", "list_email": "netdev@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20170926153522.31500-3-jakub.kicinski@netronome.com>", "list_archive_url": null, "date": "2017-09-26T15:35:22", "name": "[net-next,2/2] tools: bpf: add bpftool", "commit_ref": null, "pull_url": null, "state": "changes-requested", "archived": true, "hash": "56799a2da19d47bd2aa2e9815d535d8c05ae1e26", "submitter": { "id": 67484, "url": "http://patchwork.ozlabs.org/api/people/67484/?format=api", "name": "Jakub Kicinski", "email": "jakub.kicinski@netronome.com" }, "delegate": { "id": 34, "url": "http://patchwork.ozlabs.org/api/users/34/?format=api", "username": "davem", "first_name": "David", "last_name": "Miller", "email": "davem@davemloft.net" }, "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/20170926153522.31500-3-jakub.kicinski@netronome.com/mbox/", "series": [ { "id": 5168, "url": "http://patchwork.ozlabs.org/api/series/5168/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=5168", "date": "2017-09-26T15:35:20", "name": "tools: add bpftool", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/5168/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/818718/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/818718/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<netdev-owner@vger.kernel.org>", "X-Original-To": "patchwork-incoming@ozlabs.org", "Delivered-To": "patchwork-incoming@ozlabs.org", "Authentication-Results": [ "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=netronome-com.20150623.gappssmtp.com\n\theader.i=@netronome-com.20150623.gappssmtp.com\n\theader.b=\"nHtdgHnH\"; dkim-atps=neutral" ], "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y1lQp5KqGz9t3R\n\tfor <patchwork-incoming@ozlabs.org>;\n\tWed, 27 Sep 2017 01:35:46 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1031442AbdIZPfp (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tTue, 26 Sep 2017 11:35:45 -0400", "from mail-pg0-f43.google.com ([74.125.83.43]:54502 \"EHLO\n\tmail-pg0-f43.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1031429AbdIZPfj (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Tue, 26 Sep 2017 11:35:39 -0400", "by mail-pg0-f43.google.com with SMTP id c137so6101210pga.11\n\tfor <netdev@vger.kernel.org>; Tue, 26 Sep 2017 08:35:39 -0700 (PDT)", "from jkicinski-Precision-T1700.netronome.com ([75.53.12.129])\n\tby smtp.gmail.com with ESMTPSA id\n\tt125sm4435909pgc.88.2017.09.26.08.35.36\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tTue, 26 Sep 2017 08:35:37 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=netronome-com.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=Sz/L5CdcHwXkvI60EIH67gGDYF9ygqebDrkkR8vJJ2U=;\n\tb=nHtdgHnHxoxzFwBd+d1Wp0XEUxBkxV2vMQmpLLQIw2yYnuKoWIG3xfPnjm4y2Qqs7D\n\tBb+FNt+Q9eO7+psMsLtbg+w96XkVT/TUwViNfOdGvoucDWmqkKBYZoXBzOtJjdDam7Gf\n\tHC3jOBxJrWU4YLJOL7Z6xAQh/cyOO97AGW1sBJFdInhJ5XkB9CfHuW1N5VgKsBltVxur\n\tl1D7enhcyn+5ThLKG+UdFtElJ4+Dewz9f6N1eVIPvDKEVNmsOmBErXBGq16MfVNYVwR8\n\tRJFxD94xulm+wjCNJ2Kgi4Blw1EXJKF13PEx4PZzXjSFKfPL9zWPZnBbUr08Nb9sSzwn\n\tcJAA==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=Sz/L5CdcHwXkvI60EIH67gGDYF9ygqebDrkkR8vJJ2U=;\n\tb=PWirS4JMO15iSj0u/RZTTu7UE98CFswxS0XKFDKvto+fcmZqzxljzPKQdIRnAPiuxV\n\tSaEMxBiBaEcXMQtSNaoMkxKzFHCZUv4ln25pO4gxRHNefoyub/uw6sPPSupuntrdQczW\n\tE6mI+KqUK+zCgYpYHpCLohWyw2p5ESJduWR2YnFPurLK1E1c5yy7WMVbfLPfZ17cCxwn\n\twsNdFI5dYi641n4D6+TmDV4MUwsu3PdRJdGbjCxPG6J/viXStmPK7P81Yp9b62aJVVTu\n\tGuk6cUkMGoyUP1zguqO81K0DSkhihnZbbEdGU1B5dC77b3HiDYM1mC3mlIerNBV75H1Z\n\tvsPA==", "X-Gm-Message-State": "AHPjjUjh0KNZNfHC782vPkZvOKuAHsKX1N6mpwAKkYPLMWNj+LDFqYAF\n\tdNI9bf6woBYUpeh0Yc9RxjaaDc2A", "X-Google-Smtp-Source": "AOwi7QC1eq4YY7i4t8A1v5C4G6VlKxBq6Trak5WXxEaZ+Q4Br6VMF71pYfCwcYi0v80mcYmNwEoiBw==", "X-Received": "by 10.98.79.157 with SMTP id f29mr11261763pfj.9.1506440137739;\n\tTue, 26 Sep 2017 08:35:37 -0700 (PDT)", "From": "Jakub Kicinski <jakub.kicinski@netronome.com>", "To": "netdev@vger.kernel.org", "Cc": "daniel@iogearbox.net, alexei.starovoitov@gmail.com,\n\tdavem@davemloft.net, hannes@stressinduktion.org, dsahern@gmail.com,\n\toss-drivers@netronome.com, Jakub Kicinski <jakub.kicinski@netronome.com>", "Subject": "[PATCH net-next 2/2] tools: bpf: add bpftool", "Date": "Tue, 26 Sep 2017 08:35:22 -0700", "Message-Id": "<20170926153522.31500-3-jakub.kicinski@netronome.com>", "X-Mailer": "git-send-email 2.14.1", "In-Reply-To": "<20170926153522.31500-1-jakub.kicinski@netronome.com>", "References": "<20170926153522.31500-1-jakub.kicinski@netronome.com>", "Sender": "netdev-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<netdev.vger.kernel.org>", "X-Mailing-List": "netdev@vger.kernel.org" }, "content": "Add a simple tool for querying and updating BPF objects on the system.\n\nSigned-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>\nReviewed-by: Simon Horman <simon.horman@netronome.com>\n---\n tools/bpf/Makefile | 18 +-\n tools/bpf/bpftool/Makefile | 80 +++++\n tools/bpf/bpftool/common.c | 214 ++++++++++++\n tools/bpf/bpftool/jit_disasm.c | 83 +++++\n tools/bpf/bpftool/main.c | 212 ++++++++++++\n tools/bpf/bpftool/main.h | 99 ++++++\n tools/bpf/bpftool/map.c | 742 +++++++++++++++++++++++++++++++++++++++++\n tools/bpf/bpftool/prog.c | 392 ++++++++++++++++++++++\n 8 files changed, 1837 insertions(+), 3 deletions(-)\n create mode 100644 tools/bpf/bpftool/Makefile\n create mode 100644 tools/bpf/bpftool/common.c\n create mode 100644 tools/bpf/bpftool/jit_disasm.c\n create mode 100644 tools/bpf/bpftool/main.c\n create mode 100644 tools/bpf/bpftool/main.h\n create mode 100644 tools/bpf/bpftool/map.c\n create mode 100644 tools/bpf/bpftool/prog.c", "diff": "diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile\nindex ddf888010652..325a35e1c28e 100644\n--- a/tools/bpf/Makefile\n+++ b/tools/bpf/Makefile\n@@ -3,6 +3,7 @@ prefix = /usr\n CC = gcc\n LEX = flex\n YACC = bison\n+MAKE = make\n \n CFLAGS += -Wall -O2\n CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include\n@@ -13,7 +14,7 @@ CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include\n %.lex.c: %.l\n \t$(LEX) -o $@ $<\n \n-all : bpf_jit_disasm bpf_dbg bpf_asm\n+all: bpf_jit_disasm bpf_dbg bpf_asm bpftool\n \n bpf_jit_disasm : CFLAGS += -DPACKAGE='bpf_jit_disasm'\n bpf_jit_disasm : LDLIBS = -lopcodes -lbfd -ldl\n@@ -26,10 +27,21 @@ bpf_asm : LDLIBS =\n bpf_asm : bpf_asm.o bpf_exp.yacc.o bpf_exp.lex.o\n bpf_exp.lex.o : bpf_exp.yacc.c\n \n-clean :\n+clean: bpftool_clean\n \trm -rf *.o bpf_jit_disasm bpf_dbg bpf_asm bpf_exp.yacc.* bpf_exp.lex.*\n \n-install :\n+install: bpftool_install\n \tinstall bpf_jit_disasm $(prefix)/bin/bpf_jit_disasm\n \tinstall bpf_dbg $(prefix)/bin/bpf_dbg\n \tinstall bpf_asm $(prefix)/bin/bpf_asm\n+\n+bpftool:\n+\t$(MAKE) -C bpftool\n+\n+bpftool_install:\n+\t$(MAKE) -C bpftool install\n+\n+bpftool_clean:\n+\t$(MAKE) -C bpftool clean\n+\n+.PHONY: bpftool FORCE\ndiff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile\nnew file mode 100644\nindex 000000000000..a7151f47fb40\n--- /dev/null\n+++ b/tools/bpf/bpftool/Makefile\n@@ -0,0 +1,80 @@\n+include ../../scripts/Makefile.include\n+\n+include ../../scripts/utilities.mak\n+\n+ifeq ($(srctree),)\n+srctree := $(patsubst %/,%,$(dir $(CURDIR)))\n+srctree := $(patsubst %/,%,$(dir $(srctree)))\n+srctree := $(patsubst %/,%,$(dir $(srctree)))\n+#$(info Determined 'srctree' to be $(srctree))\n+endif\n+\n+ifneq ($(objtree),)\n+#$(info Determined 'objtree' to be $(objtree))\n+endif\n+\n+ifneq ($(OUTPUT),)\n+#$(info Determined 'OUTPUT' to be $(OUTPUT))\n+# Adding $(OUTPUT) as a directory to look for source files,\n+# because use generated output files as sources dependency\n+# for flex/bison parsers.\n+VPATH += $(OUTPUT)\n+export VPATH\n+endif\n+\n+ifeq ($(V),1)\n+ Q =\n+else\n+ Q = @\n+endif\n+\n+BPF_DIR\t= $(srctree)/tools/lib/bpf/\n+\n+ifneq ($(OUTPUT),)\n+ BPF_PATH=$(OUTPUT)\n+else\n+ BPF_PATH=$(BPF_DIR)\n+endif\n+\n+LIBBPF = $(BPF_PATH)libbpf.a\n+\n+$(LIBBPF): FORCE\n+\t$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)\n+\n+$(LIBBPF)-clean:\n+\t$(call QUIET_CLEAN, libbpf)\n+\t$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null\n+\n+prefix = /usr\n+\n+CC = gcc\n+\n+CFLAGS += -O2\n+CFLAGS += -W -Wall -Wextra -Wno-unused-parameter -Wshadow\n+CFLAGS += -D__EXPORTED_HEADERS__ -I$(srctree)/tools/include/uapi -I$(srctree)/tools/include -I$(srctree)/tools/lib/bpf\n+LIBS = -lelf -lbfd -lopcodes $(LIBBPF)\n+\n+include $(wildcard *.d)\n+\n+all: $(OUTPUT)bpftool\n+\n+SRCS=$(wildcard *.c)\n+OBJS=$(patsubst %.c,$(OUTPUT)%.o,$(SRCS))\n+\n+$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)\n+\t$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $^ $(LIBS)\n+\n+$(OUTPUT)%.o: %.c\n+\t$(QUIET_CC)$(COMPILE.c) -MMD -o $@ $<\n+\n+clean: $(LIBBPF)-clean\n+\t$(call QUIET_CLEAN, bpftool)\n+\t$(Q)rm -rf $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d\n+\n+install:\n+\tinstall $(OUTPUT)bpftool $(prefix)/sbin/bpftool\n+\n+FORCE:\n+\n+.PHONY: all clean FORCE\n+.DEFAULT_GOAL := all\ndiff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c\nnew file mode 100644\nindex 000000000000..db7bb966c844\n--- /dev/null\n+++ b/tools/bpf/bpftool/common.c\n@@ -0,0 +1,214 @@\n+/*\n+ * Copyright (C) 2017 Netronome Systems, Inc.\n+ *\n+ * This software is dual licensed under the GNU General License Version 2,\n+ * June 1991 as shown in the file COPYING in the top-level directory of this\n+ * source tree or the BSD 2-Clause License provided below. You have the\n+ * option to license this software under the complete terms of either license.\n+ *\n+ * The BSD 2-Clause License:\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * 1. Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer.\n+ *\n+ * 2. Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials\n+ * provided with the distribution.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ */\n+\n+/* Author: Jakub Kicinski <kubakici@wp.pl> */\n+\n+#include <errno.h>\n+#include <libgen.h>\n+#include <stdbool.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <unistd.h>\n+#include <linux/limits.h>\n+#include <linux/magic.h>\n+#include <sys/types.h>\n+#include <sys/vfs.h>\n+\n+#include <bpf.h>\n+\n+#include \"main.h\"\n+\n+static bool is_bpffs(char *path)\n+{\n+\tstruct statfs st_fs;\n+\n+\tif (statfs(path, &st_fs) < 0)\n+\t\treturn false;\n+\n+\treturn (unsigned long)st_fs.f_type == BPF_FS_MAGIC;\n+}\n+\n+int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)\n+{\n+\tenum bpf_obj_type type;\n+\tint fd;\n+\n+\tfd = bpf_obj_get(path);\n+\tif (fd < 1) {\n+\t\terr(\"bpf obj get (%s): %s\\n\", path,\n+\t\t errno == EACCES && !is_bpffs(dirname(path)) ?\n+\t\t \"directory not in bpf file system (bpffs)\" :\n+\t\t strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\ttype = get_fd_type(fd);\n+\tif (type < 0) {\n+\t\tclose(fd);\n+\t\treturn type;\n+\t}\n+\tif (type != exp_type) {\n+\t\terr(\"incorrect object type: %s\\n\", get_fd_type_name(type));\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\treturn fd;\n+}\n+\n+int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))\n+{\n+\tunsigned int id;\n+\tchar *endptr;\n+\tint err;\n+\tint fd;\n+\n+\tif (!is_prefix(*argv, \"id\")) {\n+\t\terr(\"expected 'id' got %s\\n\", *argv);\n+\t\treturn -1;\n+\t}\n+\tNEXT_ARG();\n+\n+\tid = strtoul(*argv, &endptr, 0);\n+\tif (*endptr) {\n+\t\terr(\"can't parse %s as ID\\n\", *argv);\n+\t\treturn -1;\n+\t}\n+\tNEXT_ARG();\n+\n+\tif (argc != 1)\n+\t\tusage();\n+\n+\tfd = get_fd_by_id(id);\n+\tif (fd < 1) {\n+\t\terr(\"can't get prog by id (%u): %s\\n\", id, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\terr = bpf_obj_pin(fd, *argv);\n+\tclose(fd);\n+\tif (err) {\n+\t\terr(\"can't pin the object (%s): %s\\n\", *argv,\n+\t\t errno == EACCES && !is_bpffs(dirname(*argv)) ?\n+\t\t \"directory not in bpf file system (bpffs)\" :\n+\t\t strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+const char *get_fd_type_name(enum bpf_obj_type type)\n+{\n+\tstatic const char * const names[] = {\n+\t\t[BPF_OBJ_PROG]\t= \"prog\",\n+\t\t[BPF_OBJ_MAP]\t= \"map\",\n+\t};\n+\n+\tif (type > 0 && type < ARRAY_SIZE(names) && names[type])\n+\t\treturn names[type];\n+\n+\treturn \"unknown\";\n+}\n+\n+int get_fd_type(int fd)\n+{\n+\tchar path[PATH_MAX];\n+\tchar buf[512];\n+\tssize_t n;\n+\n+\tsnprintf(path, sizeof(path), \"/proc/%d/fd/%d\", getpid(), fd);\n+\n+\tn = readlink(path, buf, sizeof(buf));\n+\tif (n < 0) {\n+\t\terr(\"can't read link type: %s\\n\", strerror(errno));\n+\t\treturn -1;\n+\t}\n+\tif (n == sizeof(path)) {\n+\t\terr(\"can't read link type: path too long!\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tif (strstr(buf, \"bpf-map\"))\n+\t\treturn BPF_OBJ_MAP;\n+\telse if (strstr(buf, \"bpf-prog\"))\n+\t\treturn BPF_OBJ_PROG;\n+\n+\treturn BPF_OBJ_UNKNOWN;\n+}\n+\n+char *get_fdinfo(int fd, const char *key)\n+{\n+\tchar path[PATH_MAX];\n+\tchar *line = NULL;\n+\tsize_t line_n = 0;\n+\tssize_t n;\n+\tFILE *fdi;\n+\n+\tsnprintf(path, sizeof(path), \"/proc/%d/fdinfo/%d\", getpid(), fd);\n+\n+\tfdi = fopen(path, \"r\");\n+\tif (!fdi) {\n+\t\terr(\"can't open fdinfo: %s\\n\", strerror(errno));\n+\t\treturn NULL;\n+\t}\n+\n+\twhile ((n = getline(&line, &line_n, fdi))) {\n+\t\tchar *value;\n+\t\tint len;\n+\n+\t\tif (!strstr(line, key))\n+\t\t\tcontinue;\n+\n+\t\tfclose(fdi);\n+\n+\t\tvalue = strchr(line, '\\t');\n+\t\tif (!value) {\n+\t\t\terr(\"malformed fdinfo!?\\n\");\n+\t\t\tfree(line);\n+\t\t\treturn NULL;\n+\t\t}\n+\t\tvalue++;\n+\n+\t\tlen = strlen(value);\n+\t\tmemmove(line, value, len);\n+\t\tline[len - 1] = '\\0';\n+\n+\t\treturn line;\n+\t}\n+\n+\terr(\"key '%s' not found in fdinfo\\n\", key);\n+\tfclose(fdi);\n+\treturn NULL;\n+}\ndiff --git a/tools/bpf/bpftool/jit_disasm.c b/tools/bpf/bpftool/jit_disasm.c\nnew file mode 100644\nindex 000000000000..e2bcfbf9b824\n--- /dev/null\n+++ b/tools/bpf/bpftool/jit_disasm.c\n@@ -0,0 +1,83 @@\n+/*\n+ * Based on:\n+ *\n+ * Minimal BPF JIT image disassembler\n+ *\n+ * Disassembles BPF JIT compiler emitted opcodes back to asm insn's for\n+ * debugging or verification purposes.\n+ *\n+ * Copyright 2013 Daniel Borkmann <daniel@iogearbox.net>\n+ * Licensed under the GNU General Public License, version 2.0 (GPLv2)\n+ */\n+\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <assert.h>\n+#include <unistd.h>\n+#include <string.h>\n+#include <bfd.h>\n+#include <dis-asm.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+\n+static void get_exec_path(char *tpath, size_t size)\n+{\n+\tssize_t len;\n+\tchar *path;\n+\n+\tsnprintf(tpath, size, \"/proc/%d/exe\", (int) getpid());\n+\ttpath[size - 1] = 0;\n+\n+\tpath = strdup(tpath);\n+\tassert(path);\n+\n+\tlen = readlink(path, tpath, size);\n+\ttpath[len] = 0;\n+\n+\tfree(path);\n+}\n+\n+void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)\n+{\n+\tdisassembler_ftype disassemble;\n+\tstruct disassemble_info info;\n+\tint count, i, pc = 0;\n+\tchar tpath[256];\n+\tbfd *bfdf;\n+\n+\tmemset(tpath, 0, sizeof(tpath));\n+\tget_exec_path(tpath, sizeof(tpath));\n+\n+\tbfdf = bfd_openr(tpath, NULL);\n+\tassert(bfdf);\n+\tassert(bfd_check_format(bfdf, bfd_object));\n+\n+\tinit_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);\n+\tinfo.arch = bfd_get_arch(bfdf);\n+\tinfo.mach = bfd_get_mach(bfdf);\n+\tinfo.buffer = image;\n+\tinfo.buffer_length = len;\n+\n+\tdisassemble_init_for_target(&info);\n+\n+\tdisassemble = disassembler(bfdf);\n+\tassert(disassemble);\n+\n+\tdo {\n+\t\tprintf(\"%4x:\\t\", pc);\n+\n+\t\tcount = disassemble(pc, &info);\n+\n+\t\tif (opcodes) {\n+\t\t\tprintf(\"\\n\\t\");\n+\t\t\tfor (i = 0; i < count; ++i)\n+\t\t\t\tprintf(\"%02x \", (uint8_t) image[pc + i]);\n+\t\t}\n+\t\tprintf(\"\\n\");\n+\n+\t\tpc += count;\n+\t} while (count > 0 && pc < len);\n+\n+\tbfd_close(bfdf);\n+}\ndiff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c\nnew file mode 100644\nindex 000000000000..622be3b3a28a\n--- /dev/null\n+++ b/tools/bpf/bpftool/main.c\n@@ -0,0 +1,212 @@\n+/*\n+ * Copyright (C) 2017 Netronome Systems, Inc.\n+ *\n+ * This software is dual licensed under the GNU General License Version 2,\n+ * June 1991 as shown in the file COPYING in the top-level directory of this\n+ * source tree or the BSD 2-Clause License provided below. You have the\n+ * option to license this software under the complete terms of either license.\n+ *\n+ * The BSD 2-Clause License:\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * 1. Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer.\n+ *\n+ * 2. Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials\n+ * provided with the distribution.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ */\n+\n+/* Author: Jakub Kicinski <kubakici@wp.pl> */\n+\n+#include <bfd.h>\n+#include <ctype.h>\n+#include <errno.h>\n+#include <linux/bpf.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include <bpf.h>\n+\n+#include \"main.h\"\n+\n+const char *bin_name;\n+static int last_argc;\n+static char **last_argv;\n+static int (*last_do_help)(int argc, char **argv);\n+\n+void usage(void)\n+{\n+\tlast_do_help(last_argc - 1, last_argv + 1);\n+\n+\texit(-1);\n+}\n+\n+static int do_help(int argc, char **argv)\n+{\n+\tfprintf(stderr,\n+\t\t\"Usage: %s OBJECT { COMMAND | help }\\n\"\n+\t\t\" %s batch file FILE\\n\"\n+\t\t\"\\n\"\n+\t\t\" OBJECT := { prog | map }\\n\",\n+\t\tbin_name, bin_name);\n+\n+\treturn 0;\n+}\n+\n+int cmd_select(const struct cmd *cmds, int argc, char **argv,\n+\t int (*help)(int argc, char **argv))\n+{\n+\tunsigned int i;\n+\n+\tlast_argc = argc;\n+\tlast_argv = argv;\n+\tlast_do_help = help;\n+\n+\tif (argc < 1 && cmds[0].func)\n+\t\treturn cmds[0].func(argc, argv);\n+\n+\tfor (i = 0; cmds[i].func; i++)\n+\t\tif (is_prefix(*argv, cmds[i].cmd))\n+\t\t\treturn cmds[i].func(argc - 1, argv + 1);\n+\n+\thelp(argc - 1, argv + 1);\n+\n+\treturn -1;\n+}\n+\n+bool is_prefix(const char *pfx, const char *str)\n+{\n+\tif (!pfx)\n+\t\treturn false;\n+\tif (strlen(str) < strlen(pfx))\n+\t\treturn false;\n+\n+\treturn !memcmp(str, pfx, strlen(pfx));\n+}\n+\n+void print_hex(void *arg, unsigned int n, const char *sep)\n+{\n+\tunsigned char *data = arg;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < n; i++) {\n+\t\tconst char *pfx = \"\";\n+\n+\t\tif (!i)\n+\t\t\t/* nothing */;\n+\t\telse if (!(i % 16))\n+\t\t\tprintf(\"\\n\");\n+\t\telse if (!(i % 8))\n+\t\t\tprintf(\" \");\n+\t\telse\n+\t\t\tpfx = sep;\n+\n+\t\tprintf(\"%s%02hhx\", i ? pfx : \"\", data[i]);\n+\t}\n+}\n+\n+static int do_batch(int argc, char **argv);\n+\n+static const struct cmd cmds[] = {\n+\t{ \"help\",\tdo_help },\n+\t{ \"batch\",\tdo_batch },\n+\t{ \"prog\",\tdo_prog },\n+\t{ \"map\",\tdo_map },\n+\t{ 0 }\n+};\n+\n+static int do_batch(int argc, char **argv)\n+{\n+\tunsigned int lines = 0;\n+\tchar *n_argv[4096];\n+\tchar buf[65536];\n+\tint n_argc;\n+\tchar *ptr;\n+\tFILE *fp;\n+\tint err;\n+\n+\tif (argc < 2) {\n+\t\terr(\"too few parameters for batch\\n\");\n+\t\treturn -1;\n+\t} else if (!is_prefix(*argv, \"file\")) {\n+\t\terr(\"expected 'file', got: %s\\n\", *argv);\n+\t\treturn -1;\n+\t} else if (argc > 2) {\n+\t\terr(\"too many parameters for batch\\n\");\n+\t\treturn -1;\n+\t}\n+\tNEXT_ARG();\n+\n+\tfp = fopen(*argv, \"r\");\n+\tif (!fp) {\n+\t\terr(\"Can't open file (%s): %s\\n\", *argv, strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\twhile (fgets(buf, sizeof(buf), fp)) {\n+\t\tif (strlen(buf) == sizeof(buf) - 1) {\n+\t\t\terrno = E2BIG;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tptr = buf;\n+\t\tn_argc = 0;\n+\t\twhile (*ptr) {\n+\t\t\tif (isspace(*ptr)) {\n+\t\t\t\tptr++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tn_argv[n_argc++] = ptr;\n+\n+\t\t\tptr += strcspn(ptr, \" \\t\\n\");\n+\t\t\t*ptr++ = 0;\n+\t\t}\n+\n+\t\tif (!n_argc)\n+\t\t\tcontinue;\n+\n+\t\terr = cmd_select(cmds, n_argc, n_argv, do_help);\n+\t\tif (err)\n+\t\t\tgoto err_close;\n+\n+\t\tlines++;\n+\t}\n+\n+\tif (errno && errno != ENOENT) {\n+\t\tperror(\"reading batch file failed\");\n+\t\terr = -1;\n+\t} else {\n+\t\tinfo(\"processed %d lines\\n\", lines);\n+\t\terr = 0;\n+\t}\n+err_close:\n+\tfclose(fp);\n+\n+\treturn err;\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\tbin_name = argv[0];\n+\tNEXT_ARG();\n+\n+\tbfd_init();\n+\n+\treturn cmd_select(cmds, argc, argv, do_help);\n+}\ndiff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h\nnew file mode 100644\nindex 000000000000..85d2d7870a58\n--- /dev/null\n+++ b/tools/bpf/bpftool/main.h\n@@ -0,0 +1,99 @@\n+/*\n+ * Copyright (C) 2017 Netronome Systems, Inc.\n+ *\n+ * This software is dual licensed under the GNU General License Version 2,\n+ * June 1991 as shown in the file COPYING in the top-level directory of this\n+ * source tree or the BSD 2-Clause License provided below. You have the\n+ * option to license this software under the complete terms of either license.\n+ *\n+ * The BSD 2-Clause License:\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * 1. Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer.\n+ *\n+ * 2. Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials\n+ * provided with the distribution.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ */\n+\n+/* Author: Jakub Kicinski <kubakici@wp.pl> */\n+\n+#ifndef __BPF_TOOL_H\n+#define __BPF_TOOL_H\n+\n+#include <stdbool.h>\n+#include <stdio.h>\n+#include <linux/bpf.h>\n+\n+#define ARRAY_SIZE(a)\t(sizeof(a) / sizeof(a[0]))\n+\n+#define err(msg...)\tfprintf(stderr, \"Error: \" msg)\n+#define warn(msg...)\tfprintf(stderr, \"Warning: \" msg)\n+#define info(msg...)\tfprintf(stderr, msg)\n+\n+#define ptr_to_u64(ptr)\t((__u64)(unsigned long)(ptr))\n+\n+#define min(a, b)\t\t\t\t\t\t\t\\\n+\t({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _b : _a; })\n+#define max(a, b)\t\t\t\t\t\t\t\\\n+\t({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _b : _a; })\n+\n+#define NEXT_ARG()\t({ argc--; argv++; if (argc < 0) usage(); })\n+#define NEXT_ARGP()\t({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })\n+#define BAD_ARG()\t({ err(\"what is '%s'?\\n\", *argv); -1; })\n+\n+#define BPF_TAG_FMT\t\"%02hhx:%02hhx:%02hhx:%02hhx:\"\t\\\n+\t\t\t\"%02hhx:%02hhx:%02hhx:%02hhx\"\n+\n+#define HELP_SPEC_PROGRAM\t\t\t\t\t\t\\\n+\t\"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }\"\n+\n+enum bpf_obj_type {\n+\tBPF_OBJ_UNKNOWN,\n+\tBPF_OBJ_PROG,\n+\tBPF_OBJ_MAP,\n+};\n+\n+extern const char *bin_name;\n+\n+bool is_prefix(const char *pfx, const char *str);\n+void print_hex(void *arg, unsigned int n, const char *sep);\n+void usage(void) __attribute__((noreturn));\n+\n+struct cmd {\n+\tconst char *cmd;\n+\tint (*func)(int argc, char **argv);\n+};\n+\n+int cmd_select(const struct cmd *cmds, int argc, char **argv,\n+\t int (*help)(int argc, char **argv));\n+\n+int get_fd_type(int fd);\n+const char *get_fd_type_name(enum bpf_obj_type type);\n+char *get_fdinfo(int fd, const char *key);\n+int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type);\n+int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32));\n+\n+int do_prog(int argc, char **arg);\n+int do_map(int argc, char **arg);\n+\n+int prog_parse_fd(int *argc, char ***argv);\n+\n+void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes);\n+\n+#endif\ndiff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c\nnew file mode 100644\nindex 000000000000..db46986fef73\n--- /dev/null\n+++ b/tools/bpf/bpftool/map.c\n@@ -0,0 +1,742 @@\n+/*\n+ * Copyright (C) 2017 Netronome Systems, Inc.\n+ *\n+ * This software is dual licensed under the GNU General License Version 2,\n+ * June 1991 as shown in the file COPYING in the top-level directory of this\n+ * source tree or the BSD 2-Clause License provided below. You have the\n+ * option to license this software under the complete terms of either license.\n+ *\n+ * The BSD 2-Clause License:\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * 1. Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer.\n+ *\n+ * 2. Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials\n+ * provided with the distribution.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ */\n+\n+/* Author: Jakub Kicinski <kubakici@wp.pl> */\n+\n+#include <ctype.h>\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <stdbool.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <unistd.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+\n+#include <bpf.h>\n+\n+#include \"main.h\"\n+\n+static const char * const map_type_name[] = {\n+\t[BPF_MAP_TYPE_UNSPEC]\t\t= \"unspec\",\n+\t[BPF_MAP_TYPE_HASH]\t\t= \"hash\",\n+\t[BPF_MAP_TYPE_ARRAY]\t\t= \"array\",\n+\t[BPF_MAP_TYPE_PROG_ARRAY]\t= \"prog_array\",\n+\t[BPF_MAP_TYPE_PERF_EVENT_ARRAY]\t= \"perf_event_array\",\n+\t[BPF_MAP_TYPE_PERCPU_HASH]\t= \"percpu_hash\",\n+\t[BPF_MAP_TYPE_PERCPU_ARRAY]\t= \"percpu_array\",\n+\t[BPF_MAP_TYPE_STACK_TRACE]\t= \"stack_trace\",\n+\t[BPF_MAP_TYPE_CGROUP_ARRAY]\t= \"cgroup_array\",\n+\t[BPF_MAP_TYPE_LRU_HASH]\t\t= \"lru_hash\",\n+\t[BPF_MAP_TYPE_LRU_PERCPU_HASH]\t= \"lru_percpu_hash\",\n+\t[BPF_MAP_TYPE_LPM_TRIE]\t\t= \"lpm_trie\",\n+\t[BPF_MAP_TYPE_ARRAY_OF_MAPS]\t= \"array_of_maps\",\n+\t[BPF_MAP_TYPE_HASH_OF_MAPS]\t= \"hash_of_maps\",\n+\t[BPF_MAP_TYPE_DEVMAP]\t\t= \"devmap\",\n+\t[BPF_MAP_TYPE_SOCKMAP]\t\t= \"sockmap\",\n+};\n+\n+static unsigned int get_possible_cpus(void)\n+{\n+\tstatic unsigned int result;\n+\tchar buf[128];\n+\tlong int n;\n+\tchar *ptr;\n+\tint fd;\n+\n+\tif (result)\n+\t\treturn result;\n+\n+\tfd = open(\"/sys/devices/system/cpu/possible\", O_RDONLY);\n+\tif (fd < 1) {\n+\t\terr(\"can't open sysfs possible cpus\\n\");\n+\t\texit(-1);\n+\t}\n+\n+\tn = read(fd, buf, sizeof(buf));\n+\tif (n < 2) {\n+\t\terr(\"can't read sysfs possible cpus\\n\");\n+\t\texit(-1);\n+\t}\n+\tclose(fd);\n+\n+\tif (n == sizeof(buf)) {\n+\t\terr(\"read sysfs possible cpus overflow\\n\");\n+\t\texit(-1);\n+\t}\n+\n+\tptr = buf;\n+\tn = 0;\n+\twhile (*ptr && *ptr != '\\n') {\n+\t\tunsigned int a, b;\n+\n+\t\tif (sscanf(ptr, \"%u-%u\", &a, &b) == 2) {\n+\t\t\tn += b - a + 1;\n+\n+\t\t\tptr = strchr(ptr, '-') + 1;\n+\t\t} else if (sscanf(ptr, \"%u\", &a) == 1) {\n+\t\t\tn++;\n+\t\t}\n+\n+\t\twhile (isdigit(*ptr))\n+\t\t\tptr++;\n+\t\tif (*ptr == ',')\n+\t\t\tptr++;\n+\t}\n+\n+\tresult = n;\n+\n+\treturn result;\n+}\n+\n+static bool map_is_per_cpu(__u32 type)\n+{\n+\treturn type == BPF_MAP_TYPE_PERCPU_HASH ||\n+\t type == BPF_MAP_TYPE_PERCPU_ARRAY ||\n+\t type == BPF_MAP_TYPE_LRU_PERCPU_HASH;\n+}\n+\n+static bool map_is_map_of_maps(__u32 type)\n+{\n+\treturn type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||\n+\t type == BPF_MAP_TYPE_HASH_OF_MAPS;\n+}\n+\n+static bool map_is_map_of_progs(__u32 type)\n+{\n+\treturn type == BPF_MAP_TYPE_PROG_ARRAY;\n+}\n+\n+static void *alloc_value(struct bpf_map_info *info)\n+{\n+\tif (map_is_per_cpu(info->type))\n+\t\treturn malloc(info->value_size * get_possible_cpus());\n+\telse\n+\t\treturn malloc(info->value_size);\n+}\n+\n+static int map_parse_fd(int *argc, char ***argv)\n+{\n+\tint fd;\n+\n+\tif (is_prefix(**argv, \"id\")) {\n+\t\tunsigned int id;\n+\t\tchar *endptr;\n+\n+\t\tNEXT_ARGP();\n+\n+\t\tid = strtoul(**argv, &endptr, 0);\n+\t\tif (*endptr) {\n+\t\t\terr(\"can't parse %s as ID\\n\", **argv);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tNEXT_ARGP();\n+\n+\t\tfd = bpf_map_get_fd_by_id(id);\n+\t\tif (fd < 1)\n+\t\t\terr(\"get map by id (%u): %s\\n\", id, strerror(errno));\n+\t\treturn fd;\n+\t} else if (is_prefix(**argv, \"pinned\")) {\n+\t\tchar *path;\n+\n+\t\tNEXT_ARGP();\n+\n+\t\tpath = **argv;\n+\t\tNEXT_ARGP();\n+\n+\t\treturn open_obj_pinned_any(path, BPF_OBJ_MAP);\n+\t}\n+\n+\terr(\"expected 'id' or 'pinned', got: '%s'?\\n\", **argv);\n+\treturn -1;\n+}\n+\n+static int\n+map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)\n+{\n+\tint err;\n+\tint fd;\n+\n+\tfd = map_parse_fd(argc, argv);\n+\tif (fd < 1)\n+\t\treturn -1;\n+\n+\terr = bpf_obj_get_info_by_fd(fd, info, info_len);\n+\tif (err) {\n+\t\terr(\"can't get map info: %s\\n\", strerror(errno));\n+\t\tclose(fd);\n+\t\treturn err;\n+\t}\n+\n+\treturn fd;\n+}\n+\n+static void print_entry(struct bpf_map_info *info, unsigned char *key,\n+\t\t\tunsigned char *value)\n+{\n+\tunsigned int i, n;\n+\n+\tif (!map_is_per_cpu(info->type)) {\n+\t\t/* Single line print */\n+\t\tif (info->key_size + info->value_size <= 24 &&\n+\t\t max(info->key_size, info->value_size) <= 16) {\n+\t\t\tprintf(\"key: \");\n+\t\t\tprint_hex(key, info->key_size, \" \");\n+\t\t\tprintf(\" value: \");\n+\t\t\tprint_hex(value, info->value_size, \" \");\n+\t\t\tprintf(\"\\n\");\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tprintf(\"key: \");\n+\t\tprint_hex(key, info->key_size, \" \");\n+\t\tprintf(\"\\nvalue: \");\n+\t\tprint_hex(value, info->value_size, \" \");\n+\t\tprintf(\"\\n\");\n+\n+\t\treturn;\n+\t}\n+\n+\tn = get_possible_cpus();\n+\n+\tprintf(\"key:\\n\");\n+\tprint_hex(key, info->key_size, \" \");\n+\tprintf(\"\\n\");\n+\tfor (i = 0; i < n; i++) {\n+\t\tprintf(\"value (CPU %02d):%c\",\n+\t\t i, info->value_size > 16 ? '\\n' : ' ');\n+\t\tprint_hex(value + i * info->value_size, info->value_size, \" \");\n+\t\tprintf(\"\\n\");\n+\t}\n+}\n+\n+static char **parse_val(char **argv, const char *name, unsigned char *val,\n+\t\t\tunsigned int n)\n+{\n+\tunsigned int i = 0;\n+\tchar *endptr;\n+\n+\twhile (i < n && argv[i]) {\n+\t\tval[i] = strtoul(argv[i], &endptr, 0);\n+\t\tif (*endptr) {\n+\t\t\terr(\"error parsing byte: %s\\n\", argv[i]);\n+\t\t\tbreak;\n+\t\t}\n+\t\ti++;\n+\t}\n+\n+\tif (i != n) {\n+\t\terr(\"%s expected %d bytes got %d\\n\", name, n, i);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn argv + i;\n+}\n+\n+static int parse_elem(char **argv, struct bpf_map_info *info,\n+\t\t void *key, void *value, __u32 key_size, __u32 value_size,\n+\t\t __u32 *flags, __u32 **value_fd)\n+{\n+\tif (!*argv) {\n+\t\tif (!key && !value)\n+\t\t\treturn 0;\n+\t\terr(\"did not find %s\\n\", key ? \"key\" : \"value\");\n+\t\treturn -1;\n+\t}\n+\n+\tif (is_prefix(*argv, \"key\")) {\n+\t\tif (!key) {\n+\t\t\tif (key_size)\n+\t\t\t\terr(\"duplicate key\\n\");\n+\t\t\telse\n+\t\t\t\terr(\"unnecessary key\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\targv = parse_val(argv + 1, \"key\", key, key_size);\n+\t\tif (!argv)\n+\t\t\treturn -1;\n+\n+\t\treturn parse_elem(argv, info, NULL, value, key_size, value_size,\n+\t\t\t\t flags, value_fd);\n+\t} else if (is_prefix(*argv, \"value\")) {\n+\t\tint fd;\n+\n+\t\tif (!value) {\n+\t\t\tif (value_size)\n+\t\t\t\terr(\"duplicate value\\n\");\n+\t\t\telse\n+\t\t\t\terr(\"unnecessary value\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\targv++;\n+\n+\t\tif (map_is_map_of_maps(info->type)) {\n+\t\t\tint argc = 2;\n+\n+\t\t\tif (value_size != 4) {\n+\t\t\t\terr(\"value smaller than 4B for map in map?\\n\");\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tif (!argv[0] || !argv[1]) {\n+\t\t\t\terr(\"not enough value arguments for map in map\\n\");\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tfd = map_parse_fd(&argc, &argv);\n+\t\t\tif (fd < 1)\n+\t\t\t\treturn -1;\n+\n+\t\t\t*value_fd = value;\n+\t\t\t**value_fd = fd;\n+\t\t} else if (map_is_map_of_progs(info->type)) {\n+\t\t\tint argc = 2;\n+\n+\t\t\tif (value_size != 4) {\n+\t\t\t\terr(\"value smaller than 4B for map of progs?\\n\");\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\t\t\tif (!argv[0] || !argv[1]) {\n+\t\t\t\terr(\"not enough value arguments for map of progs\\n\");\n+\t\t\t\treturn -1;\n+\t\t\t}\n+\n+\t\t\tfd = prog_parse_fd(&argc, &argv);\n+\t\t\tif (fd < 1)\n+\t\t\t\treturn -1;\n+\n+\t\t\t*value_fd = value;\n+\t\t\t**value_fd = fd;\n+\t\t} else {\n+\t\t\targv = parse_val(argv, \"value\", value, value_size);\n+\t\t\tif (!argv)\n+\t\t\t\treturn -1;\n+\t\t}\n+\n+\t\treturn parse_elem(argv, info, key, NULL, key_size, value_size,\n+\t\t\t\t flags, NULL);\n+\t} else if (is_prefix(*argv, \"any\") || is_prefix(*argv, \"noexist\") ||\n+\t\t is_prefix(*argv, \"exist\")) {\n+\t\tif (!flags) {\n+\t\t\terr(\"flags specified multiple times: %s\\n\", *argv);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (is_prefix(*argv, \"any\"))\n+\t\t\t*flags = BPF_ANY;\n+\t\telse if (is_prefix(*argv, \"noexist\"))\n+\t\t\t*flags = BPF_NOEXIST;\n+\t\telse if (is_prefix(*argv, \"exist\"))\n+\t\t\t*flags = BPF_EXIST;\n+\n+\t\treturn parse_elem(argv + 1, info, key, value, key_size,\n+\t\t\t\t value_size, NULL, value_fd);\n+\t}\n+\n+\terr(\"expected key or value, got: %s\\n\", *argv);\n+\treturn -1;\n+}\n+\n+static int show_map_close(int fd, struct bpf_map_info *info)\n+{\n+\tchar *memlock;\n+\n+\tmemlock = get_fdinfo(fd, \"memlock\");\n+\tclose(fd);\n+\n+\tprintf(\" %u: \", info->id);\n+\tif (info->type < ARRAY_SIZE(map_type_name))\n+\t\tprintf(\"%s \", map_type_name[info->type]);\n+\telse\n+\t\tprintf(\"type:%u \", info->type);\n+\n+\tprintf(\"flags:0x%x key:%uB value:%uB max_entries:%u\",\n+\t info->map_flags, info->key_size, info->value_size,\n+\t info->max_entries);\n+\n+\tif (memlock)\n+\t\tprintf(\" memlock:%sB\", memlock);\n+\tfree(memlock);\n+\n+\tprintf(\"\\n\");\n+\n+\treturn 0;\n+}\n+\n+static int do_show(int argc, char **argv)\n+{\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\t__u32 id = 0;\n+\tint err;\n+\tint fd;\n+\n+\tif (argc == 2) {\n+\t\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\t\tif (fd < 0)\n+\t\t\treturn -1;\n+\n+\t\treturn show_map_close(fd, &info);\n+\t}\n+\n+\tif (argc)\n+\t\treturn BAD_ARG();\n+\n+\twhile (true) {\n+\t\terr = bpf_map_get_next_id(id, &id);\n+\t\tif (err) {\n+\t\t\tif (errno == ENOENT)\n+\t\t\t\tbreak;\n+\t\t\terr(\"can't get next map: %s\\n\", strerror(errno));\n+\t\t\tif (errno == EINVAL)\n+\t\t\t\terr(\"kernel too old?\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tfd = bpf_map_get_fd_by_id(id);\n+\t\tif (fd < 1) {\n+\t\t\terr(\"can't get map by id (%u): %s\\n\",\n+\t\t\t id, strerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\terr = bpf_obj_get_info_by_fd(fd, &info, &len);\n+\t\tif (err) {\n+\t\t\terr(\"can't get map info: %s\\n\", strerror(errno));\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tshow_map_close(fd, &info);\n+\t}\n+\n+\treturn errno == ENOENT ? 0 : -1;\n+}\n+\n+static int do_dump(int argc, char **argv)\n+{\n+\tvoid *key, *value, *prev_key;\n+\tunsigned int num_elems = 0;\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\tint err;\n+\tint fd;\n+\n+\tif (argc != 2)\n+\t\tusage();\n+\n+\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tif (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) {\n+\t\terr(\"Dumping maps of maps and program maps not supported\\n\");\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\tkey = malloc(info.key_size);\n+\tvalue = alloc_value(&info);\n+\tif (!key || !value) {\n+\t\terr(\"mem alloc failed\\n\");\n+\t\terr = -1;\n+\t\tgoto exit_free;\n+\t}\n+\n+\tprev_key = NULL;\n+\twhile (true) {\n+\t\terr = bpf_map_get_next_key(fd, prev_key, key);\n+\t\tif (err) {\n+\t\t\tif (errno == ENOENT)\n+\t\t\t\terr = 0;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\terr = bpf_map_lookup_elem(fd, key, value);\n+\t\tif (err) {\n+\t\t\tinfo(\"can't lookup element with key: \");\n+\t\t\tprint_hex(key, info.key_size, \" \");\n+\t\t\tprintf(\"\\n\");\n+\t\t\tgoto next_key;\n+\t\t}\n+\n+\t\tprint_entry(&info, key, value);\n+next_key:\n+\t\tprev_key = key;\n+\t\tnum_elems++;\n+\t}\n+\n+\tprintf(\"Found %u element%s\\n\", num_elems, num_elems != 1 ? \"s\" : \"\");\n+\n+exit_free:\n+\tfree(key);\n+\tfree(value);\n+\tclose(fd);\n+\n+\treturn err;\n+}\n+\n+static int do_update(int argc, char **argv)\n+{\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\t__u32 *value_fd = NULL;\n+\t__u32 flags = BPF_ANY;\n+\tvoid *key, *value;\n+\tint fd, err;\n+\n+\tif (argc < 2)\n+\t\tusage();\n+\n+\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tkey = malloc(info.key_size);\n+\tvalue = alloc_value(&info);\n+\tif (!key || !value) {\n+\t\terr(\"mem alloc failed\");\n+\t\terr = -1;\n+\t\tgoto exit_free;\n+\t}\n+\n+\terr = parse_elem(argv, &info, key, value, info.key_size,\n+\t\t\t info.value_size, &flags, &value_fd);\n+\tif (err)\n+\t\tgoto exit_free;\n+\n+\terr = bpf_map_update_elem(fd, key, value, flags);\n+\tif (err) {\n+\t\terr(\"update failed: %s\\n\", strerror(errno));\n+\t\tgoto exit_free;\n+\t}\n+\n+exit_free:\n+\tif (value_fd)\n+\t\tclose(*value_fd);\n+\tfree(key);\n+\tfree(value);\n+\tclose(fd);\n+\n+\treturn err;\n+}\n+\n+static int do_lookup(int argc, char **argv)\n+{\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\tvoid *key, *value;\n+\tint err;\n+\tint fd;\n+\n+\tif (argc < 2)\n+\t\tusage();\n+\n+\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tkey = malloc(info.key_size);\n+\tvalue = alloc_value(&info);\n+\tif (!key || !value) {\n+\t\terr(\"mem alloc failed\");\n+\t\terr = -1;\n+\t\tgoto exit_free;\n+\t}\n+\n+\terr = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);\n+\tif (err)\n+\t\tgoto exit_free;\n+\n+\terr = bpf_map_lookup_elem(fd, key, value);\n+\tif (!err) {\n+\t\tprint_entry(&info, key, value);\n+\t} else if (errno == ENOENT) {\n+\t\tprintf(\"key:\\n\");\n+\t\tprint_hex(key, info.key_size, \" \");\n+\t\tprintf(\"\\n\\nNot found\\n\");\n+\t} else {\n+\t\terr(\"lookup failed: %s\\n\", strerror(errno));\n+\t}\n+\n+exit_free:\n+\tfree(key);\n+\tfree(value);\n+\tclose(fd);\n+\n+\treturn err;\n+}\n+\n+static int do_getnext(int argc, char **argv)\n+{\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\tvoid *key, *nextkey;\n+\tint err;\n+\tint fd;\n+\n+\tif (argc < 2)\n+\t\tusage();\n+\n+\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tkey = malloc(info.key_size);\n+\tnextkey = malloc(info.key_size);\n+\tif (!key || !nextkey) {\n+\t\terr(\"mem alloc failed\");\n+\t\terr = -1;\n+\t\tgoto exit_free;\n+\t}\n+\n+\tif (argc) {\n+\t\terr = parse_elem(argv, &info, key, NULL, info.key_size, 0,\n+\t\t\t\t NULL, NULL);\n+\t\tif (err)\n+\t\t\tgoto exit_free;\n+\t} else {\n+\t\tfree(key);\n+\t\tkey = NULL;\n+\t}\n+\n+\terr = bpf_map_get_next_key(fd, key, nextkey);\n+\tif (err) {\n+\t\terr(\"can't get next key: %s\\n\", strerror(errno));\n+\t\tgoto exit_free;\n+\t}\n+\n+\tif (key) {\n+\t\tprintf(\"key:\\n\");\n+\t\tprint_hex(key, info.key_size, \" \");\n+\t\tprintf(\"\\n\");\n+\t} else {\n+\t\tprintf(\"key: None\\n\");\n+\t}\n+\n+\tprintf(\"next key:\\n\");\n+\tprint_hex(nextkey, info.key_size, \" \");\n+\tprintf(\"\\n\");\n+\n+exit_free:\n+\tfree(nextkey);\n+\tfree(key);\n+\tclose(fd);\n+\n+\treturn err;\n+}\n+\n+static int do_delete(int argc, char **argv)\n+{\n+\tstruct bpf_map_info info = {};\n+\t__u32 len = sizeof(info);\n+\tvoid *key;\n+\tint err;\n+\tint fd;\n+\n+\tif (argc < 2)\n+\t\tusage();\n+\n+\tfd = map_parse_fd_and_info(&argc, &argv, &info, &len);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tkey = malloc(info.key_size);\n+\tif (!key) {\n+\t\terr(\"mem alloc failed\");\n+\t\terr = -1;\n+\t\tgoto exit_free;\n+\t}\n+\n+\terr = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL);\n+\tif (err)\n+\t\tgoto exit_free;\n+\n+\terr = bpf_map_delete_elem(fd, key);\n+\tif (err)\n+\t\terr(\"delete failed: %s\\n\", strerror(errno));\n+\n+exit_free:\n+\tfree(key);\n+\tclose(fd);\n+\n+\treturn err;\n+}\n+\n+static int do_pin(int argc, char **argv)\n+{\n+\treturn do_pin_any(argc, argv, bpf_map_get_fd_by_id);\n+}\n+\n+static int do_help(int argc, char **argv)\n+{\n+\tfprintf(stderr,\n+\t\t\"Usage: %s %s show [MAP]\\n\"\n+\t\t\" %s %s dump MAP\\n\"\n+\t\t\" %s %s update MAP key BYTES value VALUE [UPDATE_FLAGS]\\n\"\n+\t\t\" %s %s lookup MAP key BYTES\\n\"\n+\t\t\" %s %s getnext MAP [key BYTES]\\n\"\n+\t\t\" %s %s delete MAP key BYTES\\n\"\n+\t\t\" %s %s pin MAP FILE\\n\"\n+\t\t\" %s %s help\\n\"\n+\t\t\"\\n\"\n+\t\t\" MAP := { id MAP_ID | pinned FILE }\\n\"\n+\t\t\" \" HELP_SPEC_PROGRAM \"\\n\"\n+\t\t\" VALUE := { BYTES | MAP | PROG }\\n\"\n+\t\t\" UPDATE_FLAGS := { any | exist | noexist }\\n\"\n+\t\t\"\",\n+\t\tbin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],\n+\t\tbin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],\n+\t\tbin_name, argv[-2], bin_name, argv[-2]);\n+\n+\treturn 0;\n+}\n+\n+static const struct cmd cmds[] = {\n+\t{ \"show\",\tdo_show },\n+\t{ \"help\",\tdo_help },\n+\t{ \"dump\",\tdo_dump },\n+\t{ \"update\",\tdo_update },\n+\t{ \"lookup\",\tdo_lookup },\n+\t{ \"getnext\",\tdo_getnext },\n+\t{ \"delete\",\tdo_delete },\n+\t{ \"pin\",\tdo_pin },\n+\t{ 0 }\n+};\n+\n+int do_map(int argc, char **argv)\n+{\n+\treturn cmd_select(cmds, argc, argv, do_help);\n+}\ndiff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c\nnew file mode 100644\nindex 000000000000..3129159c593e\n--- /dev/null\n+++ b/tools/bpf/bpftool/prog.c\n@@ -0,0 +1,392 @@\n+/*\n+ * Copyright (C) 2017 Netronome Systems, Inc.\n+ *\n+ * This software is dual licensed under the GNU General License Version 2,\n+ * June 1991 as shown in the file COPYING in the top-level directory of this\n+ * source tree or the BSD 2-Clause License provided below. You have the\n+ * option to license this software under the complete terms of either license.\n+ *\n+ * The BSD 2-Clause License:\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * 1. Redistributions of source code must retain the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer.\n+ *\n+ * 2. Redistributions in binary form must reproduce the above\n+ * copyright notice, this list of conditions and the following\n+ * disclaimer in the documentation and/or other materials\n+ * provided with the distribution.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ */\n+\n+/* Author: Jakub Kicinski <kubakici@wp.pl> */\n+\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <unistd.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+\n+#include <bpf.h>\n+\n+#include \"main.h\"\n+\n+static const char * const prog_type_name[] = {\n+\t[BPF_PROG_TYPE_UNSPEC]\t\t= \"unspec\",\n+\t[BPF_PROG_TYPE_SOCKET_FILTER]\t= \"socket_filter\",\n+\t[BPF_PROG_TYPE_KPROBE]\t\t= \"kprobe\",\n+\t[BPF_PROG_TYPE_SCHED_CLS]\t= \"sched_cls\",\n+\t[BPF_PROG_TYPE_SCHED_ACT]\t= \"sched_act\",\n+\t[BPF_PROG_TYPE_TRACEPOINT]\t= \"tracepoint\",\n+\t[BPF_PROG_TYPE_XDP]\t\t= \"xdp\",\n+\t[BPF_PROG_TYPE_PERF_EVENT]\t= \"perf_event\",\n+\t[BPF_PROG_TYPE_CGROUP_SKB]\t= \"cgroup_skb\",\n+\t[BPF_PROG_TYPE_CGROUP_SOCK]\t= \"cgroup_sock\",\n+\t[BPF_PROG_TYPE_LWT_IN]\t\t= \"lwt_in\",\n+\t[BPF_PROG_TYPE_LWT_OUT]\t\t= \"lwt_out\",\n+\t[BPF_PROG_TYPE_LWT_XMIT]\t= \"lwt_xmit\",\n+\t[BPF_PROG_TYPE_SOCK_OPS]\t= \"sock_ops\",\n+\t[BPF_PROG_TYPE_SK_SKB]\t\t= \"sk_skb\",\n+};\n+\n+static int prog_fd_by_tag(unsigned char *tag)\n+{\n+\tstruct bpf_prog_info info = {};\n+\t__u32 len = sizeof(info);\n+\tunsigned int id = 0;\n+\tint err;\n+\tint fd;\n+\n+\twhile (true) {\n+\t\terr = bpf_prog_get_next_id(id, &id);\n+\t\tif (err) {\n+\t\t\terr(\"%s\\n\", strerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tfd = bpf_prog_get_fd_by_id(id);\n+\t\tif (fd < 1) {\n+\t\t\terr(\"can't get prog by id (%u): %s\\n\",\n+\t\t\t id, strerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\terr = bpf_obj_get_info_by_fd(fd, &info, &len);\n+\t\tif (err) {\n+\t\t\terr(\"can't get prog info (%u): %s\\n\",\n+\t\t\t id, strerror(errno));\n+\t\t\tclose(fd);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tif (!memcmp(tag, info.tag, BPF_TAG_SIZE))\n+\t\t\treturn fd;\n+\n+\t\tclose(fd);\n+\t}\n+}\n+\n+int prog_parse_fd(int *argc, char ***argv)\n+{\n+\tint fd;\n+\n+\tif (is_prefix(**argv, \"id\")) {\n+\t\tunsigned int id;\n+\t\tchar *endptr;\n+\n+\t\tNEXT_ARGP();\n+\n+\t\tid = strtoul(**argv, &endptr, 0);\n+\t\tif (*endptr) {\n+\t\t\terr(\"can't parse %s as ID\\n\", **argv);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tNEXT_ARGP();\n+\n+\t\tfd = bpf_prog_get_fd_by_id(id);\n+\t\tif (fd < 1)\n+\t\t\terr(\"get by id (%u): %s\\n\", id, strerror(errno));\n+\t\treturn fd;\n+\t} else if (is_prefix(**argv, \"tag\")) {\n+\t\tunsigned char tag[BPF_TAG_SIZE];\n+\n+\t\tNEXT_ARGP();\n+\n+\t\tif (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,\n+\t\t\t tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)\n+\t\t != BPF_TAG_SIZE) {\n+\t\t\terr(\"can't parse tag\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\t\tNEXT_ARGP();\n+\n+\t\treturn prog_fd_by_tag(tag);\n+\t} else if (is_prefix(**argv, \"pinned\")) {\n+\t\tchar *path;\n+\n+\t\tNEXT_ARGP();\n+\n+\t\tpath = **argv;\n+\t\tNEXT_ARGP();\n+\n+\t\treturn open_obj_pinned_any(path, BPF_OBJ_PROG);\n+\t}\n+\n+\terr(\"expected 'id', 'tag' or 'pinned', got: '%s'?\\n\", **argv);\n+\treturn -1;\n+}\n+\n+static int show_prog(int fd)\n+{\n+\tstruct bpf_prog_info info = {};\n+\t__u32 len = sizeof(info);\n+\tchar *memlock;\n+\tint err;\n+\n+\terr = bpf_obj_get_info_by_fd(fd, &info, &len);\n+\tif (err) {\n+\t\terr(\"can't get prog info: %s\\n\", strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tprintf(\" %u: \", info.id);\n+\tif (info.type < ARRAY_SIZE(prog_type_name))\n+\t\tprintf(\"%s \", prog_type_name[info.type]);\n+\telse\n+\t\tprintf(\"type:%u \", info.type);\n+\n+\tprintf(\"tag \");\n+\tprint_hex(info.tag, BPF_TAG_SIZE, \":\");\n+\n+\tprintf(\" xlated:%uB\", info.xlated_prog_len);\n+\n+\tif (info.jited_prog_len)\n+\t\tprintf(\" jited:%uB\", info.jited_prog_len);\n+\telse\n+\t\tprintf(\" not jited\");\n+\n+\tmemlock = get_fdinfo(fd, \"memlock\");\n+\tif (memlock)\n+\t\tprintf(\" memlock:%sB\", memlock);\n+\tfree(memlock);\n+\n+\tprintf(\"\\n\");\n+\n+\treturn 0;\n+}\n+\n+static int do_show(int argc, char **argv)\n+{\t__u32 id = 0;\n+\tint err;\n+\tint fd;\n+\n+\tif (argc == 2) {\n+\t\tfd = prog_parse_fd(&argc, &argv);\n+\t\tif (fd < 1)\n+\t\t\treturn -1;\n+\n+\t\treturn show_prog(fd);\n+\t}\n+\n+\tif (argc)\n+\t\treturn BAD_ARG();\n+\n+\twhile (true) {\n+\t\terr = bpf_prog_get_next_id(id, &id);\n+\t\tif (err) {\n+\t\t\tif (errno == ENOENT)\n+\t\t\t\tbreak;\n+\t\t\terr(\"can't get next program: %s\\n\", strerror(errno));\n+\t\t\tif (errno == EINVAL)\n+\t\t\t\terr(\"kernel too old?\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tfd = bpf_prog_get_fd_by_id(id);\n+\t\tif (fd < 1) {\n+\t\t\terr(\"can't get prog by id (%u): %s\\n\",\n+\t\t\t id, strerror(errno));\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\terr = show_prog(fd);\n+\t\tclose(fd);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int do_dump(int argc, char **argv)\n+{\n+\tstruct bpf_prog_info info = {};\n+\t__u32 len = sizeof(info);\n+\tbool can_disasm = false;\n+\tunsigned int buf_size;\n+\tchar *filepath = NULL;\n+\tbool opcodes = false;\n+\tunsigned char *buf;\n+\t__u32 *member_len;\n+\t__u64 *member_ptr;\n+\tssize_t n;\n+\tint err;\n+\tint fd;\n+\n+\tif (is_prefix(*argv, \"jited\")) {\n+\t\tmember_len = &info.jited_prog_len;\n+\t\tmember_ptr = &info.jited_prog_insns;\n+\t\tcan_disasm = true;\n+\t} else if (is_prefix(*argv, \"xlated\")) {\n+\t\tmember_len = &info.xlated_prog_len;\n+\t\tmember_ptr = &info.xlated_prog_insns;\n+\t} else {\n+\t\terr(\"expected 'xlated' or 'jited', got: %s\\n\", *argv);\n+\t\treturn -1;\n+\t}\n+\tNEXT_ARG();\n+\n+\tif (argc < 2)\n+\t\tusage();\n+\n+\tfd = prog_parse_fd(&argc, &argv);\n+\tif (fd < 0)\n+\t\treturn -1;\n+\n+\tif (is_prefix(*argv, \"file\")) {\n+\t\tNEXT_ARG();\n+\t\tif (!argc) {\n+\t\t\terr(\"expected file path\\n\");\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tfilepath = *argv;\n+\t\tNEXT_ARG();\n+\t} else if (is_prefix(*argv, \"opcodes\")) {\n+\t\topcodes = true;\n+\t\tNEXT_ARG();\n+\t}\n+\n+\tif (!filepath && !can_disasm) {\n+\t\terr(\"expected 'file' got %s\\n\", *argv);\n+\t\treturn -1;\n+\t}\n+\tif (argc) {\n+\t\tusage();\n+\t\treturn -1;\n+\t}\n+\n+\terr = bpf_obj_get_info_by_fd(fd, &info, &len);\n+\tif (err) {\n+\t\terr(\"can't get prog info: %s\\n\", strerror(errno));\n+\t\treturn -1;\n+\t}\n+\n+\tif (!*member_len) {\n+\t\tinfo(\"no instructions returned\\n\");\n+\t\tclose(fd);\n+\t\treturn 0;\n+\t}\n+\n+\tbuf_size = *member_len;\n+\n+\tbuf = malloc(buf_size);\n+\tif (!buf) {\n+\t\terr(\"mem alloc failed\\n\");\n+\t\tclose(fd);\n+\t\treturn -1;\n+\t}\n+\n+\tmemset(&info, 0, sizeof(info));\n+\n+\t*member_ptr = ptr_to_u64(buf);\n+\t*member_len = buf_size;\n+\n+\terr = bpf_obj_get_info_by_fd(fd, &info, &len);\n+\tclose(fd);\n+\tif (err) {\n+\t\terr(\"can't get prog info: %s\\n\", strerror(errno));\n+\t\tgoto err_free;\n+\t}\n+\n+\tif (*member_len > buf_size) {\n+\t\tinfo(\"too many instructions returned\\n\");\n+\t\tgoto err_free;\n+\t}\n+\n+\tif (filepath) {\n+\t\tfd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);\n+\t\tif (fd < 1) {\n+\t\t\terr(\"can't open file %s: %s\\n\", filepath,\n+\t\t\t strerror(errno));\n+\t\t\tgoto err_free;\n+\t\t}\n+\n+\t\tn = write(fd, buf, *member_len);\n+\t\tclose(fd);\n+\t\tif (n != *member_len) {\n+\t\t\terr(\"error writing output file: %s\\n\",\n+\t\t\t n < 0 ? strerror(errno) : \"short write\");\n+\t\t\tgoto err_free;\n+\t\t}\n+\t} else {\n+\t\tdisasm_print_insn(buf, *member_len, opcodes);\n+\t}\n+\n+\tfree(buf);\n+\n+\treturn 0;\n+\n+err_free:\n+\tfree(buf);\n+\treturn -1;\n+}\n+\n+static int do_pin(int argc, char **argv)\n+{\n+\treturn do_pin_any(argc, argv, bpf_prog_get_fd_by_id);\n+}\n+\n+static int do_help(int argc, char **argv)\n+{\n+\tfprintf(stderr,\n+\t\t\"Usage: %s %s show [PROG]\\n\"\n+\t\t\" %s %s dump xlated PROG file FILE\\n\"\n+\t\t\" %s %s dump jited PROG [file FILE] [opcodes]\\n\"\n+\t\t\" %s %s pin PROG FILE\\n\"\n+\t\t\" %s %s help\\n\"\n+\t\t\"\\n\"\n+\t\t\" \" HELP_SPEC_PROGRAM \"\\n\"\n+\t\t\"\",\n+\t\tbin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],\n+\t\tbin_name, argv[-2], bin_name, argv[-2]);\n+\n+\treturn 0;\n+}\n+\n+static const struct cmd cmds[] = {\n+\t{ \"show\",\tdo_show },\n+\t{ \"dump\",\tdo_dump },\n+\t{ \"pin\",\tdo_pin },\n+\t{ 0 }\n+};\n+\n+int do_prog(int argc, char **argv)\n+{\n+\treturn cmd_select(cmds, argc, argv, do_help);\n+}\n", "prefixes": [ "net-next", "2/2" ] }