Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / tcpconns.c
index 7036d08..9fdd16e 100644 (file)
 #include "common.h"
 #include "plugin.h"
 
-#if defined(__OpenBSD__) || defined(__NetBSD__)
+#if defined(__OpenBSD__)
+#define HAVE_KVM_GETFILES 1
+#endif
+
+#if defined(__NetBSD__)
 #undef HAVE_SYSCTLBYNAME /* force HAVE_LIBKVM_NLIST path */
 #endif
 
-#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_LIBKVM_NLIST && !KERNEL_AIX
+#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_KVM_GETFILES &&               \
+    !HAVE_LIBKVM_NLIST && !KERNEL_AIX
 #error "No applicable input method."
 #endif
 
 #include <netinet/tcpip.h>
 /* #endif HAVE_SYSCTLBYNAME */
 
-/* This is for OpenBSD and NetBSD. */
+#elif HAVE_KVM_GETFILES
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#define _KERNEL /* for DTYPE_SOCKET */
+#include <sys/file.h>
+#undef _KERNEL
+
+#include <netinet/in.h>
+
+#include <kvm.h>
+/* #endif HAVE_KVM_GETFILES */
+
+/* This is for NetBSD. */
 #elif HAVE_LIBKVM_NLIST
 #include <arpa/inet.h>
 #include <net/route.h>
@@ -169,6 +186,19 @@ static const char *tcp_state[] = {"CLOSED",    "LISTEN",      "SYN_SENT",
 #define TCP_STATE_MAX 10
 /* #endif HAVE_SYSCTLBYNAME */
 
+#elif HAVE_KVM_GETFILES
+static const char *tcp_state[] = {"CLOSED",    "LISTEN",      "SYN_SENT",
+                                  "SYN_RECV",  "ESTABLISHED", "CLOSE_WAIT",
+                                  "FIN_WAIT1", "CLOSING",     "LAST_ACK",
+                                  "FIN_WAIT2", "TIME_WAIT"};
+
+#define TCP_STATE_LISTEN 1
+#define TCP_STATE_MIN 0
+#define TCP_STATE_MAX 10
+
+static kvm_t *kvmd;
+/* #endif HAVE_KVM_GETFILES */
+
 #elif HAVE_LIBKVM_NLIST
 static const char *tcp_state[] = {"CLOSED",    "LISTEN",      "SYN_SENT",
                                   "SYN_RECV",  "ESTABLISHED", "CLOSE_WAIT",
@@ -263,8 +293,8 @@ static void conn_submit_port_entry(port_entry_t *pe) {
 
   if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING)) ||
       (pe->flags & PORT_COLLECT_LOCAL)) {
-    ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
-              "%" PRIu16 "-local", pe->port);
+    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
+             "%" PRIu16 "-local", pe->port);
 
     for (int i = 1; i <= TCP_STATE_MAX; i++) {
       vl.values[0].gauge = pe->count_local[i];
@@ -276,8 +306,8 @@ static void conn_submit_port_entry(port_entry_t *pe) {
   }
 
   if (pe->flags & PORT_COLLECT_REMOTE) {
-    ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
-              "%" PRIu16 "-remote", pe->port);
+    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance),
+             "%" PRIu16 "-remote", pe->port);
 
     for (int i = 1; i <= TCP_STATE_MAX; i++) {
       vl.values[0].gauge = pe->count_remote[i];
@@ -327,14 +357,14 @@ static port_entry_t *conn_get_port_entry(uint16_t port, int create) {
   if ((ret == NULL) && (create != 0)) {
     ret = calloc(1, sizeof(*ret));
     if (ret == NULL)
-      return (NULL);
+      return NULL;
 
     ret->port = port;
     ret->next = port_list_head;
     port_list_head = ret;
   }
 
-  return (ret);
+  return ret;
 } /* port_entry_t *conn_get_port_entry */
 
 /* Removes ports that were added automatically due to the `ListeningPorts'
@@ -388,7 +418,7 @@ static int conn_handle_ports(uint16_t port_local, uint16_t port_remote,
     NOTICE("tcpconns plugin: Ignoring connection with "
            "unknown state 0x%02" PRIx8 ".",
            state);
-    return (-1);
+    return -1;
   }
 
   count_total[state]++;
@@ -411,7 +441,7 @@ static int conn_handle_ports(uint16_t port_local, uint16_t port_remote,
   if (pe != NULL)
     pe->count_remote[state]++;
 
-  return (0);
+  return 0;
 } /* int conn_handle_ports */
 
 #if KERNEL_LINUX
@@ -430,7 +460,7 @@ static int conn_read_netlink(void) {
     ERROR("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, "
           "NETLINK_INET_DIAG) failed: %s",
           sstrerror(errno, buf, sizeof(buf)));
-    return (-1);
+    return -1;
   }
 
   struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
@@ -462,7 +492,7 @@ static int conn_read_netlink(void) {
     ERROR("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s",
           sstrerror(errno, buf, sizeof(buf)));
     close(fd);
-    return (-1);
+    return -1;
   }
 
   iov.iov_base = buf;
@@ -486,12 +516,12 @@ static int conn_read_netlink(void) {
       ERROR("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s",
             sstrerror(errno, buf, sizeof(buf)));
       close(fd);
-      return (-1);
+      return -1;
     } else if (status == 0) {
       close(fd);
       DEBUG("tcpconns plugin: conn_read_netlink: Unexpected zero-sized "
             "reply from netlink socket.");
-      return (0);
+      return 0;
     }
 
     h = (struct nlmsghdr *)buf;
@@ -503,7 +533,7 @@ static int conn_read_netlink(void) {
 
       if (h->nlmsg_type == NLMSG_DONE) {
         close(fd);
-        return (0);
+        return 0;
       } else if (h->nlmsg_type == NLMSG_ERROR) {
         struct nlmsgerr *msg_error;
 
@@ -512,7 +542,7 @@ static int conn_read_netlink(void) {
                 msg_error->error);
 
         close(fd);
-        return (1);
+        return 1;
       }
 
       r = NLMSG_DATA(h);
@@ -526,9 +556,9 @@ static int conn_read_netlink(void) {
   }   /* while (1) */
 
   /* Not reached because the while() loop above handles the exit condition. */
-  return (0);
+  return 0;
 #else
-  return (1);
+  return 1;
 #endif /* HAVE_STRUCT_LINUX_INET_DIAG_REQ */
 } /* int conn_read_netlink */
 
@@ -550,40 +580,40 @@ static int conn_handle_line(char *buffer) {
   while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32))
     buffer[--buffer_len] = '\0';
   if (buffer_len <= 0)
