【原创】如何选择“定时抓包”方案

坚强是说给别人听的谎言 提交于 2019-12-04 07:35:49

需求:能抓取指定时间长度的包,比如抓取 10s 长度的包;

可选方案:

  • 使用 tcpdump 命令的 -G-W 参数;
  • 自己通过脚本实现在指定时间到达后通过 kill 命令杀掉 tcpdump 抓包进程;
  • 使用 tshark 命令的 -a duration:xx 参数;

基于 tcpdump-G -W 参数实现定时的方案

因为 tcpdump 太有名了,所以一般人十有八九会先想到这个工具;

查阅 tcpdump 的 man 手册可以发现与定时功能相关的参数如下:

-G rotate_seconds

如果设置了该参数,tcpdump 将会以 rotate_seconds 为周期对通过 -w 选项指定命名的 dump 文件进行轮转;保存文件命名通过 -w 选项指定,并且应该包含符合 strftime(3) 定义的时间戳格式;如果未指定时间格式,则每一个新产生的文件将会覆盖之前的文件;

如果和 -C 选项配合使用,文件命名将会采用 'file<count>' 格式;

-W

-C 选项配合使用,可以将创建文件数目限制指定值,并且咋达到该数值后,从头开始进行文件覆盖;从行为上,类似于实现了一个 'rotating' buffer 的功能;另外,该选项会在为文件命名时使用足够多的前导 0 , 以便正确支持我们要求的最大数目,同时允许基于该数值进行正确排序;

-G 选项配合使用,可以将 rotated dump 文件的数目限制在指定值,并在达到该数值的时候以状态码 0 退出;如果与 -C 选项配合使用,the behavior will result in cyclical files per timeslice.

-C file_size

在将 raw packet 写入存储文件前,会先检查当前文件的大小是否超过了 file_size 值;如果是,则关闭当前存储文件,并重开一个新文件;在首个保存文件之后,文件命名的构成变成 -w 指定内容加上一个从 1 开始的数字;file_size 的单位为兆字节(是 1,000,000 字节,不是 1,048,576 字节);

看似只要使用 -G-W 参数就可以实现指定抓包时间长度的功能;试验如下:

验证没有网络流量时的情况

说明:

time 用于计算 tcpdump 执行的时间; 通过 tcpdump 抓取 22 端口上的 ssh 通信包; -G 3 -W 1 用于设置只抓取一次 3 秒长度的通信包;

试验步骤:启动如下命令后,不进行任何操作,过了 N 久后敲击 enter 键;

root@vagrant-ubuntu-trusty:~# time tcpdump -i any tcp port 22 -G 3 -W 1 -w timer_test.pcap
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

(什么都不做,等了 N 久后敲一下 enter)

Maximum file limit reached: 1

real	0m28.999s    -- 可以看到过去的时间远远超过 3 秒
user	0m0.008s
sys	    0m0.000s
root@vagrant-ubuntu-trusty:~#
root@vagrant-ubuntu-trusty:~# capinfos timer_test.pcap
File name:           timer_test.pcap
File type:           Wireshark/tcpdump/... - pcap
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: 262144 bytes
Number of packets:   0    -- 一个包也没抓到
File size:           24 bytes
Data size:           0 bytes
Capture duration:    n/a
Start time:          n/a
End time:            n/a
Data byte rate:      0 bytes/s
Data bit rate:       0 bits/s
Average packet size: 0.00 bytes
Average packet rate: 0 packets/sec
SHA1:                25b620a6d7e275a5b06401559e9c06bfd2e0f975
RIPEMD160:           60b5d5287c6e8fbd298ff2dac91e5a802e4b706a
MD5:                 3ad37ddb8fad5d769e0df74c7678ba15
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

查看包的内容发现:确实一个包也没抓到;

root@vagrant-ubuntu-trusty:~# tshark -r timer_test.pcap
Running as user "root" and group "root". This could be dangerous.
root@vagrant-ubuntu-trusty:~#

验证存在少量网络流量时的情况

试验步骤:启动如下命令后,按照大概每秒一次的速度敲击 enter 键;

root@vagrant-ubuntu-trusty:~# time tcpdump -i any tcp port 22 -G 3 -W 1 -w timer_test.pcap
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

(enter)
(enter)
(enter)

Maximum file limit reached: 1

real	0m3.000s   -- 这次刚好运行了 3 秒
user	0m0.000s
sys	    0m0.004s
root@vagrant-ubuntu-trusty:~#
root@vagrant-ubuntu-trusty:~# capinfos timer_test.pcap
File name:           timer_test.pcap
File type:           Wireshark/tcpdump/... - pcap
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: 262144 bytes
Number of packets:   3      -- 抓到了 3 个数据包
File size:           318 bytes
Data size:           246 bytes
Capture duration:    0 seconds
Start time:          Fri Dec 16 14:21:39 2016
End time:            Fri Dec 16 14:21:39 2016
Data byte rate:      719 kBps
Data bit rate:       5752 kbps
Average packet size: 82.00 bytes
Average packet rate: 8768 packets/sec
SHA1:                3edbbc777b6732fdc8817f9da1ce0f0f98542936
RIPEMD160:           95bbc3c5b4bdab0cfe1d719dcff30e757fe47c79
MD5:                 147792c10dbe9595cbfc63bf6fb8fe1b
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

查看包的内容发现:抓取到 3 个包,3 个包均在 0.001 秒内发生;

root@vagrant-ubuntu-trusty:~# tshark -r timer_test.pcap
Running as user "root" and group "root". This could be dangerous.
  1   0.000000     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
  2   0.000136    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  3   0.000342     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=37 Ack=37 Win=65535 Len=0
