TCP Posix 网络抓包分析 示例

Android QNX内部Virtio net 抓包分析

工具:tcpdump + wireshark

背景:负责项目出现Android QNX内部网络丢包问题

解决思路:通过抓包进一步分析root cause,需要通过tcpdump抓两个系统的网络包,并通过wireshark分析

步骤

1. tcpdum抓包

通过ifconfig确定两个系统Virtio-Ethernet对应的网卡

得出Android的网卡是eth0, QNX的网卡是vp0

应用的服务端跑在QNX,端口号为49598,所以抓包命令为

QNX侧:
tcpdump -v -i vp0 port 49598 -c 400000 -w /blackbox/tcpdump49598.pcap

Android侧:
tcpdump -i eth0 -vvn -w tcpdump_android.pcap

后续通过tcpdump获得的pcap文件,导入wireshark继续分析

附上tcpdump常用参数:

1、指定网卡。
tcpdump -i eth0
2、指定ip
tcpdump -i eth0 ip host 172.168.1.1
3、指定端口
tcpdump -i eth0 port 80
4、指定协议
tcpdump -i eth0 udp/tcp
5、指定源和目的
tcpdump -i eth0 src/dst host 172.168.1.1
6、写入指定的文件中
tcpdump -i eth0 ip host 172.168.1.1 -w/opt/wenjian,cap
7、将抓包信息打印到前台。
tcpdump -A -i etho ip host 172.168.1.1 -vvn
8、写入1000个包到文件中
tcpdump -i eth0 ip host 172.168.1.1 -c 1000 -w /opt/wenjian,cap
9、环路抓包
tcpdump -i ol
# tcpdump -v -i vp0 port 49598 -c 400000 -w blackbox/tcpdump49598.pcap
tcpdump: listening on vp0, link-type EN10MB (Ethernet), capture size 262144 bytes
547 packets captured
9576 packets received by filter
0 packets dropped by kernel

# ls /blackbox/tcpdump49598.pcap  
/blackbox/tcpdump49598.pcap

2. 用wireshark分析pcap文件

用wireshark打开,大概是这样的界面,由于tcpdump已经过滤了port 49598的数据,所以打开不需要过滤就能看到192.068.1.1(server)和192.168.1.3(client)之间在49598上通信的报文

但是报文那么多,我们怎么知道哪条报文是我们需要找的?

为此我

1. 修改了servcer端的代码,减少了我不想抓的报文

2. 通过应用的log打印,获取发送到socket的大致时间


通过log我们能够定位发送报文的时间大概是Jan 05 01:58:34.054

3. 通过wireshar过滤掉192.168.1.3 -> 192.068.1.1的报文,只保留192.068.1.1 -> 192.168.1.3的报文

通过ip.src == 192.168.1.1只显示192.068.1.1 -> 192.168.1.3的报文

附上wireshark常用过滤参数:
注: 过滤表达式可通过&&, |, !, ^^等逻辑符号进行组合

1. 针对ip的过滤

对源地址进行过滤
ip.src == 192.168.0.1
对目的地址进行过滤
ip.dst == 192.168.0.1
对源地址或者目的地址进行过滤
ip.addr == 192.168.0.1
如果想排除以上的数据包,只需要将其用括号囊括,然后使用 "!" 即可
!(ip.addr == 192.168.0.1)

2. 针对协议的过滤
获某种协议的数据包,表达式很简单仅仅需要把协议的名字输入即可
http
注意:是否区分大小写?答:区分,只能为小写

捕获多种协议的数据包
http or telnet
排除某种协议的数据包
not arp   或者   !tcp

3. 针对端口的过滤(视传输协议而定)
捕获某一端口的数据包(以tcp协议为例)
tcp.port == 80
捕获多端口的数据包,可以使用and来连接,下面是捕获高于某端口的表达式(以udp协议为例)
udp.port >= 2048

4. 针对长度和内容的过滤
针对长度的过虑(这里的长度指定的是数据段的长度)
udp.length < 20   
http.content_length <=30
针对uri 内容的过滤
http.request.uri matches "user" (请求的uri中包含“user”关键字的)
注意:matches 后的关键字是不区分大小写的!

http.request.uri contains "User" (请求的uri中包含“user”关键字的)
注意:contains 后的关键字是区分大小写的!

5. 针对http请求的一些过滤实例。
过滤出请求地址中包含“user”的请求,不包括域名;
http.request.uri contains "User"
精确过滤域名
http.host==baidu.com
模糊过滤域名
http.host contains "baidu"
过滤请求的content_type类型
http.content_type =="text/html"
过滤http请求方法
http.request.method=="POST"
过滤tcp端口
tcp.port==80
http && tcp.port==80 or tcp.port==5566
过滤http响应状态码
http.response.code==302
过滤含有指定cookie的http数据包
http.cookie contains "userid"

