Generic Netlink详解
linux内核通信-netlink使用例子
Netlink 是一种特殊的socket,它是Linux 所特有的,类似于BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由daemon (NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket 监视(NETLINK_INET_DIAG),netfilter 日志(NETLINK_NFLOG),ipsec 安全策略(NETLINK_XFRM),SELinux 事件通知(NETLINK_SELINUX),iSCSI 子系统(NETLINK_ISCSI),进程审计(NETLINK_AUDIT),转发信息表查询(NETLINK_FIB_LOOKUP),netlinkconnector(NETLINK_CONNECTOR),netfilter 子系统(NETLINK_NETFILTER),IPv6 防火墙(NETLINK_IP6_FW),DECnet 路由信息(NETLINK_DNRTMSG),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),通用netlink (NETLINK_GENERIC)。
Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的socket API 就可以使用netlink 提供的强大功能,内核态需要使用专门的内核API 来使用netlink。
Netlink 相对于系统调用,ioctl 以及/proc 文件系统而言具有以下优点:1,为了使用netlink,用户仅需要在include/linux/netlink.h 中增加一个新类型的netlink 协议定义即可,如#define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过socket API 使用该netlink 协议类型进行数据交换。
Linux中内核通信Netlink机制
Linux中与内核通信的Netlink机制Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。
其中第一个参数和mutex参数都是最新添加的。
Mutex 也可以为空。
这里主要是关于内核空间中的netlink函数的使用。
extern struct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups,void (*input)(struct sk_buff *skb),struct mutex *cb_mutex,struct module *module)。
struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。
默认情况下都是使用init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。
在内核中,audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0,audit_receive, NULL, THIS_MODULE)。
模块调用函数netlink_unicast 来发送单播消息:int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)参数ssk为函数netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock 表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠。
netlink介绍
netlink定义用于在内核模块与在用户地址空间中的进程之间传递消息的。
它包含了用于用户进程的基于标准套接字的接口和用于内核模块的一个内部核心API。
[--manual]Netlink是套接字家族中的一员,主要用内核与用户空间的进程间、用户进程间的通讯。
然而它并不像网络套接字可以用于主机间通讯,Netlink只能用于同一主机上进程通讯,并通过PID来标识它们。
[--wiki][see more:RFC 3549]netlink出现之前,linux是用ioctl接口控制设备参数。
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。
(相对于系统调用,ioctl 以及/proc 文件系统):1,为了使用netlink,用户仅需要在include/linux/netlink.h 中增加一个新类型的netlink 协议定义即可,如#define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过socket API 使用该netlink 协议类型进行数据交换。
但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件,那需要不少代码,proc 文件系统则需要在/proc 下添加新的文件或目录,那将使本来就混乱的/proc 更加混乱。
2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,但系统调用与ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。
3.使用netlink 的内核部分可以采用模块的方式实现,使用netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。
NetLink使用实例(YGM)
如何使用Netlink Connector Author: Yang gongming简介:本文详细介绍了 Linux 2.6.34.14 内核引入的内核空间与用户空间通信的新机制连接器,并通过典型示例讲解了它的使用。
一、概述连接器是一种新的用户态与内核态的通信方式,它使用起来非常方便。
本质上,连接器是一种netlink,它的 netlink 协议号为 NETLINK_CONNECTOR,与一般的 netlink 相比,它提供了更容易的使用接口,使用起来更方便。
netlink本质上是socket,不过它可用于用户程序和内核程序的通信。
1.内核模块使用方法注册一个标识 ID 和回调函数,即可使用连接器。
cn_msg结构://标识netlink的IDstruct cb_id{__u32 idx;__u32 val;};//netlink控制信息头struct cn_msg{struct cb_id id;__u32 seq;__u32 ack;__u32 len;/* Length of the following data */__u8 data[0];};三个内核模块常用的API。
int cn_add_callback(struct cb_id *id, char *name, void (*callback) (void *));void cn_del_callback(struct cb_id *id);void cn_netlink_send(struct cn_msg *msg, u32 __group, int gfp_mask);结构 cb_id 是连接器实例的标识 ID,它用于确定 netlink 消息与回调函数的对应关系。
当连接器接收到标识 ID 为 {idx,val} 的 netlink 消息时,注册的回调函数 void (*callback) (void *) 将被调用。
该回调函数的参数为结构struct cn_msg 的指针。
用户空间和内核空间通讯之【Netlink下】
用户空间和内核空间通讯之【Netlink下】在上一篇博文中我们所遇到的情况都是用户空间作为消息进程的发起者,Netlink还支持内核作为消息的发送方的情况。
这一般用于内核主动向用户空间报告一些内核状态,例如我们在用户空间看到的USB的热插拔事件的通告就是这样的应用。
先说一下我们的目标,内核线程每个一秒钟往一个多播组里发送一条消息,然后用户空间所以加入了该组的进程都会收到这样的消息,并将消息内容打印出来。
Netlink地址结构体中的nl_groups是32位,也就是说每种Netlink协议最多支持32个多播组。
如何理解这里所说的每种Netlink 协议?在</usr/include/linux/netlink.h>里预定义的如下协议都是Netlink协议簇的具体协议,还有我们添加的NETLINK_TEST也是一种Netlink协议。
1.#define NETLINK_ROUTE 0 /*Routing/device hook */2.#define NETLINK_UNUSED 1 /*Unused number */3.#define NETLINK_USERSOCK 2 /*Reserved for user mode socket protocols */4.#define NETLINK_FIREWALL 3 /*Firewalling hook */5.#define NETLINK_INET_DIAG 4 /*INET socket monitoring */6.#define NETLINK_NFLOG 5 /*netfilter/iptables ULOG */7.#define NETLINK_XFRM 6 /* ipsec */8.#define NETLINK_SELINUX 7 /*SELinux event notifications */9.#define NETLINK_ISCSI 8 /* Open-iSCSI */10.#define NETLINK_AUDIT 9 /* auditing */11.#define NETLINK_FIB_LOOKUP 1012.#define NETLINK_CONNECTOR 1113.#define NETLINK_NETFILTER 12 /*netfilter subsystem */14.#define NETLINK_IP6_FW 1315.#define NETLINK_DNRTMSG 14 /*DECnet routing messages */16.#define NETLINK_KOBJECT_UEVENT 15 /*Kernel messages to userspace */17.#define NETLINK_GENERIC 1618./* leave room for NETLINK_DM (DM Events)*/19.#define NETLINK_SCSITRANSPORT 18 /*SCSI Transports */20.#define NETLINK_ECRYPTFS 1921.#define NETLINK_TEST 20 /* 用户添加的自定义协议 */在我们自己添加的NETLINK_TEST协议里,同样地,最多允许我们设置32个多播组,每个多播组用1个比特表示,所以不同的多播组不可能出现重复。
Openvswitch原理与代码分析(1):总体架构
Openvswitch原理与代码分析(1):总体架构⼀、Opevswitch总体架构Openvswitch的架构⽹上有如下的图表⽰:每个模块都有不同的功能ovs-vswitchd 为主要模块,实现交换机的守护进程daemon在Openvswitch 所在的服务器进⾏ps aux 可以看到以下的进程root 1008 0.1 0.8 242948 31712 ? S<Ll Aug06 32:17 ovs-vswitchd unix:/var/run/openvswitch/db.sock -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir --log-file=/var/log/openvswitch/ovs-vswitchd.log --pidfile=/var/run/openvswitch/ovs-vswitchd.pid --detach --monitor注意这⾥ovs-vswitchd监听了⼀个本机的db.sock⽂件openvswitch.ko为Linux内核模块,⽀持数据流在内核的交换我们使⽤lsmod列举加载到内核的模块:~# lsmod | grep openvswitchopenvswitch 66901 0gre 13808 1 openvswitchvxlan 37619 1 openvswitchlibcrc32c 12644 2 btrfs,openvswitch既有Openvswitch.ko,也有ovsdb-server 轻量级数据库服务器,保存配置信息,ovs-vswitchd通过这个数据库获取配置信息通过ps aux可以看到如下进程root 985 0.0 0.0 21172 2120 ? S< Aug06 1:20 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach –monitor可以看出,ovsdb-server将配置信息保存在conf.db中,并通过db.sock提供服务,ovs-vswitchd通过这个db.sock从这个进程读取配置信息。
netlink用法
netlink用法1. 什么是netlinknetlink是Linux内核中一个用于内核和用户空间进程通信的机制。
它允许内核向用户空间应用程序发送消息,并提供了一种用于配置,管理和监视内核子系统的接口。
2. netlink的特性netlink的特性使其成为了Linux环境下进行内核和用户空间通信的重要工具,具有以下特点:2.1 强大而灵活netlink提供了一种灵活的通信机制,允许内核和用户空间之间进行双向通信。
应用程序可以通过发送消息给内核来请求信息,而内核也可以通过发送消息给应用程序来通知其发生的事件。
2.2 支持多种消息类型netlink支持多种消息类型,如路由信息、网络配置、接口状态等。
这使得开发者可以利用netlink来实现各种不同的功能。
2.3 可靠和高效netlink使用基于内存的套接字,相比于其他IPC机制(如管道和信号量),netlink是一种更加可靠和高效的通信机制。
它避免了不必要的内核空间与用户空间的拷贝,从而提高了系统的性能。
3. netlink的使用3.1 netlink的工作原理netlink通信的基本单位是消息(message),消息由一个头部(header)和一个有效载荷(payload)组成。
头部包含了消息的类型、源地址、目的地址等信息,有效载荷是实际的数据。
3.2 使用netlink库函数为了方便使用netlink,Linux提供了一组用户空间的库函数。
通过这些库函数,我们可以方便地发送和接收netlink消息。
3.2.1 创建netlink套接字首先,我们需要使用socket系统调用创建一个netlink套接字。
通过指定NETLINK_XXX作为协议参数,可以选择不同的netlink协议。
3.2.2 绑定套接字在创建套接字之后,我们需要使用bind函数将套接字与一个本地地址进行绑定。
这个本地地址可以是任意的netlink协议中定义的地址。
3.2.3 构造消息使用netlink库函数,我们可以方便地构造一个netlink消息。
linux下netlink的使用简介
linux下netlink的使⽤简介⼀、什么是netlinkNetlink套接字是⽤以实现⽤户进程与内核进程通信的⼀种特殊的进程间通信(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进程发送通知等。
linuxnetlink详解3-netlink总结
linuxnetlink详解3-netlink总结1 netlink初始化2 内核netlink创建3 应⽤层netlink创建4 应⽤层向内核发送消息5 应⽤层接收内核消息6 内核向应⽤层发送消息7 内核和应⽤层netlink通信实例7.1 应⽤层代码实例1/*应⽤层代码*/2 #include <sys/stat.h>3 #include <unistd.h>4 #include <stdio.h>5 #include <stdlib.h>6 #include <sys/socket.h>7 #include <sys/types.h>8 #include <string.h>9 #include <asm/types.h>10 #include <linux/netlink.h>11 #include <linux/socket.h>1213#define MAX_PAYLOAD 1024 /* maximum payload size*/14struct sockaddr_nl src_addr, dest_addr;15struct nlmsghdr *nlh = NULL;16struct iovec iov;17int sock_fd;18struct msghdr msg;1920int main(int argc, char* argv[])21 {22 sock_fd = socket(PF_NETLINK, SOCK_RAW, 21);23 memset(&msg, 0, sizeof(msg));24 memset(&src_addr, 0, sizeof(src_addr));25 src_addr.nl_family = AF_NETLINK;26 src_addr.nl_pid = getpid(); /* self pid */27 src_addr.nl_groups = 0; /* not in mcast groups */28 bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));29 memset(&dest_addr, 0, sizeof(dest_addr));30 dest_addr.nl_family = AF_NETLINK;31 dest_addr.nl_pid = 0; /* For Linux Kernel */32 dest_addr.nl_groups = 0; /* unicast */3334 nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); 35/* Fill the netlink message header */36 nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);37 nlh->nlmsg_pid = getpid(); /* self pid */38 nlh->nlmsg_flags = 0;39/* Fill in the netlink message payload */40 strcpy(NLMSG_DATA(nlh), "Hello you!");4142 iov.iov_base = (void *)nlh;43 iov.iov_len = nlh->nlmsg_len;44 msg.msg_name = (void *)&dest_addr;45 msg.msg_namelen = sizeof(dest_addr);46 msg.msg_iov = &iov;47 msg.msg_iovlen = 1;4849 printf(" Sending message. ...\n");50 sendmsg(sock_fd, &msg, 0);//向内核发送消息5152/* Read message from kernel */53 memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));54 printf(" Waiting message. ...\n");55 recvmsg(sock_fd, &msg, 0);//接收内核的消息56 printf(" Received message payload: %s\n",NLMSG_DATA(nlh));5758/* Close Netlink Socket */59 close(sock_fd);60 }7.2 内核层代码实例1/*内核层代码*/23 #include <linux/kernel.h>4 #include <linux/module.h>5 #include <linux/types.h>6 #include <linux/sched.h>7 #include <net/sock.h>8 #include <net/netlink.h>910#define NETLINK_TEST 211112struct sock *nl_sk = NULL;13 EXPORT_SYMBOL_GPL(nl_sk);1415void nl_send(struct sk_buff *__skb)16 {17struct sk_buff *skb;18struct nlmsghdr *nlh;19 u32 pid;20int rc;21int len = NLMSG_SPACE(1200);22char str[100];2324 printk("net_link: data is ready to read.\n");25 skb = skb_get(__skb);2627if (skb->len >= NLMSG_SPACE(0)) {28 nlh = nlmsg_hdr(skb);//接收应⽤层的消息头29 printk("nl_send: recv %s.\n", (char *)NLMSG_DATA(nlh));//NLMSG_DATA(nlh)接收应⽤层消息数据30 memcpy(str,NLMSG_DATA(nlh), sizeof(str));31 pid = nlh->nlmsg_pid; /*pid of sending process */32 printk("nl_send: pid is %d\n", pid);33 kfree_skb(skb);3435 skb = alloc_skb(len, GFP_ATOMIC);36if (!skb){37 printk(KERN_ERR "nl_send: allocate failed.\n");38return;39 }40 nlh = nlmsg_put(skb,0,0,0,1200,0);41 NETLINK_CB(skb).pid = 0; /* from kernel */4243 memcpy(NLMSG_DATA(nlh), str, sizeof(str));44 printk("nl_send: going to send.\n");45 rc = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);//向应⽤层发送消息46if (rc < 0) {47 printk(KERN_ERR "nl_send: can not unicast skb (%d)\n", rc);48 }49 printk("nl_send: send is ok.\n");50 }51return;52 }5354static int kernel_netlink_test(void) {55 nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_send, NULL, THIS_MODULE);//创建netlink socket 5657if (!nl_sk) {58 printk(KERN_ERR "kernel_netlink_test: Cannot create netlink socket.\n");59return -EIO;60 }61 printk("kernel_netlink_test: create socket ok.\n");62return0;63 }6465int init_module()66 {67 kernel_netlink_test();68return0;69 }70void cleanup_module( )71 {72if (nl_sk != NULL){73 sock_release(nl_sk->sk_socket);74 }75 printk("kernel_netlink_test: remove ok.\n");76 }77 MODULE_LICENSE("GPL");78 MODULE_AUTHOR("action_er");待测试。
Linux中与内核通信的Netlink机制
Linux中与内核通信的Netlink机制Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。
其中第一个参数和mutex参数都是最新添加的。
Mutex 也可以为空。
这里主要是关于内核空间中的netlink函数的使用。
extern struct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups,void (*input)(struct sk_buff *skb),struct mutex *cb_mutex,struct module *module);struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。
默认情况下都是使用init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。
在内核中,audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0,audit_receive, NULL, THIS_MODULE);模块调用函数netlink_unicast 来发送单播消息:int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)参数ssk为函数netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock 表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠。
netlink介绍
netlink定义用于在内核模块与在用户地址空间中的进程之间传递消息的。
它包含了用于用户进程的基于标准套接字的接口和用于内核模块的一个内部核心API。
[--manual]Netlink是套接字家族中的一员,主要用内核与用户空间的进程间、用户进程间的通讯。
然而它并不像网络套接字可以用于主机间通讯,Netlink只能用于同一主机上进程通讯,并通过PID来标识它们。
[--wiki][see more:RFC 3549]netlink出现之前,linux是用ioctl接口控制设备参数。
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。
(相对于系统调用,ioctl 以及/proc 文件系统):1,为了使用netlink,用户仅需要在include/linux/netlink.h 中增加一个新类型的netlink 协议定义即可,如#define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过socket API 使用该netlink 协议类型进行数据交换。
但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件,那需要不少代码,proc 文件系统则需要在/proc 下添加新的文件或目录,那将使本来就混乱的/proc 更加混乱。
2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,但系统调用与ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。
3.使用netlink 的内核部分可以采用模块的方式实现,使用netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。
netlink用法
Netlink是Linux内核提供的一种进程间通信机制,用于完成内核与用户空间之间的通信。
常用于协议实现、网络管理、虚拟网络等场景。
下面是netlink的常用用法:
创建Socket:使用socket()系统调用来创建一个Netlink 的Socket。
绑定Socket:使用bind()系统调用将Socket与Netlink协议族绑定。
创建Netlink消息:通过Netlink的数据结构来创建Netlink 消息,并填充消息头部和传输数据部分。
发送Netlink消息:使用sendmsg()系统调用来发送Netlink 消息。
接收Netlink消息:使用recvmsg()系统调用来接收Netlink 消息。
解析Netlink消息:使用Netlink的数据结构来解析接收到的Netlink消息,包括消息头部和传输数据部分。
处理Netlink消息:根据不同的消息类型和操作,使用Netlink 的数据结构来处理接收到的Netlink消息。
关闭Socket:使用close()系统调用来关闭Netlink Socket。
总的来说,Netlink是Linux内核中非常重要的IPC机制之一,常被用于内核与用户空间之间的通信,可用于协议实现与管理等多个场景。
Generic Netlink详解
∙双向传输,异步通信∙用户空间中使用标准socket API∙内核空间中使用专门的API∙支持多播∙可由内核端发起通信∙支持32种协议类型netlink仅支持32种协议类型,这在实际应用中可能并不足够。
因此产生了generic netlink(以下简称为genl)。
generic netlink支持1023个子协议号,弥补了netlink协议类型较少的缺陷。
支持协议号自动分配。
它基于netlink,但是在内核中,generic netlink的接口与netlink并不相同。
1. Generic Netlink框架概述图1表示了Generic Netlink框架。
Kernel socket API向用户空间和内核空间分别提供接口。
Netlink子系统(1)是所有genl通信的基础。
Netlink子系统中收到的所有Generic类型的netlink数据都被送到genl总线(2)上;从内核发出的数据也经由genl总线送至netlink子系统,再打包送至用户空间。
Generic Netlink控制器(4)作为内核的一部分,负责动态地分配genl通道(即genl family id),并管理genl任务。
genl控制器是一个特殊的genl内核用户,它负责监听genl bus上的通信通道。
genl通信建立在一系列的通信通道的基础上,每个genl family对应多个通道,这些通道由genl控制器动态分配。
+---------------------+ +---------------------+| (3) application "A" | | (3) application "B" |+------+--------------+ +--------------+------+| |\ /\ /| |+-------+--------------------------------+-------+| : : |user-space=====+ : (5) Kernel socket API :+================| : : |kernel-space+--------+-------------------------------+-------+| |+-----+-------------------------------+----+| (1) Netlink subsystem |+---------------------+--------------------+|+---------------------+--------------------+| (2) Generic Netlink bus |+--+--------------------------+-------+----+| | |+-------+---------+ | || (4) Controller | / \+-----------------+ / \| |+------------------+--++--+------------------+| (3) kernel user "X" | | (3) kernel user "Y" | +---------------------++---------------------+图1:generic netlink框架2 Generic Netlink相关结构体2.1 genl familyGeneric Netlink是基于客户端-服务端模型的通信机制。
netlink使用方法
息头部使用 struct nlmsghdr 结构来描述:
struct nlmsghdr netlink消息头部
{ __u32 nlmsg_len; /* Length of message */ netlink消息总长度 __u16 nlmsg_type; /* Message type*/ 应用内部自定义对内核透明
netlink 套接字实现的,著名的内核包过滤框架 Netfilter 在与用户空间的通读,也在最新版本中改
变为 netlink,无疑,它将是 Linux 用户态与内核态交流的主要方法之一。它的通信依据是一个对
应于进程的标识,一般定为该进程的 ID。当通信的一端处于中断过程时,该标识为 0。当使用
netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。netlink 套
return -1;
}
用户空间可以调用 send 函数簇向内核发送消息,如 sendto、sendmsg 等,同样地,也可以使
用 struct sockaddr_nl 来描述一个对端地址,以待 send 函数来调用,与本地地址稍不同的是,因为
对端为内核,所以 nl_pid 成员需要设置为 0:
struct sockaddr_nl kpeer; 这里指定对端地址
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
static int skfd;
NETLINK有三种应用
一、概述Hilscher NETLINK用于将西门子MPI协议转换为以太网TCP/IP协议,并提供公开的无需授权的驱动函数库。
NETLINK有三种应用:1、对于仅仅需要STEP7编程监控和WINCC监控,安装IBHNET127驱动即可,该驱动安装后将在控制面板的Set PG/PC Interface中添加IBHNetMPI/PPI/Profibus三个驱动连接;将STEP7(S7 ONLINE)和WINCC(MPI)的访问点指向以上驱动连接即可。
2、对于第三方组态软件(如KingView、iFix等)通过NETLINK访问PLC,选择我公司开发的OPCServer。
3、对于VB或者VC++(6.0以上版本)的自行开发界面,有两种方法实现通讯:l 调用Hilscher提供的IP Driver驱动函数库进行通讯;l 使用winsock控件进行通讯;本文对在VB6.0中通过Hilscher IP Driver驱动库读写S7300/400 PLC数据的基本开发步骤作简要描述,具体资料可以参考NETLINK光盘netDEVICE System Software中的Nlmpi_pie.pdf和Drv_Ip.pdf文件,该光盘可到/co/beichen-automation/index_download.asp 页面中下载。
二、平台和配置1、平台:WINDOW2000+SP4,Microsoft VB6.0;2、软件和驱动安装:Hilscher SYCON软件和IP Driver;3、硬件要求:NETLINK电缆一根,西门子S7300 CPU一台(如CPU314),以太网交换机一台(如TP-LINK),以太网电缆;4、系统配置:如下图所示三、NETLINK参数配置如上图所示,将计算机网卡的IP地址配置为192.168.1.10,将NETLINK的RJ45端口链接到交换机端口上,另一端插入CPU314的MPI通讯口,NETLINK是由CPU314的MPI口供电的。
Netlink内核实现分析1
Netlink内核实现分析1Netlink 是⼀种IPC(Inter Process Commumicate)机制,它是⼀种⽤于内核与⽤户空间通信的机制,在⼀般情况下,⽤户态和内核态通信会使⽤传统的Ioctl、sysfs属性⽂件或者procfs属性⽂件,这3种通信⽅式都是同步通信⽅式,由⽤户态主动发起向内核态的通信,内核⽆法主动发起通信。
Netlink是⼀种异步全双⼯的通信⽅式,它⽀持由内核态主动发起通信,内核为Netlink通信提供了⼀组特殊的API接⼝,⽤户态则基于socket API,内核发送的数据会保存在接收进程socket 的接收缓存中,由接收进程处理。
netlink 优点:可以由内核发起,⽤户进程可以使⽤IO复⽤模型⽀持组播,即内核态可以将消息发送给多个接收进程Netlink ⼦系统初始化流程netlink 内核⼦接⼝初始化实在 net/netlink/af_netlink.c中初始化完成static const struct rhashtable_params netlink_rhashtable_params = {.head_offset = offsetof(struct netlink_sock, node),.key_len = netlink_compare_arg_len,.obj_hashfn = netlink_hash,.obj_cmpfn = netlink_compare,.automatic_shrinking = true,};/*这⾥的hash(哈希表)⽤来索引同种协议类型的不同netlink套接字实例,mc_list为多播使⽤的sock散列表,listeners为监听者掩码,groups为协议⽀持的最⼤多播组数量,同时还定义了⼀些函数指针,它们会在内核⾸次创建netlink时被赋值,后续应⽤层创建和绑定socket时调⽤到。
回到初始化函数中,接下来初始化应⽤层使⽤的NETLINK_USERSOCK协议类型的netlink(⽤于应⽤层进程间通信);然后调⽤sock_register向内核注册协议处理函数,即将netlink的socket创建处理函数注册到内核中,如此以后应⽤层创建netlink类型的socket时将会调⽤该协议处理函数,其中*/static int __init netlink_proto_init(void){int i;int err = proto_register(&netlink_proto, 0);if (err != 0)goto out;BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb));//之开辟了MAX_LINKS个na_talbe指针空间nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);if (!nl_table)goto panic;//分配MAX_LINKS个netlink表结构, nl_table, 每个成员代表⼀种协议类型for (i = 0; i < MAX_LINKS; i++) {if (rhashtable_init(&nl_table[i].hash,&netlink_rhashtable_params) < 0) {rhashtable_destroy(&nl_table[i].hash);kfree(nl_table);goto panic;}}INIT_LIST_HEAD(&netlink_tap_all);netlink_add_usersock_entry();sock_register(&netlink_family_ops);//将netlink socket 创建函数注册到内核中,即创建netlink socket 时回调函数为 netlink_family_ops->create 回调register_pernet_subsys(&netlink_net_ops);/* The netlink device handler may be needed early. */rtnetlink_init();out:return err;panic:panic("netlink_init: Cannot allocate nl_table\n");}core_initcall(netlink_proto_init本初始化函数⾸先向内核注册netlink协议;然后创建并初始化了nl_table表数组,这个表是整个netlink实现的最关键的⼀步,每种协议类型占数组中的⼀项,后续内核中创建的不同种协议类型的netlink都将保存在这个表中.struct netlink_table {struct rhashtable hash;// hash表控制块,内部的hash表记录了已经创建的同种协议类型的所有netlink套接字struct hlist_head mc_list; // 这个hash表头节点⽤于记录同种协议类型下所有阅订了组播功能的套接字struct listeners __rcu *listeners; // 记录了同种协议类型下所有被阅订了的组播消息集合为监听者掩码unsigned int flags;unsigned int groups;// 记录了该协议类型⽀持的最⼤组播数量(通常就是32个)struct mutex *cb_mutex;struct module *module;//函数指针会在内核⾸次创建netlink时被赋值,后续应⽤层创建和绑定socket时调⽤到int (*bind)(struct net *net, int group);void (*unbind)(struct net *net, int group);bool (*compare)(struct net *net, struct sock *sock);int registered;};/*应⽤层创建PF_NETLINK(AF_NETLINK)类型的socket()系统调⽤时将由netlink_create()函数负责处理*/static const struct net_proto_family netlink_family_ops = {.family = PF_NETLINK,.create = netlink_create,.owner = THIS_MODULE, /* for consistency 8) */};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,};sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);if (!sk)return -ENOMEM;net->rtnl = sk;return 0;}/*定义了⼀个netlink_kernel_cfg结构体实例,设置groups为RTNLGRP_MAX后指定消息接收处理函数为rtnetlink_rcv,并设置flag为NL_CFG_F_NONROOT_RECV,这表明⾮超级⽤户可以绑定到多播组,但是没有设置NL_CFG_F_NONROOT_SEND,这表明⾮超级⽤户将不能发送组播消息。
linux的nlmsghdr用法
linux的nlmsghdr用法在Linux系统中,nlmsghdr结构体用于表示Netlink消息头。
Netlink是一种进程间通信协议,旨在为内核和用户空间提供一种可靠的数据传输机制。
Netlink提供了多种协议族,其中之一是NETLINK_GENERIC协议族,它可以用来实现自定义通信协议。
nlmsghdr结构体的定义如下:```cstruct nlmsghdr {__u32 nlmsg_len;__u16 nlmsg_type;__u16 nlmsg_flags;__u32 nlmsg_seq;__u32 nlmsg_pid;};```其中,各成员变量的含义如下:- nlmsg_len 表示Netlink消息的总长度,包括消息头和消息体。
- nlmsg_type 表示Netlink消息的类型。
- nlmsg_flags 表示Netlink消息的标记,可以包括NLM_F_REQUEST、NLM_F_ACK、NLM_F_DUMP等,具体含义请参考官方文档。
- nlmsg_seq 表示Netlink消息的序列号,用于标识消息的顺序。
- nlmsg_pid 表示发送或接收Netlink消息的进程ID。
下面是一个简单的使用例子,展示如何创建一个Netlink消息并向内核发送:```c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <linux/netlink.h>#define NETLINK_TEST 25#define MAX_PAYLOAD 1024//创建Netlink socketsockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);if (sockfd < 0) {perror("socket");return -1;}//绑定源地址memset(&src_addr, 0, sizeof(src_addr));src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = getpid();src_addr.nl_groups = 0;ret = bind(sockfd, (struct sockaddr *)&src_addr, sizeof(src_addr)); if (ret < 0) {perror("bind");return -1;}//构造Netlink消息头nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_type = 1; //自定义消息类型nlh->nlmsg_flags = NLM_F_REQUEST;nlh->nlmsg_seq = 0;nlh->nlmsg_pid = getpid();//设置消息体strcpy(NLMSG_DATA(nlh), "Hello, kernel!");//设置iovec结构体iov.iov_base = (void *)nlh;iov.iov_len = nlh->nlmsg_len;//解析Netlink消息头nlh = (struct nlmsghdr *)msg.msg_iov->iov_base;printf("Received message: %s\n", (char *)NLMSG_DATA(nlh));//释放资源free(nlh);close(sockfd);return 0;}```在这个例子中,我们创建了一个Netlink socket,并设置了源地址和目的地址。
gennetlink 用例
gennetlink用例
genetlink是Linux内核中的一个机制,用于在内核空间和用户空间之间进行通信。
它允许用户空间应用程序通过Netlink套接字与内核进行交互,并传递信息和命令。
以下是一些genetlink的用例:
1.网络配置:通过genetlink,用户空间工具可以与内核进行通信,以配置网络接口、路由表、防火墙规则等。
例如,可以使用genetlink来创建、修改或删除网络接口,设置IP地址,配置路由等。
2.网络监控:用户空间监控程序可以使用genetlink来接收内核发送的网络事件和状态信息。
例如,可以使用genetlink来监控网络接口的状态变化,获取传输统计数据,检测网络故障等。
3.网络安全:通过genetlink,可以实现网络安全功能,如防火墙和包过滤。
用户空间防火墙程序可以使用genetlink与内核通信,设置规则和策略,过滤和处理传入和传出的网络数据包。
4.设备驱动程序:genetlink也可以用于设备驱动程序与用户空间应用程序之间的通信。
设备驱动程序可以使用genetlink来向用户空间传递设备状态、事件或控制命令,以便用户空间应用程序可以根据需要进行相应的操作。
5.系统管理:genetlink还可以用于系统管理任务,如配置网络性能参数、调整内核参数等。
通过与内核通信,用户空间管理工具可以实现对系统的动态管理和优化。
这些只是genetlink的一些用例示例,实际上,它可以用于许多其他的网络和系统管理任务,具体取决于应用程序的需求和开发者的创造力。
netlink介绍
netlink 编程介绍分类:LINUX学习2009-12-30 15:162946人阅读评论(0)收藏举报目录(?)[+]Linux从2.2开始支持PF_NETLINK域的通讯方式,这个方式主要的用途是在Linux的内核空间和用户空间进行通讯。
目前在网络上面关于netlink编程的中文资料很少,为了促进对netlink编程的理解我编写了这篇文章,由于我对netlink的了解不是很透彻,特别是对于内核部分不是很熟悉,所以文章中肯定有很多错误的地方还请大家指正。
文章分下面几个部分进行讲述A. netlink 基础知识B. nlmsghdr 结构介绍C. 解析nlmsghdr数据D. sockaddr_nl 结构介绍E. NETLINK_ROUTE 协议介绍F. NETLINK_SKIP 协议介绍G. NETLINK_USERSOCK协议介绍H. NETLINK_FIREWALL 协议介绍LINK_TCPDIAG 协议介绍J. NETLINK_NFLOG 协议介绍K. NETLINK_ARPD 协议介绍L. NETLINK_ROUTE6 协议介绍M. NETLINK_IP6_FW 协议介绍N. NETLINK_DNRTMSG 协议介绍O. NETLINK_TAPBASE 协议介绍P. 参考资料Q. 版权说明R. 修改记录A. netlink基础知识B. nlmsghdr结构介绍C. 解析nlmsghdr数据D. sockaddr_nl结构介绍E. NETLINK_ROUTE协议介绍F. NETLINK_SKIP 协议介绍G. NETLINK_USERSOCK协议介绍H. NETLINK_FIREWALL 协议介绍I. NETLINK_TCPDIAG 协议介绍J. NETLINK_NFLOG 协议介绍K. NETLINK_ARPD 协议介绍L. NETLINK_ROUTE6 协议介绍M. NETLINK_IP6_FW 协议介绍N. NETLINK_DNRTMSG 协议介绍O. NETLINK_TAPBASE 协议介绍P. 参考资料我们在使用socket(2)的man手册时候可以找到man手册中有下面一行说明PF_NETLINK Kernel user interface devicenetlink(7)在我们通过PF_NETLINK创建一个SOCKET以后表示我们期望同内核进行消息通讯。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
∙双向传输,异步通信∙用户空间中使用标准socket API∙内核空间中使用专门的API∙支持多播∙可由内核端发起通信∙支持32种协议类型netlink仅支持32种协议类型,这在实际应用中可能并不足够。
因此产生了generic netlink(以下简称为genl)。
generic netlink支持1023个子协议号,弥补了netlink协议类型较少的缺陷。
支持协议号自动分配。
它基于netlink,但是在内核中,generic netlink的接口与netlink并不相同。
1. Generic Netlink框架概述图1表示了Generic Netlink框架。
Kernel socket API向用户空间和内核空间分别提供接口。
Netlink子系统(1)是所有genl通信的基础。
Netlink子系统中收到的所有Generic类型的netlink数据都被送到genl总线(2)上;从内核发出的数据也经由genl总线送至netlink子系统,再打包送至用户空间。
Generic Netlink控制器(4)作为内核的一部分,负责动态地分配genl通道(即genl family id),并管理genl任务。
genl控制器是一个特殊的genl内核用户,它负责监听genl bus上的通信通道。
genl通信建立在一系列的通信通道的基础上,每个genl family对应多个通道,这些通道由genl控制器动态分配。
+---------------------+ +---------------------+| (3) application "A" | | (3) application "B" |+------+--------------+ +--------------+------+| |\ /\ /| |+-------+--------------------------------+-------+| : : |user-space=====+ : (5) Kernel socket API :+================| : : |kernel-space+--------+-------------------------------+-------+| |+-----+-------------------------------+----+| (1) Netlink subsystem |+---------------------+--------------------+|+---------------------+--------------------+| (2) Generic Netlink bus |+--+--------------------------+-------+----+| | |+-------+---------+ | || (4) Controller | / \+-----------------+ / \| |+------------------+--++--+------------------+| (3) kernel user "X" | | (3) kernel user "Y" | +---------------------++---------------------+图1:generic netlink框架2 Generic Netlink相关结构体2.1 genl familyGeneric Netlink是基于客户端-服务端模型的通信机制。
服务端注册family(family是对genl服务的各项定义的集合)。
控制器和客户端都通过已注册的信息与服务端通信。
genl family的结构体如下:struct genl_family{unsigned int id;unsigned int hdrsize;char name[GENL_NAMSIZ];unsigned int version;unsigned int maxattr;struct nlattr ** attrbuf;struct list_head ops_list;struct list_head family_list;};对此结构体元素具体解释如下:* id: family id。
当新注册一个family的时候,应该用GENL_ID_GENERATE宏(0x0),表示请控制器自动为family分配的一个id。
0x10保留供genl控制器使用。
* hdrsize: 用户自定议头部长度。
即图2中User Msg的长度。
如果没有用户自定义头部,这个值被赋为0。
* version: 版本号,一般填1即可。
* name: family名,要求不同的family使用不同的名字。
以便控制器进行正确的查找。
* maxattr:genl使用netlink标准的attr来传输数据。
此字段定义了最大attr类型数。
(注意:不是一次传输多少个attr,而是一共有多少种attr,因此,这个值可以被设为0,为0代表不区分所收到的数据的attr type)。
在接收数据时,可以根据attr type,获得指定的attr type的数据在整体数据中的位置。
* struct nlattr **attrbuf* struct list_head ops_list* struct list_head family_list以上的三个字段为私有字段,由系统自动配置,开发者不需要做配置。
图2 genl报文与linux中各变量的对应关系图3 genl报文格式2.2 genl_ops 结构体struct genl_ops{u8 cmd;unsigned int flags;struct nla_policy *policy;int (*doit)(struct sk_buff *skb,struct genl_info *info);int (*dumpit)(struct sk_buff *skb,struct netlink_callback*cb);struct list_head ops_list;};∙cmd: 命令名。
用于识别各genl_ops∙flag: 各种设置属性,以“或”连接。
在需要admin特权级别时,使用GENL_ADMIN_PERM∙policy:定义了attr规则。
如果此指针非空,genl在触发事件处理程序之前,会使用这个字段来对帧中的attr做校验(见nlmsg_parse函数)。
该字段可以为空,表示在触发事件处理程序之前,不做校验。
policy是一个struct nla_policy的数组。
struct nla_policy结构体表示如下:struct nla_policy{u16 type;u16 len;};其中,type字段表示attr中的数据类型,可被配置为:NLA_UNSPEC--未定义NLA_U8, NLA_U16, NLA_U32, NLA_U64为8bits, 16bits, 32bits, 64bits的无符号整型NLA_STRING--字符串NLA_NUL_STRING--空终止符字符串NLA_NESTED--attr流len字段的意思是:如果在type字段配置的是字符串有关的值,要把len设置为字符串的最大长度(不包含结尾的'\0')。
如果type字段未设置或被设置为NLA_UNSPEC,那么这里要设置为attr 的payload部分的长度。
∙doit:这是一个回调函数。
在generic netlink收到数据时触发,运行在进程上下文。
doit传入两个参数,skb为触发此回调函数的socket buffer。
第二个参数是一个genl_info结构体,定义如下:struct genl_info{u32 snd_seq;u32 snd_pid;struct nlmsghdr * nlhdr;struct genlmsghdr * genlhdr;void * userhdr;struct nlattr ** attrs;};* snd_seq:发送序号* snd_pid:发送客户端的PID* nlhdr:netlink header的指针* genlmsghdr:genl头部的指针(即family头部)* userhdr:用户自定义头部指针* attrs:attrs,如果定义了genl_ops->policy,这里的attrs是被policy过滤以后的结果。
在完成了操作以后,如果执行正确,返回0;否则,返回一个负数。
(一定要有返回值,不能不返回)负数的返回值会触发NLMSG_ERROR消息。
当genl_ops的flag标志被添加了NLMSG_ERROR 时,即使doit返回0,也会触发NLMSG_ERROR消息。
∙dumpit这是一个回调函数,当genl_ops的flag标志被添加了NLM_F_DUMP以后,每次收到genl消息即会回触发这个函数。
dumpit与doit的区别是:dumpit的第一个参数skb不会携带从客户端发来的数据。
相反地,开发者应该在skb中填入需要传给客户端的数据,然后,并skb的数据长度(可以用skb->len)return。
skb中携带的数据会被自动送到客户端。
只要dumpit的返回值大于0,dumpit函数就会再次被调用,并被要求在skb中填入数据。
当服务端没有数据要传给客户端时,dumpit要返回0。
如果函数中出错,要求返回一个负值。
关于doit和dumpit的触发过程,可以查看源码中的genl_rcv_msg函数。
∙ops_list为私有字段,由系统自动配置,开发者不需要做配置。
3 Generic Netlink服务端(内核)初始化初始化Generic Netlink的过程分为以下四步:定义family,定义operation,注册family,注册operation。
下面通过一个简单例子来说明如何完成Generic Netlink的初始化。
我们首先创建一个genl_family结构体的实例。
我们在这里定义一个名为"DOC_EXMPL"的family/* attribute type */enum {DOC_EXMPL_A_UNSPEC,DOC_EXMPL_A_MSG,__DOC_EXMPL_A_MAX,};#define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)/* family definition */static struct genl_family doc_exmpl_gnl_family = {.id = GENL_ID_GENERATE,.hdrsize = 0,.name = "DOC_EXMPL",.version = 1,.maxattr = DOC_EXMPL_A_MAX,};以上,我们定义了一个仅有一种attribuste type的family。