将tcpdump输出写入压缩/ gziped文件

时间:2018-05-23 13:02:08

标签: bash debian gzip tcpdump

我想将tcpdump的文本输出写入压缩文件。

首先我尝试了最明显的:

# tcpdump -l -i eth0 | gzip -c > test.gz
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C63 packets captured
244 packets received by filter
0 packets dropped by kernel
4 packets dropped by interface

# file test.gz
test.gz: empty
# 

然后我找到了Debian 9 (Stretch)的以下解决方案:

# tcpdump -l -i eth0 | ( gzip -c > test.gz & )
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C150 packets captured
160 packets received by filter
0 packets dropped by kernel

# file test.gz 
test.gz: gzip compressed data, last modified: Wed May 23 12:56:16 2018, from Unix
# 

这适用于Debian 9 (Stretch)但不适用于Debian 8 (Jessie)

# tcpdump -l -i eth0 | ( gzip -c > test.gz & )
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
tcpdump: Unable to write output: Broken pipe
# 

两个问题:

  1. “明显的解决方案”有什么问题?
  2. 如何在Debian Jessie中捕获和压缩tcpdump输出? (显而易见的解决方案也不起作用)
  3. 谢谢!

2 个答案:

答案 0 :(得分:2)

发生了什么

解释这里发生的事情:

  • Ctrl + C 将SIGINT发送到整个流程组。这意味着它不会终止tcpdump,但也会终止gzip。 (您尝试的变通方法是通过将内容移动到后台进程中来避免这种情况,因此不会出现在同一进程组中。)
  • stdout默认情况下仅在输出到TTY时进行行缓冲;当输出到FIFO时,它被块缓冲,只有当有足够大的块可用时,才能通过从左侧进程写入数据来提高效率。在许多情况下,您可以使用stdbuf -oL或类似方法来禁用此功能。然而...
  • gzip 本质上无法完全无缓冲地运行。这是因为基于块的压缩算法需要将数据收集到中;分析批量内容; &安培; C。

因此,如果gziptcpdump同时终止,则表示无法保证tcpdump实际上能够刷新其输出缓冲区,< em>然后gzip读取,压缩和写入刷新的数据,然后gzip本身退出同时收到的信号。

解决问题

请注意,代码片段包含单词&#34; Interactive&#34;用于交互式使用

可靠的交互式解决方法(针对Bash)

作为一个万无一失的解决方案,将gzip完全移出带外,因此当您在tcpdump命令上按ctrl + c时,不会发送SIGINT:

exec 3> >(gzip -c >test.gz)  # Make FD 3 point to gzip
tcpdump -l -i eth0 >&3       # run tcpdump **AS A SEPARATE COMMAND** writing to that fd
exec 3>&-                    # later, after you cancelled tcpdump, close the FD.

可靠的交互式解决方法(适用于任何POSIX Shell)

同样的事情,但稍微长一点并且不依赖于流程替换:

mkfifo test.fifo                            # create a named FIFO
gzip -c <test.fifo >test.gz & gzip_pid="$!" # start gzip, reading from that named FIFO
tcpdump -l -i eth0 >test.fifo               # start tcpdump, writing to that named FIFO
rm test.fifo                                # delete the FIFO when done
wait "$gzip_pid"                            # ...and wait for gzip to exit

请注意,wait将具有gzip进程的退出状态,因此您可以确定是否遇到错误。

可靠的脚本化解决方法(适用于任何POSIX Shell)

如果我们正在运行脚本,那么设置信号处理程序是合适的,这样我们就可以明确地处理SIGINT(仅通过杀死 tcpdump):

#!/bin/sh
[ "$#" -gt 0 ] || {
  echo "Usage: ${0##*/} file.tcpdump.gz [tcpdump-args]" >&2
  echo "  Example: ${0##*/} foo.tcpdump.gz -l -i eth0" >&2
  exit 1
}
outfile=$1; shift
fifo=test-$$.fifo # for real code, put this in a unique temporary directory

trap '[ -n "$tcpdump_pid" ] && kill "$tcpdump_pid"' INT
trap 'rm -f -- "$fifo"' EXIT

rm -f -- "$fifo"; mkfifo "$fifo" || exit
gzip -c >"$outfile" <"$fifo" & gzip_pid=$!

# avoid trying to run tcpdump if gzip obviously failed to start
{ [ -n "$gzip_pid" ] && [ "$gzip_pid" -gt 0 ] && kill -0 "$gzip_pid"; } || exit 1

tcpdump "$@" >"$fifo" & tcpdump_pid=$!

# return exit status of tcpdump if it fails, or gzip if tcpdump succeeds
wait "$tcpdump_pid" || wait "$gzip_pid"

答案 1 :(得分:1)

Charles Duffy's回答(非常感谢他!):

  

Ctrl + C 将SIGINT发送到整个流程组。这意味着它不仅会终止tcpdump,还会终止gzip。 (您尝试的变通方法是通过将内容移动到后台进程中来避免这种情况,因此不会出现在同一进程组中。)

因为gzip仅在压缩完整32k block时才写出输出文件是对的,所以我在一个终端中启动了“明显的解决方案”......

$ tcpdump -l -i eth0 | gzip -c > test.gz
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
1926 packets captured
1938 packets received by filter
0 packets dropped by kernel
$ 

并从第二个终端杀死了tcpdump:

$ killall -INT tcpdump
$

在后台tcpdump -l -i eth0 | gzip -c > test.gz &中启动“明显的解决方案”将允许从同一终端中杀死tcpdump。