/*
* 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 *const tcp, struct nlmsghdr *const nlmsghdr,
+ const kernel_ureg_t addr, const unsigned long len)
{
- struct nlmsghdr nlmsghdr;
-
- if (size < sizeof(struct nlmsghdr)) {
- printstr(tcp, addr, size);
- return;
+ if (len < sizeof(struct nlmsghdr)) {
+ printstrn(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 *const tcp,
+ const struct nlmsghdr *const nlmsghdr,
+ const kernel_ureg_t 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));
+
+ printstrn(tcp, addr + sizeof(struct nlmsghdr),
+ nlmsg_len - sizeof(struct nlmsghdr));
}
tprints("}");
}
+
+void
+decode_netlink(struct tcb *const tcp, kernel_ureg_t 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);
+ kernel_ureg_t next_addr = 0;
+ unsigned long 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("]");
+ }
+}