发新话题
打印

Linux2.4.0 IP包的接收过程

           Linux2.4.0 IP包的接收过程
QQ:505333        欲?明月  EMAIL:getmoon@163.com
下载:http://linuxsource.home.sohu.com/download/iprcv.zip

/* Handle this out of line, it is rare. */
static int ip_run_ipprot(struct sk_buff *skb, struct iphdr *iph,
                         struct inet_protocol *ipprot, int force_copy)
{
        int ret = 0;
        /*在有原始套接字的时候通过这里处理,先拷贝一个skbuff*/
        do {
                if (ipprot->protocol == iph->protocol) {
                        struct sk_buff *skb2 = skb;
                        if (ipprot->copy || force_copy)
                                skb2 = skb_clone(skb, GFP_ATOMIC);
                        if(skb2 != NULL) {
                                ret = 1;
                                ipprot->handler(skb2,
                                                ntohs(iph->tot_len) - (iph->ihl * 4));
                        }
                }
                ipprot = (struct inet_protocol *) ipprot->next;
        } while(ipprot != NULL);

        return ret;
}

static inline int ip_local_deliver_finish(struct sk_buff *skb)
{
        struct iphdr *iph = skb->nh.iph;

#ifdef CONFIG_NETFILTER_DEBUG
        nf_debug_ip_local_deliver(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/

        /* Point into the IP datagram, just past the header. */
        skb->h.raw = skb->nh.raw + iph->ihl*4;

        {
                /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
                int hash = iph->protocol & (MAX_INET_PROTOS - 1);
                struct sock *raw_sk = raw_v4_htable[hash];
                struct inet_protocol *ipprot;
                int flag;

                /* If there maybe a raw socket we must check - if not we
                 * don't care less
                 */


        /*向上层分发收到的目的地址是本机的包,首先分发原始套接字*/
                if(raw_sk != NULL)
                        raw_sk = raw_v4_input(skb, iph, hash);

                /*没有冲突的哈希表,直接定向到协议指针*/
                ipprot = (struct inet_protocol *) inet_protos[hash];
                flag = 0;
                if(ipprot != NULL) {
                        if(raw_sk == NULL &&                       
                           ipprot->next == NULL &&
                           ipprot->protocol == iph->protocol) {                /*没有原始套接字的时候这里处理*/
                                int ret;
                               
                                /* Fast path... */
                                ret = ipprot->handler(skb, (ntohs(iph->tot_len) -
                                                            (iph->ihl * 4)));
                                /*对应的操作函数是tcp_v4_rcv,icmp_rcv,igmp_rcv,udp_rcv*/

                                return ret;
                        } else {
                                flag = ip_run_ipprot(skb, iph, ipprot, (raw_sk != NULL));
                                /*有 raw socket 的时候克隆一个包,调用这个分发给上层*/
                        }
                }

                /* All protocols checked.
                 * If this packet was a broadcast, we may *not* reply to it, since that
                 * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
                 * ICMP reply messages get queued up for transmission...)
                 */
                if(raw_sk != NULL) {        /* Shift to last raw user */
                        raw_rcv(raw_sk, skb);
                        sock_put(raw_sk);
                } else if (!flag) {                /* Free and report errors */
                        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);       
                        kfree_skb(skb);
                }
        }

        return 0;
}

/*
*         Deliver IP Packets to the higher protocol layers.
*/
int ip_local_deliver(struct sk_buff *skb)
{
        struct iphdr *iph = skb->nh.iph;

        /*
         *        Reassemble IP fragments.
         */

        if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {        /*判断是否是一个分片包*/
                skb = ip_defrag(skb);                        /*分片重组*/
                if (!skb)
                        return 0;
        }
        /*netfilter NF_IP_LOCAL_IN检测点,检查完以后如果没有被丢弃的话调用ip_local_deliver_finish*/
        return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
                       ip_local_deliver_finish);
}

static inline int ip_rcv_finish(struct sk_buff *skb)
{
        struct net_device *dev = skb->dev;
        struct iphdr *iph = skb->nh.iph;

        /*
         *        Initialise the virtual path cache for the packet. It describes
         *        how the packet travels inside Linux networking.
         */
        if (skb->dst == NULL) {                /*路由为空的时候检查路由,因为要确定这个
                                                                包是怎么处理的,是转发还是本地分发*/
                if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))
                        /*为一个包选择路由*/
                        goto drop;
        }