root@vagrant-ubuntu-trusty:~#

验证存在大量网络流量时的情况

试验步骤:启动如下命令后,按照大概每秒 3~4 次的速度连续敲击 enter 键;

root@vagrant-ubuntu-trusty:~# time tcpdump -i any tcp port 22 -G 3 -W 1 -w timer_test.pcap
tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

(enter)
(enter)
(enter)
(enter)
(enter)
(enter)
...
(enter)
(enter)
(enter)
(enter)
(enter)
(enter)

Maximum file limit reached: 1

real	0m2.998s    -- 这次也刚好运行了 3 秒
user	0m0.000s
sys 	0m0.000s
root@vagrant-ubuntu-trusty:~#
root@vagrant-ubuntu-trusty:~# capinfos timer_test.pcap
File name:           timer_test.pcap
File type:           Wireshark/tcpdump/... - pcap
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: 262144 bytes
Number of packets:   18    -- 抓到了 18 个数据包
File size:           1788 bytes
Data size:           1476 bytes
Capture duration:    1 seconds
Start time:          Fri Dec 16 14:29:33 2016
End time:            Fri Dec 16 14:29:34 2016
Data byte rate:      1955 bytes/s
Data bit rate:       15 kbps
Average packet size: 82.00 bytes
Average packet rate: 23 packets/sec
SHA1:                3d4c457b55ac7f496c5c15b1fbf60775aaab23e7
RIPEMD160:           b4a009650d64e5aa90e405d39a735e47e3f904e5
MD5:                 426dba1247972b13257eb12fea76817e
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

查看包的内容发现:抓取到 18 个包,18 个包在 1 秒内发生;

root@vagrant-ubuntu-trusty:~# tshark -r timer_test.pcap
Running as user "root" and group "root". This could be dangerous.
  1   0.000000     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
  2   0.000138    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  3   0.000303     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=37 Ack=37 Win=65535 Len=0
  4   0.149969     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
  5   0.150119    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  6   0.150323     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=73 Ack=73 Win=65535 Len=0
  7   0.302591     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
  8   0.302728    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  9   0.302907     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=109 Ack=109 Win=65535 Len=0
 10   0.449231     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
 11   0.449369    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
 12   0.449550     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=145 Ack=145 Win=65535 Len=0
 13   0.600790     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
 14   0.600929    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
 15   0.601112     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=181 Ack=181 Win=65535 Len=0
 16   0.754338     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
 17   0.754482    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
 18   0.754697     10.0.2.2 -> 10.0.2.15    TCP 62 53815→22 [ACK] Seq=217 Ack=217 Win=65535 Len=0
root@vagrant-ubuntu-trusty:~#

小结

上面费了那么多劲,其实就是想要告诉你,使用 tcpdump-G -W 参数实现抓取指定时间长度的功能不那么靠谱~~

定制 shell 脚本通过 kill 实现 tcpdump 的退出

该方案在网上能搜出很多,基本上大同小异;这里想要说明的问题是,使用哪种信号停止 tcpdump 的运行是靠谱的~

脚本是组内小伙写的,简单讲经历了三次主要变更:

初级阶段

a. 后台(&)启动 tcpdump 进行抓包;

b. 通过 sleep 进行定时;

c. 在时间到达后通过 kill -9 杀掉 tcpdump 进程;

中级阶段

将上述步骤 c 中的 kill -9 换成 kill -2

或者换成先 kill 尝试杀,通过 sleep 等 N 秒,发现 tcpdump 未退出再 kill -9 强杀;

高级阶段

使用 timeout 命令运行 tcpdump ,直接替代掉上述 abc 步骤;

关于 timeout 的原理后面单独介绍;

变更的原因

变更的原因和下面几个问题有关:

  • kill -9 强杀导致保存的数据包出错;
  • kill -2 优雅的杀,但会导致定时不够准确;
  • 通过 sleep 进行定时本身带来的不准确性;

下面基于试验进行说明,事先准备好三个抓包文件:

  1. test_ctrl_c.pcap 为通过 Ctrl+C 停止的抓包;
  2. test_kill_2.pcap 为通过 kill -2 停止的抓包;
  3. test_kill_9.pcap 为通过 kill -9 停止的抓包;
root@vagrant-ubuntu-trusty:~# ll
...
-rw-r--r-- 1 root root     2874 Dec 13 17:56 test_ctrl_c.pcap
-rw-r--r-- 1 root root    12746 Dec 13 17:56 test_kill_2.pcap
-rw-r--r-- 1 root root    12288 Dec 13 17:56 test_kill_9.pcap

分别确认每个包中的数据状态;

  • test_ctrl_c.pcap 中的数据没有问题;
root@vagrant-ubuntu-trusty:~# tshark -r test_ctrl_c.pcap
Running as user "root" and group "root". This could be dangerous.
  1   0.000000    10.0.2.15 -> 10.0.2.2     SSH 100 Server: Encrypted packet (len=44)
  2   0.000196     10.0.2.2 -> 10.0.2.15    TCP 62 51858→22 [ACK] Seq=1 Ack=45 Win=65535 Len=0
  3   0.000289    10.0.2.15 -> 10.0.2.2     SSH 156 Server: Encrypted packet (len=100)
  4   0.000479    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
...
 29   9.941804   11.11.11.1 -> 11.11.11.15  TCP 80 5673→44144 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=32 TSval=973233923 TSecr=31683832 SACK_PERM=1
 30   9.941866  11.11.11.15 -> 11.11.11.1   TCP 68 44144→5673 [RST, ACK] Seq=1 Ack=1 Win=29248 Len=0 TSval=0 TSecr=973233923
