tcpdump抓包实现过程

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tcpdump抓包实现过程相关的知识,希望对你有一定的参考价值。

目录

应用层

socket内核实现

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

过滤包抓包点netfilter(重要)

总结

应用层实现抓包关键程序


tcpdump抓包源码分析

应用层

协议族AF_PACKET

socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)

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

socket内核实现

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 里了,代码如下。


内核根据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

网络发包经过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 挂在上面的虚拟协议。

总结

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

应用层实现抓包关键程序

在应用层实现抓包 类型设置为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


以上是关于tcpdump抓包实现过程的主要内容,如果未能解决你的问题,请参考以下文章

安全牛学习笔记TCPDUMP-抓包筛选高级筛选过程文档记录

TCP三次握手与Tcpdump抓包分析过程

聊聊tcpdump与Wireshark抓包分析

Tcpdump命令抓包详细分析

网络报文抓包分析——ICMP

网络报文抓包分析——ICMP