• tcpdump抓包实现过程


    目录

    应用层

    socket内核实现

    内核根据ptype_all找抓包点(重要)

    过滤包抓包点netfilter(重要)

    总结

    应用层实现抓包关键程序


    tcpdump抓包源码分析

    应用层

    协议族AF_PACKET

    socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)

    协议族和地址族关系:每一种协议族都有对应的地址族。IPV4的协议族PF_INET,地址族为AF_INET,一一对应,值完全一样,经常混用。

    socket内核实现

    1. SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
    2. {
    3.     return __sys_socket(family, type, protocol);
    4. }
    5. int __sys_socket(int family, int type, int protocol)
    6. {
    7.     retval = sock_create(family, type, protocol, &sock);
    8. }
    9. int __sock_create(struct net *net, int family, int type, int protocol,
    10.              struct socket **res, int kern)
    11. {
    12.     pf = rcu_dereference(net_families[family]);
    13.     err = pf->create(net, sock, protocol, kern);
    14. }

    net_families中获取指定协议,并调用create方法创建

    1. static const struct net_proto_family packet_family_ops = {
    2.     .family =   PF_PACKET,
    3.     .create =   packet_create,
    4.     .owner  =   THIS_MODULE,
    5. };
    6. static int __init packet_init(void)
    7. {
    8. rc = sock_register(&packet_family_ops);
    9. }
    10. //注册packet_family_ops 到net_families中
    11. int sock_register(const struct net_proto_family *ops)
    12. {
    13.       rcu_assign_pointer(net_families[ops->family], ops);
    14. }
    15. //pf->create就是packet_create 
    16. static int packet_create(struct net *net, struct socket *sock, int protocol,
    17.              int kern)
    18. {
    19. //创建时的状态
    20.     sock->state = SS_UNCONNECTED;
    21.     sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto, kern);
    22.     sock->ops = &packet_ops;
    23. po = pkt_sk(sk);
    24. //拥塞控制
    25. init_completion(&po->skb_completion);
    26.     sk->sk_family = PF_PACKET;
    27. po->xmit = dev_queue_xmit;
    28. //fun上注册回调函数为 packet_rcv
    29.     po->prot_hook.func = packet_rcv;
    30.     if (proto) {
    31.         po->prot_hook.type = proto;
    32.         __register_prot_hook(sk);
    33.     }
    34. }
    35. static void __register_prot_hook(struct sock *sk)
    36. {
    37.     struct packet_sock *po = pkt_sk(sk);
    38.     dev_add_pack(&po->prot_hook);//注册
    39. }
    40. void dev_add_pack(struct packet_type *pt)
    41. {
    42.     struct list_head *head = ptype_head(pt);
    43.     list_add_rcu(&pt->list, head);
    44. }
    45. //ptype_head
    46. static inline struct list_head *ptype_head(const struct packet_type *pt)
    47. {
    48.     if (pt->type == htons(ETH_P_ALL))
    49.         return pt->dev ? &pt->dev->ptype_all : &ptype_all;
    50.     else
    51.         return pt->dev ? &pt->dev->ptype_specific :
    52.                  &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
    53. }

    dev_add_pack 其实最后是把 hook 函数添加到了 ptype_all 里了,代码如下。


    内核根据ptype_all找抓包点(重要)

    1. static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
    2. {
    3. ......
    4. //遍历 ptype_all (tcpdump 在这里挂了虚拟协议)
    5. list_for_each_entry_rcu(ptype, &ptype_all, list) {
    6. if (pt_prev)
    7. ret = deliver_skb(skb, pt_prev, orig_dev);
    8. pt_prev = ptype;
    9. }
    10. }
    11. static inline int deliver_skb(struct sk_buff *skb,
    12. struct packet_type *pt_prev,
    13. struct net_device *orig_dev)
    14. {
    15. //这个回调函数就是注册的packet_rcv
    16. return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    17. }
    18. //将skb添加到sk_receive_queue中
    19. static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
    20. struct packet_type *pt, struct net_device *orig_dev)
    21. {
    22. __skb_queue_tail(&sk->sk_receive_queue, skb);
    23. }

    可见 packet_rcv 把收到的 skb 放到了当前 packet socket 的接收队列里了。调用 recvfrom 的时候就可以获取到所抓到的包


    过滤包抓包点netfilter(重要)

    网络接收不经过netfilter

    网络发包经过netfilter

    发包 IP层各种 netfilter 规则的过滤

    1. int __ip_local_out(struct sk_buff *skb)
    2. {
    3. ......
    4. return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
    5. skb_dst(skb)->dev, dst_output);
    6. }
    1. struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
    2. struct netdev_queue *txq, int *ret)
    3. {
    4. while (skb) {
    5. rc = xmit_one(skb, dev, txq, next != NULL);
    6. }
    7. }
    8. //xmit_one->dev_queue_xmit_nit
    9. void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
    10. {
    11. list_for_each_entry_rcu(ptype, ptype_list, list) {
    12. if (ptype->ignore_outgoing)
    13. continue;
    14. if (pt_prev) {
    15. deliver_skb(skb2, pt_prev, skb->dev);
    16. pt_prev = ptype;
    17. continue;
    18. }
    19. }
    1. static inline int deliver_skb(struct sk_buff *skb,
    2. struct packet_type *pt_prev,
    3. struct net_device *orig_dev)
    4. {
    5. return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    6. }

    在 dev_queue_xmit_nit 中遍历 ptype_all 中的协议,并依次调用 deliver_skb。这就会执行到 tcpdump 挂在上面的虚拟协议。

    总结

    1. tcpdump 是通过 socket 系统调用,
    2. 注册packet_rcv回调函数到ptype_all队列中
    3. 网络收发包,会在网络设备层遍历 ptype_all 中的协议,并执行其中的回调。
    4. 数据包是先经过网络设备层然后才到协议层n,etfilter在tcpdump收包过程中起不到过滤;在发包过程 IP 层进入各种 netfilter 规则的过滤起作用。

    应用层实现抓包关键程序

    在应用层实现抓包 类型设置为PF_PACKET

    1. //PF_PACKET
    2. int main(int argc, char *argv[])
    3. {
    4. if( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 ){
    5. printf("Create socket error.\n");
    6. exit(0);
    7. }
    8. while(1){
    9. len = recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
    10. if (len < 46) {
    11. printf("Catch packet length error.\n" );
    12. close(sock);
    13. exit(0);
    14. }
    15. }
    16. }

     参考
    https://course.0voice.com/v1/course/intro?courseId=2&agentId=0


  • 相关阅读:
    11.QA模型选择,欠拟合和过拟合
    【解决方案】智慧体育场馆大场景安防视频监控,可持续性保障大型场馆安全运行
    案例实践丨基于SkyWalking全链路监控的微服务系统性能调优实践篇
    音频怎么转文字?学会这3招,轻松拉满你的工作效率
    【面试题】Golang垃圾回收机制(第五篇)
    小程序中如何查看会员卡的注册时间
    HLS学习2:使用ARM核点灯
    基于神经网络的分类和预测
    linux上手动安装luasql的包(centos7)
    HarmonyOS/OpenHarmony原生应用开发-华为Serverless云端服务支持说明(一)
  • 原文地址:https://blog.csdn.net/WANGYONGZIXUE/article/details/125379293