linux下ip協議(V4)的實現(二)

來源:互聯網
上載者:User

linux下ip協議(V4)的實現(二)
這次主要介紹下forward和local delivery。

上次我們提到當ip_rcv_finish完成後後調用相關的發送函數ip_forward或者ip_local_deliver.這次就主要介紹這兩個函數。

先來看forward。

forward一般由下面幾部組成:

1 執行ip option
2 確定這個包能被forward
3 減小ttl,當ttl為0時,丟掉這個包
4 如果需要,則將這個包切片
5 發送包到輸出網路裝置

這裡要注意,如果包由於一些原因,不能被forward,則必鬚髮送ICMP訊息到發送主機。

Java代碼
int ip_forward(struct sk_buff *skb)  
{  
    struct iphdr *iph;  /* Our header */  
    struct rtable *rt;  /* Route we use */  
    struct ip_options * opt = &(IPCB(skb)->opt);  
///gso相關設定  
    if (skb_warn_if_lro(skb))  
        goto drop;  
///xfrm(ipsec)的相關檢測  
    if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))  
        goto drop;  
///判斷是否有Router_alter option(也就是儲存發送端的ip),如果有的話,調用ip_call_ra_chain處理,當空間已滿,則返回false,並繼續處理。  
    if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))  
        return NET_RX_SUCCESS;  
 
///判斷這個包是否是由本地主機的2層進行接受的。在2層設定幀的類型,當幀的目的地址就是本機2層地址的時候,skb->pkt_type設定為PCAKET_HOST.  
    if (skb->pkt_type != PACKET_HOST)  
        goto drop;  
 
///由於是forward,因此我們不需要在意4層的校正。設定ip_summed為CHECKSUM_NONE。  
    skb_forward_csum(skb);  
 
 
    /* 
     *  According to the RFC, we must first decrease the TTL field. If 
     *  that reaches zero, we must reply an ICMP control message telling 
     *  that the packet's lifetime expired. 
     */  
///ttl小於1,此時丟掉這個包  
    if (ip_hdr(skb)->ttl <= 1)  
        goto too_many_hops;  
 
///ipsec的檢測  
    if (!xfrm4_route_forward(skb))  
        goto drop;  
 
///得到路由表  
    rt = skb->rtable;  
 
///判斷是否是Strict源路由option。如果是的話,看源路由option所制定的路由能否和rt_gateway(下一跳)匹配。  
    if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)  
        goto sr_failed;  
 
///檢測一些相關域。如果出錯,則發送icmp,並丟棄這個包  
    if (unlikely(skb->len > dst_mtu(&rt->u.dst) && !skb_is_gso(skb) &&  
             (ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {  
        IP_INC_STATS(dev_net(rt->u.dst.dev), IPSTATS_MIB_FRAGFAILS);  
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,  
              htonl(dst_mtu(&rt->u.dst)));  
        goto drop;  
    }  
 
///由於我們將要修改這個skb的一些東西(在下面的ip_forward_finish中),因此我們需要複製一個拷貝(主要是防止skb共用)  
    if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))  
        goto drop;  
    iph = ip_hdr(skb);  
 
///減少ttl  
    ip_decrease_ttl(iph);  
 
///如果我們所找到的下一跳地址比請求的更好的話,源host現在將會收到一個ICMP REDIRESCT訊息(只有當源host沒有請求 source routing option時)  
    if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb->sp)  
        ip_rt_send_redirect(skb);  
 
///QOS的優先順序設定  
    skb->priority = rt_tos2priority(iph->tos);  
 
///最終返回netfilter的hook,這裡我們還是暫時忽略netfilter,只關注ip_forward_finish.  
    return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,  
               ip_forward_finish);  
 
sr_failed:  
    /* 
     *  Strict routing permits no gatewaying 
     */  
     icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);  
     goto drop;  
 
too_many_hops:  
    /* Tell the sender its packet died... */  
    IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_INHDRERRORS);  
    icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);  
drop:  
    kfree_skb(skb);  
    return NET_RX_DROP;  

下來看ip_forward_finish方法,它主要是用來處理一些剩下的options,並且ip_forward_options還會重新計算ip checksum,因為它會update一些ip頭的域:

Java代碼
static int ip_forward_finish(struct sk_buff *skb)  
{  
    struct ip_options * opt = &(IPCB(skb)->opt);  
 
    IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);  
 
    if (unlikely(opt->optlen))  
        ip_forward_options(skb);  
///最終返回dst_output,這個虛函數最終調用skb->dst_output,如果是單播則是ip_output,如果是多播則是ip_mc_output.而且切片(如果有需要)也會在這個函數進行).這裡還有一個neighboring subsystem的概念,我們後面會講到。  
    return dst_output(skb);  

來看ip_local_deliver函數:

Java代碼
int ip_local_deliver(struct sk_buff *skb)  
{  
    /* 
     *  Reassemble IP fragments. 
     */  
///如果有切片,則開始組包。  
    if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {  
        if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))  
            return 0;  
    }  
///返回netfilter hook,最終會調用ip_local_deliver_finish.它最終會將資料包發送往4層。下一次我們會詳細介紹這個函數。  
    return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,  
               ip_local_deliver_finish);  

這裡我們可以看到一個對比,那就是forward時,不需要組包,這是因為local delivery必須把完整的包發送往4層,而forward,不需要經過4層,就直接把幀發送出去。

最後我們來看下ip_local_deliver_finish函數片段:

Java代碼
static int ip_local_deliver_finish(struct sk_buff *skb)  
{  
    struct net *net = dev_net(skb->dev);  
 
    __skb_pull(skb, ip_hdrlen(skb));  
 
    /* Point into the IP datagram, just past the header. */  
    skb_reset_transport_header(skb);  
 
    rcu_read_lock();  
    {  
        int protocol = ip_hdr(skb)->protocol;  
        int hash, raw;  
        struct net_protocol *ipprot;  
 
    resubmit:  
///對raw socket進行處理。  
        raw = raw_local_deliver(skb, protocol);  
 
        hash = protocol & (MAX_INET_PROTOS - 1);  
        ipprot = rcu_dereference(inet_protos[hash]);  
        if (ipprot != NULL) {  
            int ret;  
 
...................................................  
///將資料包交給登入的高層協議的處理函數。  
            ret = ipprot->handler(skb);  
            if (ret < 0) {  
                protocol = -ret;  
                goto resubmit;  
            }  
            IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);  
        }   
...................................................  
    }  
 out:  
    rcu_read_unlock();  
 
    return 0;  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.