亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

Chinaunix

標(biāo)題: Linux內(nèi)核發(fā)送構(gòu)造數(shù)據(jù)包的方式 [打印本頁(yè)]

作者: Godbach    時(shí)間: 2009-09-02 13:04
標(biāo)題: Linux內(nèi)核發(fā)送構(gòu)造數(shù)據(jù)包的方式
本文歡迎自由轉(zhuǎn)載,但請(qǐng)標(biāo)明出處,并保證本文的完整性。

      作者:Godbach

      日期:2009/09/01


一、構(gòu)造數(shù)據(jù)包簡(jiǎn)析

這里并不詳細(xì)介紹如何在內(nèi)核中構(gòu)造數(shù)據(jù)包,下文如有需要會(huì)在適當(dāng)?shù)奈恢眠M(jìn)行分析。這里簡(jiǎn)單的分析講一下內(nèi)核態(tài)基于Netfilter框架構(gòu)造數(shù)據(jù)包的方式。
內(nèi)核中可以用到的構(gòu)造數(shù)據(jù)包的方式,個(gè)人認(rèn)為可以分為兩種。

其一,我們直接用alloc_skb申請(qǐng)一個(gè)skb結(jié)構(gòu)體,然后根據(jù)實(shí)際的應(yīng)用填充不同的成員,或者基于當(dāng)前數(shù)據(jù)包的skb,調(diào)用skb_copy_expand()函數(shù)等新申請(qǐng)一個(gè)nskb,并且拷貝skb的內(nèi)容。

其二,也是個(gè)人比較常用的,就是直接在先前接收到的數(shù)據(jù)包skb上作修改,主要有源IP、目IP,如果是TCP/UDP協(xié)議的話(huà),還有源端口目的端口號(hào)。總之,就是根據(jù)自己的需求去調(diào)整數(shù)據(jù)包的相關(guān)成員即可。

通常,這兩種方式最終可能都要涉及到重新計(jì)算各個(gè)部分的校驗(yàn)和,這也是必須的。


二、如何發(fā)送構(gòu)造的數(shù)據(jù)包

       承接上文,數(shù)據(jù)包已經(jīng)構(gòu)造完畢,下一步關(guān)鍵就是如何發(fā)送數(shù)據(jù)包了。個(gè)人這里總結(jié)的有兩種方法。

       方法一,就是讓數(shù)據(jù)包接著按照Netfilter的流程進(jìn)行傳輸。因?yàn)閿?shù)據(jù)包的一些內(nèi)容已經(jīng)被更改,尤其是當(dāng)源IP和目的IP被更改,主要是交換的情況下,是需要確保有路由可查的。

       NF框架中查路由的位置一是在PREROUTING之后,而是在LOCALOUT之后。又由于這里是需要將數(shù)據(jù)包從本地發(fā)送出去。因此,可以考慮讓修改后的數(shù)據(jù)包從LOCALOUT點(diǎn)發(fā)出。

       內(nèi)核代碼中有這種方式的典型體現(xiàn)。本文涉及的相關(guān)內(nèi)核代碼的版本都是2.6.18.3。源文件為ipt_REJECT.c,函數(shù)send_reset用于往當(dāng)前接收到數(shù)據(jù)包的源IP上發(fā)送RST包,整個(gè)函數(shù)涉及了數(shù)據(jù)包的構(gòu)造和發(fā)送,這里一起做個(gè)簡(jiǎn)單分析。
  1. /* Send RST reply */
  2. static void send_reset(struct sk_buff *oldskb, int hook)
  3. {
  4.         struct sk_buff *nskb;
  5.         struct iphdr *iph = oldskb->nh.iph;
  6.         struct tcphdr _otcph, *oth, *tcph;
  7.         struct rtable *rt;
  8.         u_int16_t tmp_port;
  9.         u_int32_t tmp_addr;
  10.         int needs_ack;
  11.         int hh_len;

  12.         /* 判斷是否是分片包*/
  13.         if (oldskb->nh.iph->frag_off & htons(IP_OFFSET))
  14.                 return;
  15.        
  16. /*得到TCP頭部指針*/
  17.         oth = skb_header_pointer(oldskb, oldskb->nh.iph->ihl * 4,
  18.                                  sizeof(_otcph), &_otcph);
  19.         if (oth == NULL)
  20.                 return;

  21.         /* 當(dāng)期收到的包就是RST包,就不用再發(fā)送RST包了*/
  22.         if (oth->rst)
  23.                 return;

  24.         /*檢查數(shù)據(jù)包的校驗(yàn)和是否正確*/
  25.         if (nf_ip_checksum(oldskb, hook, iph->ihl * 4, IPPROTO_TCP))
  26.                 return;
  27.         /*這一步比較關(guān)鍵,做的就是更新路由的工作。該函數(shù)的主要工作就是將當(dāng)前數(shù)據(jù)包的源IP當(dāng)做路由的目的IP,同時(shí)考慮數(shù)據(jù)包的目的IP,得到去往該源IP的路由*/
  28.         if ((rt = route_reverse(oldskb, oth, hook)) == NULL)
  29.                 return;

  30.         hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);

  31.         /* 拷貝當(dāng)前的oldskb,包括skb結(jié)構(gòu)體和數(shù)據(jù)部分。這就是我們上面提到的構(gòu)造數(shù)據(jù)包的第一種方式*/
  32.         nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
  33.                                GFP_ATOMIC);
  34.         if (!nskb) {
  35.                 dst_release(&rt->u.dst);
  36.                 return;
  37.         }

  38.         /*因?yàn)槭强截惖膐ldskb,這里不需要再引用了,因此釋放對(duì)該路由項(xiàng)的引用*/
  39.         dst_release(nskb->dst);
  40.         /*將新構(gòu)造數(shù)據(jù)包引用的路由指向上面由route_reverse函數(shù)返回的新的路由項(xiàng) */
  41.         nskb->dst = &rt->u.dst;

  42.         /* 清除nskb中拷貝過(guò)來(lái)的oldskb中鏈接跟蹤相關(guān)的內(nèi)容*/
  43.         nf_reset(nskb);
  44.         nskb->nfmark = 0;
  45.         skb_init_secmark(nskb);

  46.         /*以下就是構(gòu)造數(shù)據(jù)包的實(shí)際數(shù)據(jù)部分。如果我們將這里不為nskb新申請(qǐng)緩沖區(qū),而直接指向oldskb的緩沖區(qū),就使我們上面提到的第二種構(gòu)造數(shù)據(jù)包的方法。*/
  47.         /*獲取nskb的tcp header*/
  48.         tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);

  49.         /*交換源和目的IP */
  50.         tmp_addr = nskb->nh.iph->saddr;
  51.         nskb->nh.iph->saddr = nskb->nh.iph->daddr;
  52.         nskb->nh.iph->daddr = tmp_addr;

  53.         /*交換源和目的端口 */
  54.         tmp_port = tcph->source;
  55.         tcph->source = tcph->dest;
  56.         tcph->dest = tmp_port;

  57.         /*重置TCP頭部的長(zhǎng)度,并修改IP頭部中記錄的數(shù)據(jù)包的總長(zhǎng)度。因?yàn)檫@里是發(fā)送RST報(bào)文,只需要有TCP的頭部,不需要TCP的數(shù)據(jù)部分*/
  58.         tcph->doff = sizeof(struct tcphdr)/4;
  59.         skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
  60.         nskb->nh.iph->tot_len = htons(nskb->len);

  61.         /*重新設(shè)置 seq, ack_seq,分兩種情況(TCP/IP詳解有描述)*/
  62.         if (tcph->ack) { /*原始數(shù)據(jù)包中ACK標(biāo)記位置位的情況*/
  63.                 needs_ack = 0;
  64.                 tcph->seq = oth->ack_seq; /*原始數(shù)據(jù)包的ack_seq作為nskb的seq*/
  65.                 tcph->ack_seq = 0;
  66.         } else { /*原始數(shù)據(jù)包中ACK標(biāo)記位沒(méi)有置位的情況,初始連接SYN或者結(jié)束連接FIN等*/
  67.                 needs_ack = 1;
  68.                 /*這種情況應(yīng)該是SYN或者FIN包,由于SYN和FIN包都占用1個(gè)字節(jié)的長(zhǎng)度。因此ack_seq應(yīng)該等于舊包的seq+1即可。這里之所以這樣表示,可能是還存在其他情況的數(shù)據(jù)包。*/
  69.                 tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin
  70.                                       + oldskb->len - oldskb->nh.iph->ihl*4
  71.                                       - (oth->doff<<2));
  72.                 tcph->seq = 0;
  73.         }

  74.         /* RST標(biāo)記位置1*/
  75.         ((u_int8_t *)tcph)[13] = 0;
  76.         tcph->rst = 1;
  77.         tcph->ack = needs_ack;

  78.         tcph->window = 0;
  79.         tcph->urg_ptr = 0;

  80.         /*重新計(jì)算TCP校驗(yàn)和*/
  81.         tcph->check = 0;
  82.         tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
  83.                                    nskb->nh.iph->saddr,
  84.                                    nskb->nh.iph->daddr,
  85.                                    csum_partial((char *)tcph,
  86.                                                 sizeof(struct tcphdr), 0));

  87.         /* 修改IP包的TTL,并且設(shè)置禁止分片*/
  88.         nskb->nh.iph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
  89.         /* Set DF, id = 0 */
  90.         nskb->nh.iph->frag_off = htons(IP_DF);
  91.         nskb->nh.iph->id = 0;

  92.         /*重新計(jì)算IP數(shù)據(jù)包頭部校驗(yàn)和*/
  93.         nskb->nh.iph->check = 0;
  94.         nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
  95.                                            nskb->nh.iph->ihl);

  96.         /* "Never happens" */
  97.         if (nskb->len > dst_mtu(nskb->dst))
  98.                 goto free_nskb;
  99. /*使nskb和oldskb的鏈接記錄關(guān)聯(lián)*/
  100.         nf_ct_attach(nskb, oldskb);
  101.        
  102. /*這里就是最終發(fā)送數(shù)據(jù)包的方式,具體方法就是讓新數(shù)據(jù)包經(jīng)過(guò)LOACLOUT的hook點(diǎn),然后查路由,最后經(jīng)由POSTROUTING點(diǎn),將數(shù)據(jù)包發(fā)送出去。
  103. 其實(shí)這里我還是有1個(gè)疑問(wèn):(1)為什么不可以直接查找路由,而必須先經(jīng)過(guò)LOCALOUT點(diǎn)*/
  104.         NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
  105.                 dst_output);
  106.         return;

  107. free_nskb:
  108.         kfree_skb(nskb);
  109. }
