赞
踩
凑一块儿了...于是,便开工了。座椅爆炸!
struct pcap_file_header {
bpf_u_int32 magic;
u_short version_major;
u_short version_minor;
bpf_int32 thiszone; /* gmt to local correction */
bpf_u_int32 sigfigs; /* accuracy of timestamps */
bpf_u_int32 snaplen; /* max length saved portion of each pkt */
bpf_u_int32 linktype; /* data link type (LINKTYPE_*) */
};
具体我就不解释了,待会儿我会用一个实例来解析。紧接着这个文件头,后面就是一个个数据包了,为了描述每一个数据包的元信息,每一个数据包都会有一个描述头:
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present 由于tcpdump可以设置-s参数指定抓取的长度,这个字段表示实际抓取的数据包长度 */
bpf_u_int32 len; /* length this packet (off wire) 这个字段表示数据包的自然长度 */
};
这个结构体描述了数据包抓取的时间信息以及长度信息,在这个结构之后才会是数据包,因此一个典型的pcap文件应该是如下所示:
这简直清晰至极啊,再次看我的那个需求,我想统计的两个量怎么得到呢?
一个TCP连接实际发送的字节数:每一个数据包的TCP载荷长度的加和。- #!/usr/bin/python
-
- import sys
- import socket
- import struct
-
- filename = sys.argv[0]
- filename = sys.argv[1]
- ipaddr = sys.argv[2]
- direction = sys.argv[3]
-
- packed = socket.inet_aton(ipaddr)
- ip32 = struct.unpack("!L", packed)[0]
-
- file = open(filename, "rb")
-
- pcaphdrlen = 24
- pkthdrlen=16
- pkthdrlen1=14
- iphdrlen=20
- tcphdrlen=20
- stdtcp = 20
- total = 0
- pos = 0
-
- start_seq = 0
- end_seq = 0
- cnt = 0
-
- # Read 24-bytes pcap header
- data = file.read(pcaphdrlen)
- (tag, maj, min, tzone, ts, ppsize, lt) = struct.unpack("=L2p2pLLLL", data)
-
- # 具体的LinkType细节,请看:
- # http://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html#appendixBlockCodes
- if lt == 0x71:
- pkthdrlen1 = 16
- else:
- pkthdrlen1 = 14
-
- ipcmp = 0
-
- # Read 16-bytes packet header
- data = file.read(pkthdrlen)
-
- while data:
- (sec, microsec, iplensave, origlen) = struct.unpack("=LLLL", data)
-
- # read link
- link = file.read(pkthdrlen1)
-
- # read IP header
- data = file.read(iphdrlen)
- (vl, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr) = struct.unpack(">ssHHHssHLL", data)
- 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 data
- skip = file.read(iplensave-pkthdrlen1-iphdrlen-stdtcp)
-
- # read next packet
- pos += 1
- data = file.read(pkthdrlen)
-
- # 打印出实际传输的字节数,以及本应该传输的字节数
- print pos, cnt, 'Actual:'+str(total), 'ideal:'+str(end_seq-start_seq)
我们发现pcap的文件格式中,大部分的元描述结构都是固定数量且定长的,以LinkType为例,一次抓包我只能指定一个LinkType,它被记录在pcap文件开始的pcap_file_header中,这意味着,我无法同时在以太网卡和非以太的PPP网卡上抓包并同时得到详细的链路层信息!而pcapng解决了这个问题。
欲知pcapng如何,且看下篇文字。
如果说使用tcpdump -i any参数,我们不会看到标准的以太头信息,我们看到的是Cooked Capture,而不是Ethernet!关键的是,Cooked Capture描述的元信息长度是16字节而不是Ethernet的14字节。以下是Cooked Capture的头示例:
- if pos > 900:
- data = struct.pack("=LLLL", sec+40, microsec, iplensave, origlen)
- file_out.write(data)
然后就重现了这个现象:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。