用法
tcpdump -s len
原理
tcpdump会把这个长度值编译到ebpf代码中。 ebpf代码执行完后,如果filter成功,则作为返回值(return)返回给调用方(内核协议栈),内核协议栈根据返回值,修改 skb报文到指定的长度,并借助 af socket 返回给用户空间。
tcpdump命令解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| ubuntu@VM-0-7-ubuntu:~$ tcpdump -d dst host 9.9.9.9 Warning: assuming Ethernet (000) ldh [12] (001) jeq #0x800 jt 2 jf 4 (002) ld [30] (003) jeq #0x9090909 jt 8 jf 9 (004) jeq #0x806 jt 6 jf 5 (005) jeq #0x8035 jt 6 jf 9 (006) ld [38] (007) jeq #0x9090909 jt 8 jf 9 (008) ret #262144 (009) ret #0 ubuntu@VM-0-7-ubuntu:~$ tcpdump -d dst host 9.9.9.9 -s 100 Warning: assuming Ethernet (000) ldh [12] (001) jeq #0x800 jt 2 jf 4 (002) ld [30] (003) jeq #0x9090909 jt 8 jf 9 (004) jeq #0x806 jt 6 jf 5 (005) jeq #0x8035 jt 6 jf 9 (006) ld [38] (007) jeq #0x9090909 jt 8 jf 9 (008) ret #100 (009) ret #0 ubuntu@VM-0-7-ubuntu:~$
|
1
| 151 #define MAXIMUM_SNAPLEN 262144
|
代码分析
TCPDUMP代码
- tcpdump 解析’s’参数,并把长度值存放到 ndo 变量中,并最终存放到 pcap_t的句柄中。
- 编译 ebpf 指令时候, 根据pcap——中的报文长度值, 在 bpf 程序最尾部增加一个 RET 指令,并将长度值作为返回值。
- ebpf 在内核协议栈中执行, 如果报文不满足 filter 条件, 则返回负值。如果满足 filter 条件,则返回指定的长度值
- 内核协议栈,根据返回的长度值,对 skb 报文进行截断。
调用栈
1 2 3 4 5 6 7 8
| ==> tcpdump main 函数解析:ndo->ndo_snaplen = (int)strtol(optarg, &end, 0); ==> ==> pd = open_interface(device, ndo, ebuf); ==> ==> ==> status = pcap_set_snaplen(pc, ndo->ndo_snaplen); ==> ==> pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) ==> ==> ==> cstate.snaplen = pcap_snapshot(p); ==> ==> ==> cstate.ic.root = gen_retblk(&cstate, cstate.snaplen); ==> ==> ==> program->bf_insns = icode_to_fcode(&cstate.ic, ...) ==> ==> ==> ==> ==> ==>
|
tcpdump解析
- step1:tcpdump main函数,解析snaplen参数,获取长度值,存放ndo 里。创建句柄时候,再赋值到句柄里。
1 2 3 4 5 6
| 1846 case 's': 1847 ndo->ndo_snaplen = (int)strtol(optarg, &end, 0); ... 2229 pd = open_interface(device, ndo, ebuf); ... 2347 if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
|
1 2 3 4 5
| 1259 static pcap_t * 1260 open_interface(const char *device, netdissect_options *ndo, char *ebuf) 1261 { ... 1358 status = pcap_set_snaplen(pc, ndo->ndo_snaplen);
|
1 2 3 4 5 6 7 8
| 2591 int 2592 pcap_set_snaplen(pcap_t *p, int snaplen) 2593 { 2594 if (pcapint_check_activated(p)) 2595 return (PCAP_ERROR_ACTIVATED); 2596 p->snapshot = snaplen; 2597 return (0); 2598 }
|
1 2 3 4 5 6 7
| 3490 int 3491 pcap_snapshot(pcap_t *p) 3492 { 3493 if (!p->activated) 3494 return (PCAP_ERROR_NOT_ACTIVATED); 3495 return (p->snapshot); 3496 }
|
1 2 3 4 5 6 7 8 9
| 861 int 862 pcap_compile(pcap_t *p, struct bpf_program *program, 863 const char *buf, int optimize, bpf_u_int32 mask) 864 { ... 934 cstate.snaplen = pcap_snapshot(p); ... 971 if (cstate.ic.root == NULL) { 972
|
kernel 代码
1 2 3 4 5 6 7 8 9 10
| 2219 snaplen = skb_frags_readable(skb) ? skb->len : skb_headlen(skb); 2220 2221 res = run_filter(skb, sk, snaplen); 2222 if (!res) 2223 goto drop_n_restore; 2224 if (snaplen > res) 2225 snaplen = res; ... 2260 if (pskb_trim(skb, snaplen)) 2261 goto drop_n_acct;
|