内核模块可以主动用netlink给用户space发消息吗?

Can kernel module take initiative to send message to user space with netlink?

我正在尝试 运行 以下从 here 复制的代码。我用旧内核版本对 运行 做了一些改动。

当我插入内核模块时,nlmsg_multicast() 失败并在 /var/log/messages 中记录为 nlmsg_multicast() error: -3。 当 运行ning 用户 space 程序时,socket() 失败。

我真正想做的是,

因为,可能会发生用户 space 中没有进程可用于响应事件的情况,即使在这种情况下,模块也必须发送事件并等待一段时间才能响应。

是否可以从内核模块向用户 space 中的进程发送第一条消息?我该怎么做?

内核模块代码:

生成文件

obj-m   := foo.o

KDIR    := /lib/modules/$(shell uname -r)/build
PWD    := $(shell pwd)

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

foo.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>

/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYGRP 21

static struct sock *nl_sk = NULL;

static void send_to_user(void)
{
        struct sk_buff *skb;
        struct nlmsghdr *nlh;
        char *msg = "Hello from kernel";
        int msg_size = strlen(msg) + 1;
        int res;

        pr_info("Creating skb.\n");
        skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_KERNEL);
        if (!skb) {
                pr_err("Allocation failure.\n");
                return;
        }

        nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
        strcpy(nlmsg_data(nlh), msg);

        pr_info("Sending skb.\n");
        res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
        if (res < 0)
                pr_info("nlmsg_multicast() error: %d\n", res);
        else
                pr_info("Success.\n");
  }

static int __init hello_init(void)
{
        pr_info("Inserting hello module.\n");

        //nl_sk = netlink_kernel_create(&init_net, MYPROTO, NULL);
        nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, NULL, NULL, THIS_MODULE);
        if (!nl_sk) {
                pr_err("Error creating socket.\n");
                return -10;
        }
        send_to_user();

        netlink_kernel_release(nl_sk);
        return 0;
}

static void __exit hello_exit(void)
{
        pr_info("Exiting hello module.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

用户space程序: (用gcc somename.c编译)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>

/* Protocol family, consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYMGRP 21

int open_netlink(void)
{
        int sock;
        struct sockaddr_nl addr;
        int group = MYMGRP;

        sock = socket(AF_NETLINK, SOCK_RAW, MYPROTO);
        if (sock < 0) {
                printf("sock < 0.\n");
                return sock;
        }

        memset((void *) &addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
        addr.nl_pid = getpid();
        /* This doesn't work for some reason. See the setsockopt() below. */
        addr.nl_groups = MYMGRP;

        if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                printf("bind < 0.\n");
                return -1;
        }
        /*
         * 270 is SOL_NETLINK. See
         * http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314
         * and
         * 
         */
        /*if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
                printf("setsockopt < 0\n");
                return -1;
        }*/

        return sock;
}

void read_event(int sock)
{
        struct sockaddr_nl nladdr;
        struct msghdr msg;
        struct iovec iov;
        char buffer[65536];
        int ret;

        iov.iov_base = (void *) buffer;
        iov.iov_len = sizeof(buffer);
        msg.msg_name = (void *) &(nladdr);
        msg.msg_namelen = sizeof(nladdr);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;

        printf("Ok, listening.\n");
        ret = recvmsg(sock, &msg, 0);
        if (ret < 0)
                printf("ret < 0.\n");
        else
                printf("Received message payload: %s\n", NLMSG_DATA((struct nlmsghdr *) &buffer));
}

int main(int argc, char *argv[])
{
        int nls;

        nls = open_netlink();
        if (nls < 0)
                return nls;

        while (1)
                read_event(nls);

        return 0;
}

感谢您的宝贵时间!

这看起来像是糟糕的设计(因为上层应该依赖于下层,而不是相反)。但是,如果您确信内核在用户空间可以获取信息之前无法闲置或使用默认配置运行,那么首先也要安装 this tool (might want to read the core guide),然后执行如下操作:

内核:

#include <linux/module.h>
#include <linux/kernel.h>
#include <net/netlink.h>
#include <net/net_namespace.h>

#define MYPROTO NETLINK_USERSOCK
#define MYGRP 22

static struct sock *nl_sk;
static struct timer_list timer;

