欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
linux實(shí)現流量監控的幾種方法

http://blog.sina.com.cn/s/blog_6a1837e90100v9ye.html


一、使用iptables命令;

    URL:http://www.linuxfly.org/post/340/

二、修改Netfilter的limit模塊;

    URL:http://blog.csdn.net/dog250/article/details/6940578

三、iptables的五個(gè)HOOK點(diǎn);

四、使用libpcap:需移植,暫未深入研究;

五、修改內核:暫無(wú)方案;

 

附:http://whyxx.blog.51cto.com/2227948/560914 

 

--------------------------------------------------------------------------------------------------

一、使用iptables命令:

 

 相信不少朋友都知道,使用Linux搭建路由網(wǎng)關(guān),提供nat上網(wǎng)服務(wù)是非常簡(jiǎn)單的事情,而且性能也不錯。但現在p2p的工具很多,有時(shí)候帶寬會(huì )被這些工具在無(wú)意中就占滿(mǎn)了(例如:使用迅雷、BT下載等)。這時(shí)候,總希望看看到底是誰(shuí)在占用帶寬。這樣的工具有很多,如ntop、bandwidthd、iftop、IPTraf、MRTG等等,它們也提供了非常方便的圖形監控界面,操作也非常簡(jiǎn)單??上?,它們都有一些缺點(diǎn),像實(shí)時(shí)性不夠、IP流量分散、需要使用Web來(lái)查看等,恰好這些就是好我需要的。
    為此,我利用iptables的統計功能,編寫(xiě)了一個(gè)小腳本來(lái)實(shí)現要求。(原想用Perl的Net::Pcap模塊的對數據包解碼統計的,但既然有現成的,為什么不用呢?)O(∩_∩)O哈哈~

一、查看網(wǎng)卡流量
首先,可能我們需要查看的是服務(wù)器上總的網(wǎng)卡流量。這個(gè)Linux提供了很好的數據:

引用
# cat /proc/net/dev
Inter-|  Receive                                                 Transmit
face|bytes    packetserrs drop fifo frame compressedmulticast|bytes    packetserrs drop fifo colls carrier compressed
    lo:10020933  79976                             0 10020933  79976                           0
  eth0:3274190272 226746109438150 858758369237                    0 2496830239218418052                           0
  sit0:                                                                             0
  tun0:                                                                             0
  tun1:    4675      51                                8116      48                           0
  tun2:  51960    562                              249612    3077                           0
  ppp0:416357167912086605                             0 308928566515934370                           0


這是網(wǎng)絡(luò )啟動(dòng)后,通過(guò)服務(wù)器上各網(wǎng)卡的總流量,但不是很直觀(guān)。(受排版影響,不太好看)
這里,提供一個(gè)小工具:


這工具不是我寫(xiě)的,作者是。使用非常簡(jiǎn)單:

引用
# sh flow.sh
Usage: flow.sh <ethernet device><sleep time>
    e.g.flow.sh eth0 2
# sh flow.sh ppp0 2
IN: 232 KByte/s   OUT: 30KByte/s
IN: 230 KByte/s   OUT: 38KByte/s
IN: 241 KByte/s   OUT: 30KByte/s


給出您要監控的網(wǎng)卡設備,然后是間隔時(shí)間,即會(huì )告訴您該設備的流量。

二、查看客戶(hù)端IP實(shí)際流量的原理
接下來(lái),進(jìn)入我們的正題。除了通過(guò)上述腳本可以查看到網(wǎng)卡的實(shí)際流量外,我們該如何查看每個(gè)客戶(hù)端的單獨流量呢?先說(shuō)說(shuō)原理吧。
1、iptables設置
該過(guò)程最主要的就是利用了iptables的統計功能。
當我們用iptables實(shí)現nat轉發(fā)后,所有的數據包要出去,必須要通過(guò)這臺網(wǎng)關(guān)服務(wù)器,也就是說(shuō),我們只要在上面監控即可。并且,這些數據包都會(huì )經(jīng)過(guò)iptables的FORWARDchain。這時(shí),我們只要給iptables加上下述語(yǔ)句:

# iptables -I FORWARD -s 192.168.228.200 -jACCEPT
# iptables -I FORWARD -d 192.168.228.200 -j ACCEPT


那么,通過(guò)192.168.228.200(客戶(hù)端)經(jīng)該服務(wù)器路由網(wǎng)關(guān)轉發(fā)出去的數據包就會(huì )記錄在iptables FORWARDchain中。
如:

引用
# iptables -v -n -x -L FORWARD
Chain FORWARD (policy DROP 5 packets, 351 bytes)
    pkts      bytestarget    prot optin    out    source              destination
2834533 360907743ACCEPT    all  --            192.168.228.200      0.0.0.0/0
3509528 3253144061ACCEPT    all  --            0.0.0.0/0            192.168.228.200


