> Linux编程 >

linux内核编程之netlink

上一节说到proc文件系统,这是用户态和内核态通信的一种方法,本节将要说到另外一种通信的方法,该方法相比于其他的内核和用户通信有时在于:
Netlink相对于其他的通信机制具有以下优点:
    1.使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。
    2.Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。
    3.Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。
    4.Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。
 
建立netlink会话如下:
1.在内核使用netlink_kernel_create()创建套接字,指明接受函数。
2.在用户态创建套接字,并将进程ID发送至内核空间。
3.内核接受函数收到用户空间进程ID。
4.用户空间与内核空间可以通信。
 
需要详细描述的内容太多,下面直接给一个例子算了,以后有时间在细说,
 
下面是内核部分代码:
 
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h> //LINUX_VERSION_CODE
 
#include <net/sock.h>   //struct sock *
#include <linux/netlink.h> //netlink_kernel_create()
#include <linux/sched.h>
 
#include "netlink_k.h"
 
static struct sock  *nlfd;
static int          pid;
 
int netlink_deliver(uint16_t type, void *data, unsigned short dlen)
{
    int             ret, len;
    char            *old_tail;
    struct sk_buff  *skb = NULL;
    struct nlmsghdr *nlh = NULL;
 
    if (0 == pid) {
        return 0;
    }
 
    len = NLMSG_SPACE(dlen + sizeof(unsigned short));
 
    skb = alloc_skb(len, GFP_ATOMIC);
    if (NULL == skb) {
        printk(KERN_ALERT"alloc skb failed!\n");
        goto nlmsg_failure;
    }
    old_tail = (char *)skb->tail;
 
    nlh = NLMSG_PUT(skb, 0, 0, type, len - sizeof(*nlh));
 
    memcpy((char *)NLMSG_DATA(nlh), &dlen, sizeof(unsigned short));
    memcpy((char *)NLMSG_DATA(nlh) + sizeof(unsigned short), data, dlen);
 
    nlh->nlmsg_len = (char *)skb->tail - (char *)old_tail;
    NETLINK_CB(skb).dst_group = 0;
 
    printk(KERN_ALERT"Send: data: %s, dlen = %d, nlh->nlmsg_len: %d\n", (char*)data, (int)dlen, nlh->nlmsg_len);
    ret = netlink_unicast(nlfd, skb, pid, MSG_DONTWAIT);
    return ret;
 
nlmsg_failure:
    if(skb)
        kfree_skb(skb);
    return -1;
}
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
void nlsock_receive_process(struct sock *sk, int len)
{
    struct sk_buff          *skb = NULL;
    struct nlmsghdr         *nlh = NULL;
    char                info[] = "Haha, welcome to Kernel!";
    unsigned char       data[MAX_PACKET];
    unsigned short      dlen;
 
    while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
        nlh = (struct nlmsghdr *)skb->data;
#else
void nlsock_receive_process(struct sk_buff *skb)
{
    struct nlmsghdr     *nlh = NULL;
    char                info[] = "Haha, welcome to Kernel!";
    unsigned char       *data = NULL;
    unsigned short      dlen;
 
    nlh = nlmsg_hdr(skb);
 
#endif
    printk(KERN_ALERT"===%s===\n", __func__);
 
    pid = nlh->nlmsg_pid;
    memcpy(&dlen, (char *)NLMSG_DATA(nlh), sizeof(unsigned short));
 
    printk(KERN_ALERT"dlen: %d\n", dlen);
    data = (char *)NLMSG_DATA(nlh) + sizeof(unsigned short);
    
    //sln_dump_pkt(NLMSG_DATA(nlh), 100);
    printk(KERN_ALERT"Receive netlink data: %s, nlh->nlmsg_len = %d\n",
            data, nlh->nlmsg_len);
 
    netlink_deliver(NL_FRAMEIO, info, strlen(info));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
    }
#endif
}
 
static int __init nlk_init(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
    nlfd = netlink_kernel_create(NL_FRAMEIO,
                                0,
                                nlsock_receive_process,
                                THIS_MODULE);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,5)
    struct netlink_kernel_cfg cfg = {
        .input = nlsock_receive_process,
    };
    nlfd = netlink_kernel_create(&init_net,
                                NL_FRAMEIO,
                                THIS_MODULE,
                                &cfg);
#else
    nlfd = netlink_kernel_create(&init_net,
                                NL_FRAMEIO,
                                0,
                                nlsock_receive_process,
                                NULL,
                                THIS_MODULE);
#endif
 
    printk(KERN_ALERT"===%s===\n", __func__);
 
    if (NULL == nlfd) {
        printk(KERN_ALERT"Can't create netlink sock!\n");
        return -1;
    }
 
    return 0;
}
 
static void __exit nlk_exit(void)
{
    printk(KERN_ALERT"===%s===\n", __func__);
 
    if (NULL != nlfd) {
        netlink_kernel_release(nlfd);
        nlfd = NULL;
    }
}
 
