内核与用户空间的通信实现—netlink

这篇具有很好参考价值的文章主要介绍了内核与用户空间的通信实现—netlink。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

        netlink是一个内核空间与用户空间通信的机制,相对ioctl和procfs方式来说,netlink有很多优点:

  • netlink使用简单,与UDO的socket编程类似,直接使用socket编程的API即可。只需要自定义一个新类型的 netlink 协议定义即可。
  • netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息。
  • netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组。
  • 内核可以使用 netlink 首先发起会话。

        netlink通信的用户程序与平时使用的UDPsocket类似,只是多了几个结构体,在刚开始学习netlink时被那么多结构体间的关系搞的头脑爆炸,很重要的一点也是在学习UDPsocket时没有打好基础,就正如sendto、sendmsg的使用。

        只要一开始能搞清楚整个netlink通信的创建和交互过程就能很容易理解。关于netlink更全面的理解大家自行百度,本文单纯从编程角度入手,只是简单介绍一下基础的流程实现。

netlink编程

应用程序

流程图

sockaddr_nl,驱动开发,嵌入式,linux,网络

 图1.sendto发送信息流程图

sockaddr_nl,驱动开发,嵌入式,linux,网络

图2.sendmsg发送信息流程图 

socket()

/* 函数原型就是socket编程中的socket()函数,就不介绍函数原型了,只介绍netlink相关。
 * 创建netlink套接字
 * SOCK_RAW表示原始的数据流
 * NETLINK_TEST是自己定义的一个宏,用来标识协议号,也可以用定义在linux/netlink.h中内核预留的宏NETLINK_GENERIC,实际上就是定义一个内核还没占用的数字。
 */
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)

bind()

/* 函数原型就是socket编程中的bind()函数,就不介绍函数原型了,只介绍netlink相关。
 * 用户程序进程绑定sockaddr_nl元素
 * saddr是struct sockaddr_nl的一个结构体元素,是netlink专用的地址结构体。
 */
bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr))

        Struct sockaddr_ln为Netlink的地址,和我们通常socket编程中的sockaddr_in作用一样sockaddr_nl,驱动开发,嵌入式,linux,网络

struct sockaddr_nl
{
    sa_family_t nl_family; /*该字段总是为AF_NETLINK */
    unsigned short nl_pad; /* 目前未用到,填充为0*/
    __u32 nl_pid;          /* PORT-ID 用来标识单播地址,是netlink通信标识,类似于ip地址,0表示内核*/
    __u32 nl_groups;       /* 多播使用,0表示不用多播 */
};

创建struct nlmsghdr

/* netlink通信的报文分为消息头和消息体。
 * netlink与tcp、udp的通信区别就是netlink的报文需要自己定义头部信息。
 * 而tcp、udp的socket直接发送消息内容,内核协议栈会根据源、目的地址(sockaddr_in)自己填充头部信息。 
 * netlink的消息头就是结构体nlmsghdr。 
 */
struct nlmsghdr
{
    __u32        nlmsg_len;      // 整个消息的长度,包括消息头
    __u16        nlmsg_type;     // 消息的类型,区别数据消息还是控制消息
    __u16        nlmsg_flags;    // 附加在消息上的额外说明
    __u32        nlmsg_seq;      // 消息序列号,防止消息丢失
    __u32        nlmsg_pid;      // 用来表示自己的进程标识。为netlink连接通道唯一数字标识符,用于避免内核发送给用户进程时发错对象,通常是用户进程的pid。
};

nlmsg_type:
#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    // It is request message.
#define NLM_F_MULTI        2    // Multipart message, terminated by NLMSG_DONE 
#define NLM_F_ACK          4    // Reply with ack, with zero or error code
#define NLM_F_ECHO         8    // Echo this request
#define NLM_F_DUMP_INTR    16   // Dump was inconsistent due to sequence change
 
// Modifiers to GET request
#define NLM_F_ROOT      0x100   // specify tree    root
#define NLM_F_MATCH     0x200   // return all matching
#define NLM_F_ATOMIC    0x400   // atomic GET
#define NLM_F_DUMP    (NLM_F_ROOT|NLM_F_MATCH)
 
// Modifiers to NEW request 
#define NLM_F_REPLACE   0x100   //Override existing
#define NLM_F_EXCL      0x200   // Do not touch, if it exists
#define NLM_F_CREATE    0x400   // Create, if it does not exist
#define NLM_F_APPEND    0x800   // Add to end of list

