Linux中与内核通信的Netlink机制(实例)

Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这里主要是关于内核空间中的netlink函数的使用。

<span style="color:rgb(0,0,0);"><font face="Courier New"><span style="color:rgb(0,0,255);">extern</span> <span style="color:rgb(0,0,255);">struct</span> sock <span style="color:rgb(0,0,204);">*</span>netlink_kernel_create<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> net <span style="color:rgb(0,0,204);">*</span>net<span style="color:rgb(0,0,204);">,</span><br />                     <span style="color:rgb(0,0,255);">int</span> unit<span style="color:rgb(0,0,204);">,</span><span style="color:rgb(0,0,255);">unsigned</span> <span style="color:rgb(0,0,255);">int</span> groups<span style="color:rgb(0,0,204);">,</span><br />                     <span style="color:rgb(0,0,255);">void</span> <span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">*</span>input<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> sk_buff <span style="color:rgb(0,0,204);">*</span>skb<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">,</span><br />                     <span style="color:rgb(0,0,255);">struct</span> mutex <span style="color:rgb(0,0,204);">*</span>cb_mutex<span style="color:rgb(0,0,204);">,</span><br />                     <span style="color:rgb(0,0,255);">struct</span> module <span style="color:rgb(0,0,204);">*</span>module<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br /></font></span>

  struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用 init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。
在内核中,

<span style="color:rgb(0,0,0);"><font face="Courier New">audit_sock <span style="color:rgb(0,0,204);">=</span> netlink_kernel_create<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">&</span>init_net<span style="color:rgb(0,0,204);">,</span> NETLINK_AUDIT<span style="color:rgb(0,0,204);">,</span> 0<span style="color:rgb(0,0,204);">,</span><br />                                      audit_receive<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(255,0,0);">NULL</span><span style="color:rgb(0,0,204);">,</span> THIS_MODULE<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br /></font></span>

模块调用函数 netlink_unicast 来发送单播消息:

<span style="color:rgb(0,0,0);"><font face="Courier New"><span style="color:rgb(0,0,255);">int</span> netlink_unicast<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> sock <span style="color:rgb(0,0,204);">*</span>ssk<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(0,0,255);">struct</span> sk_buff <span style="color:rgb(0,0,204);">*</span>skb<span style="color:rgb(0,0,204);">,</span> u32 pid<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(0,0,255);">int</span> nonblock<span style="color:rgb(0,0,204);">)</span></font></span>


    参数ssk为函数 netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而 skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用 定时睡眠。
    netlink的内核实现在.c文件 net/core/af_netlink.c中,内核模块要想使用netlink,也必须包含头文件linux/netlink.h。内核使用 netlink需要专门的API,这完全不同于用户态应用对netlink的使用。如果用户需要增加新的netlink协议类型,必须通过修改 linux/netlink.h来实现,当然,目前的netlink实现已经包含了一个通用的协议类型NETLINK_GENERIC以方便用户使用,用户可以直接使用它而不必增加新的协议类型。前面讲到,为了增加新的netlink协议类型,用户仅需增加如下定义到linux/netlink.h就可以:
只要增加这个定义之后,用户就可以在内核的任何地方引用该协议。
在内核中,为了创建一个netlink socket用户需要调用如下函数:

<span style="color:rgb(0,0,0);"><font face="Courier New"><span style="color:rgb(0,0,255);">extern</span> <span style="color:rgb(0,0,255);">struct</span> sock <span style="color:rgb(0,0,204);">*</span>netlink_kernel_create<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> net <span style="color:rgb(0,0,204);">*</span>net<span style="color:rgb(0,0,204);">,</span><br />                                     <span style="color:rgb(0,0,255);">int</span> unit<span style="color:rgb(0,0,204);">,</span><span style="color:rgb(0,0,255);">unsigned</span> <span style="color:rgb(0,0,255);">int</span> groups<span style="color:rgb(0,0,204);">,</span><br />                                     <span style="color:rgb(0,0,255);">void</span> <span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">*</span>input<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> sk_buff <span style="color:rgb(0,0,204);">*</span>skb<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">,</span><br />                                     <span style="color:rgb(0,0,255);">struct</span> mutex <span style="color:rgb(0,0,204);">*</span>cb_mutex<span style="color:rgb(0,0,204);">,</span><br />                                     <span style="color:rgb(0,0,255);">struct</span> module <span style="color:rgb(0,0,204);">*</span>module<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span></font></span>

struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量

    参数unit表示netlink协议类型,如 NETLINK_MYTEST,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被引用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct sock结构来表示。

     函数input()会在发送进程执行sendmsg()时被调用,这样处理消息比较及时,但是,如果消息特别长时,这样处理将增加系统调用sendmsg()的执行时间,也就是说当用户的程序调用sendmsg ()函数时,如果input()函数处理时间过长,也就是说input()函数不执行不完,用户程序调用的sendmsg()函数就不会返回。只有当内核空间中的input()函数返回时,用户调用的sendmsg()函数才会返回。对于这种情况,可以定义一个内核线程专门负责消息接收,而函数input 的工作只是唤醒该内核线程,这样sendmsg将很快返回。(这里网上的的说明)不过在查看Linux2.6.37版本的内核时并没有发现这种处理过程,一般都是按下面的方法进行处理。

这里注册的netlink协议为NETLINK_XFRM。

<span style="color:rgb(0,0,0);"><font face="Courier New">nlsk <span style="color:rgb(0,0,204);">=</span> netlink_kernel_create<span style="color:rgb(0,0,204);">(</span>net<span style="color:rgb(0,0,204);">,</span> NETLINK_XFRM<span style="color:rgb(0,0,204);">,</span> XFRMNLGRP_MAX<span style="color:rgb(0,0,204);">,</span><br />                                 xfrm_netlink_rcv<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(255,0,0);">NULL</span><span style="color:rgb(0,0,204);">,</span> THIS_MODULE<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br /> <br /><br /><span style="color:rgb(0,0,255);">static</span> <span style="color:rgb(0,0,255);">void</span> xfrm_netlink_rcv<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> sk_buff <span style="color:rgb(0,0,204);">*</span>skb<span style="color:rgb(0,0,204);">)</span><br /><span style="color:rgb(0,0,204);">{</span><br />       mutex_lock<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">&</span>xfrm_cfg_mutex<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br /><br />       netlink_rcv_skb<span style="color:rgb(0,0,204);">(</span>skb<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(0,0,204);">&</span>xfrm_user_rcv_msg<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br />       mutex_unlock<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">&</span>xfrm_cfg_mutex<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">;</span><br /><span style="color:rgb(0,0,204);">}</span><br />在netlink_rcv_skb<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,204);">)</span>函数中进行接收处理。<br /> <br /><br /><span style="color:rgb(0,0,255);">int</span> netlink_broadcast<span style="color:rgb(0,0,204);">(</span><span style="color:rgb(0,0,255);">struct</span> sock <span style="color:rgb(0,0,204);">*</span>ssk<span style="color:rgb(0,0,204);">,</span> <span style="color:rgb(0,0,255);">struct</span> sk_buff <span style="color:rgb(0,0,204);">*</span>skb<span style="color:rgb(0,0,204);">,</span> u32 pid<span style="color:rgb(0,0,204);">,</span><br />                    u32 group<span style="color:rgb(0,0,204);">,</span> gfp_t allocation<span style="color:rgb(0,0,204);">)</span><br /></font></span>


    前面的三个参数与 netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。

<span style="color:rgb(0,0,0);"><font face="Courier New">NETLINK_CB<span style="color:rgb(0,0,204);">(</span>skb<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">.</span>pid <span style="color:rgb(0,0,204);">=</span> 0<span style="color:rgb(0,0,204);">;</span><br /><br />NETLINK_CB<span style="color:rgb(0,0,204);">(</span>skb<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">.</span>dst_pid <span style="color:rgb(0,0,204);">=</span> 0<span style="color:rgb(0,0,204);">;</span><br /><br />NETLINK_CB<span style="color:rgb(0,0,204);">(</span>skb<span style="color:rgb(0,0,204);">)</span><span style="color:rgb(0,0,204);">.</span>dst_group <span style="color:rgb(0,0,204);">=</span> 1<span style="color:rgb(0,0,204);">;</span><br /></font></span>

   字段pid表示消息发送者进程 ID,也即源地址,对于内核,它为 0, dst_pid 表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group 应当设置为 0。
  

相关推荐