netlinknetlinksocket是用户态进程和内核态进程之间的一种通信机制。它通过为内核模块提供一组专用API和为用户程序提供一组标准套接字接口,实现全双工通信连接。特点:双向传输,异步通信。用户空间使用标准套接字API。内核空间使用了特殊的API来支持多播。通信可以由内核端发起。支持32种协议类型。Netlink只支持32种协议类型,在实际应用中可能不太实用。够了,于是创建了GenericNetlink(以下简称genl)。GenericNetlink支持1023个子协议号,弥补了netlink协议类型的不足。netlink通信架构Netlink子系统:所有genl通信的基础,Netlink子系统中接收到的所有Generic类型的netlink数据都发送到genl总线;从内核发送过来的数据,也是通过genl总线发送到netlink子系统,然后打包发送到用户空间GenericNetlinkcontroller:作为内核的一部分,负责动态分配genlchannels(即genlfamilyid)和管理genl任务。genlcontroller是一个特殊的genlkerneluser,它负责监控genlbus通信通道genlcommunication是基于一系列的通信通道。每个genlfamily对应多个channel。这些通道由genl控制器动态分配。相关结构genlfamilyGenericNetlink是一种基于client-server模型的通信机制,server端注册family(family是genl服务定义的集合),controller和client都通过注册的信息与server进行通信.//genl_family的主要字段structgenl_family{unsignedintid;//家庭idunsignedinthdrsize;//用户自定义头长度charname[GENL_NAMSIZ];//familyname,不同的family需要使用不同的nameunsignedintversion;//版本unsignedintmaxattr;//最大attr类型数,使用netlink标准attr传输数据genl_ops*ops;//操作集};genl_ops定义netlinkfamily相关操作//genl_opsmainfieldstructgenl_ops{u8cmd;//命令名,用于标识genl_opsunsignedintflags;//设置属性structnla_policy*policy;//定义attr规则,genl会在触发事件处理器之前用它来检查attrint(*doit)(structsk_buff*skb,structgenl_info*info);int(*dumpit)(structsk_buff*skb,structnetlink_callback*cb);};doit:回调函数,genericnetlink收到数据时触发,运行在进程上下文中dumpit:回调函数。当genl_ops的flag加上NLM_F_DUMP时,每次接收到genl消息都会触发这个函数。dumpit和doit的区别在于dumpit的第一个参数skb不会携带到来的数据。相反,开发者应该在skb中填写需要发送给客户端的数据,skb中携带的数据会自动发送给客户端。只要dumpit的返回值大于0,就会再次调用dumpit函数,要求填写skb中的数据。当服务端没有数据发送给客户端时,dumpit应该返回0。如果函数出错,则要求返回负值。nal_policy定义attr规则structnla_policy{u16type;//attr中的数据类型u16len;//如果type字段配置了字符串相关的值,则设置len为字符串的最大长度};genl_info内核收到用户的genetlink报文后,会对该报文进行解析,封装成一个genl_info结构体structgenl_info{u32snd_seq;//发送序列号u32snd_pid;//发送客户端的PIDstructnlmsghdr*nlhdr;//netlink头指针structgenlmsghdr*genlhdr;//genlhead(即familyhead)的指针void*userhdr;//用户自定义头指针structnlattr**attrs;//如果定义了genl_ops->policy,则保存按策略过滤的结果};GenericNetlinkserver(kernel)initialization下面是OVS中数据包处理的例子:1.Definefamily//definepacketfamilystaticstructgenl_familydp_packet_genl_family__ro_after_init={.hdrsize=sizeof(structovs_header),.name=OVS_PACKET_FAMILY,.version=OVS_PACKET_VERSION,.maxattr=OVS_PACKET_ATTR_MAX,.netnsok=true,.parallel_ops=true,.ops=dp_packet_genl_ops,//操作集.n_ops=ARRAY_SIZE(dp_packet_genl_ops),.module=THIS_MODULE,};2.defineoperation//定义packetfamily的操作---packet类型操作只支持OVS_PACKET_CMD_EXECUTEstaticstructgenl_opsdp_packet_genl_ops[]={{.cmd=OVS_PACKET_CMD_EXECUTE,.flags=GENL_UNS_ADMIN_policy=packet_policy,.doit=ovs_packet_cmd_execute//接收时一个数据包,调用ovs_packet_cmd_execute处理}};//定义数据包族的过滤规则staticconststructnla_policypacket_policy[OVS_PACKET_ATTR_MAX+1]={[OVS_PACKET_ATTR_PACKET={.ETH_HLEN},[OVS_PACKET_ATTR_KEY]={.type=NLA_NESTED},[OVS_PACKET_ATTR_ACTIONS]={.type=NLA_NESTED},[OVS_PACKET_ATTR_PROBE]={.type=NLA_FLAG},[OVS_PACKET_ATTR_MRU]={.type=NLA},_U36注册familygenl_register_family(&dp_packet_genl_family);通用Netlink客户端(用户空间)初始化structsockaddr_nlsaddr;intsock;sock=socket(AF_NETLINK,SOCK_RAW,NETLINK_GENERIC);//创建一个netlink类型的socketif(sock<0){return-1;}memset(&;saddr,0,sizeof(saddr));saddr.nl_family=AF_NETLINK;saddr.nl_pid=getpid();//getfamilyidif(bind(sock,(structsockaddr*)&saddr,sizeof(saddr))<0){//绑定printf("bindfail!\n");关闭(*p_sock);return-1;}内核空间接受发送数据Acceptingdata:内核端一旦收到通用的netlink数据,就会触发doit函数运行,通过回调函数处理发送数据:数据打包后,可以在单播(genlmsg_unicast)或multicast()形式用户空间接受和发送数据接受数据:调用recv函数完成从内核接收数据接收和发送数据:调用sendto发送数据ovs为例参考内容GeneRicNetlink详解作者:yearsj转载请注明出处:https://segmentfault.com/a/11...
