linux内核编程之netlink

【版权声明:转载请保留出处:blog.csdn.net/gentleliu。邮箱:shallnew*163.com】


上一节说到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;
}


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值