没有分析过代码,很多都是猜测
先有一个例子tcpdump如何抓GET包:
sudo tcpdump -ni eth0 'ip and tcp and dst port 80 and tcp[(tcp[12:1]&0xf0)>>2:4]=1195725856'
tcpdump:
建一个PF_SOCKET 协议的socket
选择一个接口并bind
把filter 表达式翻译成BPF指令(由libpcap完成, BPF的介绍在
bpf.pdf 中介绍)
setsockopt SO_ATTACH_FILTER 安装filter
收包(很可能是通过mmap来进行的, 曾经有用过,Documemtation/networking/packet_mmap.txt 有介绍)
打印或者写入文件
kernel: af_paket.c
packet_create 调用 dev_add_pack添加一个handler到协议栈入口
网卡驱动收包之后调用
int netif_receive_skb(struct sk_buff *skb);
上协议栈
如果指定了socket指定ETH_P_ALL, 那么dev_add_pack把handler放这
- list_for_each_entry_rcu(ptype, &ptype_all, list) {
-
if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
-
ptype->dev == orig_dev) {
-
if (pt_prev)
-
ret = deliver_skb(skb, pt_prev, orig_dev);
-
pt_prev = ptype;
-
}
-
}
否则在下面按照协议类型处理
- type = skb->protocol;
-
list_for_each_entry_rcu(ptype,
-
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
-
if (ptype->type == type &&
-
(ptype->dev == null_or_orig || ptype->dev == skb->dev ||
-
ptype->dev == orig_dev)) {
-
if (pt_prev)
-
ret = deliver_skb(skb, pt_prev, orig_dev);
-
pt_prev = ptype;
-
}
-
}
比如ip协议或者arp注册的函数就会在这里处理
即使iptables禁止了,tcpdump也能抓到进来的包
ip注册的函数叫ip_rcv, packet注册的叫packet_rcv
packet_rcv调用run_filter过已经设置好的filter, 它的值作为返回给用户的字节数(默认是96)
run_filter解释执行BPF指令,但是通常效率很高, 有关BPF指令的介绍可以参考bpf.pdf
如果想在程序中抓取特定类型的包, 那么不需要使用libpcap,tcpdump直接翻译一下就可以了
# tcpdump -dd ip and tcp and dst port 80 -ni eth0
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 8, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 6, 0x00000006 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 4, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00000050 },
{ 0x6, 0, 0, 0x00000060 },
{ 0x6, 0, 0, 0x00000000 },
把这个写入到C代码中就可以用了
tcpdump -d ip and tcp and dst port 80 -ni eth0可以看出这些指令的细节
(000) ldh [12] 协议2字节
(001) jeq #0x800 jt 2 jf 10 // ipv4 下面其它的根据协议头部很容易理解了
(002) ldb [23]
(003) jeq #0x6 jt 4 jf 10
(004) ldh [20]
(005) jset #0x1fff jt 10 jf 6
(006) ldxb 4*([14]&0xf)
(007) ldh [x + 16]
(008) jeq #0x50 jt 9 jf 10
(009) ret #96
(010) ret #0
开头那个例子表明filter expression可以很复杂 参考
expr_bpf.pdf ip and tcp and dst port 80 and
tcp[(tcp[12:1]&0xf0)>>2:4]=1195725856
ipv4 payload tcp[tcp headlen "(>> 4) *4" == ">>2": 4b] = "GET "
阅读(4924) | 评论(0) | 转发(0) |