復(fù)制代碼

通過(guò)以上對(duì)send_reset函數(shù)的分析,應(yīng)該明白了利用NF框架將構(gòu)造數(shù)據(jù)包發(fā)送出去的方法。
         源碼分析中提到的1個(gè)疑問(wèn),35樓給出了解釋?zhuān)@里引用過(guò)來(lái):
其實(shí),這不是丟到了高層,而是和ip_queue_xmit()發(fā)送過(guò)程意義一樣。
對(duì)這包進(jìn)行重新路由后,封裝了頭部,之后,放到了NF_IP_LOCAL_IN之前而已。

其實(shí),這里面只要修改了中途修改了ip地址,肯定是需要手動(dòng)重新路由的。
這就涉及到一些比較復(fù)雜的route cache的查找,如果沒(méi)有就去查找route tables;之后,進(jìn)行路由結(jié)構(gòu)和neighbour結(jié)構(gòu)的關(guān)聯(lián),就涉及到鄰居子系統(tǒng)的相關(guān)操作;接著就涉及到arp cache的查找,如果沒(méi)有,進(jìn)行一些操作,arp的過(guò)程等等,才找到了相關(guān)的ip對(duì)應(yīng)的mac信息。
息。

        方法二,就是直接調(diào)用dev_queue_xmit函數(shù),將構(gòu)造完畢的數(shù)據(jù)包直接發(fā)送到網(wǎng)卡驅(qū)動(dòng)。從NF框架來(lái)看,該函數(shù)的調(diào)用是在POSTROUTING點(diǎn)之后了,也可以理解為直接通過(guò)調(diào)用二層的發(fā)送函數(shù),將三層構(gòu)造的數(shù)據(jù)包發(fā)送出去。該函數(shù)實(shí)際上會(huì)調(diào)用skb->dev->hard_start_xmit,即對(duì)應(yīng)網(wǎng)卡的驅(qū)動(dòng)函數(shù),將數(shù)據(jù)包直接發(fā)送的出去。
        很顯然,這個(gè)工作在二層的函數(shù),發(fā)送數(shù)據(jù)包(數(shù)據(jù)包在二層的時(shí)候準(zhǔn)確叫法應(yīng)該是幀,我們這里是在三層直接調(diào)用的,權(quán)且還稱(chēng)作數(shù)據(jù)包)的方式是不需要再查路由了。
但是,二層發(fā)送的時(shí)候是需要根據(jù)目的MAC來(lái)進(jìn)行的。在第一種方法構(gòu)造的數(shù)據(jù)包中,僅僅交換了IP地址,而沒(méi)有對(duì)MAC做任何修改。這樣直接調(diào)用dev_queue_xmit是會(huì)產(chǎn)生問(wèn)題的,并且該函數(shù)發(fā)送的內(nèi)容應(yīng)該是從二層頭部開(kāi)始,到數(shù)據(jù)包的結(jié)束。因此,如果三層構(gòu)造的數(shù)據(jù)包,想調(diào)用該函數(shù)直接發(fā)送數(shù)據(jù)包的話(huà),則需要修改數(shù)據(jù)包的源和目的MAC,并將skb->data指針指向MAC頭部,以及skb->len的值也要加上頭部的長(zhǎng)度方法。以下是可參考的示例代碼:
  1.   unsigned char mac_temp[ETH_ALEN] = {0};
  2.   struct ethhdr *mach = NULL;
  3. ……
  4. /*code…… 構(gòu)造數(shù)據(jù)包的IP即上層協(xié)議及數(shù)據(jù)*/
  5. ……
  6. /*交換源和目的MAC*/
  7. mach = (struct ethhdr *)skb->mac.raw;
  8.    memcpy(mac_temp, (unsigned char *)mach->h_dest, ETH_ALEN);
  9.    memcpy(mach->h_dest, (unsigned char *)mach->h_source, ETH_ALEN);
  10.    memcpy(mach->h_source, mac_temp, ETH_ALEN);

  11. /*修改skb->data指針,使其指向MAC頭部,并且增加skb->len*/
  12. skb_push(skb , ETH_HLEN);
  13. /*直接調(diào)用該函數(shù),將數(shù)據(jù)包從網(wǎng)卡上發(fā)送出去*/
  14. ret = dev_queue_xmit(skb);
復(fù)制代碼

    這里還要順便說(shuō)一下構(gòu)造的數(shù)據(jù)包發(fā)送完畢之后,對(duì)于hook函數(shù)的返回值問(wèn)題。
   (1)第一種發(fā)送數(shù)據(jù)包的實(shí)現(xiàn),對(duì)于send_reset函數(shù)的實(shí)現(xiàn)中,由于單獨(dú)申請(qǐng)了nskb的內(nèi)存,并構(gòu)造的新的數(shù)據(jù)包。新數(shù)據(jù)包接著走NF的流程了。而對(duì)于原始的skb,就通過(guò)模塊的返回值return NF_DROP做出了處理。
   (2)第二種發(fā)送數(shù)據(jù)包的實(shí)現(xiàn),若是基于已有數(shù)據(jù)包的基礎(chǔ)上重新構(gòu)造的數(shù)據(jù)包,那么實(shí)際上原始數(shù)據(jù)包的內(nèi)容已經(jīng)不復(fù)存在,而且調(diào)用完畢dev_queue_xmit已將同一塊緩沖區(qū),只是填充了新數(shù)據(jù)的數(shù)據(jù)包發(fā)送出去,因此,這里已經(jīng)沒(méi)有原始數(shù)據(jù)包的存在了,需要返回NF_STOLEN,告訴協(xié)議棧不用關(guān)心原始的包即可。否則,若是新數(shù)據(jù)包是單獨(dú)申請(qǐng)的內(nèi)存,那么對(duì)于原數(shù)據(jù)包還應(yīng)該是返回NF_DROP.


