相关文章推荐
旅途中的鼠标垫  ·  图解Android ...·  2 月前    · 

主要涉及net/netlink/af_netlink.c与net/core/rtnetlink.c两个主文件。内核的网络子系统定义了rtnetlink,用做和用户空间的交互,rtnetlink为AF_NETLINK协议的一个类别NETLINK_ROUTE,其它类别包括NETLINK_XFRM、NETLINK_GENERIC等。renetlink主要注册了LINK、ROUTE、ADDRESS、NEIGHOUR等相关的操作。

本文以PF_UNSPEC协议族的RTM_GETLINK为例进行介绍,首先注册rtnetlink内核套接口,主要是注册一个接收函数挂载到AF_NETLINK的处理流程中:

static int __net_init rtnetlink_net_init(struct net *net)
    struct netlink_kernel_cfg cfg = {
        .input      = rtnetlink_rcv,
    sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);

其次,注册三个回调函数在全局的数组rtnl_msg_handlers上,其结构如下:

struct rtnl_link {
    rtnl_doit_func      doit;
    rtnl_dumpit_func    dumpit;
    rtnl_calcit_func    calcit;
} *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1];

PF_UNSPEC协议组的RTM_GETLINK类型注册了以下三个操作:

void rtnl_register(int protocol, int msgtype,
           rtnl_doit_func doit, rtnl_dumpit_func dumpit, rtnl_calcit_func calcit)
rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, rtnl_dump_ifinfo, rtnl_calcit);

其中,doit函数rtnl_getlink主要获取指定设备的接口信息,dumpit函数rtnl_dump_ifinfo获取全部接口的信息,calcit函数rtnl_calcit配合dumpit函数使用,计算出一个设备的接口信息所占用的空间(if_nlmsg_size()),用来控制分配返回数据的存储空间最小值。

用户空间应用获取全部接口信息时,需要在下发的nlmsghdr结构体成员nlmsg_flags变量中增加NLM_F_DUMP标志:

struct nlmsghdr nlh;
nlh.nlmsg_type = RTM_GETLINK;
nlh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST;

用户空间应用调用sendmsg发送rtnetlink消息,相应的内核处理函数为netlink_sendmsg,如下所示其最终调用的函数为rtnetlink初始化时注册的rtnetlink_rcv,准备返回用户的数据:

rtnetlink_rcv主要功能在函数rtnetlink_rcv_msg中实现,判断NLM_F_DUMP标志,选择执行doit或者dumpit函数:

static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
    if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
            struct netlink_dump_control c = {
                .dump       = dumpit,
                .min_dump_alloc = min_dump_alloc,
            err = netlink_dump_start(rtnl, skb, nlh, &c);
        rtnl_lock();
        return err;
    doit = rtnl_get_doit(family, type);
    if (doit == NULL)
        return -EOPNOTSUPP;
    return doit(skb, nlh);

doit函数即rtnl_getlink(),获取指定设备的链路信息,存储于sk_buff中,调用netlink_sendskb(),挂载到相应sock的sk->sk_receive_queue,等待用户空间应用调用recvmsg()返回。

dumpit函数即rtnl_dump_ifinfo(),获取指定命名空间的全部接口信息,数据量存在过大情况,分为多次传输完成。在netlink_dump_start函数中初始化传输参数,netlink_sock结构体成员cb_running标示传输开始,cb->args用作记录当前传输的位置,初始化时清零。netlink_dump获取第一次传输数据,挂载到sk_receive_queue,并且更新当前传输位置cb->args:

int __netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
             const struct nlmsghdr *nlh,
             struct netlink_dump_control *control)
    struct netlink_callback *cb;
    struct netlink_sock *nlk;
    cb = &nlk->cb;
    memset(cb, 0, sizeof(*cb));
    nlk->cb_running = true;	
    ret = netlink_dump(sk);

用户空间应用调用recvmsg获取数据,相应的内核处理函数为netlink_recvmsg,读取sk_receive_queue上之前准备好的数据返回。除此之外,对于NL_F_DUMP调用,调用netlink_dump从之前记录的位置开始(保存在cb->args),继续准备要返回的数据。如果判断数据已经读取完成,在netlink的头机构的nlmsg_flags成员中设置NLMSG_DONE通知应用程序,清除cb_running标志,结束dump传输。