root@vagrant-ubuntu-trusty:~#
  • test_kill_2.pcap 中的数据没有问题;
root@vagrant-ubuntu-trusty:~# tshark -r test_kill_2.pcap
Running as user "root" and group "root". This could be dangerous.
  1   0.000000    10.0.2.15 -> 10.0.2.2     SSH 108 Server: Encrypted packet (len=52)
  2   0.000521     10.0.2.2 -> 10.0.2.15    TCP 62 51858→22 [ACK] Seq=1 Ack=53 Win=65535 Len=0
  3   0.381098     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
  4   0.381410    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
...
129  10.342446     10.0.2.2 -> 10.0.2.15    SSH 92 Client: Encrypted packet (len=36)
130  10.342960    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
131  10.343120     10.0.2.2 -> 10.0.2.15    TCP 62 51858→22 [ACK] Seq=1261 Ack=1461 Win=65535 Len=0
root@vagrant-ubuntu-trusty:~#
  • test_kill_9.pcap 中的数据存在问题,报 "appears to have been cut short in the middle of a packet." 错误;
root@vagrant-ubuntu-trusty:~# tshark -r test_kill_9.pcap
Running as user "root" and group "root". This could be dangerous.
  1   0.000000    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  2   0.000012     10.0.2.2 -> 10.0.2.15    TCP 62 51858→22 [ACK] Seq=1 Ack=37 Win=65535 Len=0
  3   0.000201    10.0.2.15 -> 10.0.2.2     SSH 92 Server: Encrypted packet (len=36)
  4   0.000388     10.0.2.2 -> 10.0.2.15    TCP 62 51858→22 [ACK] Seq=1 Ack=73 Win=65535 Len=0
...
123   7.513127  11.11.11.15 -> 11.11.11.1   TCP 68 44153→5673 [RST, ACK] Seq=1 Ack=1 Win=29248 Len=0 TSval=0 TSecr=973248843
124   8.513389  11.11.11.15 -> 11.11.11.1   TCP 76 44892→5674 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=31687833 TSecr=0 WS=64
125   8.513853   11.11.11.1 -> 11.11.11.15  TCP 80 5674→44892 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 WS=32 TSval=973249841 TSecr=31687833 SACK_PERM=1

tshark: The file "test_kill_9.pcap" appears to have been cut short in the middle of a packet.
root@vagrant-ubuntu-trusty:~#

通过 wireshark 打开上述问题包,同样会看到相应错误信息,如下图:

原理分析

通过 Ctrl + C 停止 tcpdump

root@vagrant-ubuntu-trusty:~# strace -f -t -s 1024 -o ctrl_c.txt tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:05:48.372349 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 3659061230:3659061266, ack 2948511, win 40880, length 36
15:05:48.372504 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 36:72, ack 1, win 40880, length 36
15:05:48.372578 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 36, win 65535, length 0
15:05:48.372659 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 72, win 65535, length 0
...
15:05:50.412337 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 804:936, ack 37, win 40880, length 132
15:05:50.412660 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 936, win 65535, length 0
15:05:50.412728 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 936:980, ack 37, win 40880, length 44
15:05:50.413661 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 980:1128, ack 37, win 40880, length 148
15:05:50.413777 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 980, win 65535, length 0
15:05:50.413905 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1128, win 65535, length 0
15:05:50.414717 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1128:1324, ack 37, win 40880, length 196
15:05:50.414926 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1324, win 65535, length 0
^C
54 packets captured
58 packets received by filter
0 packets dropped by kernel
root@vagrant-ubuntu-trusty:~#

停止前瞬间

