]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
ss: allow to retrieve AF_PACKET info via netlink
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Fri, 17 May 2013 15:42:34 +0000 (08:42 -0700)
committerStephen Hemminger <stephen@networkplumber.org>
Fri, 17 May 2013 15:42:34 +0000 (08:42 -0700)
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 <nicolas.dichtel@6wind.com>
misc/ss.c

index 54936308484fe430315b69284322f6e1716a064f..2730cb21b63c15664079f57bd6ce03002d77d1c8 100644 (file)
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -36,6 +36,9 @@
 #include <linux/sock_diag.h>
 #include <linux/inet_diag.h>
 #include <linux/unix_diag.h>
+#include <linux/netdevice.h>   /* for MAX_ADDR_LEN */
+#include <linux/filter.h>
+#include <linux/packet_diag.h>
 
 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<<SS_CLOSE)))
                return 0;
 
+       if (packet_show_netlink(f, NULL) == 0)
+               return 0;
+
        if ((fp = net_packet_open()) == NULL)
                return -1;
        fgets(buf, sizeof(buf)-1, fp);
@@ -2730,6 +2912,7 @@ static void _usage(FILE *dest)
 "   -p, --processes    show process using socket\n"
 "   -i, --info         show internal TCP information\n"
 "   -s, --summary      show socket usage summary\n"
+"   -b, --bfp           show bpf filter socket information\n"
 "\n"
 "   -4, --ipv4          display only IP version 4 sockets\n"
 "   -6, --ipv6          display only IP version 6 sockets\n"
@@ -2800,6 +2983,7 @@ static const struct option long_opts[] = {
        { "memory", 0, 0, 'm' },
        { "info", 0, 0, 'i' },
        { "processes", 0, 0, 'p' },
+       { "bpf", 0, 0, 'b' },
        { "dccp", 0, 0, 'd' },
        { "tcp", 0, 0, 't' },
        { "udp", 0, 0, 'u' },
@@ -2836,7 +3020,7 @@ int main(int argc, char *argv[])
 
        current_filter.states = default_filter.states;
 
-       while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spf:miA:D:F:vV",
+       while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbf:miA:D:F:vV",
                                 long_opts, NULL)) != EOF) {
                switch(ch) {
                case 'n':
@@ -2862,6 +3046,10 @@ int main(int argc, char *argv[])
                        show_users++;
                        user_ent_hash_build();
                        break;
+               case 'b':
+                       show_options = 1;
+                       show_bpf++;
+                       break;
                case 'd':
                        current_filter.dbs |= (1<<DCCP_DB);
                        do_default = 0;