From 372c30d2aaaa7847b0e0554f37807355eddc58d7 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 17 May 2013 08:42:34 -0700 Subject: [PATCH] ss: allow to retrieve AF_PACKET info via netlink This patch add support of netlink messages for AF_PACKET and thus it allows to get filter information of this kind of sockets. To dump these filters info the option --bfp must be specified and the user must have admin rights. Signed-off-by: Nicolas Dichtel --- misc/ss.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 54936308..2730cb21 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -36,6 +36,9 @@ #include #include #include +#include /* for MAX_ADDR_LEN */ +#include +#include int resolve_hosts = 0; int resolve_services = 1; @@ -45,6 +48,7 @@ int show_details = 0; int show_users = 0; int show_mem = 0; int show_tcpinfo = 0; +int show_bpf = 0; int netid_width; int state_width; @@ -2389,6 +2393,181 @@ static int unix_show(struct filter *f) return 0; } +static int packet_show_sock(struct nlmsghdr *nlh, struct filter *f) +{ + struct packet_diag_msg *r = NLMSG_DATA(nlh); + struct rtattr *tb[PACKET_DIAG_MAX+1]; + __u32 rq; + + parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + /* use /proc/net/packet if all info are not available */ + if (!tb[PACKET_DIAG_MEMINFO]) + return -1; + + if (netid_width) + printf("%-*s ", netid_width, + r->pdiag_type == SOCK_RAW ? "p_raw" : "p_dgr"); + if (state_width) + printf("%-*s ", state_width, "UNCONN"); + + if (tb[PACKET_DIAG_MEMINFO]) { + __u32 *skmeminfo = RTA_DATA(tb[PACKET_DIAG_MEMINFO]); + + rq = skmeminfo[SK_MEMINFO_RMEM_ALLOC]; + } else + rq = 0; + printf("%-6d %-6d ", rq, 0); + + if (r->pdiag_num == 3) { + printf("%*s:", addr_width, "*"); + } else { + char tb2[16]; + printf("%*s:", addr_width, + ll_proto_n2a(htons(r->pdiag_num), tb2, sizeof(tb2))); + } + if (tb[PACKET_DIAG_INFO]) { + struct packet_diag_info *pinfo = RTA_DATA(tb[PACKET_DIAG_INFO]); + + if (pinfo->pdi_index == 0) + printf("%-*s ", serv_width, "*"); + else + printf("%-*s ", serv_width, xll_index_to_name(pinfo->pdi_index)); + } else + printf("%-*s ", serv_width, "*"); + + printf("%*s*%-*s", + addr_width, "", serv_width, ""); + + if (show_users) { + char ubuf[4096]; + if (find_users(r->pdiag_ino, ubuf, sizeof(ubuf)) > 0) + printf(" users:(%s)", ubuf); + } + if (show_details) { + __u32 uid = 0; + + if (tb[PACKET_DIAG_UID]) + uid = *(__u32 *)RTA_DATA(tb[PACKET_DIAG_UID]); + + printf(" ino=%u uid=%u sk=", r->pdiag_ino, uid); + if (r->pdiag_cookie[1] != 0) + printf("%08x", r->pdiag_cookie[1]); + printf("%08x", r->pdiag_cookie[0]); + } + + if (show_bpf && tb[PACKET_DIAG_FILTER]) { + struct sock_filter *fil = + RTA_DATA(tb[PACKET_DIAG_FILTER]); + int num = RTA_PAYLOAD(tb[PACKET_DIAG_FILTER]) / + sizeof(struct sock_filter); + + printf("\n\tbpf filter (%d): ", num); + while (num) { + printf(" 0x%02x %u %u %u,", + fil->code, fil->jt, fil->jf, fil->k); + num--; + fil++; + } + } + printf("\n"); + return 0; +} + +static int packet_show_netlink(struct filter *f, FILE *dump_fp) +{ + int fd; + struct { + struct nlmsghdr nlh; + struct packet_diag_req r; + } req; + char buf[8192]; + + if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG)) < 0) + return -1; + + memset(&req, 0, sizeof(req)); + 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_seq = 123456; + + req.r.sdiag_family = AF_PACKET; + req.r.pdiag_show = PACKET_SHOW_INFO | PACKET_SHOW_MEMINFO | PACKET_SHOW_FILTER; + + if (send(fd, &req, sizeof(req), 0) < 0) { + close(fd); + return -1; + } + + while (1) { + ssize_t status; + struct nlmsghdr *h; + struct sockaddr_nl nladdr; + socklen_t slen = sizeof(nladdr); + + status = recvfrom(fd, buf, sizeof(buf), 0, + (struct sockaddr *) &nladdr, &slen); + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + goto close_it; + } + + 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_seq != 123456) + goto skip_it; + + if (h->nlmsg_type == NLMSG_DONE) + goto close_it; + + 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; + if (errno != ENOENT) + fprintf(stderr, "UDIAG answers %d\n", errno); + } + close(fd); + return -1; + } + if (!dump_fp) { + err = packet_show_sock(h, f); + if (err < 0) { + close(fd); + return err; + } + } + +skip_it: + h = NLMSG_NEXT(h, status); + } + + if (status) { + fprintf(stderr, "!!!Remnant of size %zd\n", status); + exit(1); + } + } + +close_it: + close(fd); + return 0; +} + static int packet_show(struct filter *f) { @@ -2406,6 +2585,9 @@ static int packet_show(struct filter *f) if (!(f->states & (1<