前言
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文章来源: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消息头类型的指针
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模板网!