RTNETLINK内核与用户空间网络子系统交互机制

TCPIP协议 专栏收录该内容
105 篇文章 6 订阅

主要涉及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传输。


展开阅读全文
  • 0
    点赞
  • 0
    评论
  • 7
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值