這樣,我們通過(guò)一些簡(jiǎn)單的運算,就可以得到實(shí)際的流量:

引用
# iptables -L -v -n -x|grep'192.168.228.200';sleep 3;iptables -L -v -n -x|grep'192.168.228.200'
2872143 365711591ACCEPT    all  --            192.168.228.200      0.0.0.0/0
3555831 3297100630ACCEPT    all  --            0.0.0.0/0            192.168.228.200
2872750 365777302ACCEPT    all  --            192.168.228.200      0.0.0.0/0
3556591 3297814562ACCEPT    all  --            0.0.0.0/0            192.168.228.200
# echo '(3297814562-3297100630)/1024/3'|bc
232
# echo '(365777302-365711591)/1024/3'|bc
21


原理就是這么簡(jiǎn)單。
※ 注意,FORWARDchain記錄的流量中,不經(jīng)過(guò)該網(wǎng)關(guān)轉發(fā)的流量不會(huì )記錄。也就是說(shuō),若你從該服務(wù)器上直接下載,流量是記錄在INPUT和OUTPUTchain,而不是FORWARD中的。要統計那些數據,方法是相同的。

--------------------------------------------------------------------------------------------------

二、修改Netfilter的limit模塊

1.問(wèn)題和思路

linux內核的netfilter框架中有一個(gè)叫做limit的模塊,用于匹配單位時(shí)間內過(guò)往的包的數量,注意,這個(gè)模塊實(shí)現了一個(gè)match,而不能直接用于流控的目的,因此你不能直接使用下列的命令實(shí)現流控:
iptables –A FORWARD –s xxx –d yyy –m limit ...  –jDROP
因為這樣的話(huà),所有匹配到的數據包就都被drop掉了。你應該這么做:
iptables –A FORWARD –s xxx –d yyy –m limit ... –j ACCEPT
iptables –A FORWARD –s xxx –d yyy –j DROP
然而仍然需要注意的是,這個(gè)match是基于包的數量的,而不是基于數據字節流量的,因此這種流控方式很不準確,如上,限制單個(gè)基于ip地址的流在每秒發(fā)送20個(gè)數據包,而這20個(gè)數據包可能是20個(gè)mtu大小的數據包,也可能是20個(gè)1字節ip載荷大小的數據包,也可能僅僅是20個(gè)tcp的ack包,這樣的流控顯然不是真正的流控。
   我現在需要做的是基于單個(gè)源ip進(jìn)行秒級別的入口流量的字節限速,怎么做呢?當然可以通過(guò)tc來(lái)做,那就是使用tc的police策略來(lái)進(jìn)行配置,可是那樣的話(huà)有問(wèn)題,第一個(gè)問(wèn)題就是police沒(méi)有隊列,這就意味著(zhù)所有超額的流量將被丟棄而不是被緩存,這也許就是tc社區為何說(shuō)linux入口限速做的不甚好的原因之所在吧;第二個(gè)問(wèn)題就是你需要把所有需要被限速的ip地址作為filter的匹配規則顯式的配置出來(lái),而這會(huì )導致策略表的快速膨脹,大大增加了內存的占用。因此不到萬(wàn)不得已,我不會(huì )再考慮使用tc來(lái)完成這個(gè)流控。
   接下來(lái)要考慮的就是使用iptables統計來(lái)完成流控。因為netfilter會(huì )紀錄所有rule的統計信息,因此周期的調用iptables–L–x –n…然后將統計信息相減后除以調用周期,使用外部腳本來(lái)完成這個(gè)流控實(shí)際上也是可以的。然而這又會(huì )面對和tc同樣的問(wèn)題,既然需要iptables來(lái)統計信息,那么統計哪些流量的信息你同樣需要顯式配置出來(lái),這同樣會(huì )導致filter表的膨脹,最終導致內存占用以及遍歷filter的轉發(fā)效率的降低。
   于是乎,辦法還要想別的,最直接的辦法就是自己實(shí)現。簡(jiǎn)單點(diǎn)考慮,我也不要什么隊列,既然tc都沒(méi)有入口整形隊列,那我也不要,超過(guò)限額的全部丟棄即可。最直接的方案就是修改netfilter的limit模塊,因為它足夠簡(jiǎn)單,擴展它時(shí)阻力最小,于是乎,改了它!修改動(dòng)作很少,基本分為四點(diǎn):
