From: Dmitry V. L. <ld...@al...> - 2016-07-07 12:34:27
|
On Wed, Jul 06, 2016 at 03:49:24PM +0000, Fabien Siron wrote: > Handle the case where there are several messages in the buffer. > This is very useful to some protocols like SOCK_DIAG. > > * netlink.c (fetch_nlmsg, next_nlmsg): New functions. > (decode_netlink_msg): New function. > (decode_netlink): Call decode_netlink_msg(). > * tests/netlink_parsing.c (send_query): Adapt test. This is v9 but the code hasn't matured for the merge yet, and I'm getting tired explaining its problems over and over again, so here is a kind of commit I was expecting all this time, feel free to reuse it. [PATCH] netlink: handle multipart netlink messages 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 | 102 +++++++++++++++++++++++++++++++++++++++-------- tests/netlink_protocol.c | 94 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 17 deletions(-) diff --git a/netlink.c b/netlink.c index c43f6e7..71573d4 100644 --- a/netlink.c +++ b/netlink.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Fabien Siron <fab...@ep...> + * Copyright (c) 2016 Dmitry V. Levin <ld...@al...> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,34 +32,101 @@ #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("]"); + } +} diff --git a/tests/netlink_protocol.c b/tests/netlink_protocol.c index 8afea59..e0d7b52 100644 --- a/tests/netlink_protocol.c +++ b/tests/netlink_protocol.c @@ -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) -- ldv |