注意:struct nlmsghdr结构体中

    __u32        nlmsg_pid;      // 用来表示自己的进程标识。

        内核通过获取这个值知道用户进程的标识,然后通过netlink_unicast的第三个参数pid发送出去。

        需要注意的是内核设置消息头的结构体nlmsg_put第四个参数设置nlmsg_pid应为0,标识自己的pid为0级内核。

        我们在创建netlink报文消息头nlsmhdr结构体时会一起申请消息体的结构。

struct nlmsghdr *nlh = NULL; //定义netlink消息头类型的指针
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); // 申请netlink报文空间:消息头+消息体。NLMSG_SPACE是系统头文件定义的宏,后面会介绍netlink通信常用的宏;MAX_PAYLOAD是用户自己定义的消息负载大小

netlink常用的系统定义的宏

#define NLMSG_ALIGNTO   4U

/* 宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

/* Netlink 头部长度 */
#define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))

/* 计算消息数据len的真实消息长度(消息体 + 消息头)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)

/* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

/* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

/* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 */
#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

/* 判断消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len <= (len))

/* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

sendto、recvfrom

/* sendto函数原型 */
int sendto(int s, const void * msg, int len, unsigned int flags, const struct sockaddr * to, int tolen);

/* netlink使用
 * sock_fd : socket函数创建的fd。
 * nlh : nlmsghdr结构体指针
 * nlh->nlmsg_le : netlink报文长度
 * 0 : flag
 * daddr : 目的地址
 * sizeof(struct sockaddr_nl) : daddr元素大小
 */
sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct         sockaddr_nl));
/* recvfrom函数原型 */
int recvfrom(socket s,void *buf,int len,unsigned int flags, struct sockaddr *from,int *fromlen);

/* netlink使用 */
recvfrom(sock_fd, nlh, NLMSG_LENGTH(MAX_PAYLOAD), 0, NULL, NULL);

sendmsg、recvmsg

        netlink用户程序发送和接收消息可以用sendto/sendmsg、recvfrom/recvmsg。使用sendto和recvfrom则如上所述。如果使用sendmsg和recvmsg就需要用到msghdr结构体和iovec结构体。sendmsg、recvmsg更为复杂功能更强。msghdr、nlmsghdr、iovec的关系如下图所示:

sockaddr_nl,驱动开发,嵌入式,linux,网络

         下面我们来介绍一下msghdr与iovec结构体

struct iovec 
{                    /* Scatter/gather array items */
    void  *iov_base;              /* Starting address */
    size_t iov_len;               /* Number of bytes to transfer */
};
/* iov_base: iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度 (即有多少个buff)*/ 

struct msghdr 
{
    void         *msg_name;        // 数据的目的地址,网络包指向sockaddr_in, netlink则指向sockaddr_nl; 
    socklen_t     msg_namelen;    // msg_namelen: msg_name 所代表的地址长度
    struct iovec *msg_iov;        // 指向的是缓冲区数组
    size_t        msg_iovlen;     // 缓冲区数组长度 
    void         *msg_control;    // 辅助数据,控制信息(发送任何的控制信息) 
    size_t        msg_controllen; // 辅助信息长度 
    int           msg_flags;      // 消息标识
};

/* iovec、msghdr结构体的使用 */
memset(&iov, 0, sizeof(iov));  
iov.iov_base = (void *)nlh;  
iov.iov_len = nlh->nlmsg_len; 
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

        sendmsg与recvmsg的使用

/* sendmsg函数原型 */
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);

sendmsg(sock_fd, &msg, 0); //sock_fd为socket创建的fd,msg为msghdr的指针

/* recvmsg函数原型 */
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

recvmsg(sock_fd, &msg, 0);

用户程序代码-usr_netlink_demo.c

使用sendto、recvfrom

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define MAX_PAYLOAD 1024 /* netlink消息最大负载为1024B */
#define NETLINK_TEST 30

