pcapng文件的python解析实例以及抓包补遗

正文

为了弥补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 = 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 = 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!

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
PS:历史原因作者账号名为:ymh198816,但事实上作者的生日并不是1988年1月6日 今天作者要在这里通过一个简单的电商网站订单实时分析系统和大家一起梳理一下大数据环境下的实时分析系统的架构模型。当然这个架构模型只是实时分析技术的一 个简单的入门级架构,实际生产环境中的大数据实时分析技术还涉及到很多细节的处理, 比如使用Storm的ACK机制保证数据都能被正确处理, 集群的高可用架构, 消费数据时如何处理重复数据或者丢失数据等问题,根据不同的业务场景,对数据的可靠性要求以及系统的复杂度的要求也会不同
一 协议端口 如果把IP地址比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536(即:2^16)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)。 在Internet上,各主机间通过TCP/IP协议发送和接收数据包,各个数据包根据其目的主机的ip地址来进行互联网络中的路由选择,把数据包顺利的传送到目的主机。大多数操作系统都支持多程序(进程)同时运行,那么目的主机应该把接收到的数据包传送给众多同时运行的进程中的哪一个
MyBatis真正的强大,在于其映射语句的魔力。 SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序): (1)cache  给定命名空间的配置缓存。 (2)cache-ref  其他命名空间缓存配置的引用。 (3)resultMap  是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象 (4)sql 可被其他语句引用的可重用语句块。 (5)insert 映射插入语句 (6)update  映射更新语句 (7)delete   映射删除语句 (8)select   映射查询语句
1、Maven构建Spring Boot 创建Maven Web工程,引入spring-boot-starter-parent依赖 project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/m

平衡搜索树之AVLTree - 2016-07-24 14:07:56

今天我想要在这里写下个人觉得比较难的数据结构---AVL树的一些些心得。 一。了解一种新的数据结构,首先要搞懂它的定义 : AVL树又称为高度平衡的二叉搜索树,是1962年有俄罗斯的数学家G.M.Adel'son-Vel'skii和E.M.Landis提出来的。它能保持二叉树的高度 平衡,尽量降低二叉树的高度,减少树的平均搜索长度。所以严格点来说,对于一棵搜索二叉树,能达到O(logn)的只是AVL树,因为他对于二叉树的深度控制的最为严格 ,那么这是为什么呢?让我们来看看 AVL树的性质 : 左子树和右子

linux基础知识 - 2016-07-24 14:07:48

1:基本知识 微内核:是一种提供必要服务的操作系统内核,大部分内核都作为单独的进程在特权模式先运行,他们通过消息传递进行通讯 单内核:单内核是个很大的进程,他的内部又悲愤为若干个模块,是个单独的二进制但印象,其模块间的通讯是通过直接调用其他模块中的函数实现的,而不是消息传递。 linux分几种应用程序级别 Ring 0 特权模式 一般是系统底层运行级别 Ring3 应用程序级别 一般的级别 有时候应用进程为了调用系统底层的模块,可能会在用户空间和内核空间之间进行来回的切换,这是很耗时间的,平时工作中应注意
1. 概述 嵌入式系统由硬件环境、嵌入式操作系统和应用程序组成,硬件环境是操作系统和应用程序运行的硬件平台,它随应用的不同而有不同的要求。硬件平台的多样性是嵌入式系统的主要特点,如何使嵌入式操作系统在不同的硬件平台上有效地运行,是嵌入式系统开发中需要解决的关键问题。解决的方法是在硬件平台和操作系统之间提供硬件相关层来屏蔽这些硬件的差异,给操作系统提供统一的运行环境,这种硬件相关层就是嵌入式系统中的板级支持包BSP(Board Support Package,简称BSP)。 2. BSP及其作用 BSP是嵌
一.什么是装箱?什么是拆箱? 在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料。在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:   1 Integer i = new   Integer( 10 ); 而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了:   1 Integer i = 10 ;

Redis与Java - 数据结构 - 2016-07-24 14:07:41

Redis与Java 标签 : Java与NoSQL Redis( REmote DIctionary Server ) is an open source (BSD licensed), in-memory data structure store, used as database , cache and message broker . It supports data structures such as strings , hashes , lists , sets , sorted sets
0x00 实验背景 Server:选用腾讯云的云主机  Ubuntu Server 14.04.1 LTS 64位 Client-1:Acer笔记本 Win7 x64系统 Client-2:安卓机小米4  Android 6.0系统(MIUI8)   0x01  OpenVPN的背景知识 **** **** 以下内容摘自维基百科**** **** OpenVPN是一个用于创建虚拟专用网络加密通道的软件包,最早由James Yonan编写。OpenVPN允许创建的VPN使用公开密钥、电子证书、或者用户名/密