第一:維護一個(gè)list_head,保存所有的到達本機的ip數據報的源ip地址;
第二:修改match函數,在源ip鏈表中尋找該數據包的源ip,若找到,取出統計信息,看看一秒內流量是否超限,若是,則匹配,若沒(méi)有則不匹配;如果在鏈表中沒(méi)有找到,則創(chuàng )建一個(gè)entry,記錄下當前時(shí)間和當前數據包長(cháng)度,返回不匹配;將找到的entry取出,重新插入到head位置,或者將新創(chuàng )建的entry插入到head位置,這樣可以模擬lru,為第四步創(chuàng )造好處;
第三:如果鏈表長(cháng)度滿(mǎn)了,則匹配所有的數據包;
第四:需要新增加entry且鏈表已經(jīng)滿(mǎn)了時(shí),根據entry的上次更新時(shí)間以及最短不惑躍時(shí)間看是否能刪除某一個(gè)entry。
上述四個(gè)步驟大體上分兩個(gè)階段實(shí)現,第一階段暫時(shí)不實(shí)現第四點(diǎn),這也符合我的一貫風(fēng)格,第四點(diǎn)以及模塊釋放時(shí)的善后工作暫時(shí)沒(méi)有測試,首先要把功能先跑通?,F在假設已經(jīng)實(shí)現了上述所有,我只需要配置以下的規則就可以實(shí)現針對每一個(gè)源ip進(jìn)行限速了:
iptables –A –FORWARD/INPUT –m –limit 20/sec –j MY_CHAIN
注意,上述的20/sec已經(jīng)不再是基于包數量的了,而是基于字節的,并且,我沒(méi)有直接drop掉這些包,而是交給了一個(gè)自定義的chain來(lái)處理,這樣可以方便的將機制和策略進(jìn)行分離,或許管理員并不是想丟棄這些超限包,而只是紀錄下日志,或許管理員會(huì )永遠封死這些ip地址,也許僅僅封死一段時(shí)間,待收到罰金之后再給予開(kāi)放…

2.實(shí)現

首先定義數據結構。以下的數據結構是一個(gè)包裝,定義了一個(gè)全局的鏈表,以及一些控制參數,由于這個(gè)只是個(gè)測試版,因此沒(méi)有考慮多處理器的并發(fā)處理,因此也就沒(méi)有定義spin_lock,在正式的實(shí)現中,一定要定義一個(gè)lock的。

  1. struct src_controler  
  2.         struct list_head src_list;  
  3.         int curr;     //當前一共有多少了entry  
  4.         int max;    //最多能有多少個(gè)entry  
  5. };  


下面的一個(gè)結構體定義了一個(gè)源地址entry中包含哪些東西,無(wú)非就是一秒內已經(jīng)過(guò)去了多長(cháng)的數據包以及時(shí)間戳等信息。

  1. struct src_entry  
  2.         struct list_head list;  
  3.         __u32   src_addr;    //源地址  
  4.         unsigned long prev;    //上次的時(shí)間戳  
  5.         unsigned long passed;    //一秒內已經(jīng)過(guò)去了多少數據  
  6. };  


struct src_controler *src_ctl;   //全局變量
接下來(lái)就是修改模塊的初始化和卸載函數

  1. static int __init xt_limit_init(void)  
  2.  
  3.         int ret;  
  4.         src_ctl kmalloc(sizeof(struct src_controler), GFP_KERNEL); //初始化全局變量  
  5.         memset(src_ctl, 0, sizeof(struct src_controler));  
  6.         INIT_LIST_HEAD(&src_ctl->src_list);    //初始化全局變量的鏈表  
  7.         src_ctl->curr 0;  
  8.         src_ctl->max 1000;    //本應該通過(guò)模塊參數傳進(jìn)來(lái)的,這里寫(xiě)死,畢竟是個(gè)測試版  
  9.   
  10.         ret xt_register_match(&ipt_limit_reg);  
  11.         if (ret)  
  12.                 return ret;  
  13.   
  14.         ret xt_register_match(&limit6_reg);  
  15.         if (ret)  
  16.                 xt_unregister_match(&ipt_limit_reg);  
  17.   
  18.         return ret;  
  19.  
  20. static void __exit xt_limit_fini(void)  
  21.  
  22.         xt_unregister_match(&ipt_limit_reg);  
  23.         xt_unregister_match(&limit6_reg);  
  24.         //這里應該有一個(gè)清理鏈表的操作,測試版沒(méi)有實(shí)現  
  25.  