root@vagrant-ubuntu-trusty:~# cat ctrl_c.txt
...
27163 15:05:51 write(1, "15:05:50.413661 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 980:1128, ack 37, win 40880, length 148\n", 107) = 107
27163 15:05:51 write(1, "15:05:50.413777 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 980, win 65535, length 0\n", 91) = 91
27163 15:05:51 write(1, "15:05:50.413905 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1128, win 65535, length 0\n", 92) = 92
27163 15:05:51 write(1, "15:05:50.414717 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1128:1324, ack 37, win 40880, length 196\n", 108) = 108
27163 15:05:51 write(1, "15:05:50.414926 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1324, win 65535, length 0\n", 92) = 92
27163 15:05:51 poll([{fd=3, events=POLLIN}], 1, 1000) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
27163 15:05:52 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
27163 15:05:52 alarm(0)                 = 0
27163 15:05:52 rt_sigreturn()           = -1 EINTR (Interrupted system call)
27163 15:05:52 write(1, "\n", 1)        = 1
27163 15:05:52 open("/proc/net/dev", O_RDONLY) = 4
27163 15:05:52 fstat(4, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
27163 15:05:52 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa01099a000
27163 15:05:52 read(4, "Inter-|   Receive                                                |  Transmit\n face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n  eth0: 196882525  330484    0    0    0     0          0         0 18125005  192153    0    0    0     0       0          0\n  eth1: 7108624   89629    0    0    0     0          0         0 12262901  170044    0    0    0     0       0          0\n    lo:   99313     582    0    0    0     0          0         0    99313     582    0    0    0     0       0          0\n", 1024) = 571
27163 15:05:52 close(4)                 = 0
27163 15:05:52 munmap(0x7fa01099a000, 4096) = 0
27163 15:05:52 getsockopt(3, SOL_PACKET, PACKET_STATISTICS, {packets=58, drops=0}, [8]) = 0
27163 15:05:52 write(2, "54 packets captured", 19) = 19
27163 15:05:52 write(2, "\n", 1)        = 1
27163 15:05:52 write(2, "58 packets received by filter", 29) = 29
27163 15:05:52 write(2, "\n", 1)        = 1
27163 15:05:52 write(2, "0 packets dropped by kernel", 27) = 27
27163 15:05:52 write(2, "\n", 1)        = 1
27163 15:05:52 setsockopt(3, SOL_PACKET, PACKET_RX_RING, {block_size=0, block_nr=0, frame_size=0, frame_nr=0}, 16) = -1 EINVAL (Invalid argument)
27163 15:05:52 munmap(0x7fa00f989000, 2097152) = 0
27163 15:05:52 munmap(0x7fa010948000, 266240) = 0
27163 15:05:52 close(3)                 = 0
27163 15:05:52 exit_group(0)            = ?
27163 15:05:52 +++ exited with 0 +++

可以看到 Ctrl+C 触发的标识未 SI_KERNELSIGINT 信号被优雅的处理了;

通过 kill -2tcpdump

root@vagrant-ubuntu-trusty:~# strace -f -t -s 1024 -o kill_2.txt tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:17:33.533414 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 3659069618:3659069654, ack 2952795, win 40880, length 36
15:17:33.533592 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 36, win 65535, length 0
15:17:33.533693 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 36:72, ack 1, win 40880, length 36
15:17:33.534041 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 72, win 65535, length 0
15:17:33.534528 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 72:108, ack 1, win 40880, length 36
15:17:33.535004 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 108, win 65535, length 0
15:17:33.535179 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 108:144, ack 1, win 40880, length 36
15:17:33.535469 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 144, win 65535, length 0
...
15:17:34.635333 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 692:728, ack 1, win 40880, length 36
15:17:34.635443 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 728, win 65535, length 0
15:17:34.635617 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 728:764, ack 1, win 40880, length 36
15:17:34.635778 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 764, win 65535, length 0
15:17:34.635830 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 764:800, ack 1, win 40880, length 36
15:17:34.635903 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 800, win 65535, length 0

44 packets captured
65 packets received by filter
0 packets dropped by kernel
root@vagrant-ubuntu-trusty:~#

死前瞬间

root@vagrant-ubuntu-trusty:~# cat kill_2.txt
...
27205 15:17:35 write(1, "15:17:34.635617 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 728:764, ack 1, win 40880, length 36\n", 104) = 104
27205 15:17:35 write(1, "15:17:34.635778 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 764, win 65535, length 0\n", 91) = 91
27205 15:17:35 write(1, "15:17:34.635830 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 764:800, ack 1, win 40880, length 36\n", 104) = 104
27205 15:17:35 write(1, "15:17:34.635903 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 800, win 65535, length 0\n", 91) = 91
27205 15:17:35 poll([{fd=3, events=POLLIN}], 1, 1000) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
27205 15:17:36 --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=26979, si_uid=0} ---
27205 15:17:36 alarm(0)                 = 0
27205 15:17:36 rt_sigreturn()           = -1 EINTR (Interrupted system call)
27205 15:17:36 write(1, "\n", 1)        = 1
27205 15:17:36 open("/proc/net/dev", O_RDONLY) = 4
27205 15:17:36 fstat(4, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
27205 15:17:36 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5a1cf33000
27205 15:17:36 read(4, "Inter-|   Receive                                                |  Transmit\n face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n  eth0: 196911929  330885    0    0    0     0          0         0 18150465  192387    0    0    0     0       0          0\n  eth1: 7108624   89629    0    0    0     0          0         0 12262901  170044    0    0    0     0       0          0\n    lo:   99313     582    0    0    0     0          0         0    99313     582    0    0    0     0       0          0\n", 1024) = 571
27205 15:17:36 close(4)                 = 0
27205 15:17:36 munmap(0x7f5a1cf33000, 4096) = 0
27205 15:17:36 getsockopt(3, SOL_PACKET, PACKET_STATISTICS, {packets=65, drops=0}, [8]) = 0
27205 15:17:36 write(2, "44 packets captured", 19) = 19
27205 15:17:36 write(2, "\n", 1)        = 1
27205 15:17:36 write(2, "65 packets received by filter", 29) = 29
27205 15:17:36 write(2, "\n", 1)        = 1
27205 15:17:36 write(2, "0 packets dropped by kernel", 27) = 27
27205 15:17:36 write(2, "\n", 1)        = 1
27205 15:17:36 setsockopt(3, SOL_PACKET, PACKET_RX_RING, {block_size=0, block_nr=0, frame_size=0, frame_nr=0}, 16) = -1 EINVAL (Invalid argument)
27205 15:17:36 munmap(0x7f5a1bf22000, 2097152) = 0
27205 15:17:36 munmap(0x7f5a1cee1000, 266240) = 0
27205 15:17:36 close(3)                 = 0
27205 15:17:36 exit_group(0)            = ?
27205 15:17:36 +++ exited with 0 +++

可以看到 kill -2 触发的标识未 SI_USERSIGINT 信号被优雅的处理了;

通过 kill -9tcpdump

root@vagrant-ubuntu-trusty:~# strace -f -t -s 1024 -o kill_9.txt tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:51:26.838774 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 3659045002:3659045038, ack 2940875, win 40880, length 36
14:51:26.839029 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 36, win 65535, length 0
14:51:26.839111 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 36:72, ack 1, win 40880, length 36
14:51:26.839224 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 72, win 65535, length 0
14:51:26.839384 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 72:108, ack 1, win 40880, length 36
...
14:51:28.883676 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1252:1296, ack 1, win 40880, length 44
14:51:28.883884 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1296, win 65535, length 0
14:51:28.883949 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1296:1332, ack 1, win 40880, length 36
14:51:28.884059 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1332, win 65535, length 0
14:51:28.884331 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1332:1392, ack 1, win 40880, length 60
14:51:28.884497 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1392, win 65535, length 0
Killed
root@vagrant-ubuntu-trusty:~#

死前瞬间

root@vagrant-ubuntu-trusty:~# cat kill_9.txt
...
27090 14:51:29 write(1, "14:51:28.883949 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1296:1332, ack 1, win 40880, length 36\n", 106) = 106
27090 14:51:29 write(1, "14:51:28.884059 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1332, win 65535, length 0\n", 92) = 92
27090 14:51:29 write(1, "14:51:28.884331 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1332:1392, ack 1, win 40880, length 60\n", 106) = 106
27090 14:51:29 write(1, "14:51:28.884497 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1392, win 65535, length 0\n", 92) = 92
27090 14:51:29 poll([{fd=3, events=POLLIN}], 1, 1000 <unfinished ...>
27090 14:51:30 +++ killed by SIGKILL +++

可以看到 kill -9 触发的 SIGKILL 信号强杀了 tcpdump 进程,没有任何优雅而言;

小结

  • Ctrl+Ckill -2 命令行为基本等价,都可以令 tcpdump 优雅退出;
  • 优雅退出的时间长度是不可控的,若采用 sleepkill -9 的方式进行处理,理论上仍可能造成报数据不完整;
  • 数据不完整的原因是由于抓包数据写入文件时,会使用操作系统中的 buffer ,因此强杀会导致数据丢失;一般情况下,即使出错也可认为对整体分析没有影响(仅丢失最后的部分数据包);
  • 关于 tcpdump 的 buffer 控制,可以查阅 -l-U 参数;
  • 关于 tcpdump 在收到 SIGINT 信号后能优雅退出的原因,可以通过 strace 进行追踪;

包修复工具 pcapfix

pcapfix 工具用于尝试修复有损毁的 pcap 和 pcapng 文件;为了修复有损毁的 pcap 文件,该工具首先会检查未经过任何处理的(intact)pcap global header ,如果存在损毁字节则进行相应修复;如果压根不存在 global header ,那么 pcapfix 会添加一个自行创建的 header 给文件;第二步,该工具会尝试在文件内部查找 pcap packet headers ,该 header 位于 global header 的下方;之后再检查其值是否正确(或者说是否看起来正确),如果存在错误,则会尝试进行修复;

为了修复 pcapng 文件,该工具会遍历文件中全部 packet 的 headers ;并检查 mandatory Section Header 和 Interface Description Block ,若不存在则进行创建; Pcapfix 会确认 block 大小的正确性,以及 option fields 的有效性;如果存在错误,则会按需要修复有问题的 fields 或者干脆直接跳过,再进行调整以保证最终得到一个正确可用的 pcapng 文件;

算法实现:

pcapfix will first step through the packets respectively blocks top down until it recognizes corrupted data by checking field validity and using plausibility checks. After that the tool will brute force further pcap packet headers respectively block headers by reading the file byte by byte. If another proper packet or block is found, pcapfix restores the data in between by adding a well-formed pcap packet header or skips it if unusable.

可用配置;

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix -h
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

Usage: ../pcapfix [OPTIONS] filename
OPTIONS:	-d        , --deep-scan          	Deep scan (pcap only)
		-n        , --pcapng             	force pcapng format
		-o <file> , --outfile <file>     	set output file name
		-t <nr>   , --data-link-type <nr>	Data link type
		-v        , --verbose            	Verbose output

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#

修复处理过程;

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix test_ctrl_c.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: test_ctrl_c.pcap
[*] Writing to file: fixed_test_ctrl_c.pcap
[*] File size: 2874 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[-] The global pcap header seems to be corrupt! ==> CORRECTED
[*] Analyzing packets...
[*] Progress:  20.04 %
[*] Progress:  41.68 %
[*] Progress:  60.61 %
[*] Progress:  81.07 %
[*] Progress: 100.00 %
[*] Wrote 30 packets to file.
[+] SUCCESS: 1 Corruption(s) fixed!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix test_kill_2.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: test_kill_2.pcap
[*] Writing to file: fixed_test_kill_2.pcap
[*] File size: 12746 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[-] The global pcap header seems to be corrupt! ==> CORRECTED
[*] Analyzing packets...
[*] Progress:  20.05 %
[*] Progress:  40.03 %
[*] Progress:  60.57 %
[*] Progress:  80.43 %
[*] Progress: 100.00 %
[*] Wrote 131 packets to file.
[+] SUCCESS: 1 Corruption(s) fixed!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix test_kill_9.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: test_kill_9.pcap
[*] Writing to file: fixed_test_kill_9.pcap
[*] File size: 12288 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[-] The global pcap header seems to be corrupt! ==> CORRECTED
[*] Analyzing packets...
[*] Progress:  20.67 %
[*] Progress:  40.27 %
[*] Progress:  60.11 %
[*] Progress:  80.86 %
[+] CORRECTED Packet #126 at position 12216 (1481621480 | 713328 | 56 | 68).
[*] Progress: 100.00 %
[*] Wrote 126 packets to file.
[+] SUCCESS: 2 Corruption(s) fixed!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ll
...
-rw-r--r-- 1 root root  2874 Dec 16 18:25 fixed_test_ctrl_c.pcap
-rw-r--r-- 1 root root 12746 Dec 16 18:25 fixed_test_kill_2.pcap
-rw-r--r-- 1 root root 12288 Dec 16 18:26 fixed_test_kill_9.pcap
-rw-r--r-- 1 root root  2874 Dec 13 17:56 test_ctrl_c.pcap
-rw-r--r-- 1 root root 12746 Dec 13 17:56 test_kill_2.pcap
-rw-r--r-- 1 root root 12288 Dec 13 17:56 test_kill_9.pcap
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix fixed_test_ctrl_c.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: fixed_test_ctrl_c.pcap
[*] Writing to file: fixed_fixed_test_ctrl_c.pcap
[*] File size: 2874 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[+] The global pcap header seems to be fine!
[*] Analyzing packets...
[*] Progress:  20.04 %
[*] Progress:  41.68 %
[*] Progress:  60.61 %
[*] Progress:  81.07 %
[*] Progress: 100.00 %
[*] Your pcap file looks proper. Nothing to fix!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix fixed_test_kill_2.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: fixed_test_kill_2.pcap
[*] Writing to file: fixed_fixed_test_kill_2.pcap
[*] File size: 12746 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[+] The global pcap header seems to be fine!
[*] Analyzing packets...
[*] Progress:  20.05 %
[*] Progress:  40.03 %
[*] Progress:  60.57 %
[*] Progress:  80.43 %
[*] Progress: 100.00 %
[*] Your pcap file looks proper. Nothing to fix!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#
root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles# ../pcapfix fixed_test_kill_9.pcap
pcapfix 1.1.0 (c) 2012-2014 Robert Krause

[*] Reading from file: fixed_test_kill_9.pcap
[*] Writing to file: fixed_fixed_test_kill_9.pcap
[*] File size: 12288 bytes.
[+] This is a PCAP file.
[*] Analyzing Global Header...
[+] The global pcap header seems to be fine!
[*] Analyzing packets...
[*] Progress:  20.67 %
[*] Progress:  40.27 %
[*] Progress:  60.11 %
[*] Progress:  80.86 %
[*] Progress: 100.00 %
[*] Your pcap file looks proper. Nothing to fix!

root@vagrant-ubuntu-trusty:~/workspace/WGET/pcapfix-1.1.0/testfiles#

通过 tshark 或者 wireshark 打开修复后的数据包,可以发现不会再报出之前的错误信息;

基于 timeout 的定时功能杀 tcpdump

首先查看 man 手册说明

timeout: Run a command with a time limit

timeout 负责运行指定命令,若在指定时间间隔后,命令仍在运行则将其 kill 掉;语法如下:

timeout [option] duration command [arg]…

command 不可以是某些特定 built-in 实用程序

该程序可以接受如下选项;所有选项必须在 operands 之前使用;

  • --preserve-status 返回受控 command 在超时后的 exit 状态码;而不是表明 timeout 半身的特定 exit 状态码;使用场景:受控 command 需要支持无法确定运行时间的情况;

  • --foreground 不创建单独的后台程序组,以便受控 command 能够正常使用前台 TTY ;使用场景:支持针对不是直接从交互 shell 上启动的 command 的超时处理;存在两种具体情况:

  1. command 本身是交互类型的,并且需要从 terminal 上进行读取;
  2. 用户想要从 terminal 上直接发送信号给 command (比如 Ctrl-C );

需要注意,在这种操作模式下,该 command 的任何孩子进程将不会出现超时行为;同样的,信号 SIGCONT 也不会被发送给 command ,因为该信号对于前台进程来说通常是不需要的;并且可能导致那些监督了自身的执行程序(比如 GDB)出现间歇性信号投递问题;

  • -k duration
  • --kill-after=duration

在指定的 duration 过去后,将通过 KILL 信号确保受控 command 一定会被 kill 掉;若未指定该选项,在选用的信号未非致命的情况下,timeout 可能出现无法杀死 command 的情况;

  • -s signal
  • --signal=signal

在超时发生后,发送指定 signal 信号给 command ,而不是采用默认的 TERM 信号;参数 signal 可以指定为像 HUP 一样的信号名字,或者信号本身所对应的数字;

duration 为一个浮点类型的数字,并可以指定单位;

‘s’ for seconds (the default) ‘m’ for minutes ‘h’ for hours ‘d’ for days

若指定 duration 为 0 则会去使能相应的超时行为;需要注意的是,实际的超时 duration 值依赖于系统本身情况,在需要指定秒级超时以下的超时值时需要考虑到该情况;

Exit status:

124 if command times out 125 if timeout itself fails 126 if command is found but cannot be invoked 127 if command cannot be found 137 if command is sent the KILL(9) signal (128+9)

the exit status of command otherwise

通过 timeout 停止 tcpdump

root@vagrant-ubuntu-trusty:~# strace -f -t -s 1024 -o timeout.txt timeout 5 tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:59:03.337331 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 3659057082:3659057118, ack 2946567, win 40880, length 36
14:59:03.337602 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 36, win 65535, length 0
14:59:03.337662 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 36:72, ack 1, win 40880, length 36
14:59:03.337904 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 72:108, ack 1, win 40880, length 36
14:59:03.338026 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 72, win 65535, length 0
14:59:03.338033 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 108, win 65535, length 0
14:59:03.338117 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 108:144, ack 1, win 40880, length 36
14:59:03.338276 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 144, win 65535, length 0
...
14:59:05.385635 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1184:1324, ack 1, win 40880, length 140
14:59:05.385845 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1324, win 65535, length 0
14:59:05.386623 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1324:1528, ack 1, win 40880, length 204
14:59:05.386873 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1528, win 65535, length 0
14:59:06.331949 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1528:1764, ack 1, win 40880, length 236
14:59:06.332328 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1764, win 65535, length 0
14:59:06.332713 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1764:1880, ack 1, win 40880, length 116
14:59:06.332849 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1880, win 65535, length 0

70 packets captured
74 packets received by filter
0 packets dropped by kernel
root@vagrant-ubuntu-trusty:~#

死前瞬间

root@vagrant-ubuntu-trusty:~# cat timeout.txt
...
27143 14:59:07 write(1, "14:59:06.331949 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1528:1764, ack 1, win 40880, length 236\n", 107) = 107
27143 14:59:07 write(1, "14:59:06.332328 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1764, win 65535, length 0\n", 92) = 92
27143 14:59:07 write(1, "14:59:06.332713 IP 10.0.2.15.ssh > 10.0.2.2.51858: Flags [P.], seq 1764:1880, ack 1, win 40880, length 116\n", 107) = 107
27143 14:59:07 write(1, "14:59:06.332849 IP 10.0.2.2.51858 > 10.0.2.15.ssh: Flags [.], ack 1880, win 65535, length 0\n", 92) = 92
27143 14:59:07 poll([{fd=3, events=POLLIN}], 1, 1000 <unfinished ...>
27142 14:59:08 <... wait4 resumed> 0x7fff4ce181ac, 0, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
27142 14:59:08 --- SIGALRM {si_signo=SIGALRM, si_code=SI_TIMER, si_pid=0, si_uid=0, si_value=34480176} ---
27142 14:59:08 kill(27143, SIGTERM)     = 0
27142 14:59:08 rt_sigaction(SIGTERM, {SIG_IGN, [TERM], SA_RESTORER|SA_RESTART, 0x7f892818c2f0}, {0x402710, [], SA_RESTORER|SA_RESTART, 0x7f8928531d10}, 8) = 0
27142 14:59:08 kill(0, SIGTERM)         = 0
27142 14:59:08 --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=27142, si_uid=0} ---
27142 14:59:08 kill(27143, SIGCONT)     = 0
27142 14:59:08 rt_sigaction(SIGCONT, {SIG_IGN, [CONT], SA_RESTORER|SA_RESTART, 0x7f892818c2f0}, {SIG_DFL, [], 0}, 8) = 0
27142 14:59:08 kill(0, SIGCONT)         = 0
27142 14:59:08 --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=27142, si_uid=0} ---
27142 14:59:08 rt_sigreturn()           = 61
27142 14:59:08 wait4(27143,  <unfinished ...>
27143 14:59:08 <... poll resumed> )     = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
27143 14:59:08 --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=27142, si_uid=0} ---
27143 14:59:08 --- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=27142, si_uid=0} ---
27143 14:59:08 alarm(0)                 = 0
27143 14:59:08 rt_sigreturn()           = -1 EINTR (Interrupted system call)
27143 14:59:08 write(1, "\n", 1)        = 1
27143 14:59:08 open("/proc/net/dev", O_RDONLY) = 4
27143 14:59:08 fstat(4, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
27143 14:59:08 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fde1a24a000
27143 14:59:08 read(4, "Inter-|   Receive                                                |  Transmit\n face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n  eth0: 196847241  330005    0    0    0     0          0         0 18084721  191870    0    0    0     0       0          0\n  eth1: 7108624   89629    0    0    0     0          0         0 12262901  170044    0    0    0     0       0          0\n    lo:   99313     582    0    0    0     0          0         0    99313     582    0    0    0     0       0          0\n", 1024) = 571
27143 14:59:08 close(4)                 = 0
27143 14:59:08 munmap(0x7fde1a24a000, 4096) = 0
27143 14:59:08 getsockopt(3, SOL_PACKET, PACKET_STATISTICS, {packets=74, drops=0}, [8]) = 0
27143 14:59:08 write(2, "70 packets captured", 19) = 19
27143 14:59:08 write(2, "\n", 1)        = 1
27143 14:59:08 write(2, "74 packets received by filter", 29) = 29
27143 14:59:08 write(2, "\n", 1)        = 1
27143 14:59:08 write(2, "0 packets dropped by kernel", 27) = 27
27143 14:59:08 write(2, "\n", 1)        = 1
27143 14:59:08 setsockopt(3, SOL_PACKET, PACKET_RX_RING, {block_size=0, block_nr=0, frame_size=0, frame_nr=0}, 16) = -1 EINVAL (Invalid argument)
27143 14:59:08 munmap(0x7fde19239000, 2097152) = 0
27143 14:59:08 munmap(0x7fde1a1f8000, 266240) = 0
27143 14:59:08 close(3)                 = 0
27143 14:59:08 exit_group(0)            = ?
27143 14:59:08 +++ exited with 0 +++
27142 14:59:08 <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 27143
27142 14:59:08 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=27143, si_status=0, si_utime=0, si_stime=0} ---
27142 14:59:08 close(1)                 = 0
27142 14:59:08 close(2)                 = 0
27142 14:59:08 exit_group(124)          = ?
27142 14:59:08 +++ exited with 124 +++

