目录
套接字Netlink地址 sockaddr_nl
协议簇
常使用的宏
内核常用的函数
创建流程
Netlink套接字
uevent内核事件
套接字监视接口
demo
Netlink套接字接口最初是Linux内核2.2引入的,作用用户空间进程与内核间通信方法。
相对于ioctl,sysfs,proc的优势
优势:
命令iproute2包含命令(ip tc ss lnstat bridge)主要使用netlink套接字从用户空间向内核空间发送请求并获得应答。
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_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;
}
只需要从内核向用户发送数据即可,初始化为
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;
}
内核程序实例
#include
#include
#include
#include
#include
#include
#include
#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");