最后,編寫(xiě)match回調函數,刪掉原來(lái)的,自己寫(xiě)新的邏輯

  1. static int  
  2. ipt_limit_match(const struct sk_buff *skb,  
  3.                 const struct net_device *in,  
  4.                 const struct net_device *out,  
  5.                 const struct xt_match *match,  
  6.                 const void *matchinfo,  
  7.                 int offset,  
  8.                 unsigned int protoff,  
  9.                 int *hotdrop)  
  10.  
  11.         struct xt_rateinfo *r ((struct xt_rateinfo *)matchinfo)->master;  
  12.         unsigned long now jiffies, prev 0;  
  13.         struct list_head *lh;  
  14.         struct src_entry *entry NULL;  
  15.         struct src_entry *find_entry;  
  16.         unsigned long nowa;  
  17.         struct iphdr *iph skb->nh.iph;  
  18.         __u32 this_addr iph->saddr;  
  19.   
  20.         list_for_each(lh, &src_ctl->src_list) //遍歷鏈表,找到這個(gè)ip地址對應的entry  
  21.                 find_entry list_entry(lh, struct src_entry, list);  
  22.                 if (this_addr == find_entry->src_addr)  
  23.                         entry find_entry;  
  24.                         break;  
  25.                  
  26.          
  27.         if (entry) //如果找到,將其加在頭,這樣實(shí)現了一個(gè)簡(jiǎn)單的lru  
  28.                 prev entry->prev;  
  29.                 list_del(&entry->list);  
  30.                 list_add(&entry->list, &src_ctl->src_list);  
  31.         else    //如果沒(méi)有找到,看看能否添加  
  32.                 if (src_ctl->curr+1 src_ctl->max)  
  33. add_entry:  
  34.                         entry kmalloc(sizeof(struct src_entry), GFP_KERNEL);  
  35.                         memset(entry, 0, sizeof(struct src_entry));  
  36.                         entry->src_addr this_addr;  
  37.                         prev entry->prev now 1000;  
  38.                         list_add(&entry->list, &src_ctl->src_list);  
  39.             src_ctl->curr++;    //正確做法是atomic_inc  
  40.                 else //如果已經(jīng)滿(mǎn)了,那么看看能否刪除最后的那個(gè)不活動(dòng)的entry  
  41.                         entry list_entry(src_ctl->src_list.prev, struct src_entry, list);  
  42.                         if (now-entry->prev 1000)  
  43.                                 goto add_entry;  
  44.                         return 1;  
  45.                  
  46.          
  47.         nowa entry->passed skb->len;  
  48.         if (now-prev 1000)    //這里的1000其實(shí)應該是HZ變量的值,由于懶得引頭文件了,直接寫(xiě)死了。如果距上次統計還沒(méi)有到1秒,則累加數據,不匹配  
  49.                 entry->passed nowa;  
  50.                 return 0;  
  51.         else  
  52.                 entry->prev now;      
  53.                 entry->passed 0;  
  54.                 if (r->burst >= nowa)    //如果到達了1秒,則判斷是否超限,如果超限,則匹配,沒(méi)有超限則重置字段,不匹配  
  55.                         return 0;  
  56.                 else  
  57.                         return 1;  
  58.                  
  59.          
  60.         return -1;    //不會(huì )到達這里  
  61.  


編譯之:
make -C/usr/src/kernels/2.6.18-92.el5-i686 SUBDIRS=`pwd`modules
使用之:
#!/bin/bash
iptables -A INPUT-d 192.168.1.247/32 -m limit --limit 1/sec --limit-burst $1 -j$2
運行上述腳本:test.sh 1000DROP
然后下載大文件,看看是否被限速了!...
注意,上述的實(shí)現中,數據單位是字節,其實(shí)正常起碼應該是100字節,做成可配置的會(huì )更好。

3.優(yōu)化和反思

優(yōu)化一:上述實(shí)現中,使用list_head在大量源ip的情況下,遍歷鏈表的開(kāi)銷(xiāo)比較大,雖然lru原則可以最大的減小這種開(kāi)銷(xiāo),但是還是很大,特別是用戶(hù)并不想超限,反而間隔相對久的時(shí)間訪(fǎng)問(wèn)一次,大量這樣的用戶(hù)和大量頻繁訪(fǎng)問(wèn)的用戶(hù)混雜在一起,頻繁訪(fǎng)問(wèn)的用戶(hù)的entry會(huì )一直在前面,遍歷時(shí)開(kāi)銷(xiāo)較小,而大量間隔相對久訪(fǎng)問(wèn)的用戶(hù)的entry會(huì )在后面,遍歷開(kāi)銷(xiāo)比較大,這會(huì )不會(huì )導致dos攻擊,我由于沒(méi)有環(huán)境還真的沒(méi)有測試。事實(shí)上使用hash表來(lái)組織它們是更好的選擇,linux的ip_conntrack中的紀錄就是使用hash表來(lái)組織的,軟件嘛,就這幾種數據結構。
優(yōu)化二:上述實(shí)現中,僅僅是針對源地址進(jìn)行流量匹配,而沒(méi)有管目的地址,因為開(kāi)始說(shuō)了,針對目的地址的流控可以用tc實(shí)現,然而那樣的話(huà),需要顯式配置filter,很不方便,因此這個(gè)實(shí)現應該加個(gè)配置,用于針對任意目的地址進(jìn)行流量控制,比tc方便多了。

優(yōu)化三:上述實(shí)現中,數據單位是字節,這樣很不合理,應該是可以配置的才對,比如默認是字節,還可以是k,m,g等等。

優(yōu)化四:應該實(shí)現一個(gè)機制,定期清理不活躍的entry,以防止內存占用率過(guò)高。