三、總結(jié)
        以上就是個(gè)人分享和總結(jié)和內(nèi)核中構(gòu)造的數(shù)據(jù)包發(fā)送出去的兩種方式。實(shí)際中常用的就是構(gòu)造完數(shù)據(jù)包之后,調(diào)用dev_queue_xmit函數(shù)發(fā)送報(bào)文,也測(cè)試過(guò)調(diào)用send_reset發(fā)送RST方式。但并未采用send_reset中通過(guò)調(diào)用NF_HOOK發(fā)送過(guò)其他數(shù)據(jù)包。如果諸位朋友有相關(guān)的實(shí)踐經(jīng)驗(yàn),歡迎分享。

        本文在分析send_reset代碼的過(guò)程中,參考了百度中搜到的muddoghole的文章,因?yàn)橹荒軓陌俣瓤煺湛吹竭@篇文章,并且鏈接過(guò)長(zhǎng),這里就不列出連接,對(duì)于原文的作者表示感謝。

        由于對(duì)內(nèi)核中的一些地方理解不夠深入,因此文章中肯定存在很多問(wèn)題。歡迎各位朋友指正,多多交流。

        本文原文鏈接:http://blog.chinaunix.net/u/33048/showart_2043789.html

[ 本帖最后由 Godbach 于 2009-9-5 11:18 編輯 ]
作者: platinum    時(shí)間: 2009-09-02 16:19
好文!
作者: Godbach    時(shí)間: 2009-09-02 16:23
多謝白金兄啊。過(guò)獎(jiǎng)了。

其實(shí)小弟對(duì)于send_reset還是疑問(wèn)的:就是修改后的數(shù)據(jù)包,為什么還要走LOCAL_OUT點(diǎn)?
作者: platinum    時(shí)間: 2009-09-02 16:32
我的理解是 send_reset 偽造一個(gè)包然后通過(guò)正常本地協(xié)議棧,本地 socket 發(fā)出的
所以要經(jīng)過(guò) LOCAL_OUT 鏈
不知道分析的對(duì)不對(duì),還請(qǐng)指正
作者: Godbach    時(shí)間: 2009-09-02 16:39
原帖由 platinum 于 2009-9-2 16:32 發(fā)表
我的理解是 send_reset 偽造一個(gè)包然后通過(guò)正常本地協(xié)議棧,本地 socket 發(fā)出的
所以要經(jīng)過(guò) LOCAL_OUT 鏈
不知道分析的對(duì)不對(duì),還請(qǐng)指正


當(dāng)時(shí)看了代碼,我覺(jué)得可能是這樣理解的。但是從包的流程上看,如果不走h(yuǎn)ook點(diǎn),而直接查路由發(fā)送到POSTROUTING,不知道會(huì)不會(huì)有什么影響?
作者: Godbach    時(shí)間: 2009-09-02 16:43
難道是考慮鏈接跟蹤的問(wèn)題或者是nat?
作者: platinum    時(shí)間: 2009-09-02 16:55
REJECT 的設(shè)計(jì)初衷就是用自己的本地 socket 發(fā)一個(gè) RST 包,所以用的是 LOCAL_OUT,這和 NAT 無(wú)關(guān)
也就是說(shuō)
假如 REJECT 的機(jī)器是做服務(wù)器的,直接通過(guò)被訪(fǎng)問(wèn)端口發(fā)送數(shù)據(jù)
如果 REJECT 的機(jī)器是做 NAT 的,那么通過(guò)連接公網(wǎng)的接口發(fā)送數(shù)據(jù)
如果 REJECT 的機(jī)器是個(gè)橋,那么對(duì)方一定無(wú)法收到 REJECT 了,可以做個(gè)測(cè)試驗(yàn)證我的說(shuō)法
作者: Godbach    時(shí)間: 2009-09-02 16:56
原帖由 platinum 于 2009-9-2 16:55 發(fā)表
REJECT 的設(shè)計(jì)初衷就是用自己的本地 socket 發(fā)一個(gè) RST 包,所以用的是 LOCAL_OUT,這和 NAT 無(wú)關(guān)
也就是說(shuō)
假如 REJECT 的機(jī)器是做服務(wù)器的,直接通過(guò)被訪(fǎng)問(wèn)端口發(fā)送數(shù)據(jù)
如果 REJECT 的機(jī)器是做 NAT 的,那 ...


你的意思是說(shuō),如果在橋模式下使用REJECT,RST包可能發(fā)布出去。
作者: Godbach    時(shí)間: 2009-09-02 17:01
那如果我想做比較通用的發(fā)送RST包。修改完畢之后,還向讓順著NF框架進(jìn)行,白金兄覺(jué)得該怎么實(shí)現(xiàn)呢?
作者: 瀚海書(shū)香    時(shí)間: 2009-09-02 17:14
標(biāo)題: 回復(fù) #1 Godbach 的帖子
分析的非常好,小弟收藏了
作者: Godbach    時(shí)間: 2009-09-02 17:18
原帖由 瀚海書(shū)香 于 2009-9-2 17:14 發(fā)表
分析的非常好,小弟收藏了


呵呵,多多交流。帖子中也是有提出問(wèn)題的,大家有相關(guān)經(jīng)驗(yàn)的可以分享一下。
作者: 瀚海書(shū)香    時(shí)間: 2009-09-02 17:27
標(biāo)題: 回復(fù) #1 Godbach 的帖子
其實(shí)好像發(fā)送也可以通過(guò)接收來(lái)實(shí)現(xiàn)。
在PREROUTING點(diǎn)上修改數(shù)據(jù)包的目的ip,然后修改校驗(yàn)和,之后調(diào)用netif_receive_skb(),這樣數(shù)據(jù)包就會(huì)走forward鏈而發(fā)送出去。
作者: Godbach    時(shí)間: 2009-09-02 17:30
原帖由 瀚海書(shū)香 于 2009-9-2 17:27 發(fā)表
其實(shí)好像發(fā)送也可以通過(guò)接收來(lái)實(shí)現(xiàn)。
在PREROUTING點(diǎn)上修改數(shù)據(jù)包的目的ip,然后修改校驗(yàn)和,之后調(diào)用netif_receive_skb(),這樣數(shù)據(jù)包就會(huì)走forward鏈而發(fā)送出去。


你的意思相當(dāng)于,修改后的數(shù)據(jù)包再次走PREROUTING點(diǎn)了。

另外,修改之后是否會(huì)走forward還是需要看路由的。
作者: platinum    時(shí)間: 2009-09-02 17:32
原帖由 Godbach 于 2009-9-2 16:56 發(fā)表


你的意思是說(shuō),如果在橋模式下使用REJECT,RST包可能發(fā)布出去。

是的,應(yīng)該發(fā)不出去,因?yàn)橹皇切薷牧嗽茨康?IP 源目的 PORT,之后交給 LOCAL_OUT 自動(dòng)選路了
因此,本地協(xié)議棧會(huì)自動(dòng)尋找適合的 interface 去發(fā)包,整個(gè)過(guò)程沒(méi)有修改 dev

[ 本帖最后由 platinum 于 2009-9-2 17:50 編輯 ]
作者: 瀚海書(shū)香    時(shí)間: 2009-09-02 17:36
標(biāo)題: 回復(fù) #13 Godbach 的帖子
如果目的ip是本地就不會(huì)走forward,如果目的ip不是本地就會(huì)走forward。

