TCP/IP协议--IP层ip_local_deliver实现

来源:互联网 发布:微博mac客户端是什么 编辑:程序博客网 时间:2024/05/18 03:03

上一节说到当判断该数据报文是发送给本机的话那么就会直接到ip_local_deliver()进行处理

ip_local_deliver

对于收到的IP报文需要传递给更上层的协议去处理,但是如果收到的是IP分片的那么就需要在往上层传递之前先进行重组,这些就是在ip_local_deliver()函数里面进行的。我们看下代码:

/* *  Deliver IP Packets to the higher protocol layers. */ int ip_local_deliver(struct sk_buff *skb) {      /*      *  Reassemble IP fragments.      */     struct net *net = dev_net(skb->dev);     //check if it is a fragment     if (ip_is_fragment(ip_hdr(skb))) {         //fragment recombination         if (ip_defrag(net, skb, IP_DEFRAG_LOCAL_DELIVER))             return 0;     }     return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,                net, NULL, skb, skb->dev, NULL,                ip_local_deliver_finish);}

首先使用ip_hdr()获取ip头部, 然后使用ip_is_fragment()来判断当前IP报文是否是一个分组:

static inline bool ip_is_fragment(const struct iphdr *iph){     //check the ip packet is a fragment or not, why  check IP_OFFSET??     return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;}

在上面ip头部结构里面我们看到有一个16位的frag_off,其中前三位是标志位,后13位是偏移位置,(如果不是一个分组 MF和OFFSET都是0,如果是是最后一个分组那么MF==0 但是OFFSET不为0)。那如果判断是一个分组的话那么就会调用ip_defrag()进行重组,重组成功就直接返回了,不会调用到ip_local_deliver_finish()。当最后一个报文过来或者是非分组的报文过来的话,ip_is_fragment()判断不通过直接就会进入ip_local_deliver_finish()。
下面我们就看下ip_defrag()是如何进行ip分片重组的:

/* Process an incoming IP datagram fragment. */int ip_defrag(struct net *net, struct sk_buff *skb, u32 user){    struct net_device *dev = skb->dev ? : skb_dst(skb)->dev;    int vif = l3mdev_master_ifindex_rcu(dev);    struct ipq *qp;     __IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);    skb_orphan(skb);    /* Lookup (or create) queue header */    qp = ip_find(net, ip_hdr(skb), user, vif);    if (qp) {       int ret;       spin_lock(&qp->q.lock);       ret = ip_frag_queue(qp, skb);       spin_unlock(&qp->q.lock);       ipq_put(qp);       return ret;    }    __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);    kfree_skb(skb);    return -ENOMEM;}EXPORT_SYMBOL(ip_defrag);

上面的这个函数首先调用ip_find()去查找是否已经在分组队列里存在相应的queue,如果没有那么就新建一个queue等待收集之后的报文,如果已经存在那么就返回queue的入口。ip_find()主要是调用iphashfn(),使用ip头部里的identifier、source addr、dest addr、protocol四个元素去进行hash计算。而iphashfn()调用的是jash_3words(), 该函数是高性能的查找算法。之后就是调用inet_frag_find()函数进行查找。

ip_local_deliver_finish

这边会通过skb头部里面的协议类型在inet_protos里面查找处理该协议的结构指针,是net_protocol指针类型。然后调用net_protocol->handler(),我们这边只是跟踪tcp报文,因为这边就是调用的tcp_v4_rcv(),报文处理流程进入到Inet Socket层。

static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb){    __skb_pull(skb, skb_network_header_len(skb));    rcu_read_lock();    {        //get the protocol of this packet        int protocol = ip_hdr(skb)->protocol;        const struct net_protocol *ipprot;        .....        //from inet_protos list to get the correct protocol struct depending on protocol as index        ipprot = rcu_dereference(inet_protos[protocol]);        if(ipprot) {            ...            ret = ipprot->handler(skb);//call the Lay4 handler function.            ...        }    }    ....}

上面的代码只是对于正常的报文的流程进行分析,另外我们看到在source code中如果找不到相关的协议处理函数会通过icmp_send()发送一个ICMP_DEST_UNREACH的消息。

0 0
原创粉丝点击