反思:為何在入口位置的流控不實(shí)現隊列呢?我們還是要想想流控的目的是什么,其一就是避免擁塞-網(wǎng)絡(luò )的擁塞以及主機上層緩沖區的擁塞,對于接收數據而言,無(wú)論如何,流量對到達此地之前的網(wǎng)絡(luò )的影響已經(jīng)發(fā)生了,對往后的網(wǎng)絡(luò )的影響還沒(méi)有發(fā)生,因此對于已經(jīng)發(fā)生的影響,沒(méi)有必要再去進(jìn)行速率適配了,直接執行動(dòng)作即可。

    如果你真的還需要limit模塊完成它本來(lái)的功能,那么就別改limit模塊了,還是直接寫(xiě)一個(gè)為好,這樣也更靈活,畢竟我們也就不需要再配置--limit1/sec去迎合limit的語(yǔ)法了,具體方法參見(jiàn)《編寫(xiě)iptables模塊實(shí)現不連續IP地址的DNAT-POOL

修正:
如果同時(shí)下載多個(gè)局域網(wǎng)內的大文件,會(huì )發(fā)現上述的match回調函數工作的不是很好,速度并沒(méi)有被限制住,這是因為我計時(shí)統計統計的粒度太粗,一秒統計一次,這一秒中,很多大包將溜過(guò)去,因此需要更細粒度的統計,那就是實(shí)時(shí)的統計,使用數據量/時(shí)間間隔這個(gè)除式來(lái)統計,代碼如下:

  1. static int  
  2. ipt_limit_match(const struct sk_buff *skb,  
  3.                 const struct net_device *in,  
  4.                 const struct net_device *out,  
  5.                 const struct xt_match *match,  
  6.                 const void *matchinfo,  
  7.                 int offset,  
  8.                 unsigned int protoff,  
  9.                 int *hotdrop)  
  10.  
  11.         struct xt_rateinfo *r ((struct xt_rateinfo *)matchinfo)->master;  
  12.         unsigned long now jiffies, prev 0;  
  13.         struct list_head *lh;  
  14.         struct src_entry *entry NULL;  
  15.         struct src_entry *find_entry;  
  16.         unsigned long nowa;  
  17.     unsigned long rate;  
  18.         struct iphdr *iph skb->nh.iph;  
  19.         __u32 this_addr iph->saddr;  
  20.   
  21.         list_for_each(lh, &src_ctl->src_list)  
  22.                 find_entry list_entry(lh, struct src_entry, list);  
  23.                 if (this_addr == find_entry->src_addr)  
  24.                         entry find_entry;  
  25.                         break;  
  26.                  
  27.          
  28.         if (entry)  
  29.                 prev entry->prev;  
  30.                 list_del(&entry->list);  
  31.                 list_add(&entry->list, &src_ctl->src_list);  
  32.         else  
  33.                 if (src_ctl->curr+1 src_ctl->max)  
  34. add_entry:  
  35.                         entry kmalloc(sizeof(struct src_entry), GFP_KERNEL);  
  36.                         memset(entry, 0, sizeof(struct src_entry));  
  37.                         entry->src_addr this_addr;  
  38.                         prev entry->prev now 1000;  
  39.                         list_add(&entry->list, &src_ctl->src_list);  
  40.                         src_ctl->curr++;  
  41.                 else  
  42.                         entry list_entry(src_ctl->src_list.prev, struct src_entry, list);  
  43.                         if (now-entry->prev 1000)  
  44.                                 goto add_entry;  
  45.                         return 1;  
  46.                  
  47.          
  48.         nowa entry->passed skb->len;  
  49.     entry->passed nowa;  
  50.     if (now-prev 0)  
  51.         rate entry->passed/(now-prev);  
  52.     else  
  53.         rate nowa;  
  54.     entry->prev now;  
  55.     entry->passed 0;  
  56.     if (rate r->burst)  
  57.         return 1;  
  58.      
  59.         return 0;  
  60.  

 

--------------------------------------------------------------------------------------------------

三、使用iptables的HOOK掛載點(diǎn):

 

1.模塊分析

針對ipv4協(xié)議,內核的防火墻框架NetfilterIP層精心挑選了五個(gè)插入點(diǎn):NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,NF_IP_POST_ROUTING,分別對應IP層的五個(gè)不同位置。這樣,用戶(hù)可以選擇合適的切入點(diǎn)添加自己的內核模塊。

在這里,我們針對從網(wǎng)卡接收進(jìn)來(lái)的包進(jìn)行過(guò)濾處理,基于源地址+端口號進(jìn)行過(guò)濾,該程序的功能可以丟棄來(lái)自指定IP地址+端口號的數據包。需要針對所有進(jìn)入系統的包進(jìn)行過(guò)濾,不屬于發(fā)往本機的數據包我們不予理會(huì ),因此我們選擇模塊的切入點(diǎn)在NF_IP_LOCAL_IN,該切入點(diǎn)的位置在IP層向上層協(xié)議棧傳遞數據包的函數ip_local_deliver(),  

