tcpdump(8)
可以提供非常相似的東西;搜索與SYN
標誌設置的TCP數據包趕上發往您的其他同行three-way handshake第一包:
$ sudo tcpdump -c 10 -i eth0 "tcp[tcpflags] & (tcp-syn) != 0 and dst 192.168.0.1"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:26:24.800308 IP haig.59419 > 192.168.0.1.telnet: Flags [S], seq 3197302320, win 14600, options [mss 1460,sackOK,TS val 19460844 ecr 0,nop,wscale 7], length 0
...
18:26:27.420132 IP haig.59428 > 192.168.0.1.telnet: Flags [S], seq 1238498237, win 14600, options [mss 1460,sackOK,TS val 19461106 ecr 0,nop,wscale 7], length 0
10 packets captured
10 packets received by filter
0 packets dropped by kernel
你可以使用/usr/bin/time
或shell的time
內置,或者做一些算術與輸出中的時間戳,以獲得每秒的平均速率。 (用十幾包 - 這只是爲了演示。)
更新
我寫了一個小程序運行tcpdump(8)
,算上包,以及有多少包被的時間間隔內發送報告規定:
# ./counter --host 192.168.0.1 --interface eth0 --interval 3
2 requests in 3 seconds; average 0.67 req/seq
20 requests in 3 seconds; average 6.67 req/seq
19 requests in 3 seconds; average 6.33 req/seq
19 requests in 3 seconds; average 6.33 req/seq
^C
# ./counter --host 192.168.0.1 --interface eth0 --interval 5
30 requests in 5 seconds; average 6.00 req/seq
20 requests in 5 seconds; average 4.00 req/seq
1176 requests in 5 seconds; average 235.20 req/seq
1414 requests in 5 seconds; average 282.80 req/seq
0 requests in 5 seconds; average 0.00 req/seq
^C
因爲它要求tcpdump(8)
使用行緩衝輸出,我有點擔心,它可能無法擴展每秒超過200-300的請求,至少在我的硬件。但是如果沒有行緩衝輸出,在發送任何輸出之前,tcpdump(8)
將等待其輸出緩衝區(詳見setvbuf(3)
)完整,導致極度緊張的結果。
但是如果你的連接率不是那麼高,這可能會做你所需要的。如果你的連接率更高,那麼這個小小的黑客可能是最好的忽略 - 它讓我感到可能是計數流。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#define CMDLEN 1024
#define TCPDUMPLEN 4096
int show_stats;
long counter;
void alarm_handler(int signum)
{
show_stats = 1;
}
void install_handler(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = &alarm_handler;
if (sigaction(SIGALRM, &sa, NULL) == -1) {
perror("Can't install alarm handler");
exit(1);
}
}
int count_lines(char *p, int bytes)
{
int counter = 0;
char *i;
for (i=p; i < p+bytes ; i++) {
if (*i == '\n')
counter++;
}
return counter;
}
int spawn_tcpdump(char *host, char *interface)
{
int fd[2];
pid_t child;
if (pipe(fd) == -1) {
perror("Can't create pipes");
exit(1);
}
child = fork();
if (child == -1) {
perror("Can't fork(2) for tcpdump");
exit(1);
}
if (child == 0) {
int null;
int len;
char syn_and_dst[CMDLEN];
len = snprintf(syn_and_dst, CMDLEN, "tcp[tcpflags] & (tcp-syn) != 0 and dst %s", host);
if (len > CMDLEN) {
perror("host argument too long");
exit(1);
}
/* child writes into pipe */
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
/* throw away first two lines of tcpdump output */
null = open("/dev/null", O_WRONLY);
if (null == -1) {
perror("Can't open /dev/null");
exit(1);
}
dup2(null, STDERR_FILENO);
execl("/usr/sbin/tcpdump", "tcpdump", "-l", "-n", "-s 96", "-i", interface, syn_and_dst, (char *) NULL);
/* can't reach */
perror("Cannot execute tcpdump");
exit(1);
} else {
/* parent reads from pipe */
close(fd[1]);
return fd[0];
}
}
int main(int argc, char *argv[])
{
int tcpdump;
char *host;
char *interface;
long interval;
while (1) {
int option_index;
int c;
static struct option opts[] = {
{"host", required_argument, NULL, 'h'},
{"interface", required_argument, NULL, 'i'},
{"interval", required_argument, NULL, 'n'},
{0, 0, 0, 0},
};
c = getopt_long(argc, argv, "", opts, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
host = strdup(optarg);
break;
case 'i':
interface = strdup(optarg);
break;
case 'n': {
char *endptr;
interval = strtol(optarg, &endptr, 10);
if (!(optarg[0] != '\0' && endptr[0] == '\0')) {
fprintf(stderr, "Expected integer; invalid"
" input '%s'\n", optarg);
exit(1);
}
}
break;
default:
fprintf(stderr, "Option parsing error\n");
exit(1);
}
}
if (optind < argc) {
fprintf(stderr, "unexpected arguments: ");
while (optind < argc) {
fprintf(stderr, "%s ", argv[optind++]);
}
fprintf(stderr, "\n");
}
tcpdump = spawn_tcpdump(host, interface);
install_handler();
alarm(interval);
while(1) {
if (show_stats) {
printf("%ld requests in %ld seconds; average %2.2f req/seq\n",
counter, interval, (double)counter/(double)interval);
counter = 0;
show_stats = 0;
alarm(interval);
} else {
char buffer[TCPDUMPLEN];
int ret;
memset(buffer, 0, TCPDUMPLEN);
ret = read(tcpdump, buffer, TCPDUMPLEN);
if (ret == -1 && errno == EINTR) {
/* nop */
} else if (ret == -1) {
perror("read");
exit(1);
} else {
counter += count_lines(buffer, ret);
}
}
}
exit(0);
}
這是一個很好的開始,但對於-c的任何值都會在不會發生連接的情況下出現問題。我需要它能夠每秒鐘報告'0'連接,這可能會在下班時間發生,但這會在此期間掛起(等待)。有沒有辦法將tcpdump轉儲到tmp文件幾秒鐘,然後grep來計數?例如,在Munin使用這種解決方案時,我想每5分鐘獲取一次快照,並在5秒或更短的時間內獲取。 –
優秀! (我原來的評論是在更新之前)。我用perl攻擊了一些東西,定義了shell函數和字數,但是你的解決方案要好得多。感謝那。 –