基于linux-2.6.31的IPV6的数据包接收流程分析

来源:互联网 发布:学电脑打字软件 编辑:程序博客网 时间:2024/06/05 03:33

接收的流程为:ipv6_rcv--->ipv6_rcv_finish---->dst_input-àip6_input-àip6_input_finish

或者ipv6_rcv--->ipv6_rcv_finish---->ip6_route_input或者ipv6_rcv--->ipv6_rcv_finish---->dst_input-àip6_forward-àip6_forward_finish

static struct packet_type ipv6_packet_type__read_mostly = {

         .type= cpu_to_be16(ETH_P_IPV6),

         .func= ipv6_rcv,

         .gso_send_check= ipv6_gso_send_check,

         .gso_segment= ipv6_gso_segment,

         .gro_receive= ipv6_gro_receive,

         .gro_complete= ipv6_gro_complete,

};

 

//执行一些检查,判断数据包是否转发、有效性、正确性

intipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,struct net_device *orig_dev)

{

         structipv6hdr *hdr;

         u32                   pkt_len;

         structinet6_dev *idev;

//获取数据包网卡

         structnet *net = dev_net(skb->dev);

//丢弃发送给其他主机的数据包

         if(skb->pkt_type == PACKET_OTHERHOST) {

                   kfree_skb(skb);

                   return0;

         }

 

         rcu_read_lock();

 

         idev= __in6_dev_get(skb->dev);

 

         IP6_UPD_PO_STATS_BH(net,idev, IPSTATS_MIB_IN, skb->len);

//

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

             !idev ||unlikely(idev->cnf.disable_ipv6)) {

                   IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INDISCARDS);

                   gotodrop;

         }

 

         memset(IP6CB(skb),0, sizeof(struct inet6_skb_parm));

 

         /*

          * Store incoming device index. When the packetwill

          * be queued, we cannot refer to skb->devanymore.

          *

          * BTW, when we send a packet for our own localaddress on a

          * non-loopback interface (e.g. ethX), it isbeing delivered

          * via the loopback interface (lo) here;skb->dev = loopback_dev.

          * It, however, should be considered as if itis being

          * arrived via the sending interface (ethX),because of the

          * nature of scoping architecture. --yoshfuji

          */

//保存入口设备索引

         IP6CB(skb)->iif= skb_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex;

//检查数据包长度是否为IP报头的长度

         if(unlikely(!pskb_may_pull(skb, sizeof(*hdr))))

                   gotoerr;

//获取IPv6报头位置

         hdr= ipv6_hdr(skb);

//检查版本是否为IPv6

         if(hdr->version != 6)

                   gotoerr;

 

         /*

          * RFC4291 2.5.3

          * A packet received on an interface with adestination address

          * of loopback must be dropped.

          */

//丢弃环路数据包

         if(!(dev->flags & IFF_LOOPBACK) &&

             ipv6_addr_loopback(&hdr->daddr))

                   gotoerr;

 

         skb->transport_header= skb->network_header + sizeof(*hdr);

         IP6CB(skb)->nhoff= offsetof(struct ipv6hdr, nexthdr);

 

         pkt_len= ntohs(hdr->payload_len);

//处理Jumbo负载选项

         /*pkt_len may be zero if Jumbo payload option is present */

         if(pkt_len || hdr->nexthdr != NEXTHDR_HOP) {

                   if(pkt_len + sizeof(struct ipv6hdr) > skb->len) {

                            IP6_INC_STATS_BH(net,

                                                idev, IPSTATS_MIB_INTRUNCATEDPKTS);

                            gotodrop;

                   }

                   if(pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) {

                            IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INHDRERRORS);

                            gotodrop;

                   }

                   hdr= ipv6_hdr(skb);

         }

 

         if(hdr->nexthdr == NEXTHDR_HOP) {

                   if(ipv6_parse_hopopts(skb) < 0) {

                            IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INHDRERRORS);

                            rcu_read_unlock();

                            return0;

                   }

         }

 

         rcu_read_unlock();

 

         /*Must drop socket now because of tproxy. */

         skb_orphan(skb);

//由过滤器调用IP6_rcv_finish函数进一步处理数据包

         returnNF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, dev, NULL,

                          ip6_rcv_finish);

err:

         IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INHDRERRORS);

drop:

         rcu_read_unlock();

         kfree_skb(skb);

         return0;

}

//如果路由表项信息已经缓存在套接字缓冲区的dst字段,则直接用skb->dst->input指向的函数;否则,调用ip6_rout_input函数查找路由表,返回skb->dst->input的具体内容

inlineint ip6_rcv_finish( struct sk_buff *skb)

{

         if(skb_dst(skb) == NULL)

                   ip6_route_input(skb);

 

         returndst_input(skb);

}

//获取目的地址描述符

static inlinestruct dst_entry *skb_dst(const struct sk_buff *skb)

{

         return (struct dst_entry*)skb->_skb_dst;

}

/* Input packetfrom network to transport. 

   将数据包从网络层送到传输层

*/

