内核模块可以主动用netlink向用户空间发送消息吗?

时间:2016-01-30 06:48:50

标签: sockets linux-kernel kernel-module netlink kernel-mode

我正在尝试运行以下代码,该代码是从here复制的。我用较旧的内核版本进行了一些更改。

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

我到底想做的是,

  • 内核模块创建套接字,无论用户空间中的任何进程如何
  • 内核模块将一些事件发送到用户空间
  • 如果用户空间中的任何进程回复了某个事件,那么内核模块将对该回复进行处理

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

是否可以将第一条消息从内核模块发送到用户空间中的进程?我怎么能这样做?

内核模块代码:

生成文件

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");

用户空间计划: (用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
         * https://stackoverflow.com/questions/17732044/
         */
        /*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;
}

感谢您的时间!

2 个答案:

答案 0 :(得分:4)

这看起来很糟糕(因为上层应该依赖于较低层,而不是相反)。但是如果您确信内核不能闲置或使用默认配置操作,直到用户空间可以获取信息,那么首先安装this tool(也可能想要阅读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上测试过。 (对不起,这是我现在最低的。)

答案 1 :(得分:0)

这是一个没有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 http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */    
/**
 * 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 http://stackoverflow.com/questions/26265453/netlink-multicast-kernel-group/33578010#33578010 */
/* 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] != '\0')
            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 \0 */

    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;
}
用户代码Makefile
PWD := $(shell pwd) 
TARGET := genl_ml
SRC := main.c
HDR_DIR = /usr/include/
LDFLAGS = 

all:
    gcc $(SRC) $(LDFLAGS) -o $(TARGET)
clean:
    rm -fr $(TARGET)
相关问题