pcapng文件的python解析實例以及抓包補遺

分類:IT技術 時間:2016-10-16

正文

為了彌補pcap文件的缺陷,讓抓包文件可以容納更多的信息,pcapng格式應運而生。關於它的介紹詳見《PCAP Next Generation Dump File Format》

        當前的wireshark/tshark抓取的包默認都被保存為pcapng格式。

        形而上的論述就不多談了,直接給出一個pcapng數據包文件的例子:




然後我強烈建議,對著《PCAP Next Generation Dump File Format》來把一個實際抓取的pcapng文件裏面的每一個字節都對應清除,接下來寫一個python腳本,手工構造一個這樣的文件,可以用wireshark打開並解析的那種(其實這並不是一件特別難的事情)。這樣你就算徹底掌握了這個格式。

        還是那個需求,統計一個抓包文件中一個IP地址發出的數據總量,對於pcapng文件,由於其格式已經有了大不同啊大不同,python腳本也就截然不同了,列如下:
#!/usr/bin/python

# Default tcpdump buffer size seems to 2M

import sys
import socket
import struct

filename = sys.argv[0]
filename = sys.argv[1]
#print filename
ipaddr = sys.argv[2]
direction = sys.argv[3]

packed = socket.inet_aton(ipaddr)
ip32 = struct.unpack("!L", packed)[0]

file = open(filename, "rb") 

pkthdrlen=16
iphdrlen=20
tcphdrlen=20
stdtcp = 20
total = 0
pos = 0

start_seq = 0
end_seq = 0

# Read file header(type and size)
typedata = http://blog.csdn.net/dog250/article/details/file.read(8)
(type, size) = struct.unpack("=LL", typedata)

# Skip header description
skipdata = file.read(size-8)

# Read interface desc block
interfacedata = file.read(8)
(type, size) = struct.unpack("=LL", interfacedata)

# Get linktype from int-desc block
ltdata = file.read(4)
(type, ltsize) = struct.unpack("=HH", ltdata)
if ltsize == 0x71:
	pkthdrlen = 16
else:
	pkthdrlen = 14

# Skip other of int-desc block
skipdata = file.read(size-8-4)

# Read packet block
pktdata = file.read(8)
(type, size) = struct.unpack("=LL", pktdata)

ipcmp = 0
cnt = 0

while pktdata:
	# Skip Interface ID
	skipdata = file.read(4)

	# Get time and length
	# sec:
	# microsec:
	# iplensave:
	# origlen:
	data = file.read(16)
	(sec, microsec, iplensave, origlen) = struct.unpack("=LLLL", data)

	# Read linklayer
	linkdata = file.read(pkthdrlen)
	

	# Read IP header
	ipdata = file.read(iphdrlen)
	(vl, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr) = struct.unpack(">ssHHHssHLL", ipdata)
	iphdrlen = ord(vl) & 0x0F 
	iphdrlen *= 4

	# Read TCP standard header
	tcpdata = file.read(stdtcp)	
	(sport, dport, seq, ack_seq, pad1, win, check, urgp) = struct.unpack(">HHLLHHHH", tcpdata)
	tcphdrlen = pad1 & 0xF000
	tcphdrlen = tcphdrlen >> 12
	tcphdrlen = tcphdrlen*4
	
	if direction =='out':
		ipcmp = saddr
	else:
		ipcmp = daddr

	if ipcmp == ip32:
		cnt += 1
		total += tot_len
		total -= iphdrlen + tcphdrlen
		if start_seq == 0:  # BUG?
			start_seq = seq
		end_seq = seq

	# Skip options and data
	skipdata = http://blog.csdn.net/dog250/article/details/file.read(size - 8 - 4 - 16 - pkthdrlen - iphdrlen - stdtcp)
	
	# Read next packet
	pos += 1
	pktdata = file.read(8)
	(type, size) = struct.unpack("=LL", pktdata)
	if type <> 0x06:
		break
# Get interface statistics
if type == 0x05:
	skiphdr = file.read(12)
	opthdr = file.read(4)
	(code, length) = struct.unpack("=HH", opthdr)

	while length <> 0:
		# 32-bit boundary! BUG?

		if code == 5:
			opt = file.read(length)
			(drops,) = struct.unpack("=Q", opt)
			# 不能這麽將丟棄的數據包算進去!抓包時就這一個流嗎?每個被丟棄的包都是攜帶數據的嗎?...
			# 所以,pcapng僅僅統計被丟棄的數據包的數量,不夠!怎麽才夠?不知道!!
#			total += drops*1460
	
		elif code == 7:
			opt = file.read(length)
			(drops,) = struct.unpack("=Q", opt)
#			total += drops*1460

		else:
			skipopt = file.read(length)
		opthdr = file.read(4)
		(code, length) = struct.unpack("=HH", opthdr)
		

print pos,'Actual:'+str(total),  'ideal:'+str(end_seq-start_seq)

執行方式與《pcap文件的python解析實例》針對pcap文件的解析完全一樣。
        pcapng文件的內容還有很多,這裏不再贅述了。本文並沒有談及關於每一個Block裏都可以攜帶的Options信息,那個東西可以再寫一篇的。
        也許python高手會笑我,為什麽我非要用python去解析這麽底層的pcap/pcapng文件,用dpkt庫不是更好嗎?是的,如果本著我文中所提的需求而言,用dpkt固然更好,但是用dpkt的話,你無法知道pcap/pcapng文件格式的全貌,甚至都不可能見一斑!我用python直接解析pcap/pcapng文件,目的有二,首先我要學python,其次我要趁機了解一下pcap/pcapng的細節!我可不想成為女司機那樣的。誠然,最高效的方案解決問題最可取,然而,在我這裏沒有什麽問題是要解決的,僅僅是玩玩!
        僅僅是玩玩,這也是我折騰了這麽久的原因。不過還是很有收獲的。

