summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/netlink.c1
-rw-r--r--src/owner.c173
-rw-r--r--src/parser_bison.y5
-rw-r--r--src/rule.c9
5 files changed, 187 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 3041a933..2f6d434b 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 c3887d5b..8c86789b 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 00000000..2d98a2e9
--- /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 2d5d8e48..a2c15018 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 d22ab500..acb10f65 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);