From 3dd80c2e907a652f376844207cb9f13c1c89c71e Mon Sep 17 00:00:00 2001 From: lynskey Date: Mon, 23 Sep 2019 12:51:13 +0800 Subject: [PATCH] =?UTF-8?q?!1=20=E5=A2=9E=E5=8A=A0tcpdump=E5=8E=9F?= =?UTF-8?q?=E7=90=86=E7=AE=80=E4=BB=8B=20*=20dev=20*=20=E5=A2=9E=E5=8A=A0t?= =?UTF-8?q?cpdump=E5=AE=9E=E7=8E=B0=E5=8E=9F=E7=90=86=E7=AE=80=E4=BB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tcpdump/README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tcpdump/README.md b/tcpdump/README.md index 187b673..72c25df 100644 --- a/tcpdump/README.md +++ b/tcpdump/README.md @@ -198,3 +198,78 @@ tcpdump用来获取、打印指定网络接口上的包描述,可以使用表 https://stackoverflow.com/questions/740817/behavior-of-shutdownsock-shut-rd-with-tcp shutdown指定SHUT_RD时似乎仅仅是让recv返回0,而不会对socket状态、tcp状态做任何改变。在指定SHUT_WR时socket状态变成FIN_WAIT_1和FIN_WAIT_2;TCP发送FIN包,对端sock变成CLOSE_WAIT,对端持续发包不受影响,本端也会在FIN_WAIT_2状态下发送ACK。 + + +### 实现原理简介 +``` +1. tcpdump底层采用的是libpcap库来实现抓包,当我们运行tcpdump时,网卡进入promisc模式 +在这种模式下的网卡将侦听以太网的所有数据包 + +下面的输出可以看到网卡在运行和退出tcpdump时的输出 +$ dmesg +[ 9628.758959] device enp0s25 entered promiscuous mode +[ 9658.439262] device enp0s25 left promiscuous mode + +//////////////////// pcap-linux.c /////////////////// + if (!is_any_device && handle->opt.promisc) { + memset(&mr, 0, sizeof(mr)); + mr.mr_ifindex = handlep->ifindex; + mr.mr_type = PACKET_MR_PROMISC; + if (setsockopt(sock_fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, + &mr, sizeof(mr)) == -1) { + pcap_snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, + "setsockopt: %s", pcap_strerror(errno)); + close(sock_fd); + return PCAP_ERROR; + } + } + + +2. 网卡进入promisc模式后,libpcap使用SOCK_RAW方式来接收数据包 +//////////////////// pcap-linux.c /////////////////// + sock_fd = is_any_device ? + socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL)) : + socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + +3. 内核对SOCK_RAW的包优先处理,处理结束后才给其他协议使用 +/////////////////////////// kernel ip_input.c ////////////////////////// +void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol) +{ + const struct net_protocol *ipprot; + int raw, ret; + +resubmit: + raw = raw_local_deliver(skb, protocol); + + ipprot = rcu_dereference(inet_protos[protocol]); + if (ipprot) { + if (!ipprot->no_policy) { + if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { + kfree_skb(skb); + return; + } + nf_reset(skb); + } + ret = INDIRECT_CALL_2(ipprot->handler, tcp_v4_rcv, udp_rcv, + skb); + if (ret < 0) { + protocol = -ret; + goto resubmit; + } + __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS); + } else { + +4. bpf +tcpdump的过滤是采用bpf机制实现的,这是一个内核虚拟机 +bpf机制比较复杂,这里不做介绍了,具体参考: + +Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new +architecture for user-level packet capture. In Proceedings of the +USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993 +Conference Proceedings (USENIX'93). USENIX Association, Berkeley, +CA, USA, 2-2. [http://www.tcpdump.org/papers/bpf-usenix93.pdf] + +man bpf + +linux kernel/Documentation/networking/filter.txt +``` -- Gitee