diff mbox

[iproute] iptuntap: show processes using tuntap interface

Message ID 1472062105-17995-1-git-send-email-hannes@stressinduktion.org
State Accepted, archived
Delegated to: stephen hemminger
Headers show

Commit Message

Hannes Frederic Sowa Aug. 24, 2016, 6:08 p.m. UTC
Show which processes are using which tun/tap devices, e.g.:

$ ip -d tuntap
tun0: tun
	Attached to processes: vpnc(9531)
vnet0: tap vnet_hdr
	Attached to processes: qemu-system-x86(10442)
virbr0-nic: tap UNKNOWN_FLAGS:800
	Attached to processes:

Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
---
 ip/iptuntap.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)

Comments

Stephen Hemminger Aug. 25, 2016, 3:44 p.m. UTC | #1
On Wed, 24 Aug 2016 20:08:25 +0200
Hannes Frederic Sowa <hannes@stressinduktion.org> wrote:

> Show which processes are using which tun/tap devices, e.g.:
> 
> $ ip -d tuntap
> tun0: tun
> 	Attached to processes: vpnc(9531)
> vnet0: tap vnet_hdr
> 	Attached to processes: qemu-system-x86(10442)
> virbr0-nic: tap UNKNOWN_FLAGS:800
> 	Attached to processes:
> 
> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>

I think reading all of /proc like this will scale really badly on large systems.
Why reinvent lsof?
Hannes Frederic Sowa Aug. 25, 2016, 7:37 p.m. UTC | #2
On 25.08.2016 17:44, Stephen Hemminger wrote:
> On Wed, 24 Aug 2016 20:08:25 +0200
> Hannes Frederic Sowa <hannes@stressinduktion.org> wrote:
> 
>> Show which processes are using which tun/tap devices, e.g.:
>>
>> $ ip -d tuntap
>> tun0: tun
>> 	Attached to processes: vpnc(9531)
>> vnet0: tap vnet_hdr
>> 	Attached to processes: qemu-system-x86(10442)
>> virbr0-nic: tap UNKNOWN_FLAGS:800
>> 	Attached to processes:
>>
>> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
> 
> I think reading all of /proc like this will scale really badly on large systems.

Yes, it is quite heavy weight but so far I didn't see any major latency
on even more busy systems (tens of thousands of fds). Also this code is
only entered if details are requested. I don't see a cheaper way to
determine those information currently without further patches (which
probably all will have the same n:m problem, as fds can be shared
between multiple processes).

> Why reinvent lsof?

lsof currently doesn't provide this information and would also have to
brute force in the same way (it scans all fds anyway, thought). In my
opinion it is just very helpful on virtualization boxes to quickly
identify the vm associated with an interface.

I would propose that in case we see people reporting latency issues to
speed it up in possibly two ways:

a) not glob over the whole pid/fd space, but just take one number at a
time, e.g. /proc/1*/fd/*, /proc/2*/fd/* etc.

b) or/and we just push everything into an internal hash table so for
multiple interfaces we don't need to walk again.

I would just try to not add the complexity if it is not necessary.

Thoughts?

Bye,
Hannes
Stephen Hemminger Sept. 1, 2016, 4:06 p.m. UTC | #3
On Wed, 24 Aug 2016 20:08:25 +0200
Hannes Frederic Sowa <hannes@stressinduktion.org> wrote:

> Show which processes are using which tun/tap devices, e.g.:
> 
> $ ip -d tuntap
> tun0: tun
> 	Attached to processes: vpnc(9531)
> vnet0: tap vnet_hdr
> 	Attached to processes: qemu-system-x86(10442)
> virbr0-nic: tap UNKNOWN_FLAGS:800
> 	Attached to processes:
> 
> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>

Applied, but needed a followon change to fix some style issues.
Hannes Frederic Sowa Sept. 1, 2016, 4:07 p.m. UTC | #4
On 01.09.2016 18:06, Stephen Hemminger wrote:
> On Wed, 24 Aug 2016 20:08:25 +0200
> Hannes Frederic Sowa <hannes@stressinduktion.org> wrote:
> 
>> Show which processes are using which tun/tap devices, e.g.:
>>
>> $ ip -d tuntap
>> tun0: tun
>> 	Attached to processes: vpnc(9531)
>> vnet0: tap vnet_hdr
>> 	Attached to processes: qemu-system-x86(10442)
>> virbr0-nic: tap UNKNOWN_FLAGS:800
>> 	Attached to processes:
>>
>> Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
> 
> Applied, but needed a followon change to fix some style issues.

Thanks, the clean up looks good!
diff mbox

Patch

diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index 43774f96e335ef..b5aa0542c1f8f2 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -25,6 +25,7 @@ 
 #include <fcntl.h>
 #include <dirent.h>
 #include <errno.h>
+#include <glob.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -273,6 +274,109 @@  static void print_flags(long flags)
 		printf(" UNKNOWN_FLAGS:%lx", flags);
 }
 
+static char *pid_name(pid_t pid)
+{
+	char *comm;
+	FILE *f;
+	int err;
+
+	err = asprintf(&comm, "/proc/%d/comm", pid);
+	if (err < 0)
+		return NULL;
+
+	f = fopen(comm, "r");
+	free(comm);
+	if (!f) {
+		perror("fopen");
+		return NULL;
+	}
+
+	if (fscanf(f, "%ms\n", &comm) != 1) {
+		perror("fscanf");
+		comm = NULL;
+	}
+
+
+	if (fclose(f))
+		perror("fclose");
+
+	return comm;
+}
+
+static void show_processes(const char *name)
+{
+	glob_t globbuf = { };
+	char **fd_path;
+	int err;
+
+	err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT,
+		   NULL, &globbuf);
+	if (err)
+		return;
+
+	fd_path = globbuf.gl_pathv;
+	while (*fd_path) {
+		const char *dev_net_tun = "/dev/net/tun";
+		const size_t linkbuf_len = strlen(dev_net_tun) + 2;
+		char linkbuf[linkbuf_len], *fdinfo;
+		int pid, fd;
+		FILE *f;
+
+		if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2)
+			goto next;
+
+		if (pid == getpid())
+			goto next;
+
+		err = readlink(*fd_path, linkbuf, linkbuf_len - 1);
+		if (err < 0) {
+			perror("readlink");
+			goto next;
+		}
+		linkbuf[err] = '\0';
+		if (strcmp(dev_net_tun, linkbuf))
+			goto next;
+
+		if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0)
+			goto next;
+
+		f = fopen(fdinfo, "r");
+		free(fdinfo);
+		if (!f) {
+			perror("fopen");
+			goto next;
+		}
+
+		while (!feof(f)) {
+			char *key = NULL, *value = NULL;
+
+			err = fscanf(f, "%m[^:]: %ms\n", &key, &value);
+			if (err == EOF) {
+				if (ferror(f))
+					perror("fscanf");
+				break;
+			} else if (err == 2 &&
+				   !strcmp("iff", key) && !strcmp(name, value)) {
+				char *pname = pid_name(pid);
+				printf(" %s(%d)", pname ? pname : "<NULL>", pid);
+				free(pname);
+			}
+
+			free(key);
+			free(value);
+		}
+		if (fclose(f))
+			perror("fclose");
+
+next:
+		++fd_path;
+	}
+
+	globfree(&globbuf);
+	return;
+}
+
+
 static int do_show(int argc, char **argv)
 {
 	DIR *dir;
@@ -302,6 +406,11 @@  static int do_show(int argc, char **argv)
 		if (group != -1)
 			printf(" group %ld", group);
 		printf("\n");
+		if (show_details) {
+			printf("\tAttached to processes:");
+			show_processes(d->d_name);
+			printf("\n");
+		}
 	}
 	closedir(dir);
 	return 0;