void try_send(unsigned long data)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    char *msg = "Hello from kernel";
    int msg_size = strlen(msg) + 1;
    int res;

    skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1), GFP_ATOMIC);
    if (!skb) {
        pr_err("Allocation failure.\n");
        return;
    }

    nlh = nlmsg_put(skb, 0, 1, NLMSG_DONE, msg_size + 1, 0);
    strcpy(nlmsg_data(nlh), msg);

    pr_info("Sending multicast.\n");
    res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_ATOMIC);
    if (res < 0) {
        pr_info("nlmsg_multicast() error: %d. Will try again later.\n", res);
        /* Wait 1 second. */
        mod_timer(&timer, jiffies + msecs_to_jiffies(1000));
    } else {
        pr_info("Success.\n");
    }
}

static int handle_netlink_message(struct sk_buff *skb_in, struct nlmsghdr *nl_hdr)
{
    char *hello;
    hello = NLMSG_DATA(nl_hdr);
    pr_info("Userspace says '%s.'\n", hello);
    return 0;
}

static void receive_answer(struct sk_buff *skb)
{
    netlink_rcv_skb(skb, &handle_netlink_message);
}

static int __init hello_init(void)
{
    pr_info("Inserting module.\n");

    nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, receive_answer, NULL, THIS_MODULE);
    if (!nl_sk) {
        pr_err("Error creating socket.\n");
        return -10;
    }

    init_timer(&timer);
    timer.function = try_send;
    timer.expires = jiffies + 1000;
    timer.data = 0;
    add_timer(&timer);

    return 0;
}

