将 tcpdump 输出写入压缩/gzip 文件
Write tcpdump output to compressed / gziped file
我想将 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
#
两个问题:
- 'obvious solution' 出了什么问题?
- 如何在 Debian Jessie 中捕获和压缩 tcpdump 输出? (显而易见的解决方案在那里也不起作用)
谢谢!
发生了什么事
解释一下这里发生了什么:
- Ctrl+C 向整个进程组发送SIGINT。这意味着它不仅会终止
tcpdump
,还会终止 gzip
。 (您尝试通过将内容移至后台进程,从而移出同一进程组来尝试避免这种情况的解决方法)。
- stdout 仅在输出到 TTY 时默认为 line-buffered;当输出到 FIFO 时,它是 block-buffered,仅当有足够大的块可用时,才通过从 left-hand 进程写入数据来提高效率。在 很多 情况下,您可以因此只使用
stdbuf -oL
或类似的来禁用它。然而...
gzip
本质上 无法完全无缓冲运行。这是因为 block-based 压缩算法需要将数据收集到 块 中;批量分析该内容; &c.
因此,如果 gzip
和 tcpdump
同时终止,这意味着无法保证 tcpdump
实际上能够刷新其输出缓冲区, 然后让 gzip
读取、压缩和写入刷新的数据 ,然后 gzip
本身从同时收到的信号中退出。
解决问题
请注意,headers 下包含单词 "Interactive" 的代码片段旨在用于交互式使用。
可靠的交互式解决方法(针对 Bash)
作为万无一失的解决方案,将 gzip
完全移动 out-of-band,这样当您在 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(通过杀死 only 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=; 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"
来自的回答(非常感谢他!):
Ctrl+C sends a SIGINT to the entire process group. That means it doesn't just terminate tcpdump
, but also terminates gzip
. (The workarounds you were attempting try to avoid this by moving content into background processes, and thus out of the same process group).
因为他是对的 gzip
只有在压缩完整 32k block
时才写入输出文件,所以我在一个终端中启动了 'obvious solution'...
$ 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
$
在后台启动 'obvious solution' tcpdump -l -i eth0 | gzip -c > test.gz &
将允许从同一终端终止 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
#
两个问题:
- 'obvious solution' 出了什么问题?
- 如何在 Debian Jessie 中捕获和压缩 tcpdump 输出? (显而易见的解决方案在那里也不起作用)
谢谢!
发生了什么事
解释一下这里发生了什么:
- Ctrl+C 向整个进程组发送SIGINT。这意味着它不仅会终止
tcpdump
,还会终止gzip
。 (您尝试通过将内容移至后台进程,从而移出同一进程组来尝试避免这种情况的解决方法)。 - stdout 仅在输出到 TTY 时默认为 line-buffered;当输出到 FIFO 时,它是 block-buffered,仅当有足够大的块可用时,才通过从 left-hand 进程写入数据来提高效率。在 很多 情况下,您可以因此只使用
stdbuf -oL
或类似的来禁用它。然而... gzip
本质上 无法完全无缓冲运行。这是因为 block-based 压缩算法需要将数据收集到 块 中;批量分析该内容; &c.
因此,如果 gzip
和 tcpdump
同时终止,这意味着无法保证 tcpdump
实际上能够刷新其输出缓冲区, 然后让 gzip
读取、压缩和写入刷新的数据 ,然后 gzip
本身从同时收到的信号中退出。
解决问题
请注意,headers 下包含单词 "Interactive" 的代码片段旨在用于交互式使用。
可靠的交互式解决方法(针对 Bash)
作为万无一失的解决方案,将 gzip
完全移动 out-of-band,这样当您在 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(通过杀死 only 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=; 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"
来自
Ctrl+C sends a SIGINT to the entire process group. That means it doesn't just terminate
tcpdump
, but also terminatesgzip
. (The workarounds you were attempting try to avoid this by moving content into background processes, and thus out of the same process group).
因为他是对的 gzip
只有在压缩完整 32k block
时才写入输出文件,所以我在一个终端中启动了 'obvious solution'...
$ 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
$
在后台启动 'obvious solution' tcpdump -l -i eth0 | gzip -c > test.gz &
将允许从同一终端终止 tcpdump。