可以看到 timeout 触发的是 SIGALRM 信号,并将该信号转换成 SIGTERM 后用于杀 tcpdump 进程,接着又以同样的方式发送了 SIGCONT 信号;可以看到 tcpdump 是以优雅的方式退出的;

基于 tshark-a duration:xx 参数实现定时

TShark 是一个网络协议分析器;其允许你实时捕获网络包数据,或者读取之前抓取后保存的文件;其允许你将包解析后的内容直接输出到 stdout 上,或者写入一个文件中;TShark 使用的原生文件格式为 pcap ,该格式也是 tcpdump 和其它各种类似工具的首选;

定时功能相关参数如下:

  • -a <capture autostop condition>

设置何时 TShark 要停止写捕获文件的规则;规则的形式为 test:value ,其中 test 为以下值:

  • duration:value 在指定秒数过去后,停止写入捕获文件;

  • filesize:value 在文件大小达到 value kB 指定的大小后,停止写入捕获文件;如果该选项和 -b 选项一起使用,TShark 会在达到 filesize 指定的字节数目时,停止写入当前捕获文件,转而切换到下一个文件继续写入;当读取捕获文件时,TShark 会在读取了指定数目的字节后停止读取文件(完整的包将会被读取,因此实际上会读取超过这个数目的字节);需要注意的是,filesize 最大值限制在 2 GiB ;

  • files:value 在 value 数目个文件被写入之后,停止写入捕获文件;