4. 由于我只想知道业务报文,不关心握手报文,ACK报文以及心跳报文,所以我们可通过PSH找到有data的报文

在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.

通过找到PSH的字段,就是我们需要的业务报文

参考: https://zhuanlan.zhihu.com/p/439614017
附上TCP层,FLAGS字段含义:

SYN表示建立连接,

FIN表示关闭连接,

ACK表示响应,

PSH表示有 DATA数据传输,

RST表示连接重置。

其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,

如果只是单个的一个SYN,它表示的只是建立连接。

TCP的几次握手就是通过这样的ACK表现出来的。

但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。

RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。

一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。

PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。

TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。

5. 通过代码定义的header找到精确的报文

由于我们的报文是通过flatbuffer序列化后的,且不是string类型,故wireshark只能看到序列化后的十六进制数据,看不到内容,所以需要想点办法通过报文中的data定位我们需要找的数据

分析发送代码:

 bool MessagingManager::sendMessage(nio::messaging::TxMessageIds messageId, unsigned int payloadLength, uint8_t *payload)
{
    std::lock_guard<std::mutex> lock(mSendMutex);   //Prevent out of order messages from being sent from multiple threads
    bool send_valid = false;

    uint8_t buf_header[8];
    uint32_t id = static_cast<uint32_t>(messageId);
    /* not good practice : network should use big-endian */
    buf_header[0] = id&0xFF;
    buf_header[1] = (id>>8)&0xFF;
    buf_header[2] = (id>>16)&0xFF;
    buf_header[3] = (id>>24)&0xFF;

    buf_header[4] = payloadLength&0xFF;
    buf_header[5] = (payloadLength>>8)&0xFF;
    buf_header[6] = (payloadLength>>16)&0xFF;
    buf_header[7] = (payloadLength>>24)&0xFF;

    for (int i=0; i<MAX_CONNECTIONS; i++)
    {
        if (mConnections[i].isValid())
        {
            struct iovec iov[2];
            iov[0].iov_base = (void *)buf_header;
            iov[0].iov_len  = sizeof(buf_header);
            iov[1].iov_base = payload;
            iov[1].iov_len = payloadLength;

            mTcpServer->SendV(mConnections[i].getSocket(), iov, 2);
            send_valid = true;
        }
    }

    if (false == send_valid)
    {
        NioLog::warning("MessagingManager: {}  Can't send MessageId {}", __FUNCTION__, messageId);
    }

    return send_valid;
}

bool CTCPServer::SendV(const Socket socket, struct iovec *iov, int iovcnt)
{
   int iResult = 0;

   iResult = writev(socket, iov, iovcnt);
   if (iResult < 0) 
   {
      NioLog::error("[TCPServer][Error] ret {} IOV writing to socket : {} ({})", iResult, errno, strerror(errno));
      return false;
   }
   return true;
}

可以看出,报文是由两部分组成,第一部分是buf_header,第二部分是序列化后的payload。

buf_header是由8 bytes组成(uint8_t buf_header[8]),messageId是一个五位数的十进制整数,最大不超过2^17。

buf_header[0]是messageId二进制的0-7位(0xFF即 1111 1111)。
buf_header[1]是messageId二进制的8-15位。
buf_header[2]是messageId二进制的16-23位。
buf_header[3]是messageId二进制的24-31位。

分析到这里就比较简单了,我查了我想抓的报文的messageId是57275
转换成十六进制是0xDFBB,二进制为1101 1111 1011 1011。
(注: 一位十六进制是2^4, 即四位二进制,故四位二进制正好表达一位十六进制)

故这个messageId总共只有16位二进制,故buf_header[0]为BB,buf_header[1]为DF,故buf_header[2]和buf_header[3]均为0.

我们只要找到报文的data开头是BB DF 00 00 的报文,即是我们需要的报文。
到这一步,就很轻易的找到了

如下图所示,即为我们需要找的报文


   转载规则


《TCP Posix 网络抓包分析 示例》 pglprome 采用 知识共享署名 4.0 国际许可协议 进行许可。

Related Issues not found

Please contact @pglprome to initialize the comment

表情 | 预览
Code -1: Request has been terminated
Possible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.
Powered By Valine
v1.3.9
 本篇
TCP Posix 网络抓包分析 示例 TCP Posix 网络抓包分析 示例
Android QNX内部Virtio net 抓包分析工具:tcpdump + wireshark 背景:负责项目出现Android QNX内部网络丢包问题 解决思路:通过抓包进一步分析root cause,需要通过tcpdump抓两个系
2023-01-05
下一篇 
POSIX 定时器_QNX实现 POSIX 定时器_QNX实现
一、POSXI接口定时器_QNX实现设置定时器,并设置通知方式,内核以signal的方式通知到时 #include "Handler.hpp" #include <iostream> #include <memory> #inc
2022-12-21