NF_HOOK(PF_INET,NF_IP_LOCAL_IN,skb,skb->dev,NULL,ip_local_deliver_finish)

IP packet NF_HOOK() IPv4協(xié)議棧上鉤出來(lái)以后,就進(jìn)入 linux-2.4.19/net/core/netfilter.c 中的 nf_hook_slow()函數進(jìn)行處理。這個(gè)函數干的主要事情,就是根據 nf_hooks[][] 數組開(kāi)始處理 packet。而這個(gè)nf_hooks[][]鏈表二維數組的原型為:  

struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];

該二維數組就是根據【協(xié)議類(lèi)型】【鉤子點(diǎn)】存儲的該協(xié)議類(lèi)型在這個(gè)鉤子點(diǎn)上的處理函數,任何一個(gè)希望使用Netfilter鉤子的模塊都需要將模塊處理函數在nf_hooks數組的相應鏈表上進(jìn)行注冊。我們編寫(xiě)的防火墻函數模塊,實(shí)際上就是生成一個(gè)struct nf_hook_ops結構的實(shí)例,并用nf_register_hook將其登記在nf_hooks[][]上。該結構的原型為:

struct nf_hook_ops
{
      struct list_head list;  //
鏈表
      nf_hookfn *hook;  //
處理函數指針
      int pf;            //
協(xié)議好
      int hooknum;      //HOOK

      int priority;        //
優(yōu)先級
};

   該結構的本質(zhì),是一個(gè)nf_hookfn 函數。這個(gè)函數將對被釣上來(lái)的 IP packet 進(jìn)行初步的處理?,F在來(lái)看看nf_hookfn函數的原型:

           typedef unsigned int nf_hookfn(unsigned int hooknum,
                                       struct sk_buff **skb,
                                       conststruct net_device *in,
                                       const struct net_device *out,
                                       int (*okfn)(struct sk_buff *));

  nf_hookfn函數的第一個(gè)參數用于指定hook類(lèi)型。第二個(gè)參數一個(gè)指向指針的指針,該指針指向的指針指向一個(gè)sk_buff數據結構,網(wǎng)絡(luò )堆棧用sk_buff數據結構來(lái)描述數據包。緊跟在skb之后的兩個(gè)參數是指向net_device數據結構的指針,net_device數據結構被Linux內核用于描述所有類(lèi)型的網(wǎng)絡(luò )接口。這兩個(gè)參數中的第一個(gè)——in,用于描述數據包到達的接口,毫無(wú)疑問(wèn),參數out用于描述數據包離開(kāi)的接口。必須明白,在通常情況下,這兩個(gè)參數中將只有一個(gè)被提供。例如:參數in只用于NF_IP_PRE_ROUTINGNF_IP_LOCAL_INhook,參數out只用于NF_IP_LOCAL_OUTNF_IP_POST_ROUTINGhook。最后,傳遞給hook函數的最后一個(gè)參數是一個(gè)命名為okfn函數指針,該函數以一個(gè)sk_buff數據結構作為它唯一的參數,并且返回一個(gè)整型的值。

 

2.模塊設計                  

了解了HOOK函數接收到的信息中最有趣和最有用的部分后,看看我們如何以各種各樣的方式來(lái)利用這些信息來(lái)過(guò)濾數據包。我們的模塊實(shí)現的基于源地址+端口號進(jìn)行過(guò)濾的功能其實(shí)可以劃分為兩個(gè)規則:

首先,根據數據包的源地址進(jìn)行過(guò)濾,我們從sk_buff數據結構中提取感興趣的信息。skb參數是一個(gè)指向sk_buff數據結構的指針的指針嗎。為了避免犯錯誤,聲明一個(gè)另外的指向skb_buff數據結構的指針并且將skb指針指向的指針賦值給這個(gè)新的指針是一個(gè)好習慣,就像這樣:
  
    struct sk_buff *sb = *skb;

   
這樣,你訪(fǎng)問(wèn)這個(gè)數據結構的元素時(shí)只需要反引用一次就可以了。獲取一個(gè)數據包的IP頭通過(guò)使用sk_buff數據結構中的網(wǎng)絡(luò )層包頭來(lái)完成。這個(gè)頭位于一個(gè)聯(lián)合中,可以通過(guò)sk_buff->nh.iph這樣的方式來(lái)訪(fǎng)問(wèn)。示例代碼1中的函數演示了當得到一個(gè)數據包的sk_buff數據結構時(shí),如何利用它來(lái)檢查收到的數據包的源IP地址與被禁止的地址是否相同。