验证没有网络流量时的情况

试验步骤:启动如下命令后,不进行任何操作;

root@vagrant-ubuntu-trusty:~# time tshark -i any -f "tcp port 22" -s 0 -a duration:3 -w tshark_3sec_0packet.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'


real	0m2.697s
user	0m0.136s
sys	0m0.028s
root@vagrant-ubuntu-trusty:~# time tshark -i any -f "tcp port 22" -s 0 -a duration:3 -w tshark_3sec_0packet.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'


real	0m3.199s
user	0m0.132s
sys	0m0.032s
root@vagrant-ubuntu-trusty:~# time tshark -i any -f "tcp port 22" -s 0 -a duration:3 -w tshark_3sec_0packet.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'


real	0m2.683s
user	0m0.124s
sys	0m0.028s
root@vagrant-ubuntu-trusty:~#

可以看到,上面的定时似乎也没有那么准;

查看包详情

root@vagrant-ubuntu-trusty:~# capinfos tshark_3sec_0packet.pcap
File name:           tshark_3sec_0packet.pcap
File type:           Wireshark/... - pcapng
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: (not set)
Number of packets:   0           -- 一个包也没有
File size:           304 bytes
Data size:           0 bytes
Capture duration:    n/a
Start time:          n/a
End time:            n/a
Data byte rate:      0 bytes/s
Data bit rate:       0 bits/s
Average packet size: 0.00 bytes
Average packet rate: 0 packets/sec
SHA1:                24eca697b1a43c7fa5e4ca4ebbc3e29f693bcbdd
RIPEMD160:           cae57ebbfbef89df1d55be52015f0629e94725fa
MD5:                 91c2cbebf81065dc0a43b6e65cb6c0ba
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