-    return (-1);
+    return -1;
 
   fields_len = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
   if (fields_len < 12) {
     DEBUG("tcpconns plugin: Got %i fields, expected at least 12.", fields_len);
-    return (-1);
+    return -1;
   }
 
   port_local_str = strchr(fields[1], ':');
   port_remote_str = strchr(fields[2], ':');
 
   if ((port_local_str == NULL) || (port_remote_str == NULL))
-    return (-1);
+    return -1;
   port_local_str++;
   port_remote_str++;
   if ((*port_local_str == '\0') || (*port_remote_str == '\0'))
-    return (-1);
+    return -1;
 
   endptr = NULL;
   port_local = (uint16_t)strtol(port_local_str, &endptr, 16);
   if ((endptr == NULL) || (*endptr != '\0'))
-    return (-1);
+    return -1;
 
   endptr = NULL;
   port_remote = (uint16_t)strtol(port_remote_str, &endptr, 16);
   if ((endptr == NULL) || (*endptr != '\0'))
-    return (-1);
+    return -1;
 
   endptr = NULL;
   state = (uint8_t)strtol(fields[3], &endptr, 16);
   if ((endptr == NULL) || (*endptr != '\0'))
-    return (-1);
+    return -1;
 
-  return (conn_handle_ports(port_local, port_remote, state));
+  return conn_handle_ports(port_local, port_remote, state);
 } /* int conn_handle_line */
 
 static int conn_read_file(const char *file) {
@@ -592,7 +622,7 @@ static int conn_read_file(const char *file) {
 
   fh = fopen(file, "r");
   if (fh == NULL)
-    return (-1);
+    return -1;
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     conn_handle_line(buffer);
@@ -600,7 +630,7 @@ static int conn_read_file(const char *file) {
 
   fclose(fh);
 
-  return (0);
+  return 0;
 } /* int conn_read_file */
 /* #endif KERNEL_LINUX */
 
@@ -623,13 +653,13 @@ static int conn_config(const char *key, const char *value) {
 
     if ((port < 1) || (port > 65535)) {
       ERROR("tcpconns plugin: Invalid port: %i", port);
-      return (1);
+      return 1;
     }
 
     pe = conn_get_port_entry((uint16_t)port, 1 /* create */);
     if (pe == NULL) {
       ERROR("tcpconns plugin: conn_get_port_entry failed.");
-      return (1);
+      return 1;
     }
 
     if (strcasecmp(key, "LocalPort") == 0)
@@ -642,10 +672,10 @@ static int conn_config(const char *key, const char *value) {
     else
       port_collect_total = 0;
   } else {
-    return (-1);
+    return -1;
   }
 
-  return (0);
+  return 0;
 } /* int conn_config */
 
 #if KERNEL_LINUX
@@ -653,7 +683,7 @@ static int conn_init(void) {
   if (port_collect_total == 0 && port_list_head == NULL)
     port_collect_listening = 1;
 
-  return (0);
+  return 0;
 } /* int conn_init */
 
 static int conn_read(void) {
@@ -690,16 +720,16 @@ static int conn_read(void) {
       linux_source = SRC_PROC;
 
       /* return success here to avoid the "plugin failed" message. */
-      return (0);
+      return 0;
     }
   }
 
   if (status == 0)
     conn_submit_all();
   else
-    return (status);
+    return status;
 
-  return (0);
+  return 0;
 } /* int conn_read */
 /* #endif KERNEL_LINUX */
 
@@ -719,26 +749,26 @@ static int conn_read(void) {
   status = sysctlbyname("net.inet.tcp.pcblist", NULL, &buffer_len, 0, 0);
   if (status < 0) {
     ERROR("tcpconns plugin: sysctlbyname failed.");
-    return (-1);
+    return -1;
   }
 
   buffer = malloc(buffer_len);
   if (buffer == NULL) {
     ERROR("tcpconns plugin: malloc failed.");
-    return (-1);
+    return -1;
   }
 
   status = sysctlbyname("net.inet.tcp.pcblist", buffer, &buffer_len, 0, 0);
   if (status < 0) {
     ERROR("tcpconns plugin: sysctlbyname failed.");
     sfree(buffer);
-    return (-1);
+    return -1;
   }
 
   if (buffer_len <= sizeof(struct xinpgen)) {
     ERROR("tcpconns plugin: (buffer_len <= sizeof (struct xinpgen))");
     sfree(buffer);
-    return (-1);
+    return -1;
   }
 
   in_orig = (struct xinpgen *)buffer;
@@ -777,10 +807,52 @@ static int conn_read(void) {
 
   conn_submit_all();
 
-  return (0);
+  return 0;
 } /* int conn_read */
   /* #endif HAVE_SYSCTLBYNAME */
 
+#elif HAVE_KVM_GETFILES
+
+static int conn_init(void) {
+  char buf[_POSIX2_LINE_MAX];
+
+  kvmd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
+  if (kvmd == NULL) {
+    ERROR("tcpconns plugin: kvm_openfiles failed: %s", buf);
+    return -1;
+  }
+
+  return 0;
+} /* int conn_init */
+
+static int conn_read(void) {
+  struct kinfo_file *kf;
+  int i, fcnt;
+
+  conn_reset_port_entry();
+
+  kf = kvm_getfiles(kvmd, KERN_FILE_BYFILE, DTYPE_SOCKET, sizeof(*kf), &fcnt);
+  if (kf == NULL) {
+    ERROR("tcpconns plugin: kvm_getfiles failed.");
+    return -1;
+  }
+
+  for (i = 0; i < fcnt; i++) {
+    if (kf[i].so_protocol != IPPROTO_TCP)
+      continue;
+    if (kf[i].inp_fport == 0)
+      continue;
+    conn_handle_ports(ntohs(kf[i].inp_lport), ntohs(kf[i].inp_fport),
+                      kf[i].t_state);
+  }
+
+  conn_submit_all();
+
+  return 0;
+}
+/* int conn_read */
+/* #endif HAVE_KVM_GETFILES */
+
 #elif HAVE_LIBKVM_NLIST
 static int kread(u_long addr, void *buf, int size) {
   int status;
@@ -789,9 +861,9 @@ static int kread(u_long addr, void *buf, int size) {
   if (status != size) {
     ERROR("tcpconns plugin: kvm_read failed (got %i, expected %i): %s\n",
           status, size, kvm_geterr(kvmd));
-    return (-1);
+    return -1;
   }
-  return (0);
+  return 0;
 } /* int kread */
 
 static int conn_init(void) {
@@ -804,25 +876,25 @@ static int conn_init(void) {
   kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf);
   if (kvmd == NULL) {
     ERROR("tcpconns plugin: kvm_openfiles failed: %s", buf);
-    return (-1);
+    return -1;
   }
 
   status = kvm_nlist(kvmd, nl);
   if (status < 0) {
     ERROR("tcpconns plugin: kvm_nlist failed with status %i.", status);
-    return (-1);
+    return -1;
   }
 
   if (nl[N_TCBTABLE].n_type == 0) {
     ERROR("tcpconns plugin: Error looking up kernel's namelist: "
           "N_TCBTABLE is invalid.");
-    return (-1);
+    return -1;
   }
 
   inpcbtable_off = (u_long)nl[N_TCBTABLE].n_value;
   inpcbtable_ptr = (struct inpcbtable *)nl[N_TCBTABLE].n_value;
 
-  return (0);
+  return 0;
 } /* int conn_init */
 
 static int conn_read(void) {
@@ -841,7 +913,7 @@ static int conn_read(void) {
   /* Read the pcbtable from the kernel */
   status = kread(inpcbtable_off, &table, sizeof(table));
   if (status != 0)
-    return (-1);
+    return -1;
 
 #if defined(__OpenBSD__) ||                                                    \
     (defined(__NetBSD_Version__) && __NetBSD_Version__ > 699002700)
@@ -861,7 +933,7 @@ static int conn_read(void) {
     /* Read the pcb pointed to by `next' into `inpcb' */
     status = kread((u_long)next, &inpcb, sizeof(inpcb));
     if (status != 0)
-      return (-1);
+      return -1;
 
 /* Advance `next' */
 #if defined(__OpenBSD__) ||                                                    \
@@ -887,14 +959,14 @@ static int conn_read(void) {
 
     status = kread((u_long)inpcb.inp_ppcb, &tcpcb, sizeof(tcpcb));
     if (status != 0)
-      return (-1);
+      return -1;
     conn_handle_ports(ntohs(inpcb.inp_lport), ntohs(inpcb.inp_fport),
                       tcpcb.t_state);
   } /* while (next != head) */
 
   conn_submit_all();
 
-  return (0);
+  return 0;
 }
 /* #endif HAVE_LIBKVM_NLIST */
 
@@ -912,27 +984,27 @@ static int conn_read(void) {
   size = netinfo(NETINFO_TCP, 0, 0, 0);
   if (size < 0) {
     ERROR("tcpconns plugin: netinfo failed return: %i", size);
-    return (-1);
+    return -1;
   }
 
   if (size == 0)
-    return (0);
+    return 0;
 
   if ((size - sizeof(struct netinfo_header)) % sizeof(struct netinfo_conn)) {
     ERROR("tcpconns plugin: invalid buffer size");
-    return (-1);
+    return -1;
   }
 
   data = malloc(size);
   if (data == NULL) {
     ERROR("tcpconns plugin: malloc failed");
-    return (-1);
+    return -1;
   }
 
   if (netinfo(NETINFO_TCP, data, &size, 0) < 0) {
     ERROR("tcpconns plugin: netinfo failed");
     free(data);
-    return (-1);
+    return -1;
   }
 
   header = (struct netinfo_header *)data;
@@ -947,7 +1019,7 @@ static int conn_read(void) {
 
   conn_submit_all();
 
-  return (0);
+  return 0;
 }
 #endif /* KERNEL_AIX */
 
@@ -964,7 +1036,3 @@ void module_register(void) {
 #endif
   plugin_register_read("tcpconns", conn_read);
 } /* void module_register */
-
-/*
- * vim: set shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker :
- */