static void __exit hello_exit(void)
{
    del_timer_sync(&timer);
    netlink_kernel_release(nl_sk);
    pr_info("Exiting module.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

用户(我正在使用 gcc usr.c -I/usr/include/libnl3 -lnl-3 -Wall 进行编译,您的情况可能会有所不同):

#include <netlink/netlink.h>
#include <netlink/msg.h>

#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 22

struct nl_sock *sk;

void respond_to_kernel(void)
{
    char *response = "foo bar";
    int error;

    error = nl_send_simple(sk, 12345, NLMSG_DONE, response, strlen(response) + 1);
    if (error < 0) {
        printf("nl_send_simple() threw errcode %d.\n", error);
        printf("libnl's message: %s", nl_geterror(error));
    } else {
        printf("Responded %d bytes.\n", error);
    }
}

int receive_kernel_request(struct nl_msg *msg, void *arg)
{
    char *hello;

    hello = nlmsg_data(nlmsg_hdr(msg));
    printf("Kernel says '%s'.\n", hello);
    respond_to_kernel();

    return 0;
}

int prepare_socket(void)
{
    int error;

    sk = nl_socket_alloc();
    if (!sk) {
        printf("nl_socket_alloc() returned NULL.\n");
        return -1;
    }

    nl_socket_disable_seq_check(sk);

    error = nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receive_kernel_request, NULL);
    if (error < 0) {
        printf("Could not register callback function. Errcode: %d\n", error);
        goto fail;
    }

    error = nl_connect(sk, MYPROTO);
    if (error < 0) {
        printf("Connection failed: %d\n", error);
        goto fail;
    }

    error = nl_socket_add_memberships(sk, MYMGRP, 0);
    if (error) {
        printf("Could not register to the multicast group. %d\n", error);
        goto fail;
    }

    return 0;

fail:
    printf("libnl's message: %s\n", nl_geterror(error));
    nl_socket_free(sk);
    return error;
}

int wait_for_kernel_message(void)
{
    int error;

    printf("Waiting for kernel request...\n");
    error = nl_recvmsgs_default(sk);
    if (error < 0) {
        printf("nl_send_simple() threw errcode %d.\n", error);
        printf("libnl's message: %s\n", nl_geterror(error));
        return error;
    }

    return 0;
}

void destroy_socket(void)
{
    nl_socket_free(sk);
}

int main(int argc, char *argv[])
{
    int error;

    error = prepare_socket();
    if (error)
        return error;

    error = wait_for_kernel_message();
    destroy_socket();
    return error;
}

在内核 3.2 上测试。 (抱歉;这是我目前最低的。)

这是一个没有 libnl 的例子。
我将所有功能放在一个文件中。编码风格不好。仅供举例。
希望对您有所帮助。

我已经测试了 Ubuntu 15.04 中的代码,其内核是内核 3.19.0-15。

内核模块
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>

static struct timer_list timer;

/* Code based on  */    
/**
 * This callback runs whenever the socket receives messages.
 * We don't use it now, but Linux complains if we don't define it.
 */
static int hello(struct sk_buff *skb, struct genl_info *info)
{
        pr_info("Received a message in kernelspace.\n");
        return 0;
}

/**
 * Attributes are fields of data your messages will contain.
 * The designers of Netlink really want you to use these instead of just dumping
 * data to the packet payload... and I have really mixed feelings about it.
 */
enum attributes {
        /*
         * The first one has to be a throwaway empty attribute; I don't know
         * why.
         * If you remove it, ATTR_HELLO (the first one) stops working, because
         * it then becomes the throwaway.
         */
        ATTR_DUMMY,
        ATTR_HELLO,
        ATTR_FOO,

        /* This must be last! */
        __ATTR_MAX,
};

/**
 * Here you can define some constraints for the attributes so Linux will
 * validate them for you.
 */
static struct nla_policy policies[] = {
                [ATTR_HELLO] = { .type = NLA_STRING, },
                [ATTR_FOO] = { .type = NLA_U32, },
};

/**
 * Message type codes. All you need is a hello sorta function, so that's what
 * I'm defining.
 */
enum commands {
        COMMAND_HELLO,

        /* This must be last! */
        __COMMAND_MAX,
};

/**
 * Actual message type definition.
 */
struct genl_ops ops[] = {
        {
                .cmd = COMMAND_HELLO,
                .flags = 0,
                .policy = policies,
                .doit = hello,
                .dumpit = NULL,
        },
};

/**
 * A Generic Netlink family is a group of listeners who can and want to speak
 * your language.
 * Anyone who wants to hear your messages needs to register to the same family
 * as you.
 */
struct genl_family family = {
                .id = GENL_ID_GENERATE,
                .hdrsize = 0,
                .name = "PotatoFamily",
                .version = 1,
                .maxattr = __ATTR_MAX,
};

/**
 * And more specifically, anyone who wants to hear messages you throw at
 * specific multicast groups need to register themselves to the same multicast
 * group, too.
 */
struct genl_multicast_group groups[] = {
        { .name = "PotatoGroup" },
};

void send_multicast(unsigned long arg)
{
        struct sk_buff *skb;
        void *msg_head;
        unsigned char *msg = "TEST";
        int error;

        pr_info("----- Running timer -----\n");

        pr_info("Newing message.\n");
        skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb) {
                pr_err("genlmsg_new() failed.\n");
                goto end;
        }

        pr_info("Putting message.\n");
        msg_head = genlmsg_put(skb, 0, 0, &family, 0, COMMAND_HELLO);
        if (!msg_head) {
                pr_err("genlmsg_put() failed.\n");
                kfree_skb(skb);
                goto end;
        }

        pr_info("Nla_putting string.\n");
        error = nla_put_string(skb, ATTR_HELLO, msg);
        if (error) {
                pr_err("nla_put_string() failed: %d\n", error);
                kfree_skb(skb);
                goto end;
        }

        pr_info("Nla_putting integer.\n");
        error = nla_put_u32(skb, ATTR_FOO, 12345);
        if (error) {
                pr_err("nla_put_u32() failed: %d\n", error);
                kfree_skb(skb);
                goto end;
        }

        pr_info("Ending message.\n");
        genlmsg_end(skb, msg_head);

        pr_info("Multicasting message.\n");
        /*
         * The family has only one group, so the group ID is just the family's
         * group offset.
         * mcgrp_offset is supposed to be private, so use this value for debug
         * purposes only.
         */
        pr_info("The group ID is %u.\n", family.mcgrp_offset);
        error = genlmsg_multicast_allns(&family, skb, 0, 0, GFP_KERNEL);
        if (error) {
                pr_err("genlmsg_multicast_allns() failed: %d\n", error);
                pr_err("(This can happen if nobody is listening. "
                                "Because it's not that unexpected, "
                                "you might want to just ignore this error.)\n");
                goto end;
        }

        pr_info("Success.\n");
end:
        mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}

static int init_socket(void)
{
        int error;

        pr_info("Registering family.\n");
        error = genl_register_family_with_ops_groups(&family, ops, groups);
        if (error)
                pr_err("Family registration failed: %d\n", error);

        return error;
}