验证存在少量网络流量时的情况

试验步骤:启动如下命令后,按照大概每秒一次的速度敲击 enter 键;

root@vagrant-ubuntu-trusty:~# time tshark -i any -f "tcp port 22" -s 0 -a duration:3 -w tshark_3sec_few_packet.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'

(enter)
3
(enter)
10

real	0m2.440s
user	0m0.132s
sys	0m0.036s
root@vagrant-ubuntu-trusty:~#

查看包详情

root@vagrant-ubuntu-trusty:~# capinfos tshark_3sec_few_packet.pcap
File name:           tshark_3sec_few_packet.pcap
File type:           Wireshark/... - pcapng
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: (not set)
Number of packets:   10    -- 抓包 10 个包
File size:           1432 bytes
Data size:           800 bytes
Capture duration:    1 seconds
Start time:          Mon Dec 19 15:22:03 2016
End time:            Mon Dec 19 15:22:04 2016
Data byte rate:      729 bytes/s
Data bit rate:       5836 bits/s
Average packet size: 80.00 bytes
Average packet rate: 9 packets/sec
SHA1:                465d03d1995fc6f7672e8fd3bd705150ab1088ee
RIPEMD160:           4f6f428aa24b13e462a3887be426df6f1b19a565
MD5:                 694c0e0ebf095605099c363ae2a1e48a
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