修改之后會(huì)重新進(jìn)入NF的流程
作者: platinum    時(shí)間: 2009-09-02 17:43
原帖由 瀚海書(shū)香 于 2009-9-2 17:36 發(fā)表
如果目的ip是本地就不會(huì)走forward,如果目的ip不是本地就會(huì)走forward。

修改之后會(huì)重新進(jìn)入NF的流程

如果是別人發(fā)過(guò)來(lái)的包,那確實(shí)如此,如果是自己發(fā)出去的包,就和 FORWARD 無(wú)關(guān)了
作者: Godbach    時(shí)間: 2009-09-02 17:48
原帖由 platinum 于 2009-9-2 17:32 發(fā)表

是的,應(yīng)該發(fā)不出去,因?yàn)橹皇切薷牧嗽茨康?MAC 和源目的 IP 源目的端口,之后交給 LOCAL_OUT 自動(dòng)選路了
因此,本地協(xié)議棧會(huì)自動(dòng)尋找適合的 interface 去發(fā)包,整個(gè)過(guò)程沒(méi)有修改 dev


REJECT模塊并未修改源和目的MAC的吧。

另外,在構(gòu)造包并調(diào)用dev_queue_xmit函數(shù)之前,也并未修改dev啊。
作者: Godbach    時(shí)間: 2009-09-02 17:50
修改之后會(huì)重新進(jìn)入NF的流程

這種修改之后調(diào)用netif_recieve_skb的方式你是否實(shí)踐過(guò)?
作者: platinum    時(shí)間: 2009-09-02 17:51
原帖由 Godbach 于 2009-9-2 17:48 發(fā)表


REJECT模塊并未修改源和目的MAC的吧。

另外,在構(gòu)造包并調(diào)用dev_queue_xmit函數(shù)之前,也并未修改dev啊。

是的,我看錯(cuò)了,沒(méi)有修改 MAC
送到上層后,是通過(guò)本地 IP 棧自動(dòng)選路的
作者: Godbach    時(shí)間: 2009-09-02 17:53
原帖由 platinum 于 2009-9-2 17:51 發(fā)表

是的,我看錯(cuò)了,沒(méi)有修改 MAC
送到上層后,是通過(guò)本地 IP 棧自動(dòng)選路的


對(duì)。這個(gè)地方send_reset為什么還需要自己在模塊代碼中查找新路由呢,交給IP協(xié)議棧統(tǒng)一處理不行嗎?
作者: 瀚海書(shū)香    時(shí)間: 2009-09-02 17:59
標(biāo)題: 回復(fù) #18 Godbach 的帖子
以前寫(xiě)過(guò)一個(gè)這樣的代碼,就是將所有機(jī)器發(fā)送過(guò)來(lái)的包,將目的ip修改為一個(gè)固定的ip,實(shí)現(xiàn)類(lèi)似端口鏡像的功能。
作者: platinum    時(shí)間: 2009-09-02 18:04
原帖由 Godbach 于 2009-9-2 17:53 發(fā)表


對(duì)。這個(gè)地方send_reset為什么還需要自己在模塊代碼中查找新路由呢,交給IP協(xié)議棧統(tǒng)一處理不行嗎?

自己在模塊代碼中查找新路由,什么意思?
作者: Godbach    時(shí)間: 2009-09-02 18:12
原帖由 platinum 于 2009-9-2 18:04 發(fā)表

自己在模塊代碼中查找新路由,什么意思?


send_reset的代碼中做了一些路由的工作。。
作者: platinum    時(shí)間: 2009-09-02 18:28
哦,說(shuō)的是 ip_route_me_harder 吧?
這個(gè)我倒是沒(méi)仔細(xì)看過(guò)
但是記得以前自己寫(xiě) TARGET 的時(shí)候,簡(jiǎn)單交換 PING 包的 SRC/DST,也可以自動(dòng)選路的
作者: dreamice    時(shí)間: 2009-09-02 18:29
原帖由 platinum 于 2009-9-2 16:32 發(fā)表
我的理解是 send_reset 偽造一個(gè)包然后通過(guò)正常本地協(xié)議棧,本地 socket 發(fā)出的
所以要經(jīng)過(guò) LOCAL_OUT 鏈
不知道分析的對(duì)不對(duì),還請(qǐng)指正

我覺(jué)得看到走local out的解釋不太對(duì)。
構(gòu)造的包,在發(fā)送的時(shí)候,需要查找路由,點(diǎn)對(duì)點(diǎn)發(fā)送還需要查找arp,你這里調(diào)用的時(shí)候,把包發(fā)出去,僅僅是通過(guò)二層把包丟到這個(gè)網(wǎng)卡而已,需要先確定這個(gè)發(fā)包函數(shù)是在路由處理之前還是之后
作者: platinum    時(shí)間: 2009-09-02 18:35
原帖由 dreamice 于 2009-9-2 18:29 發(fā)表
你這里調(diào)用的時(shí)候,把包發(fā)出去,僅僅是通過(guò)二層把包丟到這個(gè)網(wǎng)卡而已

可我怎么覺(jué)得是丟掉高層協(xié)議棧去了呢

  1. 152         NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
  2. 153                 dst_output);
復(fù)制代碼

作者: dreamice    時(shí)間: 2009-09-02 18:48
標(biāo)題: 回復(fù) #26 platinum 的帖子
白金兄這個(gè)函數(shù)是在哪個(gè)文件??jī)?nèi)核版本多少?
作者: dreamice    時(shí)間: 2009-09-02 18:55
標(biāo)題: 回復(fù) #26 platinum 的帖子
白金兄,我懷疑這種raw包的發(fā)送,是會(huì)回溯調(diào)用的,因?yàn)橐粋(gè)包要發(fā)出去,必然需要路由,arp等信息,否則發(fā)送也不知道路徑,因此,為了這個(gè)包能正確發(fā)送出去,所以調(diào)用了你上面羅列的這個(gè)函數(shù),主要是為了解決路由問(wèn)題的。
這個(gè)只是我的理解,具體可能要看一下代碼才明白。查到路由以后,包自然就知道發(fā)到那里了,所以回溯也僅僅是調(diào)用了一些hook而已。
作者: Godbach    時(shí)間: 2009-09-02 19:17
原帖由 dreamice 于 2009-9-2 18:48 發(fā)表
白金兄這個(gè)函數(shù)是在哪個(gè)文件??jī)?nèi)核版本多少?


很多內(nèi)核版本都是如此的。dreamice兄可以看一下源文件ipt_REJECT.c中send_reset函數(shù)
作者: Godbach    時(shí)間: 2009-09-02 19:19
原帖由 platinum 于 2009-9-2 18:35 發(fā)表

可我怎么覺(jué)得是丟掉高層協(xié)議棧去了呢

152         NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
153                 dst_output);


其實(shí)只是相當(dāng)于丟在LOCAL_OUT點(diǎn)之前了,如果說(shuō)高層的話(huà)那就是TCP了。
作者: Godbach    時(shí)間: 2009-09-02 19:21
原帖由 platinum 于 2009-9-2 18:28 發(fā)表
哦,說(shuō)的是 ip_route_me_harder 吧?
這個(gè)我倒是沒(méi)仔細(xì)看過(guò)
但是記得以前自己寫(xiě) TARGET 的時(shí)候,簡(jiǎn)單交換 PING 包的 SRC/DST,也可以自動(dòng)選路的


白金兄,是在send_reset函數(shù)里:
        /*這一步比較關(guān)鍵,做的就是更新路由的工作。該函數(shù)的主要工作就是將當(dāng)前數(shù)據(jù)包的源IP當(dāng)做路由的目的IP,同時(shí)考慮數(shù)據(jù)包的目的IP,得到去往該源IP的路由*/
        if ((rt = route_reverse(oldskb, oth, hook)) == NULL)
                return;

