diff options
Diffstat (limited to 'netlink/prettymsg.c')
-rw-r--r-- | netlink/prettymsg.c | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/netlink/prettymsg.c b/netlink/prettymsg.c new file mode 100644 index 0000000..fbf684f --- /dev/null +++ b/netlink/prettymsg.c @@ -0,0 +1,262 @@ +/* + * prettymsg.c - human readable message dump + * + * Support for pretty print of an ethtool netlink message + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <limits.h> +#include <inttypes.h> +#include <linux/genetlink.h> +#include <linux/rtnetlink.h> +#include <linux/if_link.h> +#include <libmnl/libmnl.h> + +#include "prettymsg.h" + +#define __INDENT 4 +#define __DUMP_LINE 16 +#define __DUMP_BLOCK 4 + +static void __print_binary_short(uint8_t *adata, unsigned int alen) +{ + unsigned int i; + + if (!alen) + return; + printf("%02x", adata[0]); + for (i = 1; i < alen; i++) + printf("%c%02x", (i % __DUMP_BLOCK) ? ':' : ' ', adata[i]); +} + +static void __print_binary_long(uint8_t *adata, unsigned int alen, + unsigned int level) +{ + unsigned int i; + + for (i = 0; i < alen; i++) { + if (i % __DUMP_LINE == 0) + printf("\n%*s", __INDENT * (level + 2), ""); + else if (i % __DUMP_BLOCK == 0) + printf(" "); + else + putchar(' '); + printf("%02x", adata[i]); + } +} + +static int pretty_print_attr(const struct nlattr *attr, + const struct pretty_nla_desc *desc, + unsigned int ndesc, unsigned int level, + int err_offset, bool in_array) +{ + unsigned int alen = mnl_attr_get_payload_len(attr); + unsigned int atype = mnl_attr_get_type(attr); + unsigned int desc_idx = in_array ? 0 : atype; + void *adata = mnl_attr_get_payload(attr); + const struct pretty_nla_desc *adesc; + const char *prefix = " "; + bool nested; + + adesc = (desc && desc_idx < ndesc) ? &desc[desc_idx] : NULL; + nested = (adesc && (adesc->format == NLA_NESTED || + adesc->format == NLA_ARRAY)) || + (attr->nla_type & NLA_F_NESTED); + if (err_offset >= 0 && + err_offset < (nested ? NLA_HDRLEN : attr->nla_len)) { + prefix = "===>"; + if (err_offset) + fprintf(stderr, + "ethtool: bad_attr inside an attribute (offset %d)\n", + err_offset); + } + if (adesc && adesc->name && !in_array) + printf("%s%*s%s", prefix, level * __INDENT, "", adesc->name); + else + printf("%s%*s[%u]", prefix, level * __INDENT, "", atype); + + if (nested) { + struct nlattr *child; + int ret = 0; + + putchar('\n'); + mnl_attr_for_each_nested(child, attr) { + bool array = adesc && adesc->format == NLA_ARRAY; + unsigned int child_off; + + child_off = (const char *)child - (const char *)attr; + ret = pretty_print_attr(child, + adesc ? adesc->children : NULL, + adesc ? adesc->n_children : 0, + level + 1, + err_offset - child_off, array); + if (ret < 0) + break; + } + + return ret; + } + + printf(" = "); + switch(adesc ? adesc->format : NLA_BINARY) { + case NLA_U8: + printf("%u", mnl_attr_get_u8(attr)); + break; + case NLA_U16: + printf("%u", mnl_attr_get_u16(attr)); + break; + case NLA_U32: + printf("%u", mnl_attr_get_u32(attr)); + break; + case NLA_U64: + printf("%" PRIu64, mnl_attr_get_u64(attr)); + break; + case NLA_X8: + printf("0x%02x", mnl_attr_get_u8(attr)); + break; + case NLA_X16: + printf("0x%04x", mnl_attr_get_u16(attr)); + break; + case NLA_X32: + printf("0x%08x", mnl_attr_get_u32(attr)); + break; + case NLA_X64: + printf("%" PRIx64, mnl_attr_get_u64(attr)); + break; + case NLA_S8: + printf("%d", (int)mnl_attr_get_u8(attr)); + break; + case NLA_S16: + printf("%d", (int)mnl_attr_get_u16(attr)); + break; + case NLA_S32: + printf("%d", (int)mnl_attr_get_u32(attr)); + break; + case NLA_S64: + printf("%" PRId64, (int64_t)mnl_attr_get_u64(attr)); + break; + case NLA_STRING: + printf("\"%.*s\"", alen, (const char *)adata); + break; + case NLA_FLAG: + printf("true"); + break; + case NLA_BOOL: + printf("%s", mnl_attr_get_u8(attr) ? "on" : "off"); + break; + case NLA_U8_ENUM: + case NLA_U32_ENUM: { + uint32_t val; + + if (adesc->format == NLA_U8_ENUM) + val = mnl_attr_get_u8(attr); + else + val = mnl_attr_get_u32(attr); + + if (adesc && val < adesc->n_names && adesc->names[val]) + printf("%s", adesc->names[val]); + else + printf("%u", val); + break; + } + default: + if (alen <= __DUMP_LINE) + __print_binary_short(adata, alen); + else + __print_binary_long(adata, alen, level); + } + putchar('\n'); + + return 0; +} + +static int pretty_print_nlmsg(const struct nlmsghdr *nlhdr, + unsigned int payload_offset, + const struct pretty_nla_desc *desc, + unsigned int ndesc, unsigned int err_offset) +{ + const struct nlattr *attr; + int attr_offset; + int ret; + + mnl_attr_for_each(attr, nlhdr, payload_offset) { + attr_offset = (const char *)attr - (const char *)nlhdr; + ret = pretty_print_attr(attr, desc, ndesc, 1, + err_offset - attr_offset, false); + if (ret < 0) + return ret; + } + + return 0; +} + +int pretty_print_genlmsg(const struct nlmsghdr *nlhdr, + const struct pretty_nlmsg_desc *desc, + unsigned int ndesc, unsigned int err_offset) +{ + const struct pretty_nlmsg_desc *msg_desc; + const struct genlmsghdr *genlhdr; + + if (mnl_nlmsg_get_payload_len(nlhdr) < GENL_HDRLEN) { + fprintf(stderr, "ethtool: message too short (%u bytes)\n", + nlhdr->nlmsg_len); + return -EINVAL; + } + genlhdr = mnl_nlmsg_get_payload(nlhdr); + msg_desc = (desc && genlhdr->cmd < ndesc) ? &desc[genlhdr->cmd] : NULL; + if (msg_desc && msg_desc->name) + printf(" %s\n", msg_desc->name); + else + printf(" [%u]\n", genlhdr->cmd); + + return pretty_print_nlmsg(nlhdr, GENL_HDRLEN, + msg_desc ? msg_desc->attrs : NULL, + msg_desc ? msg_desc->n_attrs : 0, err_offset); +} + +static void rtm_link_summary(const struct ifinfomsg *ifinfo) +{ + if (ifinfo->ifi_family) + printf(" family=%u", ifinfo->ifi_family); + if (ifinfo->ifi_type) + printf(" type=0x%04x", ifinfo->ifi_type); + if (ifinfo->ifi_index) + printf(" ifindex=%d", ifinfo->ifi_index); + if (ifinfo->ifi_flags) + printf(" flags=0x%x", ifinfo->ifi_flags); + if (ifinfo->ifi_change) + printf(" change=0x%x", ifinfo->ifi_change); +} + +int pretty_print_rtnlmsg(const struct nlmsghdr *nlhdr, unsigned int err_offset) +{ + const unsigned int idx = (nlhdr->nlmsg_type - RTM_BASE) / 4; + const struct pretty_nlmsg_desc *msg_desc = NULL; + unsigned int hdrlen = USHRT_MAX; + + if (nlhdr->nlmsg_type < rtnl_msg_n_desc) + msg_desc = &rtnl_msg_desc[nlhdr->nlmsg_type]; + if (idx < rtnl_msghdr_n_len) + hdrlen = rtnl_msghdr_lengths[idx]; + if (hdrlen < USHRT_MAX && mnl_nlmsg_get_payload_len(nlhdr) < hdrlen) { + fprintf(stderr, "ethtool: message too short (%u bytes)\n", + nlhdr->nlmsg_len); + return -EINVAL; + } + if (msg_desc && msg_desc->name) + printf(" %s", msg_desc->name); + else + printf(" [%u]", nlhdr->nlmsg_type); + if (idx == (RTM_NEWLINK - RTM_BASE) / 4) + rtm_link_summary(mnl_nlmsg_get_payload(nlhdr)); + putchar('\n'); + if (hdrlen == USHRT_MAX) + return 0; + + return pretty_print_nlmsg(nlhdr, hdrlen, + msg_desc ? msg_desc->attrs : NULL, + msg_desc ? msg_desc->n_attrs : 0, err_offset); +} |