static inline int dst_input(struct sk_buff *skb)

{

         return skb_dst(skb)->input(skb);

}

//交给路由模块

void ip6_route_input(struct sk_buff *skb)

{

         struct ipv6hdr *iph = ipv6_hdr(skb);

         struct net *net = dev_net(skb->dev);

         int flags = RT6_LOOKUP_F_HAS_SADDR;

         struct flowi fl = {

                   .iif = skb->dev->ifindex,

                   .nl_u = {

                            .ip6_u = {

                                     .daddr =iph->daddr,

                                     .saddr =iph->saddr,

                                     .flowlabel= (* (__be32 *) iph)&IPV6_FLOWINFO_MASK,

                            },

                   },

                   .mark = skb->mark,

                   .proto = iph->nexthdr,

         };

 

         if (rt6_need_strict(&iph->daddr)&& skb->dev->type != ARPHRD_PIMREG)

                   flags |= RT6_LOOKUP_F_IFACE;

 

         skb_dst_set(skb, fib6_rule_lookup(net,&fl, flags, ip6_pol_route_input));

}

 

 

intip6_input(struct sk_buff *skb)

{

         returnNF_HOOK(PF_INET6, NF_INET_LOCAL_IN, skb, skb->dev, NULL,

                          ip6_input_finish);

}

staticint ip6_input_finish(struct sk_buff *skb)

{

         structinet6_protocol *ipprot;

         unsignedint nhoff;

         intnexthdr, raw;

         u8hash;

         structinet6_dev *idev;

         structnet *net = dev_net(skb_dst(skb)->dev);

 

         /*

          *     Parseextension headers解析扩展头

          */

 

         rcu_read_lock();

resubmit:

//指向目的设备

         idev= ip6_dst_idev(skb_dst(skb));

//检查数据包长度是否是传输层头部

         if(!pskb_pull(skb, skb_transport_offset(skb)))

                   gotodiscard;

// #define IP6CB(skb)       ((struct inet6_skb_parm*)((skb)->cb))

         nhoff= IP6CB(skb)->nhoff;

//获取下一个扩展头部

         nexthdr= skb_network_header(skb)[nhoff];

/*

int raw6_local_deliver(struct sk_buff *skb,int nexthdr)

{

         structsock *raw_sk;

//判断可否通过原始套接字接受数据,如果可以,则返回对应原始套接字,并通过ipv6_raw_deliver函数接受套接字缓冲区中的数据内容

         raw_sk= sk_head(&raw_v6_hashinfo.ht[nexthdr & (MAX_INET_PROTOS - 1)]);

         if(raw_sk && !ipv6_raw_deliver(skb, nexthdr))

                   raw_sk= NULL;

 

         returnraw_sk != NULL;

}

*/

         raw= raw6_local_deliver(skb, nexthdr);

//查找inet6_protos表,确定是否注册过第四层协议,如果有,则调用对应的函数来接受数据包

         hash= nexthdr & (MAX_INET_PROTOS - 1);

         if((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {

                   intret;

 

                   if(ipprot->flags & INET6_PROTO_FINAL) {

                            structipv6hdr *hdr;

 

                            /*Free reference early: we don't need it any more,

                               and it may hold ip_conntrack module loaded

                               indefinitely. */

                            nf_reset(skb);

 

                            skb_postpull_rcsum(skb,skb_network_header(skb),

                                                  skb_network_header_len(skb));

                            hdr= ipv6_hdr(skb);

                            if(ipv6_addr_is_multicast(&hdr->daddr) &&

                                !ipv6_chk_mcast_addr(skb->dev,&hdr->daddr,

                                &hdr->saddr) &&

                                !ipv6_is_mld(skb, nexthdr))

                                     gotodiscard;

                   }

                   if(!(ipprot->flags & INET6_PROTO_NOPOLICY) &&

                       !xfrm6_policy_check(NULL, XFRM_POLICY_IN,skb))

                            gotodiscard;

//通过ipprot的handler指针调用上层协议的接受函数;

//对于TCP,调用tcp_v6_rcv;对于UDP,调用udpv6_rcv;对于ICMP,调用icmpv6_rcv

                   ret= ipprot->handler(skb);

                   if(ret > 0)

                            goto resubmit;

                   elseif (ret == 0)

                            IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INDELIVERS);

         }else {

                   if(!raw) {

                            if(xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {

                                     IP6_INC_STATS_BH(net,idev,

                                                         IPSTATS_MIB_INUNKNOWNPROTOS);

                                     icmpv6_send(skb,ICMPV6_PARAMPROB,

                                                   ICMPV6_UNK_NEXTHDR, nhoff,

                                                   skb->dev);

                            }

                   }else

                            IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INDELIVERS);

                   kfree_skb(skb);

         }

         rcu_read_unlock();

         return0;

 

discard:

         IP6_INC_STATS_BH(net,idev, IPSTATS_MIB_INDISCARDS);

         rcu_read_unlock();

         kfree_skb(skb);

         return0;

}

原创粉丝点击