相信不少朋友都知道,使用Linux搭建路由网关,提供nat上网服务是非常简单的事情,而且性能也不错。但现在p2p的工具很多,有时候带宽会被这些工具在无意中就占满了(例如:使用迅雷、BT下载等)。这时候,总希望看看到底是谁在占用带宽。这样的工具有很多,如ntop、bandwidthd、iftop、IPTraf、MRTG等等,它们也提供了非常方便的图形监控界面,操作也非常简单。可惜,它们都有一些缺点,像实时性不够、IP流量分散、需要使用Web来查看等,恰好这些就是好我需要的。
    为此,我 利用iptables的统计功能 ,编写了一个小脚本来实现要求。(原想用Perl的Net::Pcap模块的对数据包解码统计的,但既然有现成的,为什么不用呢?)O(∩_∩)O哈哈~

一、查看网卡流量
首先,可能我们需要查看的是服务器上总的网卡流量。这个Linux提供了很好的数据:

引用


这是网络启动后,通过服务器上各网卡的总流量,但不是很直观。(受排版影响,不太好看)

# cat /proc/net/dev 
  
 Inter-|   Receive                                                |  Transmit 
  
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed 
  
     lo:10020933   79976    0    0    0     0          0         0 10020933   79976    0    0    0     0       0          0 
  
   eth0:3274190272 226746109 438150 858758 369237     0          0         0 2496830239 218418052    0    0    0     0       0          0 
  
   sit0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0 
  
   tun0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0 
  
   tun1:    4675      51    0    0    0     0          0         0     8116      48    0    0    0     0       0          0 
  
   tun2:   51960     562    0    0    0     0          0         0   249612    3077    0    0    0     0       0          0 
  
   ppp0:4163571679 12086605    0    0    0     0          0         0 3089285665 15934370    0    0    0     0       0          0


这里,提供一个小工具:


下载文件


点击这里下载文件



这工具不是我写的,作者是 。使用非常简单:


引用


# 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: 30 KByte/s   
 IN: 230 KByte/s   OUT: 38 KByte/s   
 IN: 241 KByte/s   OUT: 30 KByte/s


给出您要监控的网卡设备,然后是间隔时间,即会告诉您该设备的流量。



二、查看客户端IP实际流量的原理


接下来,进入我们的正题。除了通过上述脚本可以查看到网卡的实际流量外,我们该如何查看每个客户端的单独流量呢?先说说原理吧。


1、iptables设置


该过程最主要的就是利用了iptables的统计功能。


当我们用iptables实现nat转发后,所有的数据包要出去,必须要通过这台网关服务器,也就是说,我们只要在上面监控即可。并且,这些数据包都会经过iptables的FORWARD chain。这时,我们只要给iptables加上下述语句:


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



那么,通过192.168.228.200(客户端)经该服务器路由网关转发出去的数据包就会记录在iptables FORWARD chain中。


如:


引用

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


这样,我们通过一些简单的运算,就可以得到实际的流量:


引用

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



原理就是这么简单。


※ 注意,FORWARD chain记录的流量中,不经过该网关转发的流量不会记录。也就是说,若你从该服务器上直接下载,流量是记录在INPUT和OUTPUT chain,而不是FORWARD中的。要统计那些数据,方法是相同的。



三、脚本


1、源码


$SEC是间隔的时间,时间太短误差比较大;$ZERO决定是否显示没变化IP。同时,显示被屏蔽的IP地址。


#!/usr/bin/perl -w 
 
 # Date:2009-03-12  
 # Author:HyphenWang  
 # Version:1.0  
 use strict;  
 my $IPTABLES_CMD="iptables -v -n -x -L FORWARD";  
 my $SEC="3";  
 my $ZERO="0";  
 my (%first_input,%first_output);  
 my (%second_input,%second_output);  
 my %ban_ip;  

 sub get_ipflow {  
   my ($ip_input,$ip_output)=@_;  
   for my $line (`$IPTABLES_CMD`) {  
     my @columns = split(/\s+/,$line);  
     $ip_input->{$columns[-1]}=$columns[2] if ($columns[3] eq "ACCEPT" and $columns[-1] =~ m/192\.168\.228\.\d+/);  
     $ip_output->{$columns[-2]}=$columns[2] if ($columns[3] eq "ACCEPT" and $columns[-2] =~ m/192\.168\.228\.\d+/);  
     $ban_ip{$columns[-1]}=1 if ($columns[3] eq "DROP" and $columns[-1] =~ m/192\.168\.228\.\d+/);  
     $ban_ip{$columns[-2]}=1 if ($columns[3] eq "DROP" and $columns[-2] =~ m/192\.168\.228\.\d+/);  
   }  
 }  
 get_ipflow(\%first_input,\%first_output);  
 sleep $SEC;  
 get_ipflow(\%second_input,\%second_output);  
 print "Now is ".localtime()."\n";  
 print "-"x53,"\n";  
 print "IP Address\t\tIn Flow Rate\tOut Flow Rate\n";  
 for (keys %first_input) {  
   if ($ZERO != 1) {  
     if (defined $second_input{$_} and defined $second_output{$_} and int(($second_input{$_}-$first_input{$_})/1024/$SEC) == 0) {  
       next;  
     }  
   }  
   if (defined $second_input{$_} and defined $second_output{$_}) {  
   printf ("%s\t\t%.fKB\t\t%.fKB\n",$_,($second_input{$_}-$first_input{$_})/1024/$SEC,($second_output{$_}-$first_output{$_})/1024/$SEC);  
   }  
 }  
 print "-"x53,"\n";  
 print "Banned IP Address:\n";  
 for (keys %ban_ip) {  
   print "$_\n";  
 }



2、结果


◎ ZERO为非1的情况


引用


# perl ipflow.pl 
  
 Now is Thu Mar 12 18:35:10 2009   
 -----------------------------------------------------   
 IP Address              In Flow Rate    Out Flow Rate   
192.168.228.212         277 KByte/s     27 KByte/s
 192.168.228.200         40 KByte/s      16 KByte/s   
 -----------------------------------------------------   
 Banned IP Address:   
 192.168.228.50



◎ ZERO为1的情况


引用


# perl ipflow.pl 
  
 Now is Thu Mar 12 18:36:21 2009   
 -----------------------------------------------------   
 IP Address              In Flow Rate    Out Flow Rate   
 192.168.228.219         0 KByte/s       0 KByte/s   
 192.168.228.150         0 KByte/s       0 KByte/s   
 192.168.228.153         0 KByte/s       0 KByte/s   
 192.168.228.215         0 KByte/s       0 KByte/s   
192.168.228.212         220 KByte/s     27 KByte/s
 192.168.228.200         14 KByte/s      9 KByte/s   
 192.168.228.154         0 KByte/s       0 KByte/s   
 192.168.228.220         0 KByte/s       0 KByte/s   
 192.168.228.99          0 KByte/s       0 KByte/s   
 192.168.228.216         0 KByte/s       0 KByte/s   
 192.168.228.211         0 KByte/s       0 KByte/s   
 192.168.228.155         0 KByte/s       0 KByte/s   
192.168.228.218         21 KByte/s      1 KByte/s   
 192.168.228.30          0 KByte/s       0 KByte/s   
 192.168.228.221         0 KByte/s       0 KByte/s   
 -----------------------------------------------------   
 Banned IP Address:   192.168.228.50