這個(gè)地方調(diào)用了router_reverse,得到oldskb中srcip作為路由表中目的IP時(shí)的路由項(xiàng)。
最后,把這個(gè)rt賦給了skb的對(duì)應(yīng)成員。
作者: peimichael    時(shí)間: 2009-09-02 19:28
學(xué)習(xí)了,覺(jué)得自己一直缺少實(shí)際動(dòng)手的經(jīng)驗(yàn),要向樓上幾位學(xué)習(xí)。

個(gè)人覺(jué)得只要把mac頭也填好,校驗(yàn)和計(jì)算好,這個(gè)包直接丟給dev_queue_xmit應(yīng)該也可以發(fā)出去,
畢竟路由已經(jīng)選好了,包的內(nèi)容也完整了
但是這里應(yīng)該是為了保證內(nèi)核整體的一致性。
如果我注冊(cè)了一個(gè)NF_IP_LOCAL_OUT的鉤子,對(duì)于RST包當(dāng)然應(yīng)該能勾到,
另外可能還有很多其他的地方可能也需要保持一致,比如neighbour狀態(tài)的更新,可能還有其他的吧
作者: Godbach    時(shí)間: 2009-09-02 20:29
個(gè)人覺(jué)得只要把mac頭也填好,校驗(yàn)和計(jì)算好,這個(gè)包直接丟給dev_queue_xmit應(yīng)該也可以發(fā)出去

調(diào)用這個(gè)函數(shù)的話(huà),就不用查路由了。
作者: wtll    時(shí)間: 2009-09-02 21:15
我倒認(rèn)為 ip_route_output_flow 這個(gè)才是重新構(gòu)造路由的函數(shù),參見(jiàn)ip_queue_xmit
作者: meijusan123    時(shí)間: 2009-09-02 22:13
原帖由 platinum 于 2009-9-2 18:35 發(fā)表

可我怎么覺(jué)得是丟掉高層協(xié)議棧去了呢

152         NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
153                 dst_output);



不然。
其實(shí),這不是丟到了高層,而是和ip_queue_xmit()發(fā)送過(guò)程意義一樣。
對(duì)這包進(jìn)行重新路由后,封裝了頭部,之后,放到了NF_IP_LOCAL_IN之前而已。

其實(shí),這里面只要修改了中途修改了ip地址,肯定是需要手動(dòng)重新路由的。
這就涉及到一些比較復(fù)雜的route cache的查找,如果沒(méi)有就去查找route tables;之后,進(jìn)行路由結(jié)構(gòu)和neighbour結(jié)構(gòu)的關(guān)聯(lián),就涉及到鄰居子系統(tǒng)的相關(guān)操作;接著就涉及到arp cache的查找,如果沒(méi)有,進(jìn)行一些操作,arp的過(guò)程等等,才找到了相關(guān)的ip對(duì)應(yīng)的mac信息。

最近小弟就小小修改了下ip地址,但,自己需要處理的后期操作還真的不少。
作者: meijusan123    時(shí)間: 2009-09-02 22:15
如果有朋友也遇到一些route涉及到鄰居子系統(tǒng),arp相關(guān)的,可以討論討論。不錯(cuò)的東東。
作者: platinum    時(shí)間: 2009-09-03 09:15
原帖由 meijusan123 于 2009-9-2 22:13 發(fā)表



不然。
其實(shí),這不是丟到了高層,而是和ip_queue_xmit()發(fā)送過(guò)程意義一樣。
對(duì)這包進(jìn)行重新路由后,封裝了頭部,之后,放到了NF_IP_LOCAL_IN之前而已。

其實(shí),這里面只要修改了中途修改了ip地址,肯 ...

原來(lái)這么復(fù)雜,一直沒(méi)接觸過(guò)路由這塊的東西,skb->dst 也沒(méi)接觸過(guò),這塊知識(shí)對(duì)我來(lái)說(shuō)還是個(gè)空白……
作者: Godbach    時(shí)間: 2009-09-03 09:45
原帖由 platinum 于 2009-9-3 09:15 發(fā)表

原來(lái)這么復(fù)雜,一直沒(méi)接觸過(guò)路由這塊的東西,skb->dst 也沒(méi)接觸過(guò),這塊知識(shí)對(duì)我來(lái)說(shuō)還是個(gè)空白……

send_reset就是修改了skb->dst.

路由這塊我也沒(méi)有深入研究過(guò)。
作者: peimichael    時(shí)間: 2009-09-03 10:16
看到路由這一部分,發(fā)現(xiàn)講了好多原理性的東西,
什么多路徑路由,策略路由,負(fù)載平衡算法,這個(gè)表那個(gè)表等等,
看了一會(huì)兒就摸不著北了,
期待前輩能把學(xué)習(xí)路由這一塊的心得拿出來(lái)分享一下
作者: Godbach    時(shí)間: 2009-09-03 10:18
不然。
其實(shí),這不是丟到了高層,而是和ip_queue_xmit()發(fā)送過(guò)程意義一樣。
對(duì)這包進(jìn)行重新路由后,封裝了頭部,之后,放到了NF_IP_LOCAL_IN之前而已。

這個(gè)地方我也是如此理解的,send_reset只是將修改后的數(shù)據(jù)包放在LOCALOUT點(diǎn)之前。確如ip_queue_xmit類(lèi)似,最后調(diào)用了
  1.         return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
  2.                        dst_output);
復(fù)制代碼
其實(shí),這里面只要修改了中途修改了ip地址,肯定是需要手動(dòng)重新路由的。


對(duì)路由理解不夠深入,如果這里不進(jìn)行手動(dòng)路由的話(huà),在交由POSTROUTING之前,不是也會(huì)查路由嗎?
另外,send_reset實(shí)際上只是交換了源和目的IP,如果rtable中已有了srcip->dstip的路由,是否還存在沒(méi)有dstip->srcip的路由的情況?

這就涉及到一些比較復(fù)雜的route cache的查找,如果沒(méi)有就去查找route tables;之后,進(jìn)行路由結(jié)構(gòu)和neighbour結(jié)構(gòu)的關(guān)聯(lián),就涉及到鄰居子系統(tǒng)的相關(guān)操作;接著就涉及到arp cache的查找,如果沒(méi)有,進(jìn)行一些操作,arp的過(guò)程等等,才找到了相關(guān)的ip對(duì)應(yīng)的mac信息。


這里是否只涉及到路由的查找,或者還有路由的修改?
作者: peimichael    時(shí)間: 2009-09-03 11:42
我看的2.6.28內(nèi)核里面協(xié)議棧中的路由選擇是在NF_IP_LOCAL_OUT之前啊,在ip_queue_xmit或者udp_sendmsg里面
就是說(shuō)這里必須自己來(lái)選擇路由

只要路由選定了,它對(duì)應(yīng)的neighbour也就定了,需不需要arp請(qǐng)求那就是neighbour子系統(tǒng)的事情了
作者: Godbach    時(shí)間: 2009-09-03 13:28
如果是在FORWARD點(diǎn)上修改了數(shù)據(jù)包,應(yīng)該也需要查路由吧,之后發(fā)送的時(shí)候應(yīng)該走那個(gè)hook點(diǎn)呢。
作者: peimichael    時(shí)間: 2009-09-03 20:06
原帖由 Godbach 于 2009-9-3 13:28 發(fā)表
如果是在FORWARD點(diǎn)上修改了數(shù)據(jù)包,應(yīng)該也需要查路由吧,之后發(fā)送的時(shí)候應(yīng)該走那個(gè)hook點(diǎn)呢。


看代碼的話(huà)整個(gè)forward流程似乎只在ip_rcv_finish中調(diào)用ip_route_input查了一次路由
這么說(shuō)來(lái)如果在forward中修改了數(shù)據(jù)包的目的地址,那么是否可以這樣:
假如目的地址改成本地的,交給dst_input處理,走ip_local_deliver
假如目的地址非本地,直接返回NF_ACCEPT繼續(xù)按原路走下去,也就是被forward

