1. 程式人生 > >Tcpdump一次抓包記錄(Postgresql通訊)

Tcpdump一次抓包記錄(Postgresql通訊)

資料庫版本:postgresql 9.2.1
作者:高銘傑
郵箱:[email protected]
日期:2016年7月14日

1 背景

  最近在除錯一個gsql灌資料時的卡死問題,問題的跟因還沒有分析清楚,和社群上的一個問題十分類似:
  

BUG #6342: libpq blocks forever in “poll” function
Hi,
I have a big and strange problem. Sometimes, libpq remains blocked in “poll”
function even if the server has already answered to the query. If I attach
to the process using kdbg I found this stack:

__kernel_vsyscall()
poll() from /lib/libc.so.6
pqSocketCheck() from /home/pg/pgsql/lib-32/libpq.so.5
pqWaitTimed() from /home/pg/pgsql/lib-32/libpq.so.5
pqWait() from /home/pg/pgsql/lib-32/libpq.so.5
PQgetResult() from /home/pg/pgsql/lib-32/libpq.so.5
PQexecFinish() from /home/pg/pgsql/lib-32/libpq.so.5

為了分析清楚這類問題,這裡記錄一下使用tcpdump的方式去分析pg的通訊過程。

2 Tcpdump安裝

- 下載tcpdump-4.6.2.tar.gz  libpcap-1.6.2.tar.gz
- 編譯安裝libpcap
- 編譯安裝tcpdump

sudo yum search tcpdump
sudo yum install tcpdump.xxx

3 Tcpdump 引數

NAME
       tcpdump - dump traffic on a network

SYNOPSIS
       tcpdump [ -AbdDefhHIJKlLnNOpqRStuUvxX
][ -B buffer_size ][ -c count ][ -C file_size ][ -G rotate_seconds ][ -F file ][ -i interface ][ -j tstamp_type ][ -m module ][ -M secret ][ -P in|out|inout ][ -r file ][ -V file ][ -s snaplen ][ -T type ][ -w file ][ -W filecount ][ -E [email protected] algo:secret,... ][ -y datalinktype ][ -z postrotate-command ][ -Z user ][ expression ]

這裡寫圖片描述

4 使用Tcpdump除錯PostgreSQL

  1. 連線資料庫,注意使用gsql -h的方式,否則直接連線會建立程序通訊用的本地socket,這樣是無法使用Tcpdump抓包的:
    這裡寫圖片描述

  2. 連線資料庫後,確認TCP連線已經建立,我的資料庫port=8473:
    這裡寫圖片描述

  3. 現在就可以使用tcpdump進行抓包了:
    sudo tcpdump tcp port 8473 -i lo -s0 -nnX
    執行後,在資料庫中執行SQL:drop table TEST;
    這裡寫圖片描述

  4. 這裡看到Tcpdump共抓到了3個TCP包,連線沒有加密,所以部分指令以明文顯示。

1、psql傳送drop table TEST。
23:44:08.563717 IP 127.0.0.1.53757 > 127.0.0.1.8473

2、資料庫後端返回結果。
23:44:08.570911 IP 127.0.0.1.8473 > 127.0.0.1.53757

3、psql向後端發包。
23:44:08.570942 IP 127.0.0.1.53757 > 127.0.0.1.8473

(20160714以上)

5 分析資料(非連線池)

5.1 執行SQL

select * from test;

5.2 抓包情況

抓到了三個包,這裡只引出分析方法,不同的SQL有不同的通訊策略。、

  • 方向:
    這裡寫圖片描述

  • 內容:
    這裡寫圖片描述

5.2.1 第一個包[PSH,ACK][Q]

這裡寫圖片描述

51開頭的高亮部分是資料庫填充的資料,前四行和第五行的60 94是IP和TCP報文。(TCP/IP報頭分析的一篇部落格)

  資料庫填充的資料為:

                       |5100這裡開始為資料庫填充的資料   
    0x0030:  0002 6094 5100 0000 1873 656c 6563 7420  ..`.Q....select.
    0x0040:  2a20 6672 6f6d 2074 6573 743b 00         *.from.test;.

  訊息的第一個位元組標識訊息型別,然後後面跟著的四個位元組宣告訊息剩下部分的長度(這個長度包括長度域自身,但是不包括訊息型別位元組)。請根據訊息型別去手冊中查詢對應的包,這裡第一個位元組為Q決定了訊息的格式:

Query (F)
    Byte1(’Q’)
        標識訊息是一個簡單查詢。
    Int32
        以位元組計的訊息內容長度,包括長度本身。
    String
        查詢字串自身
5.2.1 第二個包[PSH,ACK][T]

SQL執行結果:

postgres=# select * from test;
 i 
---
 1
(1 row)

這裡寫圖片描述

Byte1(’T’)
    標識訊息是一個行描述
    Int32
        以位元組計的訊息內容長度,包括長度本身。
    Int16
        宣告在一個行裡面的欄位數目(可以為零)
    然後對於每個欄位,有下面的東西:
    String
        欄位名字
    Int32
        如果欄位可以標識為一個特定表的欄位,那麼就是表的物件ID;否則就是零。
    Int16
        如果該欄位可以標識為一個特定表的欄位,那麼就是該表字段的屬性號;否則就是零。
    Int32
        欄位資料型別的物件ID
    Int16
        資料型別尺寸(參閱pg_type.typlen)。請注意負數表示變寬型別。
    Int32
        型別修飾詞(參閱pg_attribute.atttypmod)。修飾詞的含義是型別相關的。
    Int16
        用於該欄位的格式碼。目前會是0(文字)或者1(二進位制)。 
        從語句的Describe 返回的RowDescription裡,格式碼還是未知的,因此總是零。
5.2.1 第三個包[ACK]

這裡寫圖片描述

ps. 抓包分析過程

1、sudo tcpdump -i lo -s0 -X port 8473 -w select.cap

生成select.cap

2、使用wireshark開啟select.cap