> 文档中心 > linux 内核中Netlink

linux 内核中Netlink

目录

套接字Netlink地址 sockaddr_nl 

协议簇

常使用的宏 

内核常用的函数

创建流程

Netlink套接字

uevent内核事件

套接字监视接口

demo


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_tnl_family;/* AF_NETLINK*/unsigned shortnl_pad;/* zero*/__u32nl_pid;/* port ID*/__u32nl_groups;/* multicast groups mask */};

消息头 

struct nlmsghdr {__u32nlmsg_len;/* Length of message including header */__u16nlmsg_type;/* Message content */__u16nlmsg_flags;/* Additional flags */__u32nlmsg_seq;/* Sequence number */__u32nlmsg_pid;/* Sending process port ID */};

协议簇

#define NETLINK_ROUTE0/* Routing/device hook*/#define NETLINK_UNUSED1/* Unused number*/#define NETLINK_USERSOCK2/* Reserved for user mode socket protocols */#define NETLINK_FIREWALL3/* Unused number, formerly ip_queue*/#define NETLINK_SOCK_DIAG4/* socket monitoring*/#define NETLINK_NFLOG5/* netfilter/iptables ULOG */#define NETLINK_XFRM6/* ipsec */#define NETLINK_SELINUX7/* SELinux event notifications */#define NETLINK_ISCSI8/* Open-iSCSI */#define NETLINK_AUDIT9/* auditing */#define NETLINK_FIB_LOOKUP10#define NETLINK_CONNECTOR11#define NETLINK_NETFILTER12/* netfilter subsystem */#define NETLINK_IP6_FW13#define NETLINK_DNRTMSG14/* DECnet routing messages */#define NETLINK_KOBJECT_UEVENT15/* Kernel messages to userspace */#define NETLINK_GENERIC16/* leave room for NETLINK_DM (DM Events) */#define NETLINK_SCSITRANSPORT18/* SCSI Transports */#define NETLINK_ECRYPTFS19#define NETLINK_RDMA20#define NETLINK_CRYPTO21/* Crypto layer */#define NETLINK_SMC22/* SMC monitoring */

常使用的宏 

#define NLMSG_ALIGNTO4U#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)))

内核常用的函数

//内核创建socketstatic inline struct sock * netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)//nlmsg_new - Allocate a new netlink messagestatic inline struct sk_buff *nlmsg_new(size_t payload, gfp_t flags)//Add a new netlink message to an skbstatic 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_DIAGstatic 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 #include #include #include #include #include #include #define NETLINK_USER  22#define USER_MSG    (NETLINK_USER + 1)#define USER_PORT   50static 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参数 注册了inputstruct 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