From: Pavel Emelyanov Date: Thu, 15 Dec 2011 03:28:15 +0000 (+0000) Subject: iproute: Dump unix sockets via netlink X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lisovros/iproute2_canprio.git/commitdiff_plain/dfbaa90dec05d3798ad845db20e1b273ffcfcf0a iproute: Dump unix sockets via netlink Get the same info as from /proc file plus the peer inode. Applies on top of new sock diag patch and udp diag patch. Signed-off-by: Pavel Emelyanov --- diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h new file mode 100644 index 0000000..3f7afb0 --- /dev/null +++ b/include/linux/unix_diag.h @@ -0,0 +1,45 @@ +#ifndef __UNIX_DIAG_H__ +#define __UNIX_DIAG_H__ + +struct unix_diag_req { + __u8 sdiag_family; + __u8 sdiag_protocol; + __u16 pad; + __u32 udiag_states; + __u32 udiag_ino; + __u32 udiag_show; + __u32 udiag_cookie[2]; +}; + +#define UDIAG_SHOW_NAME 0x00000001 /* show name (not path) */ +#define UDIAG_SHOW_VFS 0x00000002 /* show VFS inode info */ +#define UDIAG_SHOW_PEER 0x00000004 /* show peer socket info */ +#define UDIAG_SHOW_ICONS 0x00000008 /* show pending connections */ +#define UDIAG_SHOW_RQLEN 0x00000010 /* show skb receive queue len */ + +struct unix_diag_msg { + __u8 udiag_family; + __u8 udiag_type; + __u8 udiag_state; + __u8 pad; + + __u32 udiag_ino; + __u32 udiag_cookie[2]; +}; + +enum { + UNIX_DIAG_NAME, + UNIX_DIAG_VFS, + UNIX_DIAG_PEER, + UNIX_DIAG_ICONS, + UNIX_DIAG_RQLEN, + + UNIX_DIAG_MAX, +}; + +struct unix_diag_vfs { + __u32 udiag_vfs_ino; + __u32 udiag_vfs_dev; +}; + +#endif diff --git a/misc/ss.c b/misc/ss.c index 5ce40c0..00cb569 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -35,6 +35,7 @@ #include #include +#include int resolve_hosts = 0; int resolve_services = 1; @@ -1993,6 +1994,179 @@ void unix_list_print(struct unixstat *list, struct filter *f) } } +static int unix_show_sock(struct nlmsghdr *nlh, struct filter *f) +{ + struct unix_diag_msg *r = NLMSG_DATA(nlh); + struct rtattr *tb[UNIX_DIAG_MAX+1]; + char name[128]; + int peer_ino; + int rqlen; + + parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (netid_width) + printf("%-*s ", netid_width, + r->udiag_type == SOCK_STREAM ? "u_str" : "u_dgr"); + if (state_width) + printf("%-*s ", state_width, sstate_name[r->udiag_state]); + + if (tb[UNIX_DIAG_RQLEN]) + rqlen = *(int *)RTA_DATA(tb[UNIX_DIAG_RQLEN]); + else + rqlen = 0; + + printf("%-6d %-6d ", rqlen, 0); + + if (tb[UNIX_DIAG_NAME]) { + int len = RTA_PAYLOAD(tb[UNIX_DIAG_NAME]); + + memcpy(name, RTA_DATA(tb[UNIX_DIAG_NAME]), len); + name[len] = '\0'; + if (name[0] == '\0') + name[0] = '@'; + } else + sprintf(name, "*"); + + if (tb[UNIX_DIAG_PEER]) + peer_ino = *(int *)RTA_DATA(tb[UNIX_DIAG_PEER]); + else + peer_ino = 0; + + printf("%*s %-*d %*s %-*d", + addr_width, name, + serv_width, r->udiag_ino, + addr_width, "*", /* FIXME */ + serv_width, peer_ino); + + if (show_users) { + char ubuf[4096]; + if (find_users(r->udiag_ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + + printf("\n"); + + return 0; +} + +static int unix_show_netlink(struct filter *f, FILE *dump_fp) +{ + int fd; + struct sockaddr_nl nladdr; + struct { + struct nlmsghdr nlh; + struct unix_diag_req r; + } req; + struct msghdr msg; + char buf[8192]; + struct iovec iov[3]; + + 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 = SOCK_DIAG_BY_FAMILY; + 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.sdiag_family = AF_UNIX; + req.r.sdiag_protocol = 0; /* ignored */ + req.r.udiag_states = f->states; + req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN; + + iov[0] = (struct iovec){ + .iov_base = &req, + .iov_len = sizeof(req) + }; + + msg = (struct msghdr) { + .msg_name = (void*)&nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = f->f ? 3 : 1, + }; + + if (sendmsg(fd, &msg, 0) < 0) + return -1; + + iov[0] = (struct iovec){ + .iov_base = buf, + .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; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return 0; + } + + if (dump_fp) + fwrite(buf, 1, NLMSG_ALIGN(status), dump_fp); + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (/*h->nlmsg_pid != rth->local.nl_pid ||*/ + h->nlmsg_seq != 123456) + goto skip_it; + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("TCPDIAG answers"); + } + return 0; + } + if (!dump_fp) { + err = unix_show_sock(h, f); + if (err < 0) + return err; + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(1); + } + } + return 0; +} + int unix_show(struct filter *f) { FILE *fp; @@ -2002,6 +2176,10 @@ int unix_show(struct filter *f) int cnt; struct unixstat *list = NULL; + if (!getenv("PROC_NET_UNIX") && !getenv("PROC_ROOT") + && unix_show_netlink(f, NULL) == 0) + return 0; + if ((fp = net_unix_open()) == NULL) return -1; fgets(buf, sizeof(buf)-1, fp);