netlink怎么读

您所在的位置:网站首页 怎么读linux netlink怎么读

netlink怎么读

2023-03-16 01:44| 来源: 网络整理| 查看: 265

一、什么是netlink

Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。

在Linux 内核中,使用netlink 进行应用与内核通信的应用有很多,如

路由 daemon(NETLINK_ROUTE)

用户态 socket 协议(NETLINK_USERSOCK)

防火墙(NETLINK_FIREWALL)

netfilter 子系统(NETLINK_NETFILTER)

内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)

通用netlink(NETLINK_GENERIC)

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink。

一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,而Netlink可以实现双工通信。

Netlink 相对于系统调用,ioctl 以及 /proc文件系统而言,具有以下优点:

netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST 20 然后,内核和用户态应用就可以立即通过 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进程发送通知等。

二、用户态数据结构

用户态应用使用标准的 socket API有sendto(),recvfrom(), sendmsg(), recvmsg()。

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

1. struct sockaddr_nl结构:

struct sockaddr_nl {

__kernel_sa_family_t nl_family;

unsigned short nl_pad;

__u32 nl_pid;

__u32 nl_groups;

};

2. struct nlmsghd 结构:

struct nlmsghdr {

__u32 nlmsg_len;

__u16 nlmsg_type;

__u16 nlmsg_flags;

__u32 nlmsg_seq;

__u32 nlmsg_pid;

};

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

3. struct msghdr 结构体

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;

};

三、netlink 内核数据结构

1. netlink消息类型:

#define NETLINK_ROUTE 0

#define NETLINK_UNUSED 1

#define NETLINK_USERSOCK 2

#define NETLINK_FIREWALL 3

#define NETLINK_SOCK_DIAG 4

#define NETLINK_NFLOG 5

#define NETLINK_XFRM 6

#define NETLINK_SELINUX 7

#define NETLINK_ISCSI 8

#define NETLINK_AUDIT 9

#define NETLINK_FIB_LOOKUP 10

#define NETLINK_CONNECTOR 11

#define NETLINK_NETFILTER 12

#define NETLINK_IP6_FW 13

#define NETLINK_DNRTMSG 14

#define NETLINK_KOBJECT_UEVENT 15

#define NETLINK_GENERIC 16

#define NETLINK_SCSITRANSPORT 18

#define NETLINK_ECRYPTFS 19

#define NETLINK_RDMA 20

#define NETLINK_CRYPTO 21

#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG

#define MAX_LINKS 32

2. netlink常用宏:

#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 nlmsg_len - NLMSG_SPACE((len)))

3. netlink 内核常用函数

netlink_kernel_create内核函数用于创建内核socket与用户态通信

static inline struct sock *

netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)

struct netlink_kernel_cfg {

unsigned int groups;

unsigned int flags;

void (*input)(struct sk_buff *skb);

struct mutex *cb_mutex;

void (*bind)(int group);

bool (*compare)(struct net *net, struct sock *sk);

};

4. 单播netlink_unicast() 和 多播netlink_broadcast()

extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);

extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,

__u32 group, gfp_t allocation);

四、netlink实例

1. 用户态程序 (sendto(), recvfrom())

#include

#include

#include

#include

#include

#include

#include

#include

#define NETLINK_TEST 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 argc, char **argv)

{

int skfd;

int ret;

user_msg_info u_info;

socklen_t len;

struct nlmsghdr *nlh = NULL;

struct sockaddr_nl saddr, daddr;

char *umsg = "hello netlink!!";

skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);

if(skfd == -1)

{

perror("create socket error\n");

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() error\n");

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 error\n");

close(skfd);

exit(-1);

}

printf("send kernel:%s\n", 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 error\n");

close(skfd);

exit(-1);

}

printf("from kernel:%s\n", u_info.msg);

close(skfd);

free((void *)nlh);

return 0;

}

2. Netlink 内核模块代码

#include

#include

#include

#include

#include

#define NETLINK_TEST 30

#define MSG_LEN 125

#define USER_PORT 100

MODULE_LICENSE("GPL");

MODULE_AUTHOR("zhangwj");

MODULE_DESCRIPTION("netlink example");

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 failure\n");

return -1;

}

nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, 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 = "hello users!!!";

if(skb->len >= nlmsg_total_size(0))

{

nlh = nlmsg_hdr(skb);

umsg = NLMSG_DATA(nlh);

if(umsg)

{

printk("kernel recv from user: %s\n", umsg);

send_usrmsg(kmsg, strlen(kmsg));

}

}

}

struct netlink_kernel_cfg cfg = {

.input = netlink_rcv_msg,

};

int test_netlink_init(void)

{

nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);

if(nlsk == NULL)

{

printk("netlink_kernel_create error !\n");

return -1;

}

printk("test_netlink_init\n");

return 0;

}

void test_netlink_exit(void)

{

if (nlsk){

netlink_kernel_release(nlsk);

nlsk = NULL;

}

printk("test_netlink_exit!\n");

}

module_init(test_netlink_init);

module_exit(test_netlink_exit);

3..Makeflie

#

#Desgin of Netlink

#

MODULE_NAME :=netlink_test

obj-m :=$(MODULE_NAME).o

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

PWD := $(shell pwd)

all:

$(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:

$(MAKE) -C $(KERNELDIR) M=$(PWD) clean

4. 运行结果

首先将编译出来的Netlink内核模块插入到系统当中(insmod netlink_test.ko),然后运行应用程序,可以看到如下输出:

# 应用程序打印

send kernel:hello netlink!!

from kernel:hello users!!!

# 内核打印

[25024.276345] test_netlink_init

[25117.548350] kernel recv from user: hello netlink!!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3