栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

【协议森林】Linux下用户态与内核态通信netlink机制说明与使用

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【协议森林】Linux下用户态与内核态通信netlink机制说明与使用

1.简介

Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。
一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,而Netlink可以实现双工通信。Netlink 相对于系统调用,ioctl 以及 /proc文件系统而言,具有以下优点:

  • netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,如 #define NETlink_TEST 30 ,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换。
  • netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息。
  • 使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖。
  • netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性
  • 内核可以使用 netlink 首先发起会话

Netlink协议基于BSD socket和AF_NETlink地址簇,使用32位的端口号寻址,每个Netlink协议通常与一个或一组内核服务/组件相关联,如NETlink_ROUTE用于获取和设置路由与链路信息、NETlink_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。

2.数据结构

Netlink通信跟常用UDP Socket通信类似,struct sockaddr_nl是netlink通信地址,跟普通socket struct sockaddr_in类似。

struct sockaddr_nl {
     __kernel_sa_family_t    nl_family;  
     unsigned short  nl_pad;     
     __u32       nl_pid;     
     __u32       nl_groups;  
};


struct nlmsghdr {   
    __u32       nlmsg_len;  
    __u16       nlmsg_type; 
    __u16       nlmsg_flags;     
    __u32       nlmsg_seq;  
    __u32       nlmsg_pid;  
};

struct iovec {                    
     void  *iov_base;              
     size_t iov_len;               
 };
  
 struct msghdr {
     void         *msg_name;       
     socklen_t     msg_namelen;    
     struct iovec *msg_iov;        
     size_t        msg_iovlen;     
     void         *msg_control;    
     size_t        msg_controllen; 
     int           msg_flags;      
 };

nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型,它们分别是:

#define NLMSG_NOOP      0x1 
#define NLMSG_ERROR     0x2 
#define NLMSG_DONE      0x3 
#define NLMSG_OVERRUN       0x4 
#define NLMSG_MIN_TYPE      0x10    

nlmsg_flags:消息标记,它们用以表示消息的类型,如下

#define NLM_F_REQUEST       1   
#define NLM_F_MULTI     2   
#define NLM_F_ACK       4   
#define NLM_F_ECHO      8   
#define NLM_F_DUMP_INTR     16  


#define NLM_F_ROOT  0x100   
#define NLM_F_MATCH 0x200   
#define NLM_F_ATOMIC    0x400   
#define NLM_F_DUMP  (NLM_F_ROOT|NLM_F_MATCH)


#define NLM_F_REPLACe   0x100   
#define NLM_F_EXCL  0x200   
#define NLM_F_CREATE    0x400   
#define NLM_F_APPEND    0x800   

常用宏

#define NLMSG_ALIGNTO   4U

#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )


#define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))


#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)


#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))


#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))


#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 
                  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))


#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && 
               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && 
               (nlh)->nlmsg_len <= (len))


#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
3.代码

用户态代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define NETlink_TYPE       30
#define MSG_LEN            125
#define MAX_PLOAD          125

typedef struct USER_MSG_INFO
{
    struct  nlmsghdr hdr;
    char    msg[MSG_LEN];

} USER_MSG_INFO;

int main()
{
    int skfd = -1;
    int ret = -1;
    USER_MSG_INFO u_info;
    socklen_t len;
    struct nlmsghdr *nlh = NULL;
    struct sockaddr_nl saddr,daddr;
    char   *umsg = "Hi, kernel! I am User!";

    
    skfd = socket(AF_NETlink, SOCK_RAW, NETlink_TYPE);
    if(skfd == -1)
    {
        perror("create socket errorn");
        return -1;
    }

    
    memset(&saddr, 0, sizeof(saddr));
    saddr.nl_family = AF_NETlink;      //AF_NETlink
    saddr.nl_pid = 100;                //端口号(port ID) 
    saddr.nl_groups = 0;
    if(bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0)
    {
        perror("bind() errorn");
        close(skfd);
        return -1;
    }

     
    memset(&daddr, 0, sizeof(daddr));
    daddr.nl_family = AF_NETlink;
    daddr.nl_pid = 0;                  // to kernel 
    daddr.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 = saddr.nl_pid;       //self port

     //发送消息至内核
    memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
    ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
    if(!ret)
    {
        perror("sendto errorn");
        close(skfd);
        exit(-1);
    }
    printf("[User===>Kernel] send kernel:%sn", umsg);

    //接收内核消息
    memset(&u_info, 0, sizeof(u_info));
    len = sizeof(struct sockaddr_nl);
    ret = recvfrom(skfd, &u_info, sizeof(USER_MSG_INFO), 0, (struct sockaddr *)&daddr, &len);
    if(!ret)
    {
        perror("recv form kernel errorn");
        close(skfd);
        exit(-1);
    }

    printf("[Kernel===>User] from kernel:%sn", u_info.msg);
    close(skfd);

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

内核态代码:

#include 
#include 
#include 
#include 
#include 

#define NETlink_TYPE       30
#define MSG_LEN            125
#define USER_PORT          100

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ToToSun");
MODULE_DEscriptION("netlink in kernel.");

struct sock *nlsk = NULL;
extern struct net init_net;

int send_usrmsg(char *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 failuren");
        return -1;
    }

    
    nlh = nlmsg_put(nl_skb, 0, 0, NETlink_TYPE, len, 0);
    if(nlh == NULL)
    {
        printk("nlmsg_put failaure n");
        nlmsg_free(nl_skb);
        return -1;
    }

    
    memcpy(nlmsg_data(nlh), pbuf, len);
    ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);

    return ret;
}

static void netlink_rcv_msg(struct sk_buff *skb)
{
    struct nlmsghdr *nlh = NULL;
    char *umsg = NULL;
    char *kmsg = "Hi, User! I am the great Kernel!";

    if(skb->len >= nlmsg_total_size(0))
    {
        nlh = nlmsg_hdr(skb);
        umsg = NLMSG_DATA(nlh);
        if(umsg)
        {
            printk("[kernel_netlink.c] kernel recv from user: %sn", umsg);
            send_usrmsg(kmsg, strlen(kmsg));
        }
    }
}

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

int knl_netlink_init(void)
{
    
    nlsk = (struct sock *)netlink_kernel_create(&init_net, NETlink_TYPE, &cfg);
    if(nlsk == NULL)
    {   
        printk("netlink_kernel_create error !n");
        return -1; 
    }   
    printk("knl_netlink_initn");

    return 0;
}

void knl_netlink_exit(void)
{
    if (nlsk){
        netlink_kernel_release(nlsk); 
        nlsk = NULL;
    }   
    printk("knl_netlink_exit!n");
}

module_init(knl_netlink_init);
module_exit(knl_netlink_exit);

编译Makefile

#编译
CC 	=	gcc
MODULE_NAME :=kernel_netlink
obj-m :=$(MODULE_NAME).o
CFLAGS	=	-O3 -Wall -W -Werror

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

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD)
	$(CC) user_netlink.c $(CFLAGS) -o user_netlink

	insmod kernel_netlink.ko
	@echo "---------make successfully!---------"


clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
	rm   -rf   user_netlink
	rmmod kernel_netlink.ko
	@echo "---------clean over!---------"
4.运行

5.参考

linux下netlink的使用简介

6.参与讨论

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/603853.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号