内核与用户空间的通信实现—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编程

应用程序

流程图

 图1.sendto发送信息流程图

图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作用一样

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的关系如下图所示:

         下面我们来介绍一下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

#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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值