在本章中,我将使用 libiptc 中的以下函数开发一个简单的带宽计量器
此外,函数 gettimeofday 将用于测量经过的时间,函数 getopt 用于从命令行获取选项。
我不确定带宽计量器这个术语在这里是否用得恰当。我将带宽理解为对流量容量的指代;或许更好的术语应该是流量计量器。
/*
* How to use libiptc- program #4
* /usr/local/src/bw.c
* By Leonardo Balliache - 04.09.2002
* e-mail: leonardo@opalsoft.net
* --WELL COMMENTED-- to be easy followed by everyone.
*/
/* include files required */
#include <getopt.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <time.h>
#include <unistd.h>
#include "libiptc/libiptc.h"
#include "iptables.h"
/* colors to differentiate chains measures */
#define RED "\033[41m\033[37m"
#define GREEN "\033[42m\033[30m"
#define ORANGE "\033[43m\033[30m"
#define BLUE "\033[44m\033[37m"
#define MAGENTA "\033[45m\033[37m"
#define CYAN "\033[46m\033[30m"
#define WHITE "\033[47m\033[30m"
#define BLACK "\033[40m\033[37m"
#define RESET "\033[00m"
/* maximum number of chains to be processed */
#define MAXUSERCHAINS 7
/* time between measures in seconds; adjust as you like */
#define SLEEPTIME 1
/* structure to count bytes per chain */
struct bwcnt {
int start; /* the chain was initialized */
u_int64_t icnt; /* bytes through; previous measure */
u_int64_t ocnt; /* bytes through; current measure */
double bw; /* bandwitdh (flow) on this chain */
};
/* function to calculate differences of time in seconds.
* micro-seconds precision.
*/
double delta(struct timeval a, struct timeval b)
{
if (a.tv_usec & b.tv_usec) {
a.tv_sec--;
a.tv_usec += 1000000;
}
return a.tv_sec-b.tv_sec + (a.tv_usec-b.tv_usec)/1000000.0;
}
/* main function */
int main(int argc, char *argv[])
{
int i, j, ok;
double totbw;
iptc_handle_t h;
int c, act_bw = 0;
const char *chain = NULL;
const char *tablename = "filter";
struct timeval ti, to;
struct bwcnt bw[MAXUSERCHAINS];
struct ipt_counters *counters;
char *col[9] = { RED,GREEN,ORANGE,BLUE,MAGENTA,CYAN,WHITE,BLACK,RESET };
program_name = "bw";
program_version = NETFILTER_VERSION;
/* check options
* we have 2 options:
* -c = display current flow (each SLEEPTIME).
* -a = display average flow (from start); default option.
*/
while ((c = getopt (argc, argv, "ac")) != -1)
switch (c) {
case 'a':
act_bw = 0;
break;
case 'c':
act_bw = 1;
break;
case '?':
if (isprint(optopt))
fprintf (stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf (stderr,"Unknown option character `\\x%x'.\n",optopt);
exit(1);
default:
abort();
}
/* initialize array of chains */
memset(&bw, 0, MAXUSERCHAINS * sizeof(struct bwcnt));
/* get time to start meter on variable ti */
gettimeofday(&ti, NULL);
/* fire meter loop */
if ( act_bw )
printf("Displaying current flow values ...\n");
else
printf("Displaying average flow values ...\n");
/* forever loop; abort the program with ^C */
while ( 1 ) {
/* you have to initialize for each measure to be done */
if ( !(h = iptc_init(tablename)) ) {
printf("Error initializing: %s\n", iptc_strerror(errno));
exit(errno);
}
ok = 0; /* we start a new loop */
gettimeofday(&to, NULL); /* have a time shoot */
/* iterate through each chain of the table */
for (chain = iptc_first_chain(&h), i = 0;
chain;
chain = iptc_next_chain(&h)) {
if ( iptc_builtin(chain, h) )
continue; /* if it is a built-in chain, ignore it */
/* ok, read the counters of this chain */
if ( !(counters = iptc_read_counter(chain, 0, &h)) ) {
printf("Error reading %s: %s\n", chain, iptc_strerror(errno));
exit(errno);
}
/* check that we do not have more chains than we can process */
if ( i >= MAXUSERCHAINS ) {
printf("Maximum of %d user chains exceeded!!\n", MAXUSERCHAINS);
exit(1);
}
/* this chain counter has not been initialized; initialize it */
if ( bw[i].start == 0 ) {
bw[i].icnt = counters->bcnt;
bw[i].start = 1;
}
/* this chain has a previous measure; take the current one */
else {
bw[i].ocnt = counters->bcnt;
if ( bw[i].ocnt == bw[i].icnt ) /* no new bytes flowing? */
bw[i].bw = 0; /* flow is zero */
else
/* flow in this chain is:
* current bytes count (bw[i].octn) *minus*
* previous bytes count (bw[i].icnt) *divided by*
* 128.0 to convert bytes to kbits *and divided by*
* difference in times in seconds *to get*
* flow in kbits/sec that is what we want.
*/
bw[i].bw = (bw[i].ocnt - bw[i].icnt) / (128.0 * delta(to, ti));
/* do you want current flow of this chain? initialize previous
* bytes count to current bytes count; we get the flow in last
* SLEEPTIME elapsed time.
*/
if ( act_bw )
bw[i].icnt = bw[i].ocnt;
ok = 1; /* ok, we have some measure to show */
}
++i; /* next chain, please */
}
/* we iterate and i == 0; we do not have user chains at all */
if ( i == 0 ) {
printf("No user chains to meter!!\n");
exit(1);
}
/* do you want to measure current flow? initialize previous time
* to actual time; we get the time elapsed in last SLEEPTIME.
*/
if ( act_bw )
ti = to;
/* do we have something to show? ok, display it */
if ( ok ) {
totbw = 0;
for ( j = 0; j < i; ++j ) {
totbw = totbw + bw[j].bw; /* calculate total flow */
}
printf("%s%6.1fk:%s ", col[7], totbw, col[8]); /* display total */
for ( j = 0; j < i; ++j ) { /* display flow of each chain in color */
printf("%s%6.1fk%s ", col[j], bw[j].bw, col[8]);
}
printf("\n");
}
sleep(SLEEPTIME); /* rest a little; you go too fast */
} /* give us enough time in order to let some bytes flow */
exit(0); /* bye, we have our measures!! */
} /* main */ |
bash# ./ipt-cc bw |
首先,我们必须至少有 2 台 PC 连接在网络中。这是我们的图表配置
+--------+ eth0 eth0 +--------+ | PC #1 +-----------------+ PC #2 | +--------+ +--------+ eth0=192.168.1.1 eth0=192.168.1.2 |
其次,我们需要安装一个非常棒且有用的软件包,名为 netcat,由 Hobbit 编写。这个优秀的软件包将帮助我们在 2 个 NIC 之间注入和接收字节流。如果你的系统中没有该软件包,请从 http://rr.sans.org/audit/netcat.php 下载。
我使用的版本是 1.10-277。要安装它,请按照以下说明进行操作
bash# cp netcat-1.10.tar.gz /usr/local/src bash# tar xzvf netcat-1.10.tar.gz bash# cd netcat-1.10 |
我的版本需要先打补丁;检查你的版本是否有 .dif 扩展名的文件,并应用它
bash# patch -p0 -i netcat-1.10.dif |
接下来使用 make 编译软件包
bash# make linux |
将二进制文件 nc 复制到你的用户 bin 目录
bash# cp nc /usr/bin |
也复制到你网络中的第二台 PC
bash# scp nc 192.168.1.2:/usr/bin |
我们将使用 netcat 从 PC #2 "监听" 字节流,并从 PC #1 "发送" 字节流。在 PC #2 上使用 tty1 到 tty4 控制台,让我们启动 netcat 从这台 PC 监听。转到 PC #2 并在 tty1 中输入
bash# nc -n -v -l -s 192.168.1.2 -p 1001 >/dev/null |
netcat 必须响应
listening on [192.168.1.2] 1001 ... |
此命令启动了 netcat 以监听地址 192.168.1.2,使用端口号 1001。参数是:-n = 使用数字地址标识;-v = 详细模式;-l = 监听。netcat 在 192.168.1.2:1001 接收到的所有流量将被重定向到 "黑洞" 中,即/dev/null.
在 tty2、tty3 和 tty4 中重复该命令;使用 ALT-F2 切换到 tty2,登录后输入
bash# nc -n -v -l -s 192.168.1.2 -p 1002 >/dev/null |
现在我们正在"监听"相同的地址,但端口号为 1002。
现在继续 tty3
bash# nc -n -v -l -s 192.168.1.2 -p 1003 >/dev/null |
和 tty4
bash# nc -n -v -l -s 192.168.1.2 -p 1004 >/dev/null |
现在我们正在 PC #2 的地址 192.168.1.2 的端口 1001、1002、1003 和 1004 中监听。
回到 PC #1,让我们设置环境以允许 iptables 帮助我们完成测试
在 PC #1 上,在 tty1 中输入如下内容
bash# iptables -F bash# iptables -X bash# iptables -N chn_1 bash# iptables -N chn_2 bash# iptables -N chn_3 bash# iptables -N chn_4 bash# iptables -A chn_1 -j ACCEPT bash# iptables -A chn_2 -j ACCEPT bash# iptables -A chn_3 -j ACCEPT bash# iptables -A chn_4 -j ACCEPT bash# iptables -A OUTPUT -o eth0 -p tcp --dport 1001 -j chn_1 bash# iptables -A OUTPUT -o eth0 -p tcp --dport 1002 -j chn_2 bash# iptables -A OUTPUT -o eth0 -p tcp --dport 1003 -j chn_3 bash# iptables -A OUTPUT -o eth0 -p tcp --dport 1004 -j chn_4 |
这些命令将
清除表 filter 中的所有链。
删除表 filter 中的所有用户链。
创建用户链 chn_1、chn_2、chn_3 和 chn_4。
在每个用户链中建立目标 ACCEPT。
在链 OUTPUT 中创建 4 条规则,匹配端口号 1001 到 1004,并将它们定向到用户链 chn_1 到 chn_4。
现在使用当前值启动 bw 计量器
bash# ./bw -c |
它必须响应
Displaying current flow values ... 0.0k: 0.0k 0.0k 0.0k 0.0k 0.0k: 0.0k 0.0k 0.0k 0.0k 0.0k: 0.0k 0.0k 0.0k 0.0k 0.0k: 0.0k 0.0k 0.0k 0.0k |
它提示测量的是当前流量。每一行都是在每个 SLEEPTIME 间隔(在我们的程序中为 1 秒)进行的测量。第一列(黑色)是总流量,接下来的列(红色、绿色、橙色和蓝色)分别是链 chn_1、chn_2、chn_3 和 chn_4 中的流量。当然,我们现在没有任何流量。但是,让 bw 运行并继续阅读。
现在让我们启动一个字节流;使用 ALT-F2 转到 PC #1 中的 tty2,登录后输入
bash# yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2001 192.168.1.2 1001 |
netcat 响应
(UNKNOWN) [192.168.1.2] 1000 (?) open |
现在我们有从 PC #1 到 PC #2 的字节流。yes 生成一个恒定的零流量;此流量通过地址 192.168.1.1、端口 2001 管道传输到 netcat,并将其发送到 PC #2 的地址 192.168.1.2、端口 1001(PC #2 在此端口监听)。
现在检查 tty1 中 bw 的显示
7653.2k: 7653.2k 0.0k 0.0k 0.0k 7829.5k: 7829.5k 0.0k 0.0k 0.0k 7786.7k: 7786.7k 0.0k 0.0k 0.0k 7892.1k: 7982.1k 0.0k 0.0k 0.0k |
你的结果可能会因你系统的物理特性而异。在我的系统中,第一个链 chn_1 中的流量约为 7700 kbits/秒,它对应于 PC #2 中的端口号 1001。
现在让我们启动第二个字节流;使用 ALT-F3 转到 PC #1 中的 tty3,登录后输入
bash# yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2002 192.168.1.2 1002 |
netcat 响应
(UNKNOWN) [192.168.1.2] 1002 (?) open |
现在我们有 2 个从 PC #1 到 PC #2 的字节流;一个从 192.168.1.1:2001 到 192.168.1.2:1001,另一个从 192.168.1.1:2002 到 192.168.1.2:1002。
现在检查 tty1 中 bw 的显示
7819.6k: 4144.2k 3675.4k 0.0k 0.0k 8090.5k: 3923.9k 4166.6k 0.0k 0.0k 7794.7k: 3920.8k 3873.9k 0.0k 0.0k 7988.3k: 3754.6k 4233.7k 0.0k 0.0k |
现在我们有 2 个流量;每个流量大约占计算机输出总流量的 50%。Linux 内核尝试平衡 2 个输出通道之间可用的带宽。
要继续,请通过通道 192.168.1.1:2003-192.168.1.2:1003 和 192.168.1.1:2004-192.168.1.2:1004 启动另外 2 个流量。
在 tty4 中输入
bash# yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2003 192.168.1.2 1003 |
在 tty5 中输入
bash# yes 000000000000000000 | nc -n -v -s 192.168.1.1 -p 2004 192.168.1.2 1004 |
tty1 中 bw 的显示将类似于
8120.6k: 1705.3k 2354.9k 1898.6k 2161.8k 7765.3k: 1634.2k 2560.2k 2011.4k 1559.5k 7911.9k: 1699.8k 2090.3k 1768.0k 2353.8k 8309.4k: 1734.5k 1999.7k 1999.9k 2575.3k |
总带宽在 4 个流量通道之间分配。