Commit dfbaa90d authored by Pavel Emelyanov's avatar Pavel Emelyanov Committed by Stephen Hemminger

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: default avatarPavel Emelyanov <xemul@parallels.com>
parent db4a7f19
#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
......@@ -35,6 +35,7 @@
#include <netinet/tcp.h>
#include <linux/inet_diag.h>
#include <linux/unix_diag.h>
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment