linux 内核中Netlink
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 内核中Netlink相关的知识,希望对你有一定的参考价值。
目录
Netlink套接字接口最初是Linux内核2.2引入的,作用用户空间进程与内核间通信方法。
相对于ioctl,sysfs,proc的优势
优势:
- IOCTL处理程序不能从内核向用户空间发送异步消息,而Netlink套接字则可以。
- 用户与内核间的通信方式,不需要轮询,用户空间应用程序打开套接字,调用recvmsg(),如果没有来自内核的消息,就进入阻塞状态。
- 内核可以主动向用户空间发送异步消息,而不需要用户空间来触发。
- 支持组播传输。
命令iproute2包含命令(ip tc ss lnstat bridge)主要使用netlink套接字从用户空间向内核空间发送请求并获得应答。
套接字Netlink地址 sockaddr_nl
struct sockaddr_nl
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
;
消息头
struct nlmsghdr
__u32 nlmsg_len; /* Length of message including header */
__u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process port ID */
;
协议簇
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define NETLINK_RDMA 20
#define NETLINK_CRYPTO 21 /* Crypto layer */
#define NETLINK_SMC 22 /* SMC monitoring */
常使用的宏
#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)))
内核常用的函数
//内核创建socket
static inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
//nlmsg_new - Allocate a new netlink message
static inline struct sk_buff *nlmsg_new(size_t payload, gfp_t flags)
//Add a new netlink message to an skb
static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 seq,
int type, int payload, int flags)
//单播
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套接字
在内核网络栈中,可以创建多种Netlink套接字,每种套接字处理不同类型消息。如NETLINK_ROUTE消息的套接字创建过程
static int __net_init rtnetlink_net_init(struct net *net)
struct sock *sk;
struct netlink_kernel_cfg cfg =
.groups = RTNLGRP_MAX,
.input = rtnetlink_rcv,
.cb_mutex = &rtnl_mutex,
.flags = NL_CFG_F_NONROOT_RECV,
.bind = rtnetlink_bind,
;
sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
if (!sk)
return -ENOMEM;
net->rtnl = sk;
return 0;
- 在netlink_kernel_create种第二个参数 NETLINK_ROUTE表示rtnetlink消息,此外还有NETLINK_XFRM表示IPsec子系统,NETLINK_AUDIT表示审计子系统。
- 成员input函数用于指定回调函数,rtnetlink_rcv用于接收用户空间的信息。
uevent内核事件
只需要从内核向用户发送数据即可,初始化为
static int uevent_net_init(struct net *net)
struct uevent_sock *ue_sk;
struct netlink_kernel_cfg cfg =
.groups = 1,
.input = uevent_net_rcv,
.flags = NL_CFG_F_NONROOT_RECV
;
ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
if (!ue_sk)
return -ENOMEM;
ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, &cfg);
if (!ue_sk->sk)
pr_err("kobject_uevent: unable to create netlink socket!\\n");
kfree(ue_sk);
return -ENODEV;
...
套接字监视接口
//支持SS 监视接口 NETLINK_SOCK_DIAG
static int __net_init diag_net_init(struct net *net)
struct netlink_kernel_cfg cfg =
.groups = SKNLGRP_MAX,
.input = sock_diag_rcv,
.bind = sock_diag_bind,
.flags = NL_CFG_F_NONROOT_RECV,
;
net->diag_nlsk = netlink_kernel_create(net, NETLINK_SOCK_DIAG, &cfg);
return net->diag_nlsk == NULL ? -ENOMEM : 0;
demo
内核程序实例
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>
#define NETLINK_USER 22
#define USER_MSG (NETLINK_USER + 1)
#define USER_PORT 50
static struct sock *netlinkfd = NULL;
int send_msg(int8_t *pbuf, uint16_t len)
struct sk_buff *nl_skb;
struct nlmsghdr *nlh;
int ret;
//创建sk_buffer
nl_skb = nlmsg_new(len, GFP_ATOMIC);
if(!nl_skb)
printk("nlmsg_new error\\n");
return -1;
//设置netlink头
nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0);
if(nlh == NULL)
printk("netlink header error\\n");
nlmsg_free(nl_skb);
return -1;
//拷贝数据
memcpy(nlmsg_data(nlh), pbuf, len);
//单播发送数据
ret = netlink_unicast(netlinkfd, nl_skb, USER_PORT, MSG_DONTWAIT);
return ret;
//接收数据
static void recv_cb(struct sk_buff *skb)
struct nlmsghdr *nlh = NULL;
void *data = NULL;
//打印接收数据的长度
printk("recv_datalen:%u\\n", skb->len);
if(skb->len >= nlmsg_total_size(0))
nlh = nlmsg_hdr(skb);
//宏 获取数据
data = NLMSG_DATA(nlh);
if(data)
printk("kernel receive data: %s\\n", (int8_t *)data);
//将数据发送给用户
send_msg(data, nlmsg_len(nlh));
//cfg参数 注册了input
struct netlink_kernel_cfg cfg =
.input = recv_cb,
;
//初始化
static int __init netlink_init(void)
//创建socket
netlinkfd = netlink_kernel_create(&init_net, USER_MSG, &cfg);
if(!netlinkfd)
printk(KERN_ERR "create a netlink socket error!\\n");
return -1;
printk("init netlink ok!\\n");
return 0;
//退出
static void __exit netlink_exit(void)
sock_release(netlinkfd->sk_socket);
printk(KERN_DEBUG "netlink exit\\n!");
module_init(netlink_init);
module_exit(netlink_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wy");
MODULE_DESCRIPTION("netlink");
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0
以上是关于linux 内核中Netlink的主要内容,如果未能解决你的问题,请参考以下文章