示例代碼1 : 檢查收到的數據包的源IP
  
    unsigned char *deny_ip = "\x7f\x00\x00\x01"; 
    
    ...

        static int check_ip_packet(struct sk_buff *skb)
        {
            
            if (!skb )return NF_ACCEPT;
            if (!(skb->nh.iph)) return NF_ACCEPT;
        
            if (skb->nh.iph->saddr == *(unsignedint *)deny_ip) {
            return NF_DROP;
            }

            return NF_ACCEPT;
        }
  
  
這樣,如果數據包的源地址與我們設定的丟棄數據包的地址匹配,那么該數據包將被丟棄。為了使這個(gè)函數能按預期的方式工作,deny_ip的值應當以網(wǎng)絡(luò )字節序(Big-endian,與Intel相反)存放。雖然這個(gè)函數不太可能以一個(gè)空的指針作為參數來(lái)調用,帶一點(diǎn)點(diǎn)偏執狂從來(lái)不會(huì )有什么壞處。當然,如果錯誤確實(shí)發(fā)生了,那么該函數將會(huì )返回NF_ACCEPT。這樣Netfilter可以繼續處理這個(gè)數據包。示例代碼2展現了用于演丟棄匹配給定IP地址的數據包的簡(jiǎn)單模塊。
  
  
示例代碼2: 基于數據包源地址的過(guò)濾


#define __KERNEL__
#define MODULE

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/skbuff.h>
#include <linux/ip.h>                 
#include<linux/netfilter.h>
#include<linux/netfilter_ipv4.h>


static struct nf_hook_ops nfho;


static unsigned char *drop_ip = "\x7f\x00\x00\x01";