#ifdef CONFIG_NET_CLS_ROUTE
        if (skb->dst->tclassid) {
                struct ip_rt_acct *st = ip_rt_acct + 256*smp_processor_id();
                u32 idx = skb->dst->tclassid;
                st[idx&0xFF].o_packets++;
                st[idx&0xFF].o_bytes+=skb->len;
                st[(idx>>16)&0xFF].i_packets++;
                st[(idx>>16)&0xFF].i_bytes+=skb->len;
        }
#endif

        if (iph->ihl > 5) {                /*选项处理*/
                struct ip_options *opt;

                /* It looks as overkill, because not all
                   IP options require packet mangling.
                   But it is the easiest for now, especially taking
                   into account that combination of IP options
                   and running sniffer is extremely rare condition.
                                                      --ANK (980813)
                */

                skb = skb_cow(skb, skb_headroom(skb));
                if (skb == NULL)
                        return NET_RX_DROP;
                iph = skb->nh.iph;

                skb->ip_summed = 0;
                if (ip_options_compile(NULL, skb))
                        goto inhdr_error;

                opt = &(IPCB(skb)->opt);
                if (opt->srr) {
                        struct in_device *in_dev = in_dev_get(dev);
                        if (in_dev) {
                                if (!IN_DEV_SOURCE_ROUTE(in_dev)) {
                                        if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
                                                printk(KERN_INFO "source route option %u.%u.%u.%u -> %u.%u.%u.%u\n",
                                                       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
                                        in_dev_put(in_dev);
                                        goto drop;
                                }
                                in_dev_put(in_dev);
                        }
                        if (ip_options_rcv_srr(skb))                /*源路由选项处理*/
                                goto drop;
                }
        }

        return skb->dst->input(skb);
        /*是发送到本机的包,往下的操作函数是ip_local_deliver*/
         /*这条路由是用来转发的,对应操作函数是ip_forward*/
        /**/
        /*查找不到路由的处理情况则是ip_error*/

inhdr_error:
        IP_INC_STATS_BH(IpInHdrErrors);
drop:
        kfree_skb(skb);
        return NET_RX_DROP;
}

/*
*         Main IP Receive routine.
*/
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
        struct iphdr *iph = skb->nh.iph;                /*网络层头*/

        /* When the interface is in promisc. mode, drop all the crap
         * that it receives, do not try to analyse it.
         */
        if (skb->pkt_type == PACKET_OTHERHOST)
                goto drop;

        IP_INC_STATS_BH(IpInReceives);                /*增加snmp计数*/

        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
                goto out;
                        /*是否是一个共享的skb,通过判断skb->user/*
        /*
         *        RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
         *
         *        Is the datagram acceptable?
         *
         *        1.        Length at least the size of an ip header
         *        2.        Version of 4
         *        3.        Checksums correctly. [Speed optimisation for later, skip loopback checksums]
         *        4.        Doesn't have a bogus length
         */


        /*检查包头的正确性*/
        if (skb->len < sizeof(struct iphdr) || skb->len < (iph->ihl<<2))
                goto inhdr_error;
        if (iph->ihl < 5 || iph->version != 4 || ip_fast_csum((u8 *)iph, iph->ihl) != 0)
                goto inhdr_error;

        {
                __u32 len = ntohs(iph->tot_len);
                if (skb->len < len || len < (iph->ihl<<2))
                        goto inhdr_error;

                /* Our transport medium may have padded the buffer out. Now we know it
                 * is IP we can trim to the true length of the frame.
                 * Note this now means skb->len holds ntohs(iph->tot_len).
                 */
                __skb_trim(skb, len);
        }

        /*netfilter检查NF_IP_PRE_ROUTING检测点,检测完以后调用ip_rcv_finish函数*/
        return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
                       ip_rcv_finish);

inhdr_error:
        IP_INC_STATS_BH(IpInHdrErrors);
drop:
        kfree_skb(skb);
out:
        return NET_RX_DROP;
}
      

TOP

发新话题