static void initialize_timer(void)
{
        pr_info("Starting timer.\n");

        init_timer(&timer);
        timer.function = send_multicast;
        timer.expires = 0;
        timer.data = 0;

        mod_timer(&timer, jiffies + msecs_to_jiffies(2000));
}

static int __init hello_init(void)
{
        int error;

        error = init_socket();
        if (error)
                return error;

        initialize_timer();

        pr_info("Hello module registered.\n");
        return 0;
}

static void __exit hello_exit(void)
{
        del_timer_sync(&timer);
        genl_unregister_family(&family);
        pr_info("Hello removed.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
内核模块 Makefile
PWD := $(shell pwd) 
KVERSION := $(shell uname -r)
KERNEL_DIR = /usr/src/linux-headers-$(KVERSION)/

MODULE_NAME = genl_kern_grp
obj-m := $(MODULE_NAME).o

all:
    make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
    make -C $(KERNEL_DIR) M=$(PWD) clean
用户代码 - main.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>

#include <linux/genetlink.h>

/* Code based on libnl-3 */
/* Code based on  */
/* Code based on http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html */
/* Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html */


/* Based on libnl-3 attr.h */
/**
 * @ingroup attr
 * Basic attribute data types
 *
 * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
 */
enum {
    NLA_UNSPEC, /**< Unspecified type, binary data chunk */
    NLA_U8,     /**< 8 bit integer */
    NLA_U16,    /**< 16 bit integer */
    NLA_U32,    /**< 32 bit integer */
    NLA_U64,    /**< 64 bit integer */
    NLA_STRING, /**< NUL terminated character string */
    NLA_FLAG,   /**< Flag */
    NLA_MSECS,  /**< Micro seconds (64bit) */
    NLA_NESTED, /**< Nested attributes */
    NLA_NESTED_COMPAT,
    NLA_NUL_STRING,
    NLA_BINARY,
    NLA_S8,
    NLA_S16,
    NLA_S32,
    NLA_S64,
    __NLA_TYPE_MAX,
};

#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)

/**
 * @ingroup attr
 * Attribute validation policy.
 *
 * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
 */
struct nla_policy {
    /** Type of attribute or NLA_UNSPEC */
    uint16_t    type;

    /** Minimal length of payload required */
    uint16_t    minlen;

    /** Maximal length of payload allowed */
    uint16_t    maxlen;
};


/**
 * Attributes and commands have to be the same as in kernelspace, so you might
 * want to move these enums to a .h and just #include that from both files.
 */
enum attributes {
        ATTR_DUMMY,
        ATTR_HELLO,
        ATTR_FOO,

        /* This must be last! */
        __ATTR_MAX,
};

enum commands {
        COMMAND_HELLO,

        /* This must be last! */
        __COMMAND_MAX,
};

/* Generic macros for dealing with netlink sockets. Might be duplicated
 * elsewhere. It is recommended that commercial grade applications use
 * libnl or libnetlink and use the interfaces provided by the library
 */
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))

/* Family string */
#define GEN_FAMILY_STR  "PotatoFamily"
#define GEN_ML_GRP_STR  "PotatoGroup"

/* SOL_NETLINK is only defined in <kernel src>/include/linux/socket.h
 * It is not defined in <kernel src>/include/uapi/linux/socket.h
 * Thus, copy the define to here if we don't include kernel header
 */
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif

/**
 * @ingroup attr
 * Iterate over a stream of attributes
 * @arg pos loop counter, set to current attribute
 * @arg head    head of attribute stream
 * @arg len length of attribute stream
 * @arg rem initialized to len, holds bytes currently remaining in stream
 */
#define nla_for_each_attr(pos, head, len, rem) \
    for (pos = head, rem = len; \
         nla_ok(pos, rem); \
         pos = nla_next(pos, &(rem)))

/**
 * @ingroup attr
 * Iterate over a stream of nested attributes
 * @arg pos loop counter, set to current attribute
 * @arg nla attribute containing the nested attributes
 * @arg rem initialized to len, holds bytes currently remaining in stream
 */
#define nla_for_each_nested(pos, nla, rem) \
    for (pos = nla_data(nla), rem = nla_len(nla); \
         nla_ok(pos, rem); \
         pos = nla_next(pos, &(rem)))