unsigned int hook_func(unsigned int hooknum,
                     struct sk_buff **skb,
                     const struct net_device *in,
                     const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
{
  struct sk_buff *sb = *skb;
  
   if(sb->nh.iph->saddr == *(unsigned int*)drop_ip) {
   //if (sb->nh.iph->saddr == drop_ip){
      printk("Dropped packet from... %d.%d.%d.%d\n",
    *drop_ip, *(drop_ip + 1),
 *(drop_ip+ 2), *(drop_ip + 3));
      return NF_DROP;
   }else {
      return NF_ACCEPT;
  }
}


int init_module()
{
  
  nfho.hook      = hook_func;        
  nfho.hooknum  = NF_IP_LOCAL_IN;
  nfho.pf      = PF_INET;
  nfho.priority = NF_IP_PRI_FIRST;  

  nf_register_hook(&nfho);

  return 0;
}


void cleanup_module()
{
  nf_unregister_hook(&nfho);
}

其次,另一個(gè)要實(shí)現的簡(jiǎn)單規則是基于數據包的TCP目的端口進(jìn)行過(guò)濾。這只比檢查IP地址的要求要高一點(diǎn)點(diǎn),因為我們需要自己創(chuàng )建一個(gè)TCP頭的指針。獲取一個(gè)TCP頭的指針是一件簡(jiǎn)單的事情——分配一個(gè)tcphdr數據結構(linux/tcp.h 中定義)的指針,并將它指向我們的數據包中IP頭之后的數據。示例代碼3給出了檢查數據包的TCP目的端口是否與某個(gè)我們要丟棄數據包的端口匹配的代碼。

  
  
示例代碼3: 檢查收到的數據包的TCP目的端口
        unsigned char *deny_port = "\x00\x19";  

    ...

        static int check_tcp_packet(struct sk_buff *skb)
        {
            struct tcphdr *thead;

            
            if (!skb ) return NF_ACCEPT;
            if (!(skb->nh.iph)) return NF_ACCEPT;

            
            if (skb->nh.iph->protocol !=IPPROTO_TCP) {
                return NF_ACCEPT;
            }

            thead = (struct tcphdr *)(skb->data +
                                     (skb->nh.iph->ihl * 4));

            
            if ((thead->dest) == *(unsigned short *)deny_port){
                return NF_DROP;
            }
        
        return NF_ACCEPT;
        }

3.行動(dòng)

 

4.總結

--------------------------------------------------------------------------------------------------

 

附:

linux iptables是由兩個(gè)組件組成:NetfilterIptables組成:

Netfilter組件稱(chēng)之為內核空間,是linux內核的一部分,是在Linux內核中為攔截額操作數據包提供的一套框架。其框架包含以下三部分:

1:為每種網(wǎng)絡(luò )協(xié)議(IPV4,IPv6等)定義一套HOOK函數,這些HOOK函數在數據包流過(guò)IP協(xié)議棧的幾個(gè)關(guān)鍵點(diǎn)被調用。在這幾個(gè)點(diǎn)鐘,協(xié)議棧將把數據包及HOOK函數標號作為參考調用Netfilter框架。

2:內核的任何模塊可以對每種協(xié)議的一個(gè)或多個(gè)HOOK函數進(jìn)行注冊以實(shí)現掛接,這樣當某個(gè)數據包被傳遞給Netfilter框架時(shí),內核能檢測是否有哪個(gè)模塊對該協(xié)議和HOOK函數進(jìn)行了注冊。若注冊了,則調用該模塊,這樣這些模塊就有機會(huì )檢查該數據包丟棄/修改/傳入用戶(hù)空間的隊列。

3:那些在用戶(hù)控件隊列中排隊的數據包是被傳遞給用戶(hù)空間異步的處理。

IPV4中定義了5個(gè)HOOK,如圖  



Netfilter根據網(wǎng)絡(luò )報文的流向,分為三部分:流入,流經(jīng),流出。

在以下幾個(gè)點(diǎn)插入處理過(guò)程:

NF_IP_PRE_ROUTING,在報文作路由以前執行;

NF_IP_FORWARD,在報文轉向另一個(gè)NIC以前執行;

NF_IP_POST_ROUTING,在報文流出以前執行;

NF_IP_LOCAL_IN,在流入本地的報文作路由以后執行;

NF_IP_LOCAL_OUT,在本地報文做流出路由前執行。

分析:當網(wǎng)絡(luò )上的數據包從左邊進(jìn)入系統,數據包經(jīng)過(guò)第一個(gè)點(diǎn)調用HOOK函數NF_IP_PRE_ROUTING進(jìn)行處理;然后就進(jìn)入本地路由表,查看該數據包是需要轉發(fā)還是發(fā)給本地;若該數據包時(shí)發(fā)往本地的,則該數據包經(jīng)過(guò)HOOK函數NF_IP_LOCAL_IN處理以后傳遞給上層協(xié)議;若該數據包是需要轉發(fā),則會(huì )被NF_IP_FORWARD處理;經(jīng)過(guò)轉發(fā)的數據包最后由NF_IP_POST_ROUTING處理后發(fā)送到網(wǎng)絡(luò )上;本地產(chǎn)生的數據經(jīng)過(guò)路由選擇后,調用NF_IP_LOCAL_OUT進(jìn)行處理,然后經(jīng)過(guò)NF_IP_POST_ROUTING處理后發(fā)送到網(wǎng)絡(luò )上。

 

當這些HOOK函數被經(jīng)過(guò)的數據包調用時(shí)將返回下列值之一,告知Netfilter如果對數據包采取相應動(dòng)作。

NF_ACCEPT   繼續正常的報文處理

NF_DROP     丟棄報文

NF_STOLEN   HOOK函數處理了該報文,不要再繼續傳送

NF_QUEUE    將報文入列,通常交由用戶(hù)程序處理

NF_REPEAT   再次調用該HOOK函數

 

Iptables用戶(hù)空間工具

iptables 組件是一種工具,也稱(chēng)為用戶(hù)空間。它使插入、修改和除去信息包過(guò)濾表中的規則變得容易。這些規則存儲在表的對象中。

Table

1filter

該表的作用主要用于包過(guò)濾。它在LOCAL_IN,FORWARD,LOCAL_OUT三處HOOK函數進(jìn)行了注冊。

2Nat

該表的作用主要用于地址轉換。它在PRE_ROUTING,POST_ROUTING兩處HOOK函數進(jìn)行了注冊。

3Mangle

該表的作用是進(jìn)行數據包內容的修改。它在Netfilter的所有5個(gè)HOOK函數進(jìn)行了注冊。

Chain

 在每張表中,內核使用鏈管理數據包,每個(gè)鏈中包含了一組規則表。

表與鏈的關(guān)系:

Table

Chain

Filter

INPUT

Filter

FORWARD

Filter

OUTPUT

Nat

PREROUTING

Nat

OUTPUT

Nat

POSTOUTING

Mangle

PREROUTING

Mangle

INPUT

Mangle

OUTPUT

Mangle

FORWARD

Mangle

POSTROUTING

 INPUT=HOOK函數LOCALIN

 OUTPUT=HOOK函數LOCALOUT

 PREROUTING=HOOK函數PREROUTING

 POSTROUTING=HOOK函數POSTROUTING

 FORWARD=HOOK函數FORWARD

 鏈的名字與HOOK函數名相似,鏈的作用和位置也與HOOK函數相吻合,可以說(shuō)鏈就是實(shí)現HOOK函數調用的方式。

Target 目標

鏈的每個(gè)規則都會(huì )有一個(gè)目標,目標決定了該規則對數據包如何處理。

常用目標:

ACCEPT:允許

DROP:拒絕

REJECT:同DROP一樣,不過(guò)它會(huì )向發(fā)送方返回個(gè)錯誤信息

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
LINUX2.4.x網(wǎng)絡(luò )安全框架
Netfilter框架
netfilter: Linux 防火墻在內核中的實(shí)現
iptables ipt_do_table
netfilter中iptables表的實(shí)現
linux中鉤子函數的理解
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久