module_init(nlk_init);
module_exit(nlk_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("sln");
下面是用户态部分代码:
 
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
 
#include <linux/netlink.h>
#include <linux/socket.h>
 
#include "netlink_u.h"
 
struct sockaddr_nl  local_addr, dest_addr;
int                 sockfd;
 
int bcm_netlink_init(void)
{
    sockfd = socket(PF_NETLINK, SOCK_RAW, NL_FRAMEIO);
    if (sockfd < 0) {
        fprintf(stderr, "sockfd: %s\n", strerror(errno));
        return -1;
    }
 
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.nl_family  = AF_NETLINK;
    local_addr.nl_pid     = getpid();     /* self pid */
    local_addr.nl_groups  = 0;            /* not in mcast groups */
 
 
    if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
        fprintf(stderr, "bind: %s\n", strerror(errno));
        close(sockfd);
        return -1;
    }
 
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid    = 0;    /* To Linux Kernel */
    dest_addr.nl_groups = 0;    /* unicast */
 
    return 0;
}
 
int bcm_netlink_send(char *data, unsigned short dlen)
{
    int                 len;
    struct iovec        iov;
    struct msghdr       msg;
    struct nlmsghdr     *nlh = NULL;
 
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(dlen + sizeof(unsigned short)));
    if (NULL == nlh) {
        printf("malloc failed!\n");
        return -1;
    }
 
    /* Fill the netlink message header */
    memset(nlh, 0, sizeof(struct nlmsghdr));
//    nlh->nlmsg_len      = NLMSG_SPACE(MAX_PACKET);
    nlh->nlmsg_len      = NLMSG_SPACE(dlen + sizeof(unsigned short));
    nlh->nlmsg_pid      = getpid();  /* self pid */
    nlh->nlmsg_flags    = 0;
 
    /* Fill in the netlink message payload */
    memcpy(NLMSG_DATA(nlh), &dlen, sizeof(unsigned short));
    memcpy((char *)NLMSG_DATA(nlh) + sizeof(unsigned short), data, dlen);
 
    /*
     * if not two line below, send will failure. Very IMPORTANT !
     */
    memset(&iov, 0, sizeof(struct iovec));
    memset(&msg, 0, sizeof(struct msghdr));
 
    iov.iov_base    = (void *)nlh;
    iov.iov_len     = nlh->nlmsg_len;
    msg.msg_name    = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov     = &iov;
    msg.msg_iovlen  = 1;
 
    while ((len = sendmsg(sockfd, &msg, 0)) < 0) {
        if (errno == EINTR) {
            continue;
        }
        fprintf(stderr, "sendmsg: %s, len = %d\n", strerror(errno), len);
        return -1;
    }
    fprintf(stderr, "senddata: %s, sizeof(struct nlmsghdr) = %d, mydatalen: %d, sendlen = %d\n",
            data, (int)sizeof(struct nlmsghdr), (int)dlen, len);
 
    return 0;
}
 
int bcm_netlink_recv()
{
    struct sockaddr_nl  peer_addr;
    struct nlmsghdr     *nlh = NULL;
    struct iovec        iov;
    int                 recvlen;
    struct msghdr       msg;
    char                *data = NULL;
    unsigned short      dlen;
 
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(dlen + sizeof(unsigned short)));
    if (NULL == nlh) {
        printf("malloc failed!\n");
        return -1;
    }
 
    /* Fill the netlink message header */
    memset(nlh, 0, sizeof(struct nlmsghdr));
    nlh->nlmsg_len      = NLMSG_SPACE(MAX_PACKET);
    nlh->nlmsg_pid      = getpid();  /* self pid */
    nlh->nlmsg_flags    = 0;
 
    /*
     * if not two line below ,send will failure. Very IMPORTANT!
     */
    memset(&iov, 0, sizeof(struct iovec));
    memset(&msg, 0, sizeof(struct msghdr));
 
    iov.iov_base    = (void *)nlh;
    iov.iov_len     = nlh->nlmsg_len;
    msg.msg_name    = (void *)&peer_addr;
    msg.msg_namelen = sizeof(peer_addr);
    msg.msg_iov     = &iov;
    msg.msg_iovlen  = 1;
 
    /* A loop to read message from kernel */
    for (;;) {
        memset(nlh, 0, nlh->nlmsg_len);
        if ((recvlen = recvmsg(sockfd, &msg, 0)) < 0) {
            fprintf(stderr, "recvmsg: %s\n", strerror(errno));
            continue;
        }
 
        memcpy(&dlen, (char *)NLMSG_DATA(nlh), sizeof(unsigned short));
        data = (char *)NLMSG_DATA(nlh) + sizeof(unsigned short);
 
        fprintf(stdout, "Received message payload: %s, recvlen: %d, dlen: %d\n",
                data,  recvlen, dlen);
    }
}
 
int main(int argc, char* argv[])
{
    int         ret;
    pthread_t   pid;
    char        sendbuf[MAX_PACKET] = "info from user!";
 
    ret = bcm_netlink_init();
    if (ret < 0) {
        goto err;
    }
 
    if (pthread_create(&pid, NULL, (void *)bcm_netlink_recv, NULL) < 0) {
        fprintf(stderr, "Create receive thread failed!\n");
        goto err;
    }
 
    for (;;) {
        sleep(1);
        bcm_netlink_send(sendbuf, strlen(sendbuf));
    }
//    pthread_join(pid, NULL);
err:
    if (sockfd > 0) {
        close(sockfd);
    }
 
    return 0;
}
 
 
 
(责任编辑:IT)