[PATCH nft] table: support for the table owner flag

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add new flag to allow userspace process to own tables: Tables that have
an owner can only be updated/destroyed by the owner. The table is
destroyed either if the owner process calls nft_ctx_free() or owner
process is terminated (implicit table release).

The ruleset listing includes the program name that owns the table:

 nft> list ruleset
 table ip x { # progname nft
        flags owner

        chain y {
                type filter hook input priority filter; policy accept;
                counter packets 1 bytes 309
        }
 }

Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
---
 include/owner.h    |   6 ++
 include/rule.h     |   4 +-
 src/Makefile.am    |   1 +
 src/netlink.c      |   1 +
 src/owner.c        | 173 +++++++++++++++++++++++++++++++++++++++++++++
 src/parser_bison.y |   5 +-
 src/rule.c         |   9 ++-
 7 files changed, 196 insertions(+), 3 deletions(-)
 create mode 100644 include/owner.h
 create mode 100644 src/owner.c

diff --git a/include/owner.h b/include/owner.h
new file mode 100644
index 000000000000..85d821cc1ad9
--- /dev/null
+++ b/include/owner.h
@@ -0,0 +1,6 @@
+#ifndef _NFT_OWNER_H_
+#define _NFT_OWNER_H_
+
+char *get_progname(uint32_t portid);
+
+#endif
diff --git a/include/rule.h b/include/rule.h
index 87b6828edca4..523435f6f5d5 100644
--- a/include/rule.h
+++ b/include/rule.h
@@ -131,8 +131,9 @@ struct symbol *symbol_get(const struct scope *scope, const char *identifier);
 
 enum table_flags {
 	TABLE_F_DORMANT		= (1 << 0),
+	TABLE_F_OWNER		= (1 << 1),
 };
-#define TABLE_FLAGS_MAX 1
+#define TABLE_FLAGS_MAX		2
 
 const char *table_flag_name(uint32_t flag);
 
@@ -162,6 +163,7 @@ struct table {
 	struct list_head	chain_bindings;
 	enum table_flags 	flags;
 	unsigned int		refcnt;
+	uint32_t		owner;
 	const char		*comment;
 };
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 3041a933bc06..2f6d434b3ad2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,6 +61,7 @@ libnftables_la_SOURCES =			\
 		netlink_delinearize.c		\
 		misspell.c			\
 		monitor.c			\
+		owner.c				\
 		segtree.c			\
 		rbtree.c			\
 		gmputil.c			\
diff --git a/src/netlink.c b/src/netlink.c
index c3887d5b6662..8c86789b8369 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -614,6 +614,7 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx,
 	table->handle.table.name = xstrdup(nftnl_table_get_str(nlt, NFTNL_TABLE_NAME));
 	table->flags	     = nftnl_table_get_u32(nlt, NFTNL_TABLE_FLAGS);
 	table->handle.handle.id = nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE);
