static int conn_read_file (const char *file) { FILE *fh; char buffer[1024]; fh = fopen (file, "r"); if (fh == NULL) return (-1); while (fgets (buffer, sizeof (buffer), fh) != NULL) { conn_handle_line (buffer); } /* while (fgets) */ fclose (fh); return (0); } /* int conn_read_file */
/* Returns zero on success, less than zero on socket error and greater than * zero on other errors. */ static int conn_read_netlink(void) { #if HAVE_STRUCT_LINUX_INET_DIAG_REQ int fd; struct inet_diag_msg *r; char buf[8192]; /* If this fails, it's likely a permission problem. We'll fall back to * reading this information from files below. */ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG); if (fd < 0) { ERROR("tcpconns plugin: conn_read_netlink: socket(AF_NETLINK, SOCK_RAW, " "NETLINK_INET_DIAG) failed: %s", sstrerror(errno, buf, sizeof(buf))); return (-1); } struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK}; struct nlreq req = { .nlh.nlmsg_len = sizeof(req), .nlh.nlmsg_type = TCPDIAG_GETSOCK, /* NLM_F_ROOT: return the complete table instead of a single entry. * NLM_F_MATCH: return all entries matching criteria (not implemented) * NLM_F_REQUEST: must be set on all request messages */ .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, .nlh.nlmsg_pid = 0, /* The sequence_number is used to track our messages. Since netlink is not * reliable, we don't want to end up with a corrupt or incomplete old * message in case the system is/was out of memory. */ .nlh.nlmsg_seq = ++sequence_number, .r.idiag_family = AF_INET, .r.idiag_states = 0xfff, .r.idiag_ext = 0}; struct iovec iov = {.iov_base = &req, .iov_len = sizeof(req)}; struct msghdr msg = {.msg_name = (void *)&nladdr, .msg_namelen = sizeof(nladdr), .msg_iov = &iov, .msg_iovlen = 1}; if (sendmsg(fd, &msg, 0) < 0) { ERROR("tcpconns plugin: conn_read_netlink: sendmsg(2) failed: %s", sstrerror(errno, buf, sizeof(buf))); close(fd); return (-1); } iov.iov_base = buf; iov.iov_len = sizeof(buf); while (1) { int status; struct nlmsghdr *h; memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)&nladdr; msg.msg_namelen = sizeof(nladdr); msg.msg_iov = &iov; msg.msg_iovlen = 1; status = recvmsg(fd, (void *)&msg, /* flags = */ 0); if (status < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; ERROR("tcpconns plugin: conn_read_netlink: recvmsg(2) failed: %s", sstrerror(errno, buf, sizeof(buf))); close(fd); return (-1); } else if (status == 0) { close(fd); DEBUG("tcpconns plugin: conn_read_netlink: Unexpected zero-sized " "reply from netlink socket."); return (0); } h = (struct nlmsghdr *)buf; while (NLMSG_OK(h, status)) { if (h->nlmsg_seq != sequence_number) { h = NLMSG_NEXT(h, status); continue; } if (h->nlmsg_type == NLMSG_DONE) { close(fd); return (0); } else if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *msg_error; msg_error = NLMSG_DATA(h); WARNING("tcpconns plugin: conn_read_netlink: Received error %i.", msg_error->error); close(fd); return (1); } r = NLMSG_DATA(h); /* This code does not (need to) distinguish between IPv4 and IPv6. */ conn_handle_ports(ntohs(r->id.idiag_sport), ntohs(r->id.idiag_dport), r->idiag_state); h = NLMSG_NEXT(h, status); } /* while (NLMSG_OK) */ } /* while (1) */ /* Not reached because the while() loop above handles the exit condition. */ return (0); #else return (1); #endif /* HAVE_STRUCT_LINUX_INET_DIAG_REQ */ } /* int conn_read_netlink */ static int conn_handle_line(char *buffer) { char *fields[32]; int fields_len; char *endptr; char *port_local_str; char *port_remote_str; uint16_t port_local; uint16_t port_remote; uint8_t state; int buffer_len = strlen(buffer); while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32)) buffer[--buffer_len] = '\0'; if (buffer_len <= 0) 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); } port_local_str = strchr(fields[1], ':'); port_remote_str = strchr(fields[2], ':'); if ((port_local_str == NULL) || (port_remote_str == NULL)) return (-1); port_local_str++; port_remote_str++; if ((*port_local_str == '\0') || (*port_remote_str == '\0')) return (-1); endptr = NULL; port_local = (uint16_t)strtol(port_local_str, &endptr, 16); if ((endptr == NULL) || (*endptr != '\0')) return (-1); endptr = NULL; port_remote = (uint16_t)strtol(port_remote_str, &endptr, 16); if ((endptr == NULL) || (*endptr != '\0')) return (-1); endptr = NULL; state = (uint8_t)strtol(fields[3], &endptr, 16); if ((endptr == NULL) || (*endptr != '\0')) return (-1); return (conn_handle_ports(port_local, port_remote, state)); } /* int conn_handle_line */ static int conn_read_file(const char *file) { FILE *fh; char buffer[1024]; fh = fopen(file, "r"); if (fh == NULL) return (-1); while (fgets(buffer, sizeof(buffer), fh) != NULL) { conn_handle_line(buffer); } /* while (fgets) */ fclose(fh); return (0); } /* int conn_read_file */ /* #endif KERNEL_LINUX */ #elif HAVE_SYSCTLBYNAME /* #endif HAVE_SYSCTLBYNAME */ #elif HAVE_LIBKVM_NLIST #endif /* HAVE_LIBKVM_NLIST */ static int conn_config(const char *key, const char *value) { if (strcasecmp(key, "ListeningPorts") == 0) { if (IS_TRUE(value)) port_collect_listening = 1; else port_collect_listening = 0; } else if ((strcasecmp(key, "LocalPort") == 0) || (strcasecmp(key, "RemotePort") == 0)) { port_entry_t *pe; int port = atoi(value); if ((port < 1) || (port > 65535)) { ERROR("tcpconns plugin: Invalid port: %i", port); 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); } if (strcasecmp(key, "LocalPort") == 0) pe->flags |= PORT_COLLECT_LOCAL; else pe->flags |= PORT_COLLECT_REMOTE; } else if (strcasecmp(key, "AllPortsSummary") == 0) { if (IS_TRUE(value)) port_collect_total = 1; else port_collect_total = 0; } else { return (-1); } return (0); } /* int conn_config */ #if KERNEL_LINUX static int conn_init(void) { if (port_collect_total == 0 && port_list_head == NULL) port_collect_listening = 1; return (0); } /* int conn_init */ static int conn_read(void) { int status; conn_reset_port_entry(); if (linux_source == SRC_NETLINK) { status = conn_read_netlink(); } else if (linux_source == SRC_PROC) { int errors_num = 0; if (conn_read_file("/proc/net/tcp") != 0) errors_num++; if (conn_read_file("/proc/net/tcp6") != 0) errors_num++; if (errors_num < 2) status = 0; else status = ENOENT; } else /* if (linux_source == SRC_DUNNO) */ { /* Try to use netlink for getting this data, it is _much_ faster on systems * with a large amount of connections. */ status = conn_read_netlink(); if (status == 0) { INFO("tcpconns plugin: Reading from netlink succeeded. " "Will use the netlink method from now on."); linux_source = SRC_NETLINK; } else { INFO("tcpconns plugin: Reading from netlink failed. " "Will read from /proc from now on."); linux_source = SRC_PROC; /* return success here to avoid the "plugin failed" message. */ return (0); } } if (status == 0) conn_submit_all(); else return (status); return (0); } /* int conn_read */