int main(int argc, char **argv)
{
	struct sockaddr_nl daddr;
	struct sockaddr_nl saddr; //定义netlink用户空间地址体
	struct nlmsghdr *nlh = NULL; //定义netlink消息头类型的指针
	
	int sock_fd = -1;
	int len;
	int ret;
	char recv_buf[MAX_PAYLOAD] = {0};
	char *umsg = "hello";

	/* 创建netlink套接字,SOCK_RAW表示原始的数据流,NETLINK_TEST表示用户自定义协议 */
	if (-1 == (sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)))
	{
		perror("can't create netlink socket!");
		return -1;
	}
	memset(&saddr, 0, sizeof(saddr));
	saddr.nl_family = AF_NETLINK;
	saddr.nl_pid = 100;
	saddr.nl_groups = 0;
	
	memset(&daddr, 0, sizeof(daddr));
	daddr.nl_family = AF_NETLINK;
	daddr.nl_pid = 0; // to kernel 
	daddr.nl_groups = 0;
	
	if (-1 == bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr)))
	{
		perror("can't bind sockfd with sockaddr_nl!");
		return -2;
	}
	
	/* 
		给结构体指针nlh分配地址
		NLMSG_SPACE(MAX_PAYLOAD): 该宏用于返回不小于MAX_PAYLOAD且4字节对齐的最小长度值
		一般用于向内存系统申请空间是指定所申请的内存字节数
	*/
	if (NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
	{
		perror("malloc mem failed!");
		return -3;
	}
	memset(nlh, 0, sizeof(struct nlmsghdr));
	nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
	nlh->nlmsg_pid = 100;
	nlh->nlmsg_type = 0;
	nlh->nlmsg_flags = 0;
	nlh->nlmsg_seq = 0;
	
	memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
	
	ret = sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
	if(0 >= ret)
	{
		perror("sendto error\n");
		ret = -4;
		goto CleanUp;
	}

	memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
	
	len = sizeof(struct sockaddr_nl);
	ret = recvfrom(sock_fd, nlh, NLMSG_LENGTH(MAX_PAYLOAD), 0, NULL, NULL);
	if(0 >= ret)
	{
		perror("recvfrom kernel error\n");
		ret = -5;
		goto CleanUp;
	}
    printf("usr recv kernel:%s\n",NLMSG_DATA(nlh));
    ret = 0;
	
CleanUp:
	close(sock_fd);
	free(nlh);
	return ret;
}

使用sendmsg、recvmsg文章来源地址https://www.toymoban.com/news/detail-589724.html

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define MAX_PAYLOAD 1024 /* netlink消息最大负载为1024B */
#define NETLINK_TEST 30

int main(int argc, char **argv)
{
	struct sockaddr_nl daddr;
	struct sockaddr_nl saddr; //定义netlink用户空间地址体
	struct nlmsghdr *nlh = NULL; //定义netlink消息头类型的指针
	struct iovec iov; //定义一个向量元素,通常用于多元素的数组
	struct msghdr msg; // 用于recvmsg、sendmsg发送和接受的数据存放;
	
	int sock_fd = -1;
	int len;
	int ret;
	char recv_buf[MAX_PAYLOAD] = {0};
	char *umsg = "hello";

	/* 创建netlink套接字,SOCK_RAW表示原始的数据流,NETLINK_TEST表示用户自定义协议 */
	if (-1 == (sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST)))
	{
		perror("can't create netlink socket!");
		return -1;
	}
	memset(&saddr, 0, sizeof(saddr));
	saddr.nl_family = AF_NETLINK;
	saddr.nl_pid = 100;
	saddr.nl_groups = 0;
	
	memset(&daddr, 0, sizeof(daddr));
	daddr.nl_family = AF_NETLINK;
	daddr.nl_pid = 0; // to kernel 
	daddr.nl_groups = 0;
	
	if (-1 == bind(sock_fd, (struct sockaddr*)&saddr, sizeof(saddr)))
	{
		perror("can't bind sockfd with sockaddr_nl!");
		return -2;
	}
	
	/* 
		给结构体指针nlh分配地址
		NLMSG_SPACE(MAX_PAYLOAD): 该宏用于返回不小于MAX_PAYLOAD且4字节对齐的最小长度值
		一般用于向内存系统申请空间是指定所申请的内存字节数
	*/
	if (NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD))))
	{
		perror("malloc mem failed!");
		return -3;
	}
	memset(nlh, 0, sizeof(struct nlmsghdr));
	nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
	nlh->nlmsg_pid = 100;
	nlh->nlmsg_type = 0;
	nlh->nlmsg_flags = 0;
	nlh->nlmsg_seq = 0;
	
	memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
	
	memset(&iov, 0, sizeof(iov));
	iov.iov_base = (void *)nlh;
	iov.iov_len = nlh->nlmsg_len;
	memset(&msg, 0, sizeof(msg));
    msg.msg_name = (void *)&(daddr);
    msg.msg_namelen = sizeof(daddr);   
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
    ret = sendmsg(sock_fd, &msg, 0);
    if(0 >= ret)
	{
		perror("sendto kernel error\n");
		ret = -5;
		goto CleanUp;
	}
    memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
    recvmsg(sock_fd, &msg, 0);
    if(0 >= ret)
	{
		perror("recvfrom kernel error\n");
		ret = -6;
		goto CleanUp;
	}
    printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
    ret = 0;
	
