]> granicus.if.org Git - strace/commitdiff
netlink: handle multipart netlink messages
authorDmitry V. Levin <ldv@altlinux.org>
Fri, 8 Jul 2016 16:01:49 +0000 (16:01 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Fri, 8 Jul 2016 16:01:49 +0000 (16:01 +0000)
Handle multipart netlink messages made of multiple struct nlmsghdr
headers with associated payload in one byte stream.

* netlink.c (fetch_nlmsghdr, print_nlmsghdr,
decode_nlmsghdr_with_payload): New functions.
(decode_netlink): Use them.
* tests/netlink_parsing.c (send_query): Check them.

netlink.c
tests/netlink_protocol.c

index c43f6e74ac6b55f9b462377ebd76df259dd67de2..71573d474604fb7aac134e57a6413e0ddc4897e8 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
+ * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "xlat/netlink_flags.h"
 #include "xlat/netlink_types.h"
 
-void
-decode_netlink(struct tcb *tcp, unsigned long addr, unsigned long size)
+/*
+ * Fetch a struct nlmsghdr from the given address.
+ */
+static bool
+fetch_nlmsghdr(struct tcb *tcp, struct nlmsghdr *nlmsghdr,
+              const unsigned long addr, const unsigned long len)
 {
-       struct nlmsghdr nlmsghdr;
-
-       if (size < sizeof(struct nlmsghdr)) {
-               printstr(tcp, addr, size);
-               return;
+       if (len < sizeof(struct nlmsghdr)) {
+               printstr(tcp, addr, len);
+               return false;
        }
-       if (umove_or_printaddr(tcp, addr, &nlmsghdr))
-               return;
 
-       tprintf("{{len=%u, type=", nlmsghdr.nlmsg_len);
+       if (umove_or_printaddr(tcp, addr, nlmsghdr))
+               return false;
+
+       return true;
+}
+
+static void
+print_nlmsghdr(struct tcb *tcp, const struct nlmsghdr *const nlmsghdr)
+{
+       /* print the whole structure regardless of its nlmsg_len */
+
+       tprintf("{len=%u, type=", nlmsghdr->nlmsg_len);
 
-       printxval(netlink_types, nlmsghdr.nlmsg_type, "NLMSG_???");
+       printxval(netlink_types, nlmsghdr->nlmsg_type, "NLMSG_???");
 
        tprints(", flags=");
-       printflags(netlink_flags, nlmsghdr.nlmsg_flags, "NLM_F_???");
-       /* manage get/new requests */
+       printflags(netlink_flags, nlmsghdr->nlmsg_flags, "NLM_F_???");
+
+       tprintf(", seq=%u, pid=%u}", nlmsghdr->nlmsg_seq,
+               nlmsghdr->nlmsg_pid);
+}
 
-       tprintf(", seq=%u, pid=%u}", nlmsghdr.nlmsg_seq,
-               nlmsghdr.nlmsg_pid);
+static void
+decode_nlmsghdr_with_payload(struct tcb *tcp,
+                            const struct nlmsghdr *const nlmsghdr,
+                            const unsigned long addr,
+                            const unsigned long len)
+{
+       tprints("{");
+
+       print_nlmsghdr(tcp, nlmsghdr);
 
-       if (size - sizeof(struct nlmsghdr) > 0) {
+       unsigned long nlmsg_len =
+               nlmsghdr->nlmsg_len > len ? len : nlmsghdr->nlmsg_len;
+       if (nlmsg_len > sizeof(struct nlmsghdr)) {
                tprints(", ");
+
                printstr(tcp, addr + sizeof(struct nlmsghdr),
-                        size - sizeof(struct nlmsghdr));
+                        nlmsg_len - sizeof(struct nlmsghdr));
        }
 
        tprints("}");
 }
+
+void
+decode_netlink(struct tcb *tcp, unsigned long addr, unsigned long len)
+{
+       struct nlmsghdr nlmsghdr;
+       bool print_array = false;
+       unsigned int elt;
+
+       for (elt = 0; fetch_nlmsghdr(tcp, &nlmsghdr, addr, len); elt++) {
+               if (abbrev(tcp) && elt == max_strlen) {
+                       tprints("...");
+                       break;
+               }
+
+               unsigned long nlmsg_len = NLMSG_ALIGN(nlmsghdr.nlmsg_len);
+               unsigned long next_addr = 0, next_len = 0;
+
+               if (nlmsghdr.nlmsg_len >= sizeof(struct nlmsghdr)) {
+                       next_len = (len >= nlmsg_len) ? len - nlmsg_len : 0;
+
+                       if (next_len && addr + nlmsg_len > addr)
+                               next_addr = addr + nlmsg_len;
+               }
+
+               if (!print_array && next_addr) {
+                       tprints("[");
+                       print_array = true;
+               }
+
+               decode_nlmsghdr_with_payload(tcp, &nlmsghdr, addr, len);
+
+               if (!next_addr)
+                       break;
+
+               tprints(", ");
+               addr = next_addr;
+               len = next_len;
+       }
+
+       if (print_array) {
+               tprints("]");
+       }
+}
index 8afea597f1e89f9eb11d39ef87161c301fb21d90..e0d7b52f8a7ddb219525815441609639bf994129 100644 (file)
@@ -123,6 +123,100 @@ send_query(const int fd)
               ", seq=0, pid=0}, \"abcd\"}, %u, MSG_DONTWAIT, NULL, 0) = %u\n",
               fd, req->nlh.nlmsg_len, NLM_F_DUMP,
               (unsigned) sizeof(*req), (unsigned) sizeof(*req));
+
+       /* nlmsg_len < sizeof(struct nlmsghdr) */
+       req->nlh.nlmsg_len = 8;
+       if (sendto(fd, req, sizeof(*req), MSG_DONTWAIT, NULL, 0) != sizeof(*req))
+               perror_msg_and_skip("sendto");
+
+       printf("sendto(%d, {{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+              ", seq=0, pid=0}}, %u, MSG_DONTWAIT, NULL, 0) = %u\n",
+              fd, req->nlh.nlmsg_len, NLM_F_DUMP,
+              (unsigned) sizeof(*req), (unsigned) sizeof(*req));
+
+       /* a sequence of two nlmsg objects */
+       struct reqs {
+               struct req req1;
+               char padding[NLMSG_ALIGN(sizeof(struct req)) - sizeof(struct req)];
+               struct req req2;
+       } *const reqs = tail_alloc(sizeof(*reqs));
+       memcpy(&reqs->req1, &c_req, sizeof(c_req));
+       memcpy(&reqs->req2, &c_req, sizeof(c_req));
+
+       sendto(fd, reqs, sizeof(*reqs), MSG_DONTWAIT, NULL, 0);
+
+       printf("sendto(%d, [{{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+              ", seq=0, pid=0}, \"abcd\"}, {{len=%u, type=NLMSG_NOOP"
+              ", flags=NLM_F_REQUEST|0x%x, seq=0, pid=0}, \"abcd\"}]"
+              ", %u, MSG_DONTWAIT, NULL, 0) = %u\n",
+              fd, reqs->req1.nlh.nlmsg_len, NLM_F_DUMP,
+              reqs->req2.nlh.nlmsg_len, NLM_F_DUMP,
+              (unsigned) sizeof(*reqs), (unsigned) sizeof(*reqs));
+
+       /* unfetchable second struct nlmsghdr */
+       void *const efault2 = tail_memdup(&reqs->req1, sizeof(reqs->req1));
+       sendto(fd, efault2, sizeof(*reqs), MSG_DONTWAIT, NULL, 0);
+
+       printf("sendto(%d, [{{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+              ", seq=0, pid=0}, \"abcd\"}, %p], %u, MSG_DONTWAIT, NULL, 0)"
+              " = -1 EFAULT (%m)\n",
+              fd, reqs->req1.nlh.nlmsg_len, NLM_F_DUMP,
+              &((struct reqs *) efault2)->req2, (unsigned) sizeof(*reqs));
+
+       /* message length is not enough for the second struct nlmsghdr */
+       if (sendto(fd, reqs, sizeof(*reqs) - sizeof(req->nlh), MSG_DONTWAIT, NULL, 0)
+           != sizeof(*reqs) - sizeof(req->nlh))
+               perror_msg_and_skip("sendto");
+
+       printf("sendto(%d, [{{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+              ", seq=0, pid=0}, \"abcd\"}, \"",
+              fd, reqs->req1.nlh.nlmsg_len, NLM_F_DUMP);
+       print_quoted_memory((void *) &reqs->req2.nlh,
+                           sizeof(reqs->req2) - sizeof(req->nlh));
+       printf("\"], %u, MSG_DONTWAIT, NULL, 0) = %u\n",
+              (unsigned) (sizeof(*reqs) - sizeof(req->nlh)),
+              (unsigned) (sizeof(*reqs) - sizeof(req->nlh)));
+
+       /* second nlmsg_len < sizeof(struct nlmsghdr) */
+       reqs->req2.nlh.nlmsg_len = 4;
+       if (sendto(fd, reqs, sizeof(*reqs), MSG_DONTWAIT, NULL, 0)
+           != sizeof(*reqs))
+               perror_msg_and_skip("sendto");
+
+       printf("sendto(%d, [{{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+              ", seq=0, pid=0}, \"abcd\"}, {{len=%u, type=NLMSG_NOOP"
+              ", flags=NLM_F_REQUEST|0x%x, seq=0, pid=0}}], %u"
+              ", MSG_DONTWAIT, NULL, 0) = %u\n",
+              fd, reqs->req1.nlh.nlmsg_len, NLM_F_DUMP,
+              reqs->req2.nlh.nlmsg_len, NLM_F_DUMP,
+              (unsigned) sizeof(*reqs), (unsigned) sizeof(*reqs));
+
+       /* abbreviated output */
+#define DEFAULT_STRLEN 32
+#define ABBREV_LEN (DEFAULT_STRLEN + 1)
+       const unsigned int msg_len = sizeof(struct nlmsghdr) * ABBREV_LEN;
+       struct nlmsghdr *const msgs = tail_alloc(msg_len);
+       unsigned int i;
+       for (i = 0; i < ABBREV_LEN; ++i) {
+               msgs[i].nlmsg_len = sizeof(*msgs);
+               msgs[i].nlmsg_type = NLMSG_NOOP;
+               msgs[i].nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+               msgs[i].nlmsg_seq = i;
+               msgs[i].nlmsg_pid = 0;
+       }
+
+       if (sendto(fd, msgs, msg_len, MSG_DONTWAIT, NULL, 0) != (int) msg_len)
+               perror_msg_and_skip("sendto");
+
+       printf("sendto(%d, [", fd);
+       for (i = 0; i < DEFAULT_STRLEN; ++i) {
+               if (i)
+                       printf(", ");
+               printf("{{len=%u, type=NLMSG_NOOP, flags=NLM_F_REQUEST|0x%x"
+                      ", seq=%u, pid=0}}",
+                      msgs[i].nlmsg_len, NLM_F_DUMP, msgs[i].nlmsg_seq);
+       }
+       printf(", ...], %u, MSG_DONTWAIT, NULL, 0) = %u\n", msg_len, msg_len);
 }
 
 int main(void)