Linux 内核中6to4隧道的处理流程

Linux大全评论2.3K views阅读模式

当IP数据包上传给IP层时,有IP层的ip_rcv()函数进行接收,这个函数的主要作用就是检查这个数据包。这个函数不会处理不属于这个主机的数据包。也就是这个包的目标MAC地址不是本机的话,该函数就不会处理,而不是L3层的IP地址。
这里我们可以看到pkt_type的类型是PACKET_OTHERHOST,说明这里是其它主机的数据包,直接进行丢弃处理。在网卡获取数据包的中断处理过程(ei_interrupt)中调用了ei_receive,而ei_receive又调用eth_type_trans,在这个过程中将包的类型设置为PACKET_OTHEHOST。

然后在ip_rcv中判断包类型是不是PACKET_OTHERHOST,如果是则丢弃之。

 

if (skb->pkt_type == PACKET_OTHERHOST)
        goto drop;

接下来是一个共享的检查,如果是共享的数据包,需要修改skb中的信息,所以需要复制一个副本,再作进一步的处理。

if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {

       IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);

       goto out;

    }

再下面的处理就是检查首部的长度是否够长,校检和,版本号等信息。
if (iph->ihl < 5 || iph->version != 4)
        goto inhdr_error;

    if (!pskb_may_pull(skb, iph->ihl*4))
        goto inhdr_error;
如果没有错误的话就会调ip_rcv_finiish()函数进行进一步的处理。
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
         ip_rcv_finish);
在这个函数中首先查找路由信息,
if (likely(skb->dst == NULL)) {
        int err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
                     skb->dev);
然后,对IP头部中的选项信息进行了进行处理。
if (iph->ihl > 5 && ip_rcv_options(skb))
        goto drop;

    return dst_input(skb);

    最后,返回调用dst_input()函数,这个函数主机是根据路由信息调用相应的input函数,

err = skb->dst->input(skb);

    但是这个input函数可能是ip_local_deliver或者是ip_forward();

Ip_forward()函数主机是对数据包进行转发发送,ip_local_deliver()函数主要是在本机进行处理的过程。

    首先,确定接收到的包是不是分片,如果是,则要将分片重新装成一个完整的IP数据包再上传给L4层。最后调用ip_local_deliver_finish()函数。

int ip_local_deliver(struct sk_buff *skb)
if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
        skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER);
        if (!skb)
            return 0;
    }
return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
         ip_local_deliver_finish);
ip_local_deliver_finish

确定将这个数据包传送给L4层,L3层的头已经没有作用,先去掉L3的头部。

int ihl = skb->nh.iph->ihl*4;

    __skb_pull(skb, ihl);//将skb->data指向L4的头部。

        /* Point into the IP datagram, just past the header. */
        skb->h.raw = skb->data;
// 重新设置skb->h.raw

接下来就是要处理与L4层相关的协议了,这里首先处理的是Raw IP,先查看raw_v4_htable有没有注册这个L4协议的Raw IP。
int protocol = skb->nh.iph->protocol;
        int hash;
        struct sock *raw_sk;
        struct net_protocol *ipprot;

    resubmit:
        hash = protocol & (MAX_INET_PROTOS - 1);
        raw_sk = sk_head(&raw_v4_htable[hash]);
如果有,则要执行raw_v4_input()对其进行处理。提交一个副本。
if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash))
            raw_sk = NULL;
然后,对L4层的各层协议进行相应的处理,调用不同的处理函数。
if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) {
            int ret;

            if (!ipprot->no_policy) {
                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                    kfree_skb(skb);
                    goto out;
                }
                nf_reset(skb);
            }
            ret = ipprot->handler(skb);
//这里是调用相应的处理函数。

            if (ret < 0) {
                protocol = -ret;
                goto resubmit;
            }

调用相应的处理函数时要进行相就的注册,这里主要依据6to4隧道为例。我们可以在源码文件中sit.c,中的sit_init()函数中看到下面的注册添加函数。

if (inet_add_protocol(&sit_protocol, IPPROTO_IPV6) < 0) {
        printk(KERN_INFO "sit init: Can't add protocol\n");
        return -EAGAIN;
    }

企鹅博客
  • 本文由 发表于 2019年7月28日 12:35:54
  • 转载请务必保留本文链接:https://www.qieseo.com/154525.html

发表评论