CleanUp:
	close(sock_fd);
	free(nlh);
	return ret;
}

内核程序-kernel_netlink_demo.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/sched.h>

#define NETLINK_TEST 30
#define MSG_LEN      125
#define USER_PORT    100

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lixuezahng");
MODULE_DESCRIPTION("netlink example");


struct sock *nl_sk = NULL;
struct net init_net;

static int netlink_send_msg(char *send_buf, size_t len, long pid)
{
	struct sk_buff *psk_buff_t;
	struct nlmsghdr *pnlmsghdr_t;
	
	int ret;
	
	psk_buff_t = nlmsg_new(len, GFP_ATOMIC); //创建sk_buff空间
	if(NULL == psk_buff_t)
	{
		printk("netlink alloc failure\n");
		return -1;
	}
	
	/* 设置netlink消息头 */
	pnlmsghdr_t = nlmsg_put(psk_buff_t, 0, 0, pid, len, 0);
	if(NULL == pnlmsghdr_t)
	{
		printk("nlmsg_put failure\n");
		nlmsg_free(psk_buff_t);        //针对nlmsg_new申请空间的释放问题,若put失败需要手动释放,若put成功netlink_unicast发送后内核会自己处理
		return -2;
	}
	
	memcpy(NLMSG_DATA(pnlmsghdr_t), send_buf, len);
	
	/*
		内核发送单播消息
		extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
		ssk: netlink socket 
		skb: skb buff 指针 
		portid:通信的端口号 
		nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为 0,该函数在没有接收缓存可利用 定时睡眠
		
		内核发送多播消息
		extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, __u32 group, gfp_t allocation);
		ssk: 同上(对应 netlink_kernel_create 返回值)、 
		skb: 内核 skb buff 
		portid:端口id 
		group: 是所有目标多播组对应掩码的"OR"操作的。 
		allocation: 指定内核内存分配方式,通常 GFP_ATOMIC 用于中断上下文,而 GFP_KERNEL 用于其他场合。这个参数的存在是因为该 API 可能需要分配一个或多个缓冲区来对多播消息进行 clone。 
	*/
	ret = netlink_unicast(nl_sk, psk_buff_t, pid, MSG_DONTWAIT);
	
	return ret;
}
	
static void netlink_rcv_msg(struct sk_buff *psk_buff_t)
{
	struct nlmsghdr *pnlmsghdr_t = NULL;
	char *umsg = NULL;
	char *kmsg = "hello users!!!";
	
	if(psk_buff_t->len >= nlmsg_total_size(0))
	{
		pnlmsghdr_t = nlmsg_hdr(psk_buff_t);
		umsg = NLMSG_DATA(pnlmsghdr_t);
		if(umsg)
		{
			printk("kernel recv from user: %s\n", umsg);
			netlink_send_msg(kmsg, strlen(kmsg), pnlmsghdr_t->nlmsg_pid);
		}
	}
}

struct netlink_kernel_cfg cfg = {
		.input = netlink_rcv_msg, /* set recv callback */
};


static int __init nlmoudle_init(void)
{
	printk(KERN_ALERT "my netlink_kernel in\n");
	
	/*
		static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) 
		net:指向所在的网络命名空间, 默认传入的是 &init_net (不需要定义);  定义在 net_namespace.c(extern struct net init_net); 
		unit:netlink协议类型
		cfg:cfg 存放的是 netlink 内核配置参数
		struct netlink_kernel_cfg { 
			unsigned int    groups; 
			unsigned int    flags; 
			void        	(*input)(struct sk_buff *skb); // input 回调函数
			struct mutex    *cb_mutex; 
			void        (*bind)(int group); 
			bool        (*compare)(struct net *net, struct sock *sk); 
}; 
	*/
	nl_sk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
	return 0;
}

static void __exit nlmoudle_exit(void)
{
	printk(KERN_ALERT "my netlink_kernel out\n");
	netlink_kernel_release(nl_sk);
}

