X-Git-Url: https://git.octo.it/?a=blobdiff_plain;ds=sidebyside;f=src%2Ftcpconns.c;h=655c53e5689ff35647216d50b2a4b01e4bbb1cf2;hb=0421d0d777f6df3cf78fd5366c82b0ed7adb9684;hp=7d6e7997e0fc3fba9f2a05c91802e98e40c9da1d;hpb=03b7ec004938ad0838e16f0eaef343a468140d40;p=collectd.git diff --git a/src/tcpconns.c b/src/tcpconns.c index 7d6e7997..655c53e5 100644 --- a/src/tcpconns.c +++ b/src/tcpconns.c @@ -23,10 +23,85 @@ #include "common.h" #include "plugin.h" -#if !KERNEL_LINUX +#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME # error "No applicable input method." #endif +#if KERNEL_LINUX +/* #endif KERNEL_LINUX */ + +#elif HAVE_SYSCTLBYNAME +# include +# include + +/* Some includes needed for compiling on FreeBSD */ +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_SYS_SOCKET_H +# include +#endif +#if HAVE_NET_IF_H +# include +#endif + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif /* HAVE_SYSCTLBYNAME */ + +#if KERNEL_LINUX +static const char *tcp_state[] = +{ + "", /* 0 */ + "ESTABLISHED", + "SYN_SENT", + "SYN_RECV", + "FIN_WAIT1", + "FIN_WAIT2", + "TIME_WAIT", + "CLOSED", + "CLOSE_WAIT", + "LAST_ACK", + "LISTEN", /* 10 */ + "CLOSING" +}; + +# define TCP_STATE_LISTEN 10 +# define TCP_STATE_MIN 1 +# define TCP_STATE_MAX 11 +/* #endif KERNEL_LINUX */ + +#elif HAVE_SYSCTLBYNAME +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 +#endif /* HAVE_SYSCTLBYNAME */ + #define PORT_COLLECT_LOCAL 0x01 #define PORT_COLLECT_REMOTE 0x02 #define PORT_IS_LISTENING 0x04 @@ -35,8 +110,8 @@ typedef struct port_entry_s { uint16_t port; uint16_t flags; - uint32_t count_local; - uint32_t count_remote; + uint32_t count_local[TCP_STATE_MAX + 1]; + uint32_t count_remote[TCP_STATE_MAX + 1]; struct port_entry_s *next; } port_entry_t; @@ -55,27 +130,44 @@ static void conn_submit_port_entry (port_entry_t *pe) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; + int i; vl.values = values; vl.values_len = 1; vl.time = time (NULL); - strcpy (vl.host, hostname_g); - strcpy (vl.plugin, "tcpconns"); - snprintf (vl.type_instance, sizeof (vl.type_instance), "%hu", pe->port); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "tcpconns", sizeof (vl.plugin)); + sstrncpy (vl.type, "tcp_connections", sizeof (vl.type)); if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING)) || (pe->flags & PORT_COLLECT_LOCAL)) { - values[0].gauge = pe->count_local; - strcpy (vl.plugin_instance, "local"); - plugin_dispatch_values ("tcp_connections", &vl); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%hu-local", pe->port); + + for (i = 1; i <= TCP_STATE_MAX; i++) + { + vl.values[0].gauge = pe->count_local[i]; + + sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); + } } + if (pe->flags & PORT_COLLECT_REMOTE) { - values[0].gauge = pe->count_remote; - strcpy (vl.plugin_instance, "remote"); - plugin_dispatch_values ("tcp_connections", &vl); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%hu-remote", pe->port); + + for (i = 1; i <= TCP_STATE_MAX; i++) + { + vl.values[0].gauge = pe->count_remote[i]; + + sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); + } } } /* void conn_submit */ @@ -114,6 +206,8 @@ static port_entry_t *conn_get_port_entry (uint16_t port, int create) return (ret); } /* port_entry_t *conn_get_port_entry */ +/* Removes ports that were added automatically due to the `ListeningPorts' + * setting but which are no longer listening. */ static void conn_reset_port_entry (void) { port_entry_t *prev = NULL; @@ -123,7 +217,9 @@ static void conn_reset_port_entry (void) { /* If this entry was created while reading the files (ant not when handling * the configuration) remove it now. */ - if ((pe->flags & (PORT_COLLECT_LOCAL | PORT_COLLECT_REMOTE)) == 0) + if ((pe->flags & (PORT_COLLECT_LOCAL + | PORT_COLLECT_REMOTE + | PORT_IS_LISTENING)) == 0) { port_entry_t *next = pe->next; @@ -141,9 +237,8 @@ static void conn_reset_port_entry (void) continue; } - pe->count_local = 0; - pe->count_remote = 0; - + memset (pe->count_local, '\0', sizeof (pe->count_local)); + memset (pe->count_remote, '\0', sizeof (pe->count_remote)); pe->flags &= ~PORT_IS_LISTENING; pe = pe->next; @@ -152,44 +247,42 @@ static void conn_reset_port_entry (void) static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t state) { - /* Listening sockets */ - if ((state == 0x0a) && (port_collect_listening != 0)) - { - port_entry_t *pe; + port_entry_t *pe = NULL; - DEBUG ("tcpconns plugin: Adding listening port %hu", port_local); + if ((state > TCP_STATE_MAX) +#if TCP_STATE_MIN > 0 + || (state < TCP_STATE_MIN) +#endif + ) + { + NOTICE ("tcpconns plugin: Ignoring connection with unknown state 0x%02x.", + state); + return (-1); + } + /* Listening sockets */ + if ((state == TCP_STATE_LISTEN) && (port_collect_listening != 0)) + { pe = conn_get_port_entry (port_local, 1 /* create */); if (pe != NULL) - { - pe->count_local++; pe->flags |= PORT_IS_LISTENING; - } - } - /* Established connections */ - else if (state == 0x01) - { - port_entry_t *pe; - - DEBUG ("tcpconns plugin: Established connection %hu <-> %hu", - port_local, port_remote); - - pe = conn_get_port_entry (port_local, 0 /* no create */); - if ((pe != NULL) && (pe->flags & PORT_COLLECT_LOCAL)) - pe->count_local++; - - pe = conn_get_port_entry (port_remote, 0 /* no create */); - if ((pe != NULL) && (pe->flags & PORT_COLLECT_REMOTE)) - pe->count_remote++; - } - else - { - DEBUG ("tcpconns plugin: Ignoring unknown state 0x%x", state); } + DEBUG ("tcpconns plugin: Connection %hu <-> %hu (%s)", + port_local, port_remote, tcp_state[state]); + + pe = conn_get_port_entry (port_local, 0 /* no create */); + if (pe != NULL) + pe->count_local[state]++; + + pe = conn_get_port_entry (port_remote, 0 /* no create */); + if (pe != NULL) + pe->count_remote[state]++; + return (0); } /* int conn_handle_ports */ +#if KERNEL_LINUX static int conn_handle_line (char *buffer) { char *fields[32]; @@ -253,12 +346,7 @@ static int conn_read_file (const char *file) fh = fopen (file, "r"); if (fh == NULL) - { - char errbuf[1024]; - ERROR ("tcpconns plugin: fopen (%s) failed: %s", - file, sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); - } while (fgets (buffer, sizeof (buffer), fh) != NULL) { @@ -269,6 +357,10 @@ static int conn_read_file (const char *file) return (0); } /* int conn_read_file */ +/* #endif KERNEL_LINUX */ + +#elif HAVE_SYSCTLBYNAME +#endif /* HAVE_SYSCTLBYNAME */ static int conn_config (const char *key, const char *value) { @@ -313,6 +405,7 @@ static int conn_config (const char *key, const char *value) return (0); } /* int conn_config */ +#if KERNEL_LINUX static int conn_init (void) { if (port_list_head == NULL) @@ -323,21 +416,113 @@ static int conn_init (void) static int conn_read (void) { + int errors_num = 0; + conn_reset_port_entry (); - conn_read_file ("/proc/net/tcp"); - conn_read_file ("/proc/net/tcp6"); + if (conn_read_file ("/proc/net/tcp") != 0) + errors_num++; + if (conn_read_file ("/proc/net/tcp6") != 0) + errors_num++; + + if (errors_num < 2) + { + conn_submit_all (); + } + else + { + ERROR ("tcpconns plugin: Neither /proc/net/tcp nor /proc/net/tcp6 " + "coult be read."); + return (-1); + } + + return (0); +} /* int conn_read */ +/* #endif KERNEL_LINUX */ + +#elif HAVE_SYSCTLBYNAME +static int conn_read (void) +{ + int status; + char *buffer; + size_t buffer_len;; + + struct xinpgen *in_orig; + struct xinpgen *in_ptr; + + conn_reset_port_entry (); + + buffer_len = 0; + status = sysctlbyname ("net.inet.tcp.pcblist", NULL, &buffer_len, 0, 0); + if (status < 0) + { + ERROR ("tcpconns plugin: sysctlbyname failed."); + return (-1); + } + + buffer = (char *) malloc (buffer_len); + if (buffer == NULL) + { + ERROR ("tcpconns plugin: malloc failed."); + 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); + } + + if (buffer_len <= sizeof (struct xinpgen)) + { + ERROR ("tcpconns plugin: (buffer_len <= sizeof (struct xinpgen))"); + sfree (buffer); + return (-1); + } + + in_orig = (struct xinpgen *) buffer; + for (in_ptr = (struct xinpgen *) (((char *) in_orig) + in_orig->xig_len); + in_ptr->xig_len > sizeof (struct xinpgen); + in_ptr = (struct xinpgen *) (((char *) in_ptr) + in_ptr->xig_len)) + { + struct tcpcb *tp = &((struct xtcpcb *) in_ptr)->xt_tp; + struct inpcb *inp = &((struct xtcpcb *) in_ptr)->xt_inp; + struct xsocket *so = &((struct xtcpcb *) in_ptr)->xt_socket; + + /* Ignore non-TCP sockets */ + if (so->xso_protocol != IPPROTO_TCP) + continue; + + /* Ignore PCBs which were freed during copyout. */ + if (inp->inp_gencnt > in_orig->xig_gen) + continue; + + if (((inp->inp_vflag & INP_IPV4) == 0) + && ((inp->inp_vflag & INP_IPV6) == 0)) + continue; + + conn_handle_ports (inp->inp_lport, inp->inp_fport, tp->t_state); + } /* for (in_ptr) */ + + in_orig = NULL; + in_ptr = NULL; + sfree (buffer); conn_submit_all (); return (0); } /* int conn_read */ +#endif /* HAVE_SYSCTLBYNAME */ void module_register (void) { plugin_register_config ("tcpconns", conn_config, config_keys, config_keys_num); +#if KERNEL_LINUX plugin_register_init ("tcpconns", conn_init); +#endif plugin_register_read ("tcpconns", conn_read); } /* void module_register */