rtnetlink 软件包rtnetlink允许读取和更改内核的路由表。 网络路由,IP地址,链接参数,邻居设置,排队规则,流量类别和数据包分类器都可以受到控制。 它基于netlink消息。 使用软件包可以方便,高级地使用API​​包装器。 基本的rtnetlink库仅向rtnetlink公开有限的低级API。 并非(也不希望)创建iproute2替代。 调试和网络链接错误 不幸的是,内核netlink接口生成的错误不是很大。 如果对消息结构有疑问,使用strace -f -esendmsg /bin/ip或类似方法查看iproute2发送的消息总是有用的。 另一种(可能甚至更灵活)的方法是使用nlmon和wireshark 。 nlmod是一个特殊的内核模块,它允许您捕获内核内部的所有netlink(而不仅仅是rtnetlink)流量。 请注意,这可能在具有大量netlink流 那当我们添加一条新路由时,在接收函数rtnetlink_rcv中的循环中,会从一个队列中调用实际的接收处理函数,这里为rtnetlink_rcv_msg函数。inet6_rtm_newroute函数通过下面的数组进行了相应的注册处理,所以上面的link->doit(skb, nlh, (void *)&rta_buf[0])就是根据下面的这个调用的。在/kernel/net/core/rtnetlink.c文件中,有一个接收从用户空间过来的Netlink消息的函数。// 多播的sock链表。 当nlmsghl中nlmsg_type为链路层消息RTM_NEWLINK ,RTM_DELLINK, RTM_GETLINK,RTM_SETLINK,时,消息头为ifinfomsg。当然不同的消息类型对应的消息头不相同。 3.1.2. Link Message Types RTM_GETLINK (user→kernel) Lookup link by 1. interface index or 2. link name (IFLA_IFNAME) and return a single RTM_NEWLINK message containing the link configuration and statistics or a netlink error message if no such link was foun 转到:http://blogold.chinaunix.net/u/15993/showart.php?id=89359 我们先从netlink说起,netlink其实就是一组宏,这组宏用来访问和创建netlink数据报,其实和其他套结字一样,只不过它是用来给用户进程和内核模块之间进行通信的,它的宏定义有:         #include <asm/types.h> netlink(2)- rtnetlinkrtnetlink 介绍: 当创建socket时, 协议类型参数选择的是NETLINK_ROUTE, 得到的socket是rtnetlink_socket, 需要使用到rtnetlink. ​ 所有rtnetlink 消息都包含一个netlink消息头和附加属性,rtnetlink 中定义的一组宏就是用于操作这些属性所用。 ​ rtnetlink 除了标准的netlink消息外还包含其它消息类型(如RTM_NEWLINK), 不同的消息对应的附加属性会有差异,后 一个字符( char ) 设备是一种可以当作一个字节流来存取的设备( 如同一个文件 ); 一个字符驱动负责实现这种行为. 这样的驱动常常至少实现 open, close, read, 和 write 系统调用. 文本控制台( /dev/console )和串口( /dev/ttyS0 及其友 )是字符设备的例子, 因为它们很好地展现了流的抽象. 字符设备通过文件系统结点来存取, 例如 一、RtnetlinkRtnetlink 允许对内核路由表进行读和更改,它用于内核与各个子系统之间(路由子系统、IP地址、链接参数等)的通信,用户空间可以通过NET_LINK_ROUTER socket 与内核进行通信,该过程基于标准的netlink消息进行。一些rtnetlink消息在初始头后有一些可选属性,下面是该属性的结构:1 struct rtattr {2 unsigned sh... 1. Introduction This library provides APIs to the kernel interfaces of the routing family. Work in progress. 2. Addresses 3. Links (Network D..   在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核交互就全部使用了netlink,著名的内核包过滤框架Netfilter在与用户空间的通读,也在最新版本中改变为netlink,无疑,它将是Linux用户态与内核态交流的主要方法之一。它的通信