+	table->owner	     = nftnl_table_get_u32(nlt, NFTNL_TABLE_OWNER);
 
 	if (nftnl_table_is_set(nlt, NFTNL_TABLE_USERDATA)) {
 		udata = nftnl_table_get_data(nlt, NFTNL_TABLE_USERDATA, &ulen);
diff --git a/src/owner.c b/src/owner.c
new file mode 100644
index 000000000000..2d98a2e98028
--- /dev/null
+++ b/src/owner.c
@@ -0,0 +1,173 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+#include <netlink.h>
+#include <owner.h>
+
+static char *pid2name(pid_t pid)
+{
+	char procname[256], *prog;
+	FILE *fp;
+	int ret;
+
+	ret = snprintf(procname, sizeof(procname), "/proc/%lu/stat", (unsigned long)pid);
+	if (ret < 0 || ret > (int)sizeof(procname))
+		return NULL;
+
+	fp = fopen(procname, "r");
+	if (!fp)
+		return NULL;
+
+	ret = fscanf(fp, "%*u (%m[^)]", &prog);
+
+	fclose(fp);
+
+	if (ret == 1)
+		return prog;
+
+	return NULL;
+}
+
+static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode)
+{
+	const struct dirent *ent;
+	char procname[256];
+	DIR *dir;
+	int ret;
+
+	ret = snprintf(procname, sizeof(procname), "/proc/%lu/fd/", (unsigned long)pid);
+	if (ret < 0 || ret >= (int)sizeof(procname))
+		return NULL;
+
+	dir = opendir(procname);
+	if (!dir)
+		return NULL;
+
+	for (;;) {
+		unsigned long ino;
+		char tmp[128];
+		ssize_t rl;
+
+		ent = readdir(dir);
+		if (!ent)
+			break;
+
+		if (ent->d_type != DT_LNK)
+			continue;
+
+		ret = snprintf(procname, sizeof(procname), "/proc/%d/fd/%s",
+			       pid, ent->d_name);
+		if (ret < 0 || ret >= (int)sizeof(procname))
+			continue;
+
+		rl = readlink(procname, tmp, sizeof(tmp));
+		if (rl <= 0 || rl > (ssize_t)sizeof(tmp))
+			continue;
+
+		tmp[rl] = 0;
+
+		ret = sscanf(tmp, "socket:[%lu]", &ino);
+		if (ret == 1 && ino == inode) {
+			closedir(dir);
+			return pid2name(pid);
+		}
+	}
+
+	closedir(dir);
+	return NULL;
+}
+
+static char *name_by_portid(uint32_t portid, unsigned long inode)
+{
+	const struct dirent *ent;
+	char *prog;
+	DIR *dir;
+
+	/* Many netlink users use their process ID to allocate the first port id. */
+	prog = portid2name(portid, portid, inode);
+	if (prog)
+		return prog;
+
+	/* no luck, search harder. */
+	dir = opendir("/proc");
+	if (!dir)
+		return NULL;
+
+	for (;;) {
+		unsigned long pid;
+		char *end;
+
+		ent = readdir(dir);
+		if (!ent)
+			break;
+
+		if (ent->d_type != DT_DIR)
+			continue;
+
+		pid = strtoul(ent->d_name, &end, 10);
+		if (pid <= 1 || *end)
+			continue;
+
+		if (pid == portid) /* already tried */
+			continue;
+
+		prog = portid2name(pid, portid, inode);
+		if (prog)
+			break;
+	}
+
+	closedir(dir);
+	return prog;
+}
+
+char *get_progname(uint32_t portid)
+{
+	FILE *fp = fopen("/proc/net/netlink", "r");
+	uint32_t portid_check;
+	unsigned long inode;
+	int ret, prot;
+
+	if (!fp)
+		return NULL;
+
+	for (;;) {
+		char line[256];
+
+		if (!fgets(line, sizeof(line), fp))
+			break;
+
+		ret = sscanf(line, "%*x %d %u %*x %*d %*d %*x %*d %*u %lu\n",
+			     &prot, &portid_check, &inode);
+
+		if (ret == EOF)
+			break;
+
+		if (ret == 3 && portid_check == portid && prot == NETLINK_NETFILTER) {
+			static uint32_t last_portid;
+			static uint32_t last_inode;
+			static char *last_program;
+			char *prog;
+
+			fclose(fp);
+
+			if (last_portid == portid && last_inode == inode)
+				return last_program;
+
+			prog = name_by_portid(portid, inode);
+
+			free(last_program);
+			last_program = prog;
+			last_portid = portid;
+			last_inode = inode;
+			return prog;
+		}
+	}
+
+	fclose(fp);
+	return NULL;
+}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 11e899ff2f20..304ab76372f3 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -1553,7 +1553,10 @@ table_block_alloc	:	/* empty */
 table_options		:	FLAGS		STRING
 			{
 				if (strcmp($2, "dormant") == 0) {
-					$<table>0->flags = TABLE_F_DORMANT;
+					$<table>0->flags |= TABLE_F_DORMANT;
+					xfree($2);
+				} else if (strcmp($2, "owner") == 0) {
+					$<table>0->flags |= TABLE_F_OWNER;
 					xfree($2);
 				} else {
 					erec_queue(error(&@2, "unknown table option %s", $2),
diff --git a/src/rule.c b/src/rule.c
index d22ab5009790..acb10f65a517 100644
--- a/src/rule.c
+++ b/src/rule.c
@@ -25,6 +25,7 @@
 #include <misspell.h>
 #include <json.h>
 #include <cache.h>
+#include <owner.h>
 
 #include <libnftnl/common.h>
 #include <libnftnl/ruleset.h>
@@ -1407,6 +1408,7 @@ struct table *table_lookup_fuzzy(const struct handle *h,
 
 static const char *table_flags_name[TABLE_FLAGS_MAX] = {
 	"dormant",
+	"owner",
 };
 
 const char *table_flag_name(uint32_t flag)
@@ -1451,8 +1453,13 @@ static void table_print(const struct table *table, struct output_ctx *octx)
 	const char *family = family2str(table->handle.family);
 
 	nft_print(octx, "table %s %s {", family, table->handle.table.name);
+	if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER)
+		nft_print(octx, " #");
 	if (nft_output_handle(octx))
-		nft_print(octx, " # handle %" PRIu64, table->handle.handle.id);
+		nft_print(octx, " handle %" PRIu64, table->handle.handle.id);
+	if (table->flags & TABLE_F_OWNER)
+		nft_print(octx, " progname %s", get_progname(table->owner));
+
 	nft_print(octx, "\n");
 	table_print_flags(table, &delim, octx);
 
-- 
2.20.1




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux