【協議分析】rpcx網絡協議分析之tcp

前言:

最近在學習rpcx,爲了更清楚搞懂rpcx的各類協議組成。對rpcx的協議作了一個深刻學習。html

系統: mac OS 10.14.3
golang: go1.12 darwin/amd64
調試工具:lldb
抓包工具:tcpdumplinux

# (一)tcpdump工具使用
tcpdump是一個網絡抓包工具,tcpdump支持針對網絡層、協議、主機、網絡或端口的過濾。
並提供and、or、not等邏輯語句來幫助你去掉無用的信息。
tcpdump格式信息以下:
1.png
參數詳解git

-a -- 將網絡地址和廣播地址轉變成名字
 -d -- 將匹配信息包的代碼以人們可以理解的彙編格式給出
 -dd -- 將匹配信息包的代碼以c語言程序段的格式給出
 -ddd -- 將匹配信息包的代碼以十進制的形式給出
 -e -- 在輸出行打印出數據鏈路層的頭部信息
 -f -- 將外部的Internet地址以數字的形式打印出來
 -l -- 使標準輸出變爲緩衝行形式
 -n -- 不把網絡地址轉換成名字
 -t -- 在輸出的每一行不打印時間戳
 -v -- 輸出一個稍微詳細的信息,例如在ip包中能夠包括ttl和服務類型的信息
 -vv -- 輸出詳細的報文信息
 -c -- 在收到指定的包的數目後,tcpdump就會中止
 -F -- 從指定的文件中讀取表達式,忽略其它的表達式
 -i -- 指定監聽的網絡接口
 -r -- 從指定的文件中讀取包(這些包通常經過-w選項產生)
 -w -- 直接將包寫入文件中,並不分析和打印出來
 -T -- 將監聽到的包直接解釋爲指定的類型的報文,常見的類型有rpc (遠程過程調用)和snmp(簡單網絡管理協議)
 -S  -- 打印TCP 數據包的順序號時, 使用絕對的順序號, 而不是相對的順序號.
 -XX -- 當分析和打印時, tcpdump 會打印每一個包的頭部數據, 同時會以16進制和ASCII碼形式打印出每一個包的數據,   
        其中包括數據鏈路層的頭部.這對於分析一些新協議的數據包很方便.

命令:github

1)想要截獲全部192.168.1.100 的主機收到的和發出的全部的數據包:
tcpdump host 192.168.1.100 

2)想要截獲主機192.168.1.100 和主機192.168.1.101 或192.168.1.102的通訊,使用命令:(在命令行中使用 括號時,必定要轉義)
tcpdump host 192.168.1.100 and (192.168.1.101 or 192.168.1.102)

3)若是想要獲取主機192.168.1.100除了和主機192.168.1.101以外全部主機通訊的ip包,使用命令:
tcpdump ip host 192.168.1.100 and ! 192.168.1.101

4)若是想要獲取主機192.168.1.100接收或發出的telnet包,使用以下命令:
tcpdump tcp port 23 and host 192.168.1.100

5)獲取迴環網卡8972端口包,例如迴環網卡爲lo0,(unix/linux)這個可使用ifconfig查看,使用命令以下:
tcpdump -i lo0 port 8972

6)獲取迴環網卡8972端口包,順序打印並打印每一個包16進制數據
tcpdump -i lo0 port 8972 -S -XX

(二)編譯rpcx和運行tcpdump

2.1第一步編譯tcp例子:

源碼
https://github.com/rpcx-ecosystem/rpcx-examples3/tree/master/101basicgolang

編譯server
go build -o server server.go

編譯client
go build -o client_async client/client_async.go

## 2.2第二步運行tcpdump:
(1)啓動server、啓動對應的server文件
(2)運行tcpdump編程

tcpdump -i lo0 port 8972 -S -XX

2.png
rpcx例子裏面默認啓動8972端口,因爲是本地去測試,我抓的是lo0網卡,lo0網卡爲迴環網卡,
能夠經過ifconfig查看到。
3.pngwindows

(3)執行client_async
執行client_async,tcpdump抓包信息以下:bash

14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 0,sackOK,eol], length 0
    0x0000:  0200 0000 4500 0040 0000 4000 4006 0000  ....E..@..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b711  ..........#.....
    0x0020:  0000 0000 b002 ffff fe34 0000 0204 3fd8  .........4....?.
    0x0030:  0103 0306 0101 080a 184f 4ca7 0000 0000  .........OL.....
    0x0040:  0402 0000                                ....
14:17:03.977157 IP localhost.8972 > localhost.58526: Flags [S.], seq 2001029784, ack 3652826898, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 407850151,sackOK,eol], length 0
    0x0000:  0200 0000 4500 0040 0000 4000 4006 0000  ....E..@..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4a98  ........#...wEJ.
    0x0020:  d9b9 b712 b012 ffff fe34 0000 0204 3fd8  .........4....?.
    0x0030:  0103 0306 0101 080a 184f 4ca7 184f 4ca7  .........OL..OL.
    0x0040:  0402 0000                                ....
14:17:03.977177 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029785, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b712  ..........#.....
    0x0020:  7745 4a99 8010 18eb fe28 0000 0101 080a  wEJ......(......
    0x0030:  184f 4ca7 184f 4ca7                      .OL..OL.
14:17:03.977184 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826898, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4a99  ........#...wEJ.
    0x0020:  d9b9 b712 8010 18eb fe28 0000 0101 080a  .........(......
    0x0030:  184f 4ca7 184f 4ca7                      .OL..OL.
14:17:03.977425 IP localhost.58526 > localhost.8972: Flags [P.], seq 3652826898:3652826961, ack 2001029785, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 63
    0x0000:  0200 0000 4500 0073 0000 4000 4006 0000  ....E..s..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b712  ..........#.....
    0x0020:  7745 4a99 8018 18eb fe67 0000 0101 080a  wEJ......g......
    0x0030:  184f 4ca7 184f 4ca7 0800 0030 0000 0000  .OL..OL....0....
    0x0040:  0000 0000 0000 002f 0000 0005 4172 6974  ......./....Arit
    0x0050:  6800 0000 034d 756c 0000 0000 0000 0017  h....Mul........
    0x0060:  82a1 41d3 0000 0000 0000 000a a142 d300  ..A..........B..
    0x0070:  0000 0000 0000 14                        .......
14:17:03.977440 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826961, win 6378, options [nop,nop,TS val 407850151 ecr 407850151], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4a99  ........#...wEJ.
    0x0020:  d9b9 b751 8010 18ea fe28 0000 0101 080a  ...Q.....(......
    0x0030:  184f 4ca7 184f 4ca7                      .OL..OL.
14:17:03.978268 IP localhost.8972 > localhost.58526: Flags [P.], seq 2001029785:2001029837, ack 3652826961, win 6378, options [nop,nop,TS val 407850152 ecr 407850151], length 52
    0x0000:  0200 0000 4500 0068 0000 4000 4006 0000  ....E..h..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4a99  ........#...wEJ.
    0x0020:  d9b9 b751 8018 18ea fe5c 0000 0101 080a  ...Q.....\......
    0x0030:  184f 4ca8 184f 4ca7 0800 8030 0000 0000  .OL..OL....0....
    0x0040:  0000 0000 0000 0024 0000 0005 4172 6974  .......$....Arit
    0x0050:  6800 0000 034d 756c 0000 0000 0000 000c  h....Mul........
    0x0060:  81a1 43d3 0000 0000 0000 00c8            ..C.........
14:17:03.978283 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029837, win 6378, options [nop,nop,TS val 407850152 ecr 407850152], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b751  ..........#....Q
    0x0020:  7745 4acd 8010 18ea fe28 0000 0101 080a  wEJ......(......
    0x0030:  184f 4ca8 184f 4ca8                      .OL..OL.
14:17:04.978001 IP localhost.58526 > localhost.8972: Flags [F.], seq 3652826961, ack 2001029837, win 6378, options [nop,nop,TS val 407851148 ecr 407850152], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b751  ..........#....Q
    0x0020:  7745 4acd 8011 18ea fe28 0000 0101 080a  wEJ......(......
    0x0030:  184f 508c 184f 4ca8                      .OP..OL.
14:17:04.978053 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826962, win 6378, options [nop,nop,TS val 407851148 ecr 407851148], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4acd  ........#...wEJ.
    0x0020:  d9b9 b752 8010 18ea fe28 0000 0101 080a  ...R.....(......
    0x0030:  184f 508c 184f 508c                      .OP..OP.
14:17:04.978236 IP localhost.8972 > localhost.58526: Flags [F.], seq 2001029837, ack 3652826962, win 6378, options [nop,nop,TS val 407851149 ecr 407851148], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 230c e49e 7745 4acd  ........#...wEJ.
    0x0020:  d9b9 b752 8011 18ea fe28 0000 0101 080a  ...R.....(......
    0x0030:  184f 508d 184f 508c                      .OP..OP.
14:17:04.978291 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029838, win 6378, options [nop,nop,TS val 407851149 ecr 407851149], length 0
    0x0000:  0200 0000 4500 0034 0000 4000 4006 0000  ....E..4..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b752  ..........#....R
    0x0020:  7745 4ace 8010 18ea fe28 0000 0101 080a  wEJ......(......
    0x0030:  184f 508d 184f 508d                      .OP..OP.

咱們發現對應抓包的信息有12個包,每一個包之間16進制數據有不少都是相同的。網絡

(三)解析TCP包

對應數據包數據結構

14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 0,sackOK,eol], length 0
    0x0000:  0200 0000 4500 0040 0000 4000 4006 0000  ....E..@..@.@...
    0x0010:  7f00 0001 7f00 0001 e49e 230c d9b9 b711  ..........#.....
    0x0020:  0000 0000 b002 ffff fe34 0000 0204 3fd8  .........4....?.
    0x0030:  0103 0306 0101 080a 184f 4ca7 0000 0000  .........OL.....
    0x0040:  0402 0000

網絡傳輸中的字節碼都是大端讀取的,這個是爲了兼容CPU架構。因此在編程中須要轉換網絡字節碼,其實就是小端轉成大端。
就像咱們的02 00

完整解析能夠看以下圖:
4.png

MAC幀頭定義

/*數據幀定義,頭14個字節,尾4個字節*/
typedef struct _MAC_FRAME_HEADER
{
    char m_cDstMacAddress[6];    //目的mac地址
    char m_cSrcMacAddress[6];    //源mac地址
    short m_cType;            //上一層協議類型,如0x0800表明上一層是IP協議,0x0806爲arp
}__attribute__((packed))MAC_FRAME_HEADER,*PMAC_FRAME_HEADER;

typedef struct _MAC_FRAME_TAIL
{
    unsigned int m_sCheckSum;    //數據幀尾校驗和
}__attribute__((packed))MAC_FRAME_TAIL, *PMAC_FRAME_TAIL;

結合上圖咱們能夠看到貌似咱們並無使用到mac幀頭,圖中鏈路層只佔用4字節。這是由於咱們使用的是迴環鏈路。針對於鏈路層有不少的形式。不一樣的形式,鏈路層佔用的字節長度不一樣。

IP頭結構的定義

/*IP頭定義,共20個字節*/
typedef struct _IP_HEADER 
{
    char m_cVersionAndHeaderLen;       //版本信息(前4位),頭長度(後4位)
    char m_cTypeOfService;           // 服務類型8位
    short m_sTotalLenOfPacket;        //數據包長度
    short m_sPacketID;              //數據包標識
    short m_sSliceinfo;              //分片使用
    char m_cTTL;                  //存活時間
    char m_cTypeOfProtocol;          //協議類型
    short m_sCheckSum;              //校驗和
    unsigned int m_uiSourIp;         //源ip
    unsigned int m_uiDestIp;         //目的ip
} __attribute__((packed))IP_HEADER, *PIP_HEADER ;

tcp頭結構定義

/*TCP頭定義,共20個字節*/
typedef struct _TCP_HEADER 
{
    short m_sSourPort;              // 源端口號16bit
    short m_sDestPort;              // 目的端口號16bit
    unsigned int m_uiSequNum;          // 序列號32bit
    unsigned int m_uiAcknowledgeNum;    // 確認號32bit
    short m_sHeaderLenAndFlag;         // 前4位:TCP頭長度;中6位:保留;後6位:標誌位
    short m_sWindowSize;             // 窗口大小16bit
    short m_sCheckSum;               // 檢驗和16bit
    short m_surgentPointer;           // 緊急數據偏移量16bit
}__attribute__((packed))TCP_HEADER, *PTCP_HEADER;

TCP頭中的選項定義

/*TCP頭中的選項定義
kind(8bit)+Length(8bit,整個選項的長度,包含前兩部分)+內容(若是有的話)
KIND = 1表示 無操做NOP,無後面的部分
  2表示 maximum segment   後面的LENGTH就是maximum segment選項的長度(以byte爲單位,1+1+內容部分長度)
  3表示 windows scale     後面的LENGTH就是 windows scale選項的長度(以byte爲單位,1+1+內容部分長度)
  4表示 SACK permitted    LENGTH爲2,沒有內容部分
  5表示這是一個SACK包     LENGTH爲2,沒有內容部分
  8表示時間戳,LENGTH爲10,含8個字節的時間戳
*/
typedef struct _TCP_OPTIONS
{
 char m_ckind;
 char m_cLength;
 char m_cContext[32];
}__attribute__((packed))TCP_OPTIONS, *PTCP_OPTIONS;

(四)tcpdump 解惑鏈路層協議

5.png
圖中爲tcpdump源碼調試過程,若是走的是迴環鏈路, 且大家系統是mac os則回走null_if_print方法。

tcpdump中print-null.c源碼:

define NULL_HDRLEN 4

這個 DLT_NULL 數據包頭的長度爲4字節。它包含主機字節順序指定系列的32位整數,例如AF_INET。OpenBSD DLT_LOOP包頭是相同的,除了整數按網絡字節順序排列。
DLT_NULL是 BSD迴路封裝,因爲mac OS是BSD系類改造的。因此用的是同樣的。DLT_LOOP是OpenBSD的迴路封裝。

迴環網卡接口打印的代碼以下:

void
null_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
{
    u_int length = h->len;
    u_int caplen = h->caplen;
    uint32_t family;

    ndo->ndo_protocol = "null";
    if (caplen < NULL_HDRLEN) {
        ndo->ndo_ll_header_length += caplen;
        nd_print_trunc(ndo);
        return;
    }
    ndo->ndo_ll_header_length += NULL_HDRLEN;

    family = GET_HE_U_4(p);   //4字節表標識

    if ((family & 0xFFFF0000) != 0)
        family = SWAPLONG(family);

    if (ndo->ndo_eflag)
        null_hdr_print(ndo, family, length);

    length -= NULL_HDRLEN;  //tcp數據包長度
    caplen -= NULL_HDRLEN;  //總體包長度,包含鏈路層
    p += NULL_HDRLEN;

    switch (family) {

    case BSD_AFNUM_INET: //ipv4
        ip_print(ndo, p, length);  //打印數據
        break;

    case BSD_AFNUM_INET6_BSD:   //ipv6
    case BSD_AFNUM_INET6_FREEBSD:
    case BSD_AFNUM_INET6_DARWIN:
        ip6_print(ndo, p, length); //打印數據
        break;

    case BSD_AFNUM_ISO:
        isoclns_print(ndo, p, length);
        break;

    case BSD_AFNUM_APPLETALK:
        atalk_print(ndo, p, length);
        break;

    case BSD_AFNUM_IPX:
        ipx_print(ndo, p, length);
        break;

    default:
        /* unknown AF_ value */
        if (!ndo->ndo_eflag)
            null_hdr_print(ndo, family, length + NULL_HDRLEN);
        if (!ndo->ndo_suppress_default_print)
            ND_DEFAULTPRINT(p, caplen);
    }

    return;
}

6.png
圖中調試爲caplen爲網絡4層協議的包大小5六、length爲數據包大小52。他們恰好相差4。

(五)數據包協議解析

5.1 tcp/ip協議解析

TCP/IP 三次握手
14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535
14:17:03.977157 IP localhost.8972 > localhost.58526: Flags [S.], seq 2001029784, ack 3652826898
14:17:03.977177 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029785, win 6379

14:17:03.977184 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826898, win 6379
客戶端往服務端發送rpc獲取請求
14:17:03.977425 IP localhost.58526 > localhost.8972: Flags [P.], seq 3652826898:3652826961, ack 2001029785, win 6379
14:17:03.977440 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826961, win 6378

服務端往客戶端回覆rpc數據
14:17:03.978268 IP localhost.8972 > localhost.58526: Flags [P.], seq 2001029785:2001029837, ack 3652826961
14:17:03.978283 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029837, win 6378

TCP/IP 四次揮手
14:17:04.978001 IP localhost.58526 > localhost.8972: Flags [F.], seq 3652826961, ack 2001029837
14:17:04.978053 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826962, win 6378
14:17:04.978236 IP localhost.8972 > localhost.58526: Flags [F.], seq 2001029837, ack 3652826962
14:17:04.978291 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029838, win 6378

localhost.58526 客戶端
localhost.8972 服務端
從我麼的12次請求中能夠看到分別有幾個部分:
(1)TCP/IP 三次握手
(2)客戶端往服務端發送rpc獲取請求
(3)服務端往客戶端回覆rpc數據
(4)TCP/IP 四次揮手

注意:四次揮手並非每次都是這樣的順序,在雙工下。或者是fin延遲狀況下。4次揮手的順序會有所變化。

有興趣能夠去讀一下TCP/IP原理。

5.2 rpc協議解析

先看看源代碼:
server端:

package main

import (
"flag"
   "fmt"
   "github.com/smallnest/rpcx/server"
)

var (
   addr = flag.String("addr", "0.0.0.0:8972", "server address")
)

type Args struct { //接收數據
   A int
   B int
}

type Reply struct { //回覆數據
   C int
}

type Arith int

//調用的方法
func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) error {
   reply.C = args.A * args.B
   fmt.Printf("call: %d * %d = %d\n", args.A, args.B, reply.C)
   return nil
}

func main() {
   flag.Parse()

   s := server.NewServer()
   s.Register(new(Arith), "")
   s.Serve("tcp", *addr)
}

client端:

package main

import (
   "context"
   "flag"
   "log"
   "time"

   example "github.com/rpcx-ecosystem/rpcx-examples3"
   "github.com/smallnest/rpcx/client"
)

var (
   addr2 = flag.String("addr", "127.0.0.1:8972", "server address")
)

func main() {
   flag.Parse()

   d := client.NewPeer2PeerDiscovery("tcp@"+*addr2, "")
   xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, client.DefaultOption)
   defer xclient.Close()

   args := &example.Args{ //發送數據
      A: 10,
      B: 20,
   }

   reply := &example.Reply{} //返回數據
   call, err := xclient.Go(context.Background(), "Mul", args, reply, nil) //調用Mul方法
   if err != nil {
      log.Fatalf("failed to call: %v", err)
   }
   time.Sleep(1e9)
   replyCall := <-call.Done
   if replyCall.Error != nil {
      log.Fatalf("failed to call: %v", replyCall.Error)
   } else {
      log.Printf("%d * %d = %d", args.A, args.B, reply.C)
   }

}

7.png
返回結果A(10) * B(20) = C(200)

對應rpcx消息體結構體,對應文件rpcx/protocol/message.go

type Message struct {
   *Header
   ServicePath   string
   ServiceMethod string
   Metadata      map[string]string //設置受權密碼時用到
   Payload       []byte
   data          []byte
}

魔術數字,對應文件rpcx/protocol/message.go

const (
   magicNumber byte = 0x08
)

func MagicNumber() byte {
   return magicNumber
}

client端request
8.png

server端Response
9.png

數據包:
10.png

總結:

1.tcp/ip的四次揮手並非4次的順序都是固定的。若是是雙工或者是延遲發送,則順序就不固定。
2.mac OS的鏈路層佔用4個字節;
3.cap包數據包大小爲鏈路層、網絡層、傳輸層、應用層4個部分。ip頭數據包大小爲網絡層、傳輸層、應用層3個部分。
4.鏈路層不一樣的網卡鏈路層佔用字節大小不同,以太網鏈路層爲MAC幀頭。

參考資料:
鏈路層類型
https://blog.csdn.net/shenwansangz/article/details/47612505

計算機網絡-數據結構-MAC幀頭-IP頭-TCP頭-UDP頭
https://www.cnblogs.com/cs-lcy/p/7462072.html

IP頭,TCP頭,UDP頭,MAC幀頭定義
https://www.cnblogs.com/li-hao/archive/2011/12/07/2279912.html

tcpdump源碼
https://github.com/the-tcpdump-group/tcpdump

rpcx源碼
https://github.com/smallnest/rpcx

相關文章
相關標籤/搜索