附錄

抓包丟棄的問題:dropped by kernel釋義與避免

如果你縱觀pcap的文件格式(在《pcap文件的python解析實例》中有),你會發現,整個文件只是記錄了數據包本身,而絲毫沒有涉及那些”由於系統處理能力所限而不得不丟棄的數據包“,而這就造成了統計上的困難。以下僅舉一例以說明。
        數據發送端為S,數據接收端為C,用我的pcapng-parser.py分析後,數據顯示了令人驚訝的結果:
接收端接收的數據總量大於發送端發送的數據總量!!
我們排除數據包被中間可恥的運營商重放這種可能,因為我是在自己的環境下測試!這顯而易見是由於發送端抓包抓漏了或者或兩邊都抓漏了,只是發送端漏的更多而導致!然而我們無法證實這種情況。非常明確的是,在你結束抓包的時候,會打印出”XXX packets dropped by kernel“這種信息,很顯然,內核並沒有成功抓取所有的包(抓包由PACKET套接字驅動,後者受限於套接字接收緩存!)這可怎麽辦?
        pcapng幫了大忙!

        pcapng可以把抓包過程中被丟棄的數據包的數量統計在一個section裏最終導出給用戶看,我們註意到pcapng文件格式的section類型中有一個”Interface Statistics Block“的可選字段,其中統計了在整個抓包過程中的以下信息:



       

        但是這些夠了嗎?

        有的時候,我可能會一次性抓取所有的數據包保存為一個超級大的pcapng文件,然後利用wireshark/tshark的析取功能去析出那些感興趣的數據流,此時被析取的數據流的大小已經很小了,但是”Interface Statistics Block“依然保持不變,因為在抓包這個層次上,系統根本沒有辦法去區分數據流!這可怎麽辦?!
        沒有辦法!
        即便我知道了”在該數據流中丟棄了幾個數據包“,我也沒有辦法計算出丟棄了多少個字節,因為這涉及到了MSS!誠然,你可以使用肉眼,然而你無敵的肉眼只能在wireshark界面裏去看那些”previous segment is not captured.“周邊的序列號,然後拿這些缺失的字節數去和pcapng保存的統計值去做四則混合運算!!夠了!(以上信息,如欲知詳情,請點擊wireshark的”統計“菜單的第一項!)
        我們怎樣可以避免抓包丟失呢?或者說即便不可避免,我們怎樣可以最小化抓包丟失(這樣可以減小我們的估算難度!)呢?幸運的是,tcpdump/tshark可以指定抓取數據包的長度以及PACKET套接字(使用RING/NetMap會更好)緩存的大小,”-s“選項可以限制抓取數據包的大小:
-s     Snarf  snaplen bytes of data from each packet rather than the
default of 65535 bytes.  Packets truncated because of a limited snapshot
are indicated in the output with ``[|proto]'', where proto is the name of
the protocol level at which the truncation has occurred. Note that  taking
larger  snapshots both increases the amount of time it  takes  to
process packets and, effectively, decreases the amount of packet buffering.
This may cause packets to be lost. You should limit snaplen to the
smallest number that will capture the protocol information you're
interested in. Setting snaplen to 0 sets it to the default of 65535, for
back-wards compatibility with recent older versions of tcpdump.
限定數據包的大小大多數情況下並不影響我們抓包的目的,畢竟我們大多數情況下抓包都是為了分析內核問題,用戶態的問題我們可以通過應用程序的日誌來獲得,因此我們幾乎不用抓取應用層的數據,而應用層以下的所有協議頭長度加在一起也不是一個很大的數字,一般而言128字節以內就可以滿足需求!所以-s 128這個選項可以讓同樣大小的緩沖區容納更多的數據包。除此之外,”-B“選項則可以指定抓包套接字接收緩沖區的大小。兩者雙管齊下,解決抓包丟棄問題自然不在話下了。

        總有人質疑,這是人之本性,我是人,我也會這樣,這是亞裏士多德的理論,是嗎?是的!由於抓包丟失我無法統計出精確的數據包發送的數值,誰能精確統計以及怎麽統計?請回答,如果不能回答,請沈默。

        我不沈默,因為我有辦法統計。我可以對照TCP的序列號,就像wireshark發現抓包丟失那樣發現抓包丟失,但是我並不認為這是有用的,這毫無意義!

pcapng的統計補遺

pcapng文件格式已經足夠我容納盡可能多的數據了,然而問題在PACKET套接字!看linux內核的packet_rcv函數,我們可看到,內核除了數據包本身之外並沒有提供更多的信息。
        比如,如果我想知道發送某一個TCP數據包的時候,其擁塞窗口是多大?我除了借助於tcpprobe而別無他法!但是,我還是有辦法的。
我們在packet_rcv函數裏將數據包交給PACKET套接字的時候,不是截斷了數據包嗎?此時我完全可以將與這個數據包相關的socket的統計信息作為替換加到後面,這些信息包括:當前的擁塞窗口,當前的ssthresh...這樣,我們就可以用強大的wireshark/tshark工具來解析這些信息了,而且還可以畫出圖形呢。

pcap與pcapng之間的轉換

說了這麽多,其實就統計傳輸字節數這個需求而言,針對pcap文件和pcapng文件而言完全沒有必要用兩個腳本!因為pcap和pcapng文件可以互相轉換。
        最簡單的方式,用wireshark打開一個文件,然後”另存為“即可,次好的方式,使用editcap命令也可以完成兩者間的轉換,只需要”-F“選項,就可以完成你需要的,具體操作,請man!


Tags: python IP地址 packed 數據包 file

文章來源:


ads
ads

相關文章
ads

相關文章

ad