module_init(nlmoudle_init);
module_exit(nlmoudle_exit);

makefile

KERNEL_DIR := /XX/XX/linux-3.10 #指定内核的路径
CURRENT_PATH := $(shell pwd)    #当前路径
obj-m += kernel_netlink_demo.o    #编译kernel_ioctl_demo.c生成.ko文件
 
build:netlink_module              #总目标
 
netlink_module:
	$(MAKE) -C $(KERNEL_DIR) ARCH=XXX CROSS_COMPILE=XXX M=$(CURRENT_PATH) modules
    #-C [CPATH]表示make要跳转到CPATH路径执行make
    #-M [MPATH]表示执行完CPATH路径下的make后再回到MPATH继续执行剩下的
    #ARCH 指定处理器架构
    #CROSS_COMPILE 指定交叉编译器
 
clean:
	$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_PATH) clean

sendmsg实例

/*************************************************************************
 * @description             : nl_send_msg 函数,通过netlink发送数据到内核
 * @param -sock_fd          : sock描述符
 * @param -dst_addr         : netlink目的地址
 * @param -message          : 发送的数据
 * @param -len              :发送数据的长度
 * @return                  : 失败返回 -1,成功返回0
 *************************************************************************/


static int nl_send_msg(int sock_fd, struct sockaddr_nl *dst_addr, char *message, int len)
{
	struct nlmsghdr *nlh = NULL;
	struct iovec iov;
	struct msghdr msg;

	if( !len || !message)
	{
		printf("message is empty or len is zero\n");
		return -1;
	}
	/* 1、构建nlh */
	if(NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(len))))
	{
		printf("nlh malloc failed\n");
		return -1;
	}

	nlh->nlmsg_len 		= NLMSG_SPACE(len);
	nlh->nlmsg_pid 		= getpid();
	nlh->nlmsg_flags 	= 0;
	memcpy(NLMSG_DATA(nlh), message, len);

	/* 2、构建iov */

	iov.iov_base = (void *)nlh;
	iov.iov_len = nlh->nlmsg_len;

	/* 3、构建msg */
	memset(&msg, 0, sizeof(struct msghdr));
	msg.msg_name = (void *)dst_addr;
	msg.msg_namelen = sizeof(struct sockaddr_nl);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	/* 4、发送消息 */
	if(0 > sendmsg(sock_fd, &msg, 0))
	{
		printf("sendmsg failed\n");
		free(nlh);
		return -1;
	}

	free(nlh);

	return 0;
}

/*************************************************************************
 * @description             : nl_recv_msg 函数,接收从内核发送的数据
 * @param -sock_fd          : sock描述符
 * @param -dst_addr         : netlink源地址
 * @param -message          : 接收到的数据
 * @param -len              :接收到数据的长度
 * @return                  : 失败返回 -1,成功返回0
 *************************************************************************/

static int nl_recv_msg(int sock_fd, struct sockaddr_nl *src_addr, char *message, int len)
{
	struct nlmsghdr *nlh = NULL;
	struct iovec iov;
	struct msghdr msg;

	if( !len || !message)
	{
		printf("message is empty or len is zero\n");
		return -1;
	}

	/* 1、构建nlh */
	if(NULL == (nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(RESCVPLAYLOAD))))
	{
		printf("recv nlh malloc failed\n");
		return -1;
	}

	/* 2、构建ivo */
	iov.iov_base = (void *)nlh;
	iov.iov_len = NLMSG_SPACE(RESCVPLAYLOAD);

	/* 3、构建msg */
	memset(src_addr, 0, sizeof(struct sockaddr_nl));
	memset(&msg, 0, sizeof(struct msghdr));
	msg.msg_name = (void *)src_addr;
	msg.msg_namelen = sizeof(struct sockaddr_nl);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	/* 4、接受信息 */
	if(0 > recvmsg(sock_fd, &msg, 0))
	{
		printf("recv_message failed\n");
		free(nlh);
		return -1;
	}

	memcpy(message, (unsigned char *)NLMSG_DATA(nlh), nlh->nlmsg_len - NLMSG_SPACE(0));

	free(nlh);
	return 0;
}

