root/lib/libsysdecode/netlink.c
/*
 * Copyright (c) 2026 Ishan Agrawal
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <sys/param.h>
#include <netlink/netlink.h>

#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>

#include "sysdecode.h"

/*
 * Decodes a buffer as a Netlink message stream.
 *
 * Returns true if the data was successfully decoded as Netlink.
 * Returns false if the data is malformed, allowing the caller
 * to fallback to a standard hex/string dump.
 */
bool
sysdecode_netlink(FILE *fp, const void *buf, size_t len)
{
        const struct nlmsghdr *nl = buf;
        size_t remaining = len;
        bool first = true;

        /* Basic sanity check: Buffer must be at least one header size. */
        if (remaining < sizeof(struct nlmsghdr))
                return (false);

        /* * Protocol Sanity Check:
         * The first message length must be valid (>= header) and fit
         * inside the provided buffer snapshot.
         */
        if (nl->nlmsg_len < sizeof(struct nlmsghdr) || nl->nlmsg_len > remaining)
                return (false);

        fprintf(fp, "netlink{");

        while (remaining >= sizeof(struct nlmsghdr)) {
                if (!first)
                        fprintf(fp, ",");

                /* Safety check for current message. */
                if (nl->nlmsg_len < sizeof(struct nlmsghdr) ||
                    nl->nlmsg_len > remaining) {
                        fprintf(fp, "<truncated>");
                        break;
                }

                fprintf(fp, "len=%u,type=", nl->nlmsg_len);

                /* Decode Standard Message Types. */
                switch (nl->nlmsg_type) {
                case NLMSG_NOOP:
                        fprintf(fp, "NLMSG_NOOP");
                        break;
                case NLMSG_ERROR:
                        fprintf(fp, "NLMSG_ERROR");
                        break;
                case NLMSG_DONE:
                        fprintf(fp, "NLMSG_DONE");
                        break;
                case NLMSG_OVERRUN:
                        fprintf(fp, "NLMSG_OVERRUN");
                        break;
                default:
                        fprintf(fp, "%u", nl->nlmsg_type);
                        break;
                }

                fprintf(fp, ",flags=");
                /* TODO: decode flags symbolically using sysdecode_mask. */
                fprintf(fp, "0x%x", nl->nlmsg_flags);

                fprintf(fp, ",seq=%u,pid=%u", nl->nlmsg_seq, nl->nlmsg_pid);

                /* Handle Alignment (Netlink messages are 4-byte aligned). */
                size_t aligned_len = NLMSG_ALIGN(nl->nlmsg_len);
                if (aligned_len > remaining)
                        remaining = 0;
                else
                        remaining -= aligned_len;

                nl = (const struct nlmsghdr *)(const void *)((const char *)nl + aligned_len);
                first = false;
        }

        fprintf(fp, "}");
        return (true);
}