netlink快速使用例程(linux-3.10.36)

Linux 专栏收录该内容
32 篇文章 0 订阅

linux-3.10.36版本的内核相比linux-2.6.36版本中netlink的代码有所变化,以前的代码已经不能成功编译了。

netlink是linux内核的一套基于socket的通信机制,那么,只需要知道怎么创建套接字,发送数据,接收数据就行了。

内核层:

初始化操作:

新的netlink_kernel_create()函数只有3个参数了。

/******************************
net:    linux网络命名空间结构体指针
uint:   netlink协议类型
cfg:    netlink内核配置参数结构体指针

返回: sock结构指针
******************************/

static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)

第一个参数:传入一个网络命名空间结构的地址,这块我不怎么清楚,只知道linux的网络命名空间的出现是为了让 用户创建的进程能够与系统分离得更加彻底,从而不需要使用更多的底层虚拟化技术。未深究,调用该函数时就传入&init_net吧,它是在net_namespace.c文件中定义的一个结构体。

第二个参数:是代表netlink协议类型,内核目前定义了如下一些类型:

#define NETLINK_ROUTE       0   /* Routing/device hook              */
#define NETLINK_UNUSED      1   /* Unused number                */
#define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
#define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
#define NETLINK_SOCK_DIAG   4   /* socket monitoring                */
#define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */
#define NETLINK_XFRM        6   /* ipsec */
#define NETLINK_SELINUX     7   /* SELinux event notifications */
#define NETLINK_ISCSI       8   /* Open-iSCSI */
#define NETLINK_AUDIT       9   /* auditing */
#define NETLINK_FIB_LOOKUP  10  
#define NETLINK_CONNECTOR   11
#define NETLINK_NETFILTER   12  /* netfilter subsystem */
#define NETLINK_IP6_FW      13
#define NETLINK_DNRTMSG     14  /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
#define NETLINK_GENERIC     16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
#define NETLINK_ECRYPTFS    19
#define NETLINK_RDMA        20
#define NETLINK_CRYPTO      21  /* Crypto layer */

#define NETLINK_INET_DIAG   NETLINK_SOCK_DIAG

#define MAX_LINKS 32

最多可以定义32中类型,若 uint > MAX_LINKS 则该函数返回NULL,源代码片段如下

__netlink_kernel_create(struct net *net, int unit, struct module *module,
            struct netlink_kernel_cfg *cfg)
{
    struct socket *sock;
    struct sock *sk;
    struct netlink_sock *nlk;
    struct listeners *listeners = NULL;
    struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
    unsigned int groups;

    BUG_ON(!nl_table);

    if (unit < 0 || unit >= MAX_LINKS)
        return NULL;
    ...
}

第三个参数:cfg存放的是netlink内核配置参数,配置参数中的input成员用于处理接收到的消息。该函数的参数变短了,实际是因为放到该结构体中来了

struct netlink_kernel_cfg {
    unsigned int    groups;// netlink组,应该是多播时使用,单播的情况就是0了
    unsigned int    flags;
    void        (*input)(struct sk_buff *skb); //回调函数,当收到消息时,内核会调用该函数指针指向的函数进行处理
    struct mutex    *cb_mutex;
    void        (*bind)(int group);
};

发送

单播netlink_unicast() 和 多播netlink_broadcast()

/******************************
ssk:    sock结构体指针
skb:    skb存放消息,它的data字段指向要发送的 netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块
portid: 端口id
nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利 用时睡眠
返回: 发送数据的长度
******************************/
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
/******************************
ssk:    sock结构体指针
skb:    skb存放消息,它的data字段指向要发送的 netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块
portid: 端口id
group:  netlink组
allocation: 内核内存分配类型,一般地为GFP_ATOMIC或 GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文
返回: 发送数据的长度
******************************/
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
                 __u32 group, gfp_t allocation);

接收

在调用netlink_kernel_create()函数是已经向cfg结构体中指定了回调函数,这个函数就是接收函数了。内核模块收到消息后会自动调用该函数。

用户层

使用标准的socket API即可( socket(), bind(), sendmsg(), recvmsg() 和 close())

消息结构

如图:
这里写图片描述

源代码

示例代码由用户层向内核发送一个字符串,内核打印接收到的消息并且将字符串发送给用户层。

内核层:

/*******************************
file:           k_netlink.c
description:    netlink demo
kernel:         linux-3.10.36
author:         arvik
email:          1216601195@qq.com
blog:           http://blog.csdn.net/u012819339
*******************************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>

#define NETLINK_USER  22
#define USER_MSG    (NETLINK_USER + 1)
#define USER_PORT   50

MODULE_LICENSE("GPL");  
MODULE_AUTHOR("arvik");  
MODULE_DESCRIPTION("netlink_demo");

static struct sock *netlinkfd = NULL;

int send_msg(int8_t *pbuf, uint16_t len)
{
    struct sk_buff *nl_skb;
    struct nlmsghdr *nlh;

    int ret;

    nl_skb = nlmsg_new(len, GFP_ATOMIC);
    if(!nl_skb)
    {
        printk("netlink_alloc_skb error\n");
        return -1;
    }

    nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0);
    if(nlh == NULL)
    {
        printk("nlmsg_put() error\n");
        nlmsg_free(nl_skb);
        return -1;
    }
    memcpy(nlmsg_data(nlh), pbuf, len);

    ret = netlink_unicast(netlinkfd, nl_skb, USER_PORT, MSG_DONTWAIT);

    return ret;
}


static void recv_cb(struct sk_buff *skb)
{
    struct nlmsghdr *nlh = NULL;
    void *data = NULL;

    printk("skb->len:%u\n", skb->len);
    if(skb->len >= nlmsg_total_size(0))
    {
        nlh = nlmsg_hdr(skb);
        data = NLMSG_DATA(nlh);
        if(data)
        {
            printk("kernel receive data: %s\n", (int8_t *)data);
            send_msg(data, nlmsg_len(nlh));
        }
    }
} 


struct netlink_kernel_cfg cfg = 
{
    .input = recv_cb,
};

static int __init test_netlink_init(void)
{
    printk("init netlink_demo!\n");


    netlinkfd = netlink_kernel_create(&init_net, USER_MSG, &cfg);
    if(!netlinkfd)
    {
        printk(KERN_ERR "can not create a netlink socket!\n");
        return -1;
    }

    printk("netlink demo init ok!\n");
    return 0;
}

static void __exit test_netlink_exit(void)
{
    sock_release(netlinkfd->sk_socket);
    printk(KERN_DEBUG "netlink exit\n!");
}

module_init(test_netlink_init);
module_exit(test_netlink_exit);

用户层

/*******************************
file:           u_netlink.c
description:    netlink demo
author:         arvik
email:          1216601195@qq.com
blog:           http://blog.csdn.net/u012819339
*******************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>

#define NETLINK_USER 22
#define USER_MSG    (NETLINK_USER + 1)
#define MSG_LEN 100

#define MAX_PLOAD 100

struct _my_msg
{
    struct nlmsghdr hdr;
    int8_t  data[MSG_LEN];
};



int main(int argc, char **argv)
{
    char *data = "hello kernel";
    struct sockaddr_nl  local, dest_addr;

    int skfd;
    struct nlmsghdr *nlh = NULL;
    struct _my_msg info;
    int ret;

    skfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG);
    if(skfd == -1)
    {
        printf("create socket error...%s\n", strerror(errno));
        return -1;
    }

    memset(&local, 0, sizeof(local));
    local.nl_family = AF_NETLINK;
    local.nl_pid = 50; 
    local.nl_groups = 0;
    if(bind(skfd, (struct sockaddr *)&local, sizeof(local)) != 0)
    {
        printf("bind() error\n");
        close(skfd);
        return -1;
    }

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0; // to kernel
    dest_addr.nl_groups = 0;

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
    memset(nlh, 0, sizeof(struct nlmsghdr));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
    nlh->nlmsg_flags = 0;
    nlh->nlmsg_type = 0;
    nlh->nlmsg_seq = 0;
    nlh->nlmsg_pid = local.nl_pid; //self port

    memcpy(NLMSG_DATA(nlh), data, strlen(data));
    ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));

    if(!ret)
    {
        perror("sendto error1\n");
        close(skfd);
        exit(-1);
    }
    printf("wait kernel msg!\n");
    memset(&info, 0, sizeof(info));
    ret = recvfrom(skfd, &info, sizeof(struct _my_msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if(!ret)
    {
        perror("recv form kernel error\n");
        close(skfd);
        exit(-1);
    }

    printf("msg receive from kernel:%s\n", info.data);
    close(skfd);

    free((void *)nlh);
    return 0;

}


说明,一定要先加载内核模块,要不然用户层创建套接字会失败!(因为内核模块会注册netlink所使用的协议类型,本例子中协议类型为USER_MSG,即23)

  • 4
    点赞
  • 6
    评论
  • 14
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值