本人沒(méi)什么修改數(shù)據(jù)包的經(jīng)驗(yàn),純粹憑想象瞎說(shuō)。
作者: Godbach    時(shí)間: 2009-09-03 20:38
假如目的地址改成本地的,交給dst_input處理,走ip_local_deliver

那就相當(dāng)于讓包會(huì)退到PREROUTING之后,查路由之前了
作者: xduser    時(shí)間: 2009-09-05 08:35
(2),oldskb的由誰(shuí)進(jìn)行釋放了?

這個(gè)問(wèn)題好解答
static unsigned int reject(struct sk_buff **pskb,
                           const struct net_device *in,
                           const struct net_device *out,
                           unsigned int hooknum,
                           const void *targinfo,
                           void *userinfo)
{


      。。。。。。。。。


        case IPT_TCP_RESET:
                send_reset(*pskb, hooknum);
        case IPT_ICMP_ECHOREPLY:
                /* Doesn't happen. */
                break;
        }

        return NF_DROP;
}


當(dāng)reject 函數(shù)返回值為NF_DROP, 上一層函數(shù)自動(dòng)會(huì)free skb
作者: Godbach    時(shí)間: 2009-09-05 10:54
標(biāo)題: 回復(fù) #45 xduser 的帖子
是的,多謝LS的。一直光記著分析send_reset,忘了它其實(shí)只是REJECT模塊的子函數(shù)。返回DROP正好。
作者: Linux5320    時(shí)間: 2009-10-02 00:44
我是菜鳥(niǎo) 我不懂
作者: cheney_lang    時(shí)間: 2009-10-06 03:18
學(xué)習(xí)
作者: kwest    時(shí)間: 2009-10-08 23:58
留個(gè)名,有空再看!以前曾經(jīng)仿照send_reset寫(xiě)過(guò)一個(gè)HTTP劫持模塊,從未想過(guò)系統(tǒng)地去分析總結(jié),Godbach強(qiáng)!
作者: meijusan123    時(shí)間: 2009-11-06 17:41
send_reset()實(shí)現(xiàn)的路由基本是由       
        fl.fl_ip_sport = otcph.dest;
        fl.fl_ip_dport = otcph.source;
        dst = ip6_route_output(NULL, &fl);
來(lái)實(shí)現(xiàn)的,最后,NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev,dst_output);過(guò)程去節(jié)間調(diào)用dev_queue_xmit()來(lái)把封裝好的幀發(fā)送出去。

現(xiàn)在,有點(diǎn)疑惑,如果我直接在hook中去實(shí)現(xiàn)封裝發(fā)送數(shù)據(jù)包。對(duì)數(shù)據(jù)包的封裝,最后調(diào)用dev_queue_xmit()來(lái)發(fā)送,但,前提是,我沒(méi)有手動(dòng)去封裝MAC頭,這是不是,我要手動(dòng)去路由一次,才行?

是不是也是通過(guò)fl.fl_ip_sport = otcph.dest;
        fl.fl_ip_dport = otcph.source;
        dst = ip6_route_output(NULL, &fl);來(lái)實(shí)現(xiàn)?
作者: godbach    時(shí)間: 2009-11-06 22:43
原帖由 meijusan123 于 2009-11-6 17:41 發(fā)表
send_reset()實(shí)現(xiàn)的路由基本是由       
        fl.fl_ip_sport = otcph.dest;
        fl.fl_ip_dport = otcph.source;
        dst = ip6_route_output(NULL, &fl);
來(lái)實(shí)現(xiàn)的,最后,NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, ns ...


對(duì),如果你不手動(dòng)處理MAC的話(huà),那就得處理路由,這樣系統(tǒng)為你封裝MAC頭,并發(fā)送出去
作者: meijusan123    時(shí)間: 2009-11-07 08:42
原帖由 godbach 于 2009-11-6 22:43 發(fā)表


對(duì),如果你不手動(dòng)處理MAC的話(huà),那就得處理路由,這樣系統(tǒng)為你封裝MAC頭,并發(fā)送出去


謝謝樓主解答,最近試驗(yàn)了3種情況,能否給點(diǎn)建議:

(1)以前,我在hook中,對(duì)發(fā)送數(shù)據(jù)包直接修改封裝,再手動(dòng)路由ip_route_output_key()后,可以成功發(fā)送出去;
(2)現(xiàn)在,我直接在hook中,alloc_skb()出一個(gè)buffer,手動(dòng)封裝了mac,ip,tcp頭,再調(diào)用dev_queue_xmit()后,也可以成功發(fā)出去;
(3)但,現(xiàn)在,我又試驗(yàn)了,在hook中,alloc_skb()出一個(gè)buffer,手動(dòng)封裝了ip,tcp頭,沒(méi)有手動(dòng)封裝mac頭,再手動(dòng)路由ip_route_output_key()后,最后再調(diào)用dev_queue_xmit(),竟然,發(fā)送失敗,可能是系統(tǒng)沒(méi)有幫我構(gòu)造mac頭,自己手動(dòng)路由失敗,導(dǎo)致系統(tǒng)直接丟棄我的構(gòu)造包。

第3種情況失敗的原因,個(gè)人感覺(jué)是自己路由沒(méi)成功導(dǎo)致,難道手動(dòng)ip_route_output_key路由后會(huì)失??
作者: godbach    時(shí)間: 2009-11-07 10:14
(3)但,現(xiàn)在,我又試驗(yàn)了,在hook中,alloc_skb()出一個(gè)buffer,手動(dòng)封裝了ip,tcp頭,沒(méi)有手動(dòng)封裝mac頭,再手動(dòng)路由 ip_route_output_key()后,最后再調(diào)用dev_queue_xmit(),竟然,發(fā)送失敗,可能是系統(tǒng)沒(méi)有幫我構(gòu)造mac頭,自己手動(dòng)路由失敗,導(dǎo)致系統(tǒng)直接丟棄我的構(gòu)造包。

你說(shuō)的第三種情況中函數(shù)dev_queue_xmit()應(yīng)該不用你自己去做調(diào)用,而是經(jīng)過(guò)路由之后的數(shù)據(jù)包要發(fā)出去時(shí)回調(diào)用到這個(gè)函數(shù)的。
作者: meijusan123    時(shí)間: 2009-11-07 10:41
原帖由 godbach 于 2009-11-7 10:14 發(fā)表

你說(shuō)的第三種情況中函數(shù)dev_queue_xmit()應(yīng)該不用你自己去做調(diào)用,而是經(jīng)過(guò)路由之后的數(shù)據(jù)包要發(fā)出去時(shí)回調(diào)用到這個(gè)函數(shù)的。


我大概明白樓主的意思,就是,手動(dòng)路由后,不需要自己去發(fā)送。
但是,我在hook中去實(shí)現(xiàn)這構(gòu)造包的功能,最終還是需要自己去發(fā)送的,難道通過(guò)回調(diào)函數(shù)去實(shí)現(xiàn)?hook的回調(diào)函數(shù)是去實(shí)現(xiàn)了有原本系統(tǒng)申請(qǐng)的skb資源的回調(diào),但是,我自己alloc_skb出來(lái)的skbuffer,咋整呀,這怎么才能發(fā)出去呀?


在linux里用tcpdump沒(méi)有抓到此包,但,在外面用ethereal可以抓到我構(gòu)造的一個(gè)缺少M(fèi)AC頭的不完整包,結(jié)果,還是路由失敗出了問(wèn)題。
不知道咋整呀??
作者: godbach    時(shí)間: 2009-11-07 11:13
標(biāo)題: 回復(fù) #54 meijusan123 的帖子
路由之后就讓按照系統(tǒng)標(biāo)準(zhǔn)的流程進(jìn)行啊。你看了ipt_REJECT.c的代碼了嗎?
作者: meijusan123    時(shí)間: 2009-11-08 09:44
看了樓主文章,對(duì)ipt_REJECT.c的部分分析不錯(cuò)。
但,小弟看了有所疑問(wèn),也是最關(guān)鍵的路由部分:

/*這一步比較關(guān)鍵,做的就是更新路由的工作。該函數(shù)的主要工作就是將當(dāng)前數(shù)據(jù)包的源IP當(dāng)做路由的目的IP,同時(shí)考慮數(shù)據(jù)包的目的IP,得到去往該源IP的路由*/
        if ((rt = route_reverse(oldskb, oth, hook)) == NULL)
                return;


/*這里就是最終發(fā)送數(shù)據(jù)包的方式,具體方法就是讓新數(shù)據(jù)包經(jīng)過(guò)LOACLOUT的hook點(diǎn),然后查路由,最后經(jīng)由POSTROUTING點(diǎn),將數(shù)據(jù)包發(fā)送出去。
其實(shí)這里我還是有1個(gè)疑問(wèn):(1)為什么不可以直接查找路由,而必須先經(jīng)過(guò)LOCALOUT點(diǎn)*/
        NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
                dst_output);
        return;


按樓主所述,這兩個(gè)部分,都是重新路由的過(guò)程,我有所不太理解。封裝多的數(shù)據(jù)包是在route_reverse()地方路由過(guò),為什么還需要經(jīng)過(guò)LOACLOUT的hook點(diǎn),然后查路由????
作者: godbach    時(shí)間: 2009-11-08 10:25
原帖由 meijusan123 于 2009-11-8 09:44 發(fā)表
看了樓主文章,對(duì)ipt_REJECT.c的部分分析不錯(cuò)。
但,小弟看了有所疑問(wèn),也是最關(guān)鍵的路由部分:

/*這一步比較關(guān)鍵,做的就是更新路由的工作。該函數(shù)的主要工作就是將當(dāng)前數(shù)據(jù)包的源IP當(dāng)做路由的目的IP,同時(shí) ...

呵呵,這個(gè)地方我也提出疑問(wèn)了。后來(lái)在帖子上加上了后面一些網(wǎng)友的解釋。

如果原帖中你看不到,那么翻一翻大家的回復(fù),看一下。
作者: godbach    時(shí)間: 2009-11-08 10:27
源碼分析中提到的1個(gè)疑問(wèn),35樓給出了解釋?zhuān)@里引用過(guò)來(lái):

其實(shí),這不是丟到了高層,而是和ip_queue_xmit()發(fā)送過(guò)程意義一樣。
對(duì)這包進(jìn)行重新路由后,封裝了頭部,之后,放到了NF_IP_LOCAL_IN之前而已。

其實(shí),這里面只要修改了中途修改了ip地址,肯定是需要手動(dòng)重新路由的。
這就涉及到一些比較復(fù)雜的route cache的查找,如果沒(méi)有就去查找route tables;之后,進(jìn)行路由結(jié)構(gòu)和neighbour結(jié)構(gòu)的關(guān)聯(lián),就涉及到鄰居子系統(tǒng)的相關(guān)操作;接著就涉及到arp cache的查找,如果沒(méi)有,進(jìn)行一些操作,arp的過(guò)程等等,才找到了相關(guān)的ip對(duì)應(yīng)的mac信息。
息。

作者: meijusan123    時(shí)間: 2009-11-09 10:54
原帖由 godbach 于 2009-11-8 10:27 發(fā)表
源碼分析中提到的1個(gè)疑問(wèn),35樓給出了解釋?zhuān)@里引用過(guò)來(lái):




再次 謝謝樓主指示、
現(xiàn)在實(shí)驗(yàn)的一個(gè)情況是,不在netfilter的hook中去構(gòu)造數(shù)據(jù)包,而是直接寫(xiě)個(gè)模塊,構(gòu)造數(shù)據(jù)包,在手動(dòng)路由,最后調(diào)用NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,  skb->dst->dev, dst_output)是可以發(fā)送出去的。
但,為什么,就不能使用dev_queue_xmit(skb)呢,難道,他們的過(guò)程,很不一樣!

而且,如果在hook中使用NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,  skb->dst->dev, dst_output)就會(huì)crash了。能否給點(diǎn)建議。
作者: godbach    時(shí)間: 2009-11-09 11:20
再次 謝謝樓主指示、
現(xiàn)在實(shí)驗(yàn)的一個(gè)情況是,不在netfilter的hook中去構(gòu)造數(shù)據(jù)包,而是直接寫(xiě)個(gè)模塊,構(gòu)造數(shù)據(jù)包,在手動(dòng)路由,最后調(diào)用 NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,  skb->dst->dev, dst_output)是可以發(fā)送出去的。
但,為什么,就不能使用dev_queue_xmit(skb)呢,難道,他們的過(guò)程,很不一樣!

這兩種方式的流程是不一樣的。一個(gè)是按照NF框架進(jìn)行處理,走完LOCALOUT,還要經(jīng)過(guò)POSTROUTING,最后才會(huì)調(diào)用到dev_queue_xmit,這中間做了多少工作,你就得了解一下NF框架的處理流程了。

而直接調(diào)用dev_queue_xmit,你需要正確的手動(dòng)處理MAC頭部,還要修改skb->data和skb->len.

而且,如果在hook中使用NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,  skb->dst->dev, dst_output)就會(huì)crash了。能否給點(diǎn)建議。

crash的原因多種多樣。你可以對(duì)照ipt_REFECT.c中的處理和你自己代碼的處理,尋找一下問(wèn)題之所在。
作者: meijusan123    時(shí)間: 2009-11-09 14:13
原帖由 godbach 于 2009-11-9 11:20 發(fā)表

這兩種方式的流程是不一樣的。一個(gè)是按照NF框架進(jìn)行處理,走完LOCALOUT,還要經(jīng)過(guò)POSTROUTING,最后才會(huì)調(diào)用到dev_queue_xmit,這中間做了多少工作,你就得了解一下NF框架的處理流程了。

而直接調(diào)用dev_qu ...



問(wèn)題解決。
是我自己的原因,可以在hook中使用dev_queue_xmit()直接發(fā)送自己封裝的包,前提是,必須手動(dòng)路由,還必須把路由過(guò)的skb->dst,hh = dst->hh;提取出來(lái),在手動(dòng)封裝已經(jīng)路由過(guò),arp過(guò)的mac頭,接著就可以放在發(fā)送函數(shù)上了。

一切還是要謝謝樓主的幫組。
作者: godbach    時(shí)間: 2009-11-09 14:35
呵呵,慚愧。都是你自己實(shí)踐的結(jié)果啊。
作者: meijusan123    時(shí)間: 2009-11-10 10:27
原帖由 godbach 于 2009-11-9 14:35 發(fā)表
呵呵,慚愧。都是你自己實(shí)踐的結(jié)果啊。


不不,都是斑竹的熱情和幫助,才有結(jié)果。再次謝謝!
作者: __dreamcatcher    時(shí)間: 2010-10-14 16:58
本帖最后由 __dreamcatcher 于 2010-10-14 17:28 編輯
# /*重新計(jì)算TCP校驗(yàn)和*/
#         tcph->check = 0;
#         tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
#                                    nskb->nh.iph->saddr,
#                                    nskb->nh.iph->daddr,
#                                    csum_partial((char *)tcph,
#                                                 sizeof(struct tcphdr), 0));
#

Godbach 發(fā)表于 2009-09-02 13:04


Godbach兄,我對(duì)這個(gè)tcp_v4_check()函數(shù)一直不是很理解,最不明白的就是csum_partial()的使用,因?yàn)閷?duì)其中的參數(shù)含義不是很明白,因?yàn)榭吹綄?duì)它的調(diào)用中有的用到了skb_>csum,
例如:
  1. void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
  2. {
  3.     struct inet_sock *inet = inet_sk(sk);
  4.     struct tcphdr *th = tcp_hdr(skb);

  5.     if (skb->ip_summed == CHECKSUM_PARTIAL) {
  6.         th->check = ~tcp_v4_check(len, inet->saddr,
  7.                      inet->daddr, 0);
  8.         skb->csum_start = skb_transport_header(skb) - skb->head;
  9.         skb->csum_offset = offsetof(struct tcphdr, check);
  10.     } else {
  11.         th->check = tcp_v4_check(len, inet->saddr, inet->daddr,
  12.                      csum_partial((char *)th,
  13.                          th->doff << 2,
  14.                          skb->csum));
  15.     }
  16. }
