strace tcpdump xx可以看到,最核心的在这里:
socket(PF_PACKET, SOCK_RAW, 768) = 3
从内核中可以看到:
创建PF_PACKET的socket最终会调到packet_create:
- // L3 entry的回调函数
- po->prot_hook.func = packet_rcv;
-
-
if (proto) { // proto项有设置则添加一个L3 handler entry
-
-
po->prot_hook.type = proto;
-
-
dev_add_pack(&po->prot_hook);
-
-
sock_hold(sk);
-
-
po->running = 1;
-
-
}
-
-
-
// 添加一个L2->l3 entry
-
void dev_add_pack(struct packet_type *pt)
-
-
{
-
-
struct list_head *head = ptype_head(pt);
-
-
-
-
spin_lock(&ptype_lock);
-
-
list_add_rcu(&pt->list, head);
-
-
spin_unlock(&ptype_lock);
-
-
}
-
-
- //根据packet_type中的类型决定放入哪个链表中
-
static inline struct list_head *ptype_head(const struct packet_type *pt)
-
-
{
-
-
if (pt->type == htons(ETH_P_ALL))
-
-
return &ptype_all;
-
-
else
-
-
return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
-
-
}
由此可见到:每个AF_PACKET类型的socekt的创建都会在L2->L3的处理表中添加一个handler entry. 其entry对应的处理函数为packet_rcv.
1) Protocol类型为ETH_P_ALL(ntohs后为768), 即所有包,则放入ptype_all链表中
2) Protocol类型为其他值,则放入ptype_base hash表中。
对于AF_PACKET类型,这里的type就是socekt传入的第三个参数protocol的htons值。
L2层向L3层进发。走netif_receive_skb:
会先遍历ptype_all链表,如果入口设备匹配,则每个发一个,然后再走到ptype_base hash表,对匹配的packet_type每个发一个。
- list_for_each_entry_rcu(ptype, &ptype_all, list) { //先遍历ptype_all
-
-
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;
-
-
}
-
-
}
-
-
….
-
-
list_for_each_entry_rcu(ptype,
-
-
&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {//再遍历ptype_base
-
-
if (ptype->type == type && (ptype->dev == null_or_orig ||
-
-
ptype->dev == skb->dev || ptype->dev == orig_dev ||
-
-
ptype->dev == orig_or_bond)) {
-
-
if (pt_prev)
-
-
ret = deliver_skb(skb, pt_prev, orig_dev);
-
-
pt_prev = ptype;
-
-
}
-
-
}
因此, 如果创建AF_PACKET
套接字, protocol为768则抓取所有二层包,如果protocol为指定协议类型(ip or icmp .. ) 则可以只抓取特性L3 protocol的包。
Tcpdump通过创建AF_PACKET, protocol为ETH_P_ALL的socket, 可以获取到所有报文,通过sk_filter可以对报文进行过滤。关于sk_filter,详见http://
阅读(2203) | 评论(0) | 转发(0) |