详解tcpdump命令和BPF规则

Last Updated: 2023-06-18 06:25:00 Sunday

-- TOC --

tcpdump是Linux环境下非常著名的命令行抓包工具,它的名称中虽然有tcp字样,但可不仅仅能抓tcp报文呀...

tcpdump命令行参数风格:

$ sudo tcpdump [options] [BPF expression]

命令行options

-i--interface,指定抓包的接口,如果要在所有接口上抓包,-i any。抓取loopback接口的报文,要用此参数指定,比如-i lo。当抓取多个接口报文时,tcpdump的打印输出中,会包含接口的名称,比如eth0。

-n,不要将host address转换成names,避免DNS查询。

-nn,包含-n,同时也不要将port号转换成协议名称,即直接显示port number。

-c,限制要抓的报文个数,达到此个数后tcpdump自动退出,否则就需要用Ctrl-C或kill来结束tcpdump进程。

-w,将抓取的报文保存到文件,一般以.pcap作为文件后缀,这种文件可以用著名的wireshark软件打开分析,也可以用-r参数读取文件。

-r file,读取一个packet文件,这个文件可以是-w参数产生的。

-S--absolute-tcp-sequence-numbers,打印出绝对sequence number,而不是相对的。Print absolute, rather than relative, TCP sequence numbers.

下面是一个case:

$ sudo tcpdump -i wlp4s0 -nn -c4 -w ping.pcap icmp and host 114.215.183.12

-d,将BPF表达式的汇编形态显示出来。

BPF expression

tcpdump命令行包含一个BPF (BSD Packet Filter) expression,它表达了一种报文过滤规则。在流量大的接口上抓包,过滤基本是必须的。

比如:

icmp and host <ip>

这个表达式,指定了host ip(源或目的ip)的icmp报文。

hostsrc hostdst host,指定主机过滤条件。

portsrc portdst port,指定端口过滤条件。

ipip6arptcpudpicmp,指定协议过滤条件。

andornot,表示逻辑关系,也可以使用&&||!

还有常见的比较符号,它们都符合直觉:

下面是一些示例:

# 用MAC地址过滤
ether host 8c:a6:df:42:55:56
ether src host 8c:a6:df:42:55:56
ether dst host 8c:a6:df:42:55:56

# 用IP地址过滤
host 192.168.1.101
src host 192.168.1.101
dst host 192.168.1.101

# 端口过滤
port 80
!port 80
src port 80
dst port 80

# 地址为192.168.1.101(源和目的)且端口为8080
host 192.168.1.101 && port 8080

# 源地址为192.168.1.101且目的端口号为8080
src host 192.168.1.101 && dst port 8080

# 192.168.1.1和192.168.1.2的流量
host 192.168.1.1 || 192.168.1.2

BPF History

Since workstations became interconnected, network administrators had a need to "see" what is flowing on the wires. The ability to sniff the network traffic is necessary when things go wrong, even for the most basic debugging.

For this reason operating systems developed APIs for packet sniffing. But, as there wasn't any real standard for it every OS had to invent a different API: Sun’s STREAMS NIT, DEC's Ultrix Packet Filter, SGI’s Snoop and Xerox Alto had CMU/Stanford Packet Filter. This led to many complications. The simpler APIs just copied all the packets to the user space sniffer, which on a busy system resulted in a flood of useless work. The more complex APIs were able to filter packets before passing them to user space, but it was often cumbersome and slow.

所以,如果自制sniffer,将所有packet都简单copy到user space,是一种性能较低的方案。

All this changed in 1993 when Steven McCanne and Van Jacobson published the paper introducing a better way of filtering packets in the kernel, they called it "The BSD Packet Filter" (BPF).

Since then the BPF has taken the world by a storm and along with libpcap and tcpdump become the de-facto standard in network debugging.

BPF bytecode

比如:

$ sudo tcpdump -ni eth0 -d ip and udp
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 5
(002) ldb      [23]
(003) jeq      #0x11            jt 4    jf 5
(004) ret      #262144
(005) ret      #0

ldh,load a half-word from packet at offset 12,这个位置往后2个byte,正好是EthernetII报文的type字段。

jeq,check the value if it is 0x0800,这个值表示IP协议,如果匹配,转到002,如果不匹配,转到005,005就结束了,ret #0,表示不是目标报文。

[23]这个位置,正好是IP报文中的protocol字段,表示更上层的协议,0x11(17)表示UDP协议,如果匹配,转到004,不匹配转到005。

https://www.kernel.org/doc/Documentation/networking/filter.txt

如果想在自己的代码中使用BPF,需要使用raw socket,并且调用setsockopt,传入特定参数:

sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
...
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, ...)

https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt

In essence Tcpdump asks the kernel to execute a BPF program within the kernel context. This might sound risky, but actually isn't. Before executing the BPF bytecode kernel ensures that it's safe:

All this guarantees that the BPF programs executed within kernel context will run fast and will never infinitely loop. That means the BPF programs are not Turing complete, but in practice they are expressive enough for the job and deal with packet filtering very well.

BPF很快很快,BPF是图灵不完备的bytecode!

Tcpdump dissected

Tcpdump is composed of three logical parts:

添加iptables规则后, tcpdump是否还能抓到包?

以下内容,还需要亲自测试?如果是自己写抓包程序,是否也是这样?

添加 iptables 限制后, tcpdump 是否能抓到包 ,这要看添加的 iptables 限制条件:

网络包进入主机后的顺序如下:

本文链接:https://cs.pynote.net/net/202204101/

-- EOF --

-- MORE --