From: Jeff Mahoney Subject: openslp: Use TCPDIAG for checking listeners References: bnc#601002 The use of /proc/net/tcp is deprecated and can cause performance issues on large systems. The issue is that there are a great many locks that must be claimed and released in order to produce the contents of the proc file. The replacement mechanism is to use the INETDIAG/TCPDIAG interface to get the results. This has the advantage of using in-kernel filtering as well as a binary interface so that the parsing of the proc file is unnecessary. Support is limited to TCP so the use of /proc/net/udp is still required. If for whatever reason the netlink connection is lost and can't be re-established, we fall back to reading /proc/net/tcp until the daemon is restarted. Signed-off-by: Jeff Mahoney --- slpd/slpd_database.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) --- a/slpd/slpd_database.c +++ b/slpd/slpd_database.c @@ -76,6 +76,9 @@ FILE *regfileFP; /* standard header files */ /*=========================================================================*/ #include +#include +#include +#include /*=========================================================================*/ SLPDDatabase G_SlpdDatabase; @@ -919,11 +922,176 @@ static void SLPDDatabaseWatcher_fd(int f } } +enum { + SS_UNKNOWN, + SS_ESTABLISHED, + SS_SYN_SENT, + SS_SYN_RECV, + SS_FIN_WAIT1, + SS_FIN_WAIT2, + SS_TIME_WAIT, + SS_CLOSE, + SS_CLOSE_WAIT, + SS_LAST_ACK, + SS_LISTEN, + SS_CLOSING, + SS_MAX +}; + +#define SS_ALL ((1< 0) { + if (sendmsg(*fd, &msg, 0) >= 0) + break; + + if (reconnect_nl(fd)) { + SLPDLog("Lost TCPDIAG netlink connection and attempts to " + "re-establish have failed. Falling back to /proc/net/tcp " + "for dead/alive updates.\n"); + *fd = -1; + return; + } + sched_yield(); + } + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + dh = SLPDatabaseOpen(&G_SlpdDatabase.database); + while (!status) { + struct nlmsghdr *h; + + status = recvmsg(*fd, &msg, 0); + if (status < 0) { + if (errno == EINTR) + continue; + goto retry_sendmsg; + } + + /* Socket has shut down */ + if (status == 0) + goto retry_sendmsg; + + for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, status); + h = NLMSG_NEXT(h, status)) { + SLPDatabaseEntry *entry; + struct inet_diag_msg *r = NLMSG_DATA(h); + + if (h->nlmsg_seq != 123456) + continue; + + if (h->nlmsg_type == NLMSG_DONE) + goto close; + + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(h); + if (h->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) + status = EINVAL; + else + status = -err->error; + break; + } + + if (r->idiag_family != AF_INET && r->idiag_family != AF_INET6) + continue; + + if (r->idiag_family == AF_INET && + ipv4_loopback.s_addr == r->id.idiag_src[0]) + continue; + + if (r->idiag_family == AF_INET6 && + !memcmp(ipv6_loopback.s6_addr32, r->id.idiag_src, + sizeof(ipv6_loopback))) + continue; + + port = ntohs(r->id.idiag_sport); + if (!(porthash[(port / 8) & 255] & (1 << (port & 7)))) + continue; + + SLPDatabaseRewind(dh); + + while ((entry = SLPDatabaseEnum(dh)) != 0) { + SLPSrvReg *srvreg = &(entry->msg->body.srvreg); + if (!(srvreg->watchflags & flag)) + continue; + if (port == srvreg->watchport) + srvreg->watchflags &= ~SLP_REG_WATCH_CHECKING; + } + } + } + +close: + SLPDatabaseClose(dh); +} + /*=========================================================================*/ void SLPDDatabaseWatcher(void) { static int initialized = 0; - static int proctcp, procudp, proctcp6, procudp6; + static int proctcp, procudp, proctcp6, procudp6, inet_diag = -1; unsigned char porthash[256]; int flags, port; SLPDatabaseHandle dh; @@ -931,6 +1099,7 @@ void SLPDDatabaseWatcher(void) SLPSrvReg* srvreg; if (!initialized) { + inet_diag = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG); proctcp = open("/proc/net/tcp_listen", O_RDONLY); if (proctcp == -1) proctcp = open("/proc/net/tcp", O_RDONLY); @@ -955,8 +1124,12 @@ void SLPDDatabaseWatcher(void) } SLPDatabaseClose(dh); if ((flags & SLP_REG_WATCH_TCP) != 0) { - SLPDDatabaseWatcher_fd(proctcp, SLP_REG_WATCH_TCP, porthash); - SLPDDatabaseWatcher_fd(proctcp6, SLP_REG_WATCH_TCP, porthash); + if (inet_diag >= 0) + SLPDDatabaseWatcher_nl(&inet_diag, SLP_REG_WATCH_TCP, porthash); + if (inet_diag < 0) { /* Fallback if _nl fails */ + SLPDDatabaseWatcher_fd(proctcp, SLP_REG_WATCH_TCP, porthash); + SLPDDatabaseWatcher_fd(proctcp6, SLP_REG_WATCH_TCP, porthash); + } } if ((flags & SLP_REG_WATCH_UDP) != 0) { SLPDDatabaseWatcher_fd(procudp, SLP_REG_WATCH_UDP, porthash);