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.参与讨论