验证存在大量网络流量时的情况

试验步骤:启动如下命令后,按照大概每秒 3~4 次的速度连续敲击 enter 键;

root@vagrant-ubuntu-trusty:~# time tshark -i any -f "tcp port 22" -s 0 -a duration:3 -w tshark_3sec_many_packet.pcap
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'

(enter)
(enter)
1
(enter)
8
(enter)
(enter)
19
(enter)
28
(enter)
(enter)
38

real	0m2.683s
user	0m0.124s
sys	0m0.024s
root@vagrant-ubuntu-trusty:~#

查看包详情

root@vagrant-ubuntu-trusty:~# capinfos tshark_3sec_many_packet.pcap
File name:           tshark_3sec_many_packet.pcap
File type:           Wireshark/... - pcapng
File encapsulation:  Linux cooked-mode capture
Packet size limit:   file hdr: (not set)
Number of packets:   38     -- 抓到 38 个包
File size:           4624 bytes
Data size:           3076 bytes
Capture duration:    2 seconds
Start time:          Mon Dec 19 15:19:48 2016
End time:            Mon Dec 19 15:19:50 2016
Data byte rate:      1752 bytes/s
Data bit rate:       14 kbps
Average packet size: 80.95 bytes
Average packet rate: 21 packets/sec
SHA1:                aa9ff5e321bae42c2dcee5357c9f844725d395c0
RIPEMD160:           b01396cce83f164165ca9032594f47a9f402db55
MD5:                 c126470f8254046029ad2e54ad8a5e6a
Strict time order:   True
root@vagrant-ubuntu-trusty:~#

小结

从实验中可以看出,无论网络中是否有流量经过,tshark 均能够进行定时处理;但似乎同样没有实现精准的定时;


本文没有详细说明的点:

  • tcpdump 本身会对哪些信号进行自定义处理;
  • timeout 作为 tcpdump 进程父进程,如何处理两者之间的信号通信;
  • tshark 如何实现的定时功能;
  • 基于 time 计时时,输出结果的含义;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!