目录
tcpdump抓包源码分析
协议族AF_PACKET
socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)
协议族和地址族关系:每一种协议族都有对应的地址族。IPV4的协议族PF_INET,地址族为AF_INET,一一对应,值完全一样,经常混用。
- SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
- {
- return __sys_socket(family, type, protocol);
- }
-
- int __sys_socket(int family, int type, int protocol)
- {
- retval = sock_create(family, type, protocol, &sock);
- }
-
- int __sock_create(struct net *net, int family, int type, int protocol,
- struct socket **res, int kern)
- {
- pf = rcu_dereference(net_families[family]);
- err = pf->create(net, sock, protocol, kern);
- }
从net_families中获取指定协议,并调用create方法创建
- static const struct net_proto_family packet_family_ops = {
- .family = PF_PACKET,
- .create = packet_create,
- .owner = THIS_MODULE,
- };
-
-
- static int __init packet_init(void)
- {
- rc = sock_register(&packet_family_ops);
- }
-
- //注册packet_family_ops 到net_families中
- int sock_register(const struct net_proto_family *ops)
- {
- rcu_assign_pointer(net_families[ops->family], ops);
- }
-
- //pf->create就是packet_create
- static int packet_create(struct net *net, struct socket *sock, int protocol,
- int kern)
- {
- //创建时的状态
- sock->state = SS_UNCONNECTED;
-
- sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto, kern);
- sock->ops = &packet_ops;
- po = pkt_sk(sk);
- //拥塞控制
- init_completion(&po->skb_completion);
- sk->sk_family = PF_PACKET;
- po->xmit = dev_queue_xmit;
-
- //fun上注册回调函数为 packet_rcv
- po->prot_hook.func = packet_rcv;
- if (proto) {
- po->prot_hook.type = proto;
- __register_prot_hook(sk);
- }
- }
-
- static void __register_prot_hook(struct sock *sk)
- {
- struct packet_sock *po = pkt_sk(sk);
- dev_add_pack(&po->prot_hook);//注册
- }
-
-
- void dev_add_pack(struct packet_type *pt)
- {
- struct list_head *head = ptype_head(pt);
- list_add_rcu(&pt->list, head);
- }
-
- //ptype_head
- static inline struct list_head *ptype_head(const struct packet_type *pt)
- {
- if (pt->type == htons(ETH_P_ALL))
- return pt->dev ? &pt->dev->ptype_all : &ptype_all;
- else
- return pt->dev ? &pt->dev->ptype_specific :
- &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
- }
dev_add_pack 其实最后是把 hook 函数添加到了 ptype_all 里了,代码如下。
- static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
- {
- ......
- //遍历 ptype_all (tcpdump 在这里挂了虚拟协议)
- list_for_each_entry_rcu(ptype, &ptype_all, list) {
- if (pt_prev)
- ret = deliver_skb(skb, pt_prev, orig_dev);
- pt_prev = ptype;
- }
- }
-
-
- static inline int deliver_skb(struct sk_buff *skb,
- struct packet_type *pt_prev,
- struct net_device *orig_dev)
- {
- //这个回调函数就是注册的packet_rcv
- return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
- }
-
- //将skb添加到sk_receive_queue中
- static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *pt, struct net_device *orig_dev)
- {
-
- __skb_queue_tail(&sk->sk_receive_queue, skb);
- }
可见 packet_rcv 把收到的 skb 放到了当前 packet socket 的接收队列里了。调用 recvfrom 的时候就可以获取到所抓到的包
网络接收不经过netfilter
网络发包经过netfilter
发包 IP层各种 netfilter 规则的过滤
- int __ip_local_out(struct sk_buff *skb)
- {
- ......
- return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
- skb_dst(skb)->dev, dst_output);
- }
- struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
- struct netdev_queue *txq, int *ret)
- {
- while (skb) {
- rc = xmit_one(skb, dev, txq, next != NULL);
- }
- }
-
- //xmit_one->dev_queue_xmit_nit
- void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
- {
- list_for_each_entry_rcu(ptype, ptype_list, list) {
- if (ptype->ignore_outgoing)
- continue;
-
- if (pt_prev) {
- deliver_skb(skb2, pt_prev, skb->dev);
- pt_prev = ptype;
- continue;
- }
- }
- static inline int deliver_skb(struct sk_buff *skb,
- struct packet_type *pt_prev,
- struct net_device *orig_dev)
- {
- return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
- }
在 dev_queue_xmit_nit 中遍历 ptype_all 中的协议,并依次调用 deliver_skb。这就会执行到 tcpdump 挂在上面的虚拟协议。
在应用层实现抓包 类型设置为PF_PACKET
- //PF_PACKET
- int main(int argc, char *argv[])
- {
-
- if( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 ){
- printf("Create socket error.\n");
- exit(0);
- }
-
- while(1){
- len = recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
- if (len < 46) {
- printf("Catch packet length error.\n" );
- close(sock);
- exit(0);
- }
- }
-
- }
-
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0