到了这里,关于内核与用户空间的通信实现—netlink的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux字符设备驱动(设备文件,用户空间与内核空间进行数据交互,ioctl接口)

    在Linux系统中“一切皆文件”,上一篇讲述了cdev结构体就描述了一个字符设备驱动,主要包括设备号和操作函数集合。但是要怎么操作这个驱动呢?例如,使用open()该打开谁,read()该从哪读取数据等等。所以就需要创建一个设备文件来代表设备驱动。 应用程序要操纵外部硬件

    2024年02月12日
    浏览(29)
  • 内核和用户空间中的TID,GID, PID,uid

    要获取关于eBPF中的进程信息,可以使用以下函数: bpf_get_current_pid_tgid()、 bpf_get_current_uid_gid()、 bpf_get_current_comm(char *buf, int size_of_buf)。 当程序被绑定到对某个内核函数调用时,就可以使用它们。UID/GID应该比较明确,但对于那些以前没有接触过内核操作细节的人来说,还是需要

    2024年02月07日
    浏览(41)
  • 【linux驱动】用户空间程序与内核模块交互-- IOCTL和Netlink

    创建自定义的IOCTL(输入/输出控制)或Netlink命令以便用户空间程序与内核模块交互涉及几个步骤。这里将分别介绍这两种方法。 1. 定义IOCTL命令 在内核模块中,需要使用宏定义你的IOCTL命令。通常情况下,IOCTL命令包括了一个命令编号、请求类型的方向(读/写/两者)以及数

    2024年01月20日
    浏览(27)
  • Windows驱动(用户层R3与内核层R0通信)

    内存空间分为用户层和系统层,普通的应用程序只能运行在用户层,为了可以操作系统层的内存 所以引入了驱动程序,有了驱动就可以通过用户层来操作系统层的内存及函数,所以驱动就是应用层和系统层之间的一个桥梁 在应用层通过创建符号链接,自动产生驱动层的IRP事

    2024年02月14日
    浏览(33)
  • 51内核单片机实现Bootloader跳转到用户程序,要求两个程序都要支持中断

    本文使用的单片机为笙科的A9129F6,Flash大小为64KB,SRAM大小为8KB。 Flash空间规划如下。 起始地址 结束地址 用途 0x0000 0x3fff Bootloader 程序 0x4000 0xefff 用户程序( APP 程序) 0xf000 0xffff 存放设备 配置信息 程序间跳转实现起来很简单,只需要使用函数指针就行了。 但是难点在于

    2024年02月16日
    浏览(29)
  • java 多用户即时通信系统的实现 万字详解

    目录 前言 一、拾枝杂谈         1.项目开发大体流程 :          2.多用户即时通信系统分析 :                  1° 需求分析                 2° 整体分析 二、用户登录         1.准备工作 :          2.客户端 :                  1° 菜单界面              

    2024年02月06日
    浏览(22)
  • 基于BGP+OSPF+路由策略实现合理穿过运营商,实现跨域内网用户之间的通信

    实验结构拓扑图:  实验要求: 实验过程: 1:ip地址规划,具体划分请见拓扑结构图所示  2:各AS域内启用OSPF协议。实现域内网络联通,宣告环回,为EBGP、IBGP建邻做准备 3:不同域之间建立EBGP对等体关系、AS域内建立IBGP对等体关系 4:在AS1、AS4上宣告内网网段,实现控制层面可达,

    2024年01月19日
    浏览(31)
  • Linux内核源码分析 (B.2)虚拟地址空间布局架构

    Linux内核只是操作系统当中的一部分,对下管理系统所有硬件设备,对上通过系统调用向 Library Routine 或其他应用程序提供API接口。 内存管理可以通过以下三个维度进行介绍: 用户空间 相当于应用程序使用 malloc() 申请内存,通过 free() 释放内存。 malloc() / free() 是 glibc 库的内

    2024年02月09日
    浏览(52)
  • 内核态、用户态概念

    MTU(Maximum Transmission Unit,最大传输单元)是指在网络中传输的数据包的最大长度限制,它是一个重要的网络参数,影响着网络的可靠性、稳定性和性能。在TCP/IP协议栈中,MTU涉及到内核态和用户态两个方面。 在内核态中,网络设备的MTU由网络驱动程序设置并保存在内核中。

    2024年02月16日
    浏览(26)
  • 内核线程与用户线程的区别

    内核线程和用户线程是操作系统中的两种不同类型的线程,它们有以下异同点: 异同点: 相同点:内核线程和用户线程都是线程的一种,都可以执行任务。 不同点:内核线程是由操作系统内核创建和管理的,而用户线程是由应用程序创建和管理的。 不同点:内核线程运行在

    2024年02月16日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包