復(fù)制代碼
就不明白skb->csum的意義,所以一直都不是很明白,能否詳細(xì)給講解一下?謝謝!
作者: Godbach    時(shí)間: 2010-10-14 18:02
skb->ip_summed 和 skb->csum 是一對(duì)相關(guān)聯(lián)的成員。
建議首先了解一下skb->ip_summed的含義,對(duì)于接收的包和發(fā)出的包,分別是什么意義。

深入理解Linux 網(wǎng)絡(luò)內(nèi)幕上有講解的
作者: __dreamcatcher    時(shí)間: 2010-10-14 19:40
恩,好的,謝謝Godbach兄指點(diǎn)了
作者: Godbach    時(shí)間: 2010-10-14 20:03
CHECKSUM_PARTIAL

對(duì)于發(fā)出的包,這個(gè)地方應(yīng)該是告訴系統(tǒng)是否有硬件計(jì)算 TCP 的校驗(yàn)和
作者: 程序c    時(shí)間: 2010-10-15 16:44
絕對(duì)是好文章,小弟收藏了
作者: picktracy    時(shí)間: 2010-12-03 11:24
挖墳勿怪,看到大家的回答沒(méi)有搔到癢處,不說(shuō)不痛快。。。

對(duì)于樓主的兩個(gè)疑問(wèn),

1. 為什么要送到LOCAL_OUT?
本地發(fā)出的包必須經(jīng)過(guò)此點(diǎn),RST包是本地發(fā)出的包,當(dāng)然要經(jīng)過(guò)LOCAL_OUT

2. 為什么需要重新查路由?
正常來(lái)說(shuō),LOCAL_OUT之后才應(yīng)該查路由,實(shí)際上Linux在實(shí)現(xiàn)時(shí),LOCAL_OUT出去的包已經(jīng)查到路由了,IP core反而不查。所以如果修改報(bào)文后要重新路由,只能自己來(lái)。
這里簡(jiǎn)單解釋下為什么LOCAL_OUT已經(jīng)查到路由,與大家提到的neigh綁定到dst上類(lèi)似,dst也被綁定到sock上,用于加速。基本上只有首包需要查路由,后續(xù)的包從sock直接得到路由(dst)。

對(duì)于大家提到的二、三層關(guān)系:
Linux三層并不與二層(dev_queue_xmit)直接聯(lián)系,而是交給neighbor層。這是一個(gè)隔層(shim),用來(lái)做L3到L2地址映射。與此類(lèi)似,四層與三層間也有隔層,添加IP頭正是在這里,添加的過(guò)程中,又涉及到源地址選擇等問(wèn)題。而這些問(wèn)題,IP core并不關(guān)心。
作者: andyzhuangyy    時(shí)間: 2011-05-19 10:41
請(qǐng)問(wèn)各位兄臺(tái),我現(xiàn)在想在forward 里面構(gòu)造一個(gè)數(shù)據(jù)包,并且接著原來(lái)的數(shù)據(jù)包一起發(fā)送出去,換句話(huà)說(shuō)就是在一個(gè)hook函數(shù)中發(fā)送兩個(gè)數(shù)據(jù)包,這怎么實(shí)現(xiàn)?
作者: jasenwan88    時(shí)間: 2012-04-29 14:40
太精彩了,怎么才能收藏下來(lái)呢或者轉(zhuǎn)載呢? 以備不時(shí)之需啊
作者: tianqiang2516    時(shí)間: 2012-08-09 15:57
Godbach 兄!我有幾點(diǎn)問(wèn)題想請(qǐng)教:
1、你的代碼是在什么內(nèi)核版本實(shí)現(xiàn)的?
2、在2.6.31內(nèi)核中sk_buff結(jié)構(gòu)和之前的有很大差別,里面并沒(méi)有L2,L3,L4首部的指針,而是用偏移量取代。那么我用你第2中數(shù)據(jù)包構(gòu)造方法,在修改L2,L3,L4首部的時(shí)候是不是無(wú)效的?(我目前在做一個(gè)IPv4和IPv6轉(zhuǎn)換的項(xiàng)目,我只想替換網(wǎng)絡(luò)層首部)請(qǐng)指教,謝謝
作者: lujian19861986    時(shí)間: 2012-08-11 14:50
很贊~:wink::wink:
作者: lujian19861986    時(shí)間: 2012-08-11 14:59
回復(fù) 1# Godbach


    哥德巴赫兄,如果一個(gè)tcp包flag欄位為PSH或者PSH,ACK,直接將skb的flag欄位設(shè)置為RST,這樣再wan口抓到的包不就是請(qǐng)求RST包了,這樣session就建立不起來(lái)了,為什么要復(fù)制一個(gè)nskb?
作者: Godbach    時(shí)間: 2012-08-11 23:35
回復(fù) 74# lujian19861986

我沒(méi)完全明白你的意思。

但是對(duì)于構(gòu)造報(bào)文來(lái)說(shuō),就是要么直接在原始報(bào)文上改動(dòng),要么就自己 alloc 一個(gè)新的 skb,然后從舊的 skb 中獲取必要信息。

至于你說(shuō)的 RST 報(bào)文,其實(shí)是可以考慮直接在原來(lái)報(bào)文的基礎(chǔ)上進(jìn)行修改,然后直接發(fā)走的,但是 ipt_REJECT.c 中應(yīng)該是考慮了多種情形的。

   
作者: lujian19861986    時(shí)間: 2012-08-14 17:12
回復(fù) 75# Godbach


    感謝,我說(shuō)的RST 報(bào)文,直接在原來(lái)報(bào)文的基礎(chǔ)上進(jìn)行修改,然后直接發(fā)走的,是把oldskb改了,不用把oldskb丟掉,可能這個(gè)session還再吧,連接跟蹤還在,等服務(wù)器回復(fù)一個(gè)FIN的包才結(jié)束此次連接吧。

ipt_REJECT.c 中應(yīng)該是考慮了多種情形的。
==>會(huì)把oldskb丟掉,砍掉此連接...

作者: Godbach    時(shí)間: 2012-08-14 22:21
回復(fù) 76# lujian19861986
嗯,你說(shuō)的對(duì),應(yīng)該是存在 conntrack 沒(méi)有及時(shí)進(jìn)行狀態(tài)變更的情況。


   
作者: lujian19861986    時(shí)間: 2012-08-15 11:17
回復(fù) 77# Godbach


    嗯嗯,好的
作者: SCDXMOE    時(shí)間: 2012-12-15 13:10
灰;页:......果斷
作者: yunchenyun    時(shí)間: 2013-01-31 16:58
很不錯(cuò)的文章 贊一個(gè)。。。。
作者: gaozongning    時(shí)間: 2013-03-26 17:33
(1)以前,我在hook中,對(duì)發(fā)送數(shù)據(jù)包直接修改封裝,再手動(dòng)路由ip_route_output_key()后,可以成功發(fā)送出去;
(2)現(xiàn)在,我直接在hook中,alloc_skb()出一個(gè)buffer,手動(dòng)封裝了mac,ip,tcp頭,再調(diào)用dev_queue_xmit()后,也可以成功發(fā)出去;
你好,我想問(wèn)一下你是怎么實(shí)現(xiàn)的(1)(2).主要是修改哪些部分。望樓主不吝賜教。


回復(fù) 52# meijusan123


   
作者: jasenwan88    時(shí)間: 2013-04-10 15:51
send_reset函數(shù)在linux-2.6.22以前還能看得懂,以后的內(nèi)核版本這個(gè)函數(shù)變了,不知道兩這有啥區(qū)別?




歡迎光臨 Chinaunix (http://www.72891.cn/) Powered by Discuz! X3.2