/* Variables used for netlink */
int nl_fd;  /* netlink socket's file descriptor */
struct sockaddr_nl nl_address; /* netlink socket address */
int nl_family_id; /* The family ID resolved by the netlink controller for this userspace program */
int nl_rxtx_length; /* Number of bytes sent or received via send() or recv() */
struct nlattr *nl_na; /* pointer to netlink attributes structure within the payload */
struct { /* memory for netlink request and response messages - headers are included */
    struct nlmsghdr n;
    struct genlmsghdr g;
    char buf[256];
} nl_request_msg, nl_response_msg;

/* Base on libnl-3 attr.c */

/**
 * Return type of the attribute.
 * @arg nla     Attribute.
 *
 * @return Type of attribute.
 */
int nla_type(const struct nlattr *nla)
{
    return nla->nla_type & NLA_TYPE_MASK;
}

/**
 * Return pointer to the payload section.
 * @arg nla     Attribute.
 *
 * @return Pointer to start of payload section.
 */
void *nla_data(const struct nlattr *nla)
{
    return (char *) nla + NLA_HDRLEN;
}

/**
 * Return length of the payload .
 * @arg nla     Attribute
 *
 * @return Length of payload in bytes.
 */
int nla_len(const struct nlattr *nla)
{
    return nla->nla_len - NLA_HDRLEN;
}

/**
 * Check if the attribute header and payload can be accessed safely.
 * @arg nla     Attribute of any kind.
 * @arg remaining   Number of bytes remaining in attribute stream.
 *
 * Verifies that the header and payload do not exceed the number of
 * bytes left in the attribute stream. This function must be called
 * before access the attribute header or payload when iterating over
 * the attribute stream using nla_next().
 *
 * @return True if the attribute can be accessed safely, false otherwise.
 */
int nla_ok(const struct nlattr *nla, int remaining)
{
    return remaining >= sizeof(*nla) &&
           nla->nla_len >= sizeof(*nla) &&
           nla->nla_len <= remaining;
}

/**
 * Return next attribute in a stream of attributes.
 * @arg nla     Attribute of any kind.
 * @arg remaining   Variable to count remaining bytes in stream.
 *
 * Calculates the offset to the next attribute based on the attribute
 * given. The attribute provided is assumed to be accessible, the
 * caller is responsible to use nla_ok() beforehand. The offset (length
 * of specified attribute including padding) is then subtracted from
 * the remaining bytes variable and a pointer to the next attribute is
 * returned.
 *
 * nla_next() can be called as long as remainig is >0.
 *
 * @return Pointer to next attribute.
 */
struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
{
    int totlen = NLA_ALIGN(nla->nla_len);

    *remaining -= totlen;
    return (struct nlattr *) ((char *) nla + totlen);
}

static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
    [NLA_U8]    = sizeof(uint8_t),
    [NLA_U16]   = sizeof(uint16_t),
    [NLA_U32]   = sizeof(uint32_t),
    [NLA_U64]   = sizeof(uint64_t),
    [NLA_STRING]    = 1,
    [NLA_FLAG]  = 0,
};

static int validate_nla(const struct nlattr *nla, int maxtype,
            const struct nla_policy *policy)
{
    const struct nla_policy *pt;
    unsigned int minlen = 0;
    int type = nla_type(nla);

    if (type < 0 || type > maxtype)
        return 0;

    pt = &policy[type];

    if (pt->type > NLA_TYPE_MAX)
        return -1;

    if (pt->minlen)
        minlen = pt->minlen;
    else if (pt->type != NLA_UNSPEC)
        minlen = nla_attr_minlen[pt->type];

    if (nla_len(nla) < minlen)
        return -2;

    if (pt->maxlen && nla_len(nla) > pt->maxlen)
        return -3;

    if (pt->type == NLA_STRING) {
        const char *data = nla_data(nla);
        if (data[nla_len(nla) - 1] != '[=12=]')
            return -4;
    }

    return 0;
}

/**
 * Create attribute index based on a stream of attributes.
 * @arg tb      Index array to be filled (maxtype+1 elements).
 * @arg maxtype     Maximum attribute type expected and accepted.
 * @arg head        Head of attribute stream.
 * @arg len     Length of attribute stream.
 * @arg policy      Attribute validation policy.
 *
 * Iterates over the stream of attributes and stores a pointer to each
 * attribute in the index array using the attribute type as index to
 * the array. Attribute with a type greater than the maximum type
 * specified will be silently ignored in order to maintain backwards
 * compatibility. If \a policy is not NULL, the attribute will be
 * validated using the specified policy.
 *
 * @see nla_validate
 * @return 0 on success or a negative error code.
 */
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
          struct nla_policy *policy)
{
    struct nlattr *nla;
    int rem, err;

    memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));

    nla_for_each_attr(nla, head, len, rem) {
        int type = nla_type(nla);

        if (type > maxtype)
            continue;

        if (policy) {
            err = validate_nla(nla, maxtype, policy);
            if (err < 0)
                goto errout;
        }

        if (tb[type])
            fprintf(stderr, "Attribute of type %#x found multiple times in message, "
                  "previous attribute is being ignored.\n", type);

        tb[type] = nla;
    }

    if (rem > 0)
        fprintf(stderr, "netlink: %d bytes leftover after parsing "
               "attributes.\n", rem);

    err = 0;
errout:
    return err;
}

/**
 * Create attribute index based on nested attribute
 * @arg tb      Index array to be filled (maxtype+1 elements).
 * @arg maxtype     Maximum attribute type expected and accepted.
 * @arg nla     Nested Attribute.
 * @arg policy      Attribute validation policy.
 *
 * Feeds the stream of attributes nested into the specified attribute
 * to nla_parse().
 *
 * @see nla_parse
 * @return 0 on success or a negative error code.
 */
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
             struct nla_policy *policy)
{
    return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
}

static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
    [CTRL_ATTR_FAMILY_ID]   = { .type = NLA_U16 },
    [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING,
                    .maxlen = GENL_NAMSIZ },
    [CTRL_ATTR_VERSION] = { .type = NLA_U32 },
    [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 },
    [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 },
    [CTRL_ATTR_OPS]     = { .type = NLA_NESTED },
    [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
};

static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = {
    [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING },
    [CTRL_ATTR_MCAST_GRP_ID]   = { .type = NLA_U32 },
};

int genlctrl_msg_parse(struct nlmsghdr *nlh, int *family_id, char **family_name, 
        int *mcast_id, char **mcast_name)
{
    struct nlattr *tb[CTRL_ATTR_MAX+1];
    struct nlattr *nla_hdr;
    int nla_length;
    int ret = 0;

    nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
    nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;

    if(ret = nla_parse(tb, CTRL_ATTR_MAX, nla_hdr, nla_length, ctrl_policy)) {
        fprintf(stderr, "nla_parse error! ret = %d\n", ret);
        return -1;
    }

    if (tb[CTRL_ATTR_FAMILY_ID])
        *family_id = *(const uint16_t *) nla_data(tb[CTRL_ATTR_FAMILY_ID]);

    if (tb[CTRL_ATTR_FAMILY_NAME])
        *family_name = (char *) nla_data(tb[CTRL_ATTR_FAMILY_NAME]);

    if (tb[CTRL_ATTR_MCAST_GROUPS]) {
        struct nlattr *nla, *grp_attr;
        int remaining, err;

        grp_attr = tb[CTRL_ATTR_MCAST_GROUPS];
        nla_for_each_nested(nla, grp_attr, remaining) {
            struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
            int id = 0;
            char *name = NULL;

            err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla,
                       family_grp_policy);
            if (err < 0) {
                fprintf(stderr, "nla_parse_nested error! err = %d\n", err);
                return -1;
            }

            if (tb[CTRL_ATTR_MCAST_GRP_ID])
                id = *(const uint32_t *) nla_data(tb[CTRL_ATTR_MCAST_GRP_ID]);

            if (tb[CTRL_ATTR_MCAST_GRP_NAME])
                name = (char *) nla_data(tb[CTRL_ATTR_MCAST_GRP_NAME]);

            if (id || name) {
                *mcast_id = id;
                *mcast_name = name;
            }
        }
    }

    return 0;
}

void genlmsg_recv(void) {
    struct nlmsghdr *nlh;
    struct nlattr *tb[__ATTR_MAX];
    struct nlattr *nla_hdr;
    int nla_length;
    int ret = 0;

    while(1)
    {
    memset(&nl_response_msg, 0, sizeof(nl_response_msg));
    nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
    if (nl_rxtx_length < 0) {
        perror("recv()");
        goto out;
    }

    nlh = &nl_response_msg.n;
    nla_hdr = (struct nlattr *)((unsigned char *) nlh + NLMSG_HDRLEN + GENL_HDRLEN);
    nla_length = nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;

    if(ret = nla_parse(tb, __ATTR_MAX-1, nla_hdr, nla_length, NULL)) {
        fprintf(stderr, "nla_parse error! ret = %d\n", ret);
        goto out;
    }

    if (tb[1])
        printf("ATTR_HELLO: len:%u type:%u data:%s\n", tb[1]->nla_len,
                tb[1]->nla_type, (char *)nla_data(tb[1]));
    else
        printf("ATTR_HELLO: null\n");

    if (tb[2])
        printf("ATTR_FOO: len:%u type:%u data:%u\n", tb[2]->nla_len,
                tb[2]->nla_type, *((__u32 *)nla_data(tb[2])));
    else
        printf("ATTR_FOO: null\n");
    }
out:
    return;
}

int main(void) {
    struct nlattr *nla1, *nla2;
    int len, rem, remaining;
    struct nlmsghdr *nlh;
    int family_id;
    char *family_name;
    int mcast_id;
    char *mcast_name;
    int err;

    /* Step 1: Open the socket. Note that protocol = NETLINK_GENERIC */
    nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
    if (nl_fd < 0) {
        perror("socket()");
        return -1;
    }

    /* Step 2: Bind the socket. */
    memset(&nl_address, 0, sizeof(nl_address));
    nl_address.nl_family = AF_NETLINK;
    nl_address.nl_groups = 0;

    if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) {
        perror("bind()");
        goto out;
    }

    /* Step 3: Resolve the family ID corresponding to the string GEN_FAMILY_STR  */
    /* Populate the netlink header */
    nl_request_msg.n.nlmsg_type = GENL_ID_CTRL;
    nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST;
    nl_request_msg.n.nlmsg_seq = 0;
    nl_request_msg.n.nlmsg_pid = getpid();
    nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
    /* Populate the payload's "family header" : which in our case is genlmsghdr */
    nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY;
    nl_request_msg.g.version = 0x1;
    /* Populate the payload's "netlink attributes" */
    nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); /* get location of genl data where to put */
    nl_na->nla_type = CTRL_ATTR_FAMILY_NAME;
    nl_na->nla_len = strlen(GEN_FAMILY_STR) + 1 + NLA_HDRLEN;
    strcpy(NLA_DATA(nl_na), GEN_FAMILY_STR); /* Family name length can be upto 16 chars including [=12=] */

    nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len);

    memset(&nl_address, 0, sizeof(nl_address));
    nl_address.nl_family = AF_NETLINK;

    /* Send the family ID request message to the netlink controller */
    nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len, 
        0, (struct sockaddr *) &nl_address, sizeof(nl_address));
    if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) {
        perror("sendto()");
        goto out;
    }

    /* Wait for the response message */
    nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0);
    if (nl_rxtx_length < 0) {
        perror("recv()");
        goto out;
    }

    /* Validate response message */
    if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) {
        fprintf(stderr, "family ID request : invalid message\n");
        goto out;
    }
    if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { /* error */
        fprintf(stderr, "family ID request : receive error\n");
        goto out;
    }

    /* Step 4: Extract family ID and mcast group ID*/
    nlh = &nl_response_msg.n;
    genlctrl_msg_parse(nlh, &family_id, &family_name, &mcast_id, &mcast_name);
    printf("[INFO] family_id = %d, family_name = %s\n", family_id, family_name);
    printf("[INFO] mcast_id = %d, mcast_name = %s\n", mcast_id, mcast_name);

    /* Step 5: Add to mulitcast group */
    err = setsockopt(nl_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &mcast_id, sizeof(mcast_id));
    if (err < 0) {
        perror ("setsockopt()");
        goto out;
    }

    /* Step 6: Receive multicast data */
    genlmsg_recv();

    /* Step 7: Close the socket and quit */
    close(nl_fd);
    return 0;

out:
    close(nl_fd);
    return -1;
}
用户代码生成文件
PWD := $(shell pwd) 
TARGET := genl_ml
SRC := main.c
HDR_DIR = /usr/include/
LDFLAGS = 

all:
    gcc $(SRC) $(LDFLAGS) -o $(TARGET)
clean:
    rm -fr $(TARGET)