From 4f2e8d6cd0144ce084938ce24b5d2a11ca7dfc14 Mon Sep 17 00:00:00 2001 From: duht Date: Mon, 23 Sep 2019 11:43:59 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0tcpdump=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=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..05d5541 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 From b2b375f7f0606832b6071d6da060bb239e2ea480 Mon Sep 17 00:00:00 2001 From: duht Date: Mon, 23 Sep 2019 11:46:01 +0800 Subject: [PATCH 2/3] dev --- tcpdump/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcpdump/README.md b/tcpdump/README.md index 05d5541..72c25df 100644 --- a/tcpdump/README.md +++ b/tcpdump/README.md @@ -200,7 +200,7 @@ https://stackoverflow.com/questions/740817/behavior-of-shutdownsock-shut-rd-with 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模式 在这种模式下的网卡将侦听以太网的所有数据包 @@ -239,7 +239,7 @@ void ip_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int protocol) int raw, ret; resubmit: - ### raw = raw_local_deliver(skb, protocol); ### + raw = raw_local_deliver(skb, protocol); ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot) { -- Gitee From 867796460b2eee63050331d47000c2a7bc7ed2a8 Mon Sep 17 00:00:00 2001 From: duht Date: Mon, 23 Sep 2019 12:59:38 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0bpf=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tcpdump/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tcpdump/README.md b/tcpdump/README.md index 72c25df..f91ec00 100644 --- a/tcpdump/README.md +++ b/tcpdump/README.md @@ -272,4 +272,40 @@ CA, USA, 2-2. [http://www.tcpdump.org/papers/bpf-usenix93.pdf] man bpf linux kernel/Documentation/networking/filter.txt + +下面以一个简单的例子,看看bpf是怎么过滤包的 +# tcpdump -ni enp0s25 tcp -d # 转变为bpf的汇编代码 + +(000) ldh [12] +(001) jeq #0x86dd jt 2 jf 7 +(002) ldb [20] +(003) jeq #0x6 jt 10 jf 4 +(004) jeq #0x2c jt 5 jf 11 +(005) ldb [54] +(006) jeq #0x6 jt 10 jf 11 +(007) jeq #0x800 jt 8 jf 11 +(008) ldb [23] +(009) jeq #0x6 jt 10 jf 11 +(010) ret #262144 +(011) ret #0 + +这里0x86dd指的是ipv6,0x800是ipv4,001行判断是否是ipv6,如果是的话,跳转到002,否则 +跳转到007,假设我们是ipv4,那么在007,判断后跳转到008,再判断是否是ipv4,如果是的话 +到009,在009处判断是否是tcp,如果是的话返回262144,提交给上层处理,否则到011, +过滤掉这个包。 + +以太网帧所承载的协议,有以下类型: + 0806:ARP + 0800:IP + 86DD:IPV6 + 8000:ISIS + 8847:MPLS + 8864:PPPoE + +IP报文封装的协议: + 1:ICMP协议 + 2:IGMP协议 + 6:TCP协议 + 14:Telenet协议 + 17:UDP协议 ``` -- Gitee