summaryrefslogtreecommitdiffstats
path: root/src/conntrack.c
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2020-12-15 14:53:00 +0100
committerFlorian Westphal <fw@strlen.de>2020-12-17 12:23:15 +0100
commitb031cd2102d9bc2b6ce20a880068022fac9e2d87 (patch)
treea7283917eb44a94dff96cb54dc0bbb875f43bca2 /src/conntrack.c
parent592bb1686053cdb5cacdb1d6266d64ce976d7bf7 (diff)
conntrack: pretty-print the portid
DESTROY events already include the portid. Add some /proc glue to lookup the portid. Problem is that there is no direct mapping to a name. Lookup steps are: 1. Obtain the portid inode from /proc/net/netlink. If we can't even find that, no luck. 2. assume portid == pid and search /proc/portid/fd/ for a socket with matching inode. This is modeled on iproute2 ss tool. If /proc/portid/fd/ comes up empty, entire process space (/proc/*/fd) is searched for a matching inode. As this is quite some work, cache the last portid result (including 'not found', so that 'conntrack -F' generating 10000k events will do this lookup only once. The lookup won't work in case the deleting/flushing program has already exited; in that case [USERSPACE] tag and portid are still included. Example: $ conntrack -E -o userspace [DESTROY] tcp 6 src=192... dst=192... sport=4404 dport=22 ... [USERSPACE] portid=5146 progname=conntrack Signed-off-by: Florian Westphal <fw@strlen.de>
Diffstat (limited to 'src/conntrack.c')
-rw-r--r--src/conntrack.c179
1 files changed, 172 insertions, 7 deletions
diff --git a/src/conntrack.c b/src/conntrack.c
index d05a599..cc58d3f 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -55,6 +55,7 @@
#include <sys/time.h>
#include <time.h>
#include <inttypes.h>
+#include <dirent.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
@@ -1671,6 +1672,169 @@ exp_event_sighandler(int s)
exit(0);
}
+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;
+}
+
+static 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;
+}
+
static int event_cb(const struct nlmsghdr *nlh, void *data)
{
struct nfgenmsg *nfh = mnl_nlmsg_get_payload(nlh);
@@ -1679,7 +1843,6 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
enum nf_conntrack_msg_type type;
unsigned int op_flags = 0;
struct nf_conntrack *ct;
- bool userspace = false;
char buf[1024];
switch(nlh->nlmsg_type & 0xff) {
@@ -1740,14 +1903,16 @@ static int event_cb(const struct nlmsghdr *nlh, void *data)
nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap);
done:
- if (output_mask & _O_US) {
- if (nlh->nlmsg_pid)
- userspace = true;
+ if ((output_mask & _O_US) && nlh->nlmsg_pid) {
+ char *prog = get_progname(nlh->nlmsg_pid);
+
+ if (prog)
+ printf("%s [USERSPACE] portid=%u progname=%s\n", buf, nlh->nlmsg_pid, prog);
else
- userspace = false;
+ printf("%s [USERSPACE] portid=%u\n", buf, nlh->nlmsg_pid);
+ } else {
+ puts(buf);
}
-
- printf("%s%s\n", buf, userspace ? " [USERSPACE]" : "");
fflush(stdout);
counter++;