Hi,

becasue reading from /proc/net/tcp[6] takes a LOT of time, especially when there are thousands of connections. I've adopted a patch on netstat that uses netlink interface:

http://www.ducksong.com/misc/netstat-netlink-diag-patch.txt

as described in:

http://amailbox.org/mailarchive/linux-netdev/2008/8/26/3086024

The difference is like night and day. (netstat -n -t vs ss -a -n t)

Maybe someone is interested in making it pretty so it could be added to the tree.

Thank you for collectd,

        Jack.
diff -rupN collectd-4.4.2.orig/src/tcpconns.c collectd-4.4.2/src/tcpconns.c
--- collectd-4.4.2.orig/src/tcpconns.c	2008-07-15 16:31:47.000000000 +0200
+++ collectd-4.4.2/src/tcpconns.c	2010-08-24 21:13:47.000000000 +0200
@@ -23,6 +23,8 @@
 #include "common.h"
 #include "plugin.h"
 
+#define HAVE_NETLINK 1
+
 #if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME
 # error "No applicable input method."
 #endif
@@ -46,6 +48,7 @@
 # include <net/if.h>
 #endif
 
+
 # include <net/route.h>
 # include <netinet/in.h>
 # include <netinet/in_systm.h>
@@ -59,6 +62,13 @@
 # include <netinet/tcp_var.h>
 #endif /* HAVE_SYSCTLBYNAME */
 
+#ifdef HAVE_NETLINK
+# include <netinet/in.h>
+# include <asm/types.h>
+# include <linux/netlink.h>
+# include <linux/inet_diag.h>
+#endif /* HAVE_NETLINK */
+
 #if KERNEL_LINUX
 static const char *tcp_state[] =
 {
@@ -285,7 +295,117 @@ static int conn_handle_ports (uint16_t p
   return (0);
 } /* int conn_handle_ports */
 
-#if KERNEL_LINUX
+#if KERNEL_LINUX && HAVE_NETLINK
+
+static int conn_read_netlink ()
+{
+	int fd;
+	struct sockaddr_nl nladdr;
+	struct {
+		struct nlmsghdr nlh;
+		struct inet_diag_req r;
+	} req;
+	struct msghdr msg;
+	char	buf[8192];
+	struct iovec iov;
+	struct inet_diag_msg *r;
+
+	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0)
+		return -1;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = TCPDIAG_GETSOCK;
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.nlh.nlmsg_pid = 0;
+	req.nlh.nlmsg_seq = 123456;
+	memset(&req.r, 0, sizeof(req.r));
+	req.r.idiag_family = AF_INET;
+	req.r.idiag_states = 0xfff;
+	req.r.idiag_ext = 0;
+
+	iov.iov_base = &req;
+	iov.iov_len = sizeof(req);
+
+	msg = (struct msghdr) {
+		.msg_name = (void*)&nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+	};
+
+	if (sendmsg(fd, &msg, 0) < 0)
+	{
+		close(fd);
+		return -1;
+	}
+
+	iov.iov_base = buf;
+	iov.iov_len = sizeof(buf);
+
+	while (1)
+	{
+		int status;
+		struct nlmsghdr *h;
+
+		msg = (struct msghdr) {
+			(void*) &nladdr,
+			sizeof(nladdr),
+			&iov,1,NULL,0,0
+		};
+
+		status = recvmsg(fd, &msg, 0);
+
+		if (status < 0)
+		{
+			if (errno == EINTR) continue;
+			close(fd);
+			return -2;
+		}
+
+		if (status == 0)
+		{
+			close(fd);
+			return 0;
+		}
+
+		h = (struct nlmsghdr*)buf;
+		while (NLMSG_OK(h, status))
+		{
+			if (h->nlmsg_seq == 123456)
+			{
+				if (h->nlmsg_type == NLMSG_DONE)
+				{
+					close(fd);
+					return 0;
+				}
+
+				if (h->nlmsg_type == NLMSG_ERROR)
+				{
+					close(fd);
+					return -2;
+				}
+
+				r = NLMSG_DATA(h);
+
+				conn_handle_ports(
+					ntohs(r->id.idiag_sport),
+					ntohs(r->id.idiag_dport),
+					(uint8_t) r->idiag_state
+				);
+			}
+			h = NLMSG_NEXT(h, status);
+		}
+	}
+
+	close(fd);
+	return 0;
+}
+
+#elif KERNEL_LINUX
+
 static int conn_handle_line (char *buffer)
 {
   char *fields[32];
@@ -423,10 +543,15 @@ static int conn_read (void)
 
   conn_reset_port_entry ();
 
-  if (conn_read_file ("/proc/net/tcp") != 0)
-    errors_num++;
-  if (conn_read_file ("/proc/net/tcp6") != 0)
-    errors_num++;
+#if HAVE_NETLINK
+	if (conn_read_netlink() != 0)
+		errors_num++;
+#else
+	if (conn_read_file ("/proc/net/tcp") != 0)
+      		errors_num++;
+	if (conn_read_file ("/proc/net/tcp6") != 0)
+		errors_num++;
+#endif
 
   if (errors_num < 2)
   {
_______________________________________________
collectd mailing list
[email protected]
http://mailman.verplant.org/listinfo/collectd

Reply via email to