目的
对数据包(pacp格式)中的数据,按照五元组(#流:源端口号、目的端口号、协议号、源IP、目的IP)对流进行划分。
举例:flow1(data[IP].src, data.sport, data[IP].dst, data.dport, data.proto)。
思路
五元组提取
首先对每个数据包进行遍历,提取出里面有效的五元组信息。
在实践中:
- 因为抓包在链路层,可能有些包是ARP协议等,所以没有上层信息,视为无效包。
- 还有些数据包缺失端口号或者协议(无tcp,udp等扽标识),暂时没思考出来为什么,会抛出异常,视为无效包。所以在代码中需要加入异常处理。
- 另外,在PCAP中,IPv4标识为“IP”,IPv6标识为“IPv6”,还有“ARP”协议等等,需要单独处理。
提取五元组后,放在tuple中。本功能单独在packetRead()函数中实现,也在flowProcess()函数中使用。
五元组信息统计
对于同一个流,所有数据报的五元组信息都相同。此处的思路为统计出不相同的五元组信息为一个字典,key为五元组,value为五元组重复次数。
接上一单元,每次tuple提取后,将其append到flow_list的尾部。统计完后列表有重复项。建立空字典,flow_list.count(item)用于统计列表中每一个表项的重复次数,将item设为字典key,count的返回值设为字典的value,即可创建字典。
数据包划分
字典key的数量既为切分结果的子pcap数量,创建子pcap,读取原pcap中每个数据包信息,遇到匹配的塞进去,重复key次即可。
完整代码
# -*- coding: utf-8 -*-
#流:源端口号、目的端口号、协议号、源IP、目的IP
import scapy
from scapy.all import *
from scapy.utils import PcapReader
import os
import json
'''数据包五元组读取'''
def packetRead(p):
packets=rdpcap(p) #提取pcap
print(packets) #打印pcap信息
flow_list=[] #用于五元组信息统计
'''提取五元组信息'''
for data in packets:
#data.show() #展示当前类型包含的属性及值
#协议:其中1,标识ICMP、2标识IGMP、6标识TCP、17标识UDP、89标识OSPF。
#data.payload.name:'IP','IPV6','ARP'或者其他
if data.payload.name == 'IP':
try:
five_tuple = "{}:{} {}:{} {}".format(data[IP].src,data.sport,data[IP].dst,data.dport,data.proto)
flow_list.append(five_tuple)
except AttributeError:
continue
print("AttributeError")
elif data.payload.name == 'IPv6':
try:
five_tuple = "{}:{} {}:{} {}".format(data[IPv6].src,data.sport,data[IPv6].dst,data.dport,data.proto)
flow_list.append(five_tuple)
except AttributeError:
continue
print("AttributeError")
elif data.payload.name == 'ARP':
print('arp')
else:
print('不是ip ipv6 arp')
print(five_tuple)
#end for
'''将大数据包按流切分'''
def flowProcess(p):
packets=rdpcap(p) #提取pcap
print(packets) #打印pcap信息
flow_list=[] #用于五元组信息统计
#用于统计不要的报文数
arp = 0
other = 0
wrong_data = 0
'''提取五元组信息'''
for data in packets:
#data.show() #展示当前类型包含的属性及值
#协议:其中1,标识ICMP、2标识IGMP、6标识TCP、17标识UDP、89标识OSPF。
#data.payload.name:'IP','IPV6','ARP'或者其他
if data.payload.name == 'IP':
try:
five_tuple = "{}:{} {}:{} {}".format(data[IP].src,data.sport,data[IP].dst,data.dport,data.proto)
flow_list.append(five_tuple)
except AttributeError:
wrong_data = wrong_data+1
elif data.payload.name == 'IPv6':
try:
five_tuple = "{}:{} {}:{} {}".format(data[IPv6].src,data.sport,data[IPv6].dst,data.dport,data.proto)
flow_list.append(five_tuple)
except AttributeError:
wrong_data = wrong_data+1
elif data.payload.name == 'ARP':
arp = arp+1
else:
other = other+1
flow_list.append(five_tuple)
#end for
print('arp:',arp,' other:', other, ' 数据包损坏:',wrong_data)
'''统计流的数目与五元组信息,并保存txt'''
i=0
dicts = {} #用于统计每个五元组出现次数
for item in flow_list:
if flow_list.count(item)>=1:
dicts[item]=flow_list.count(item) #统计五元组重复次数
i = i+1
if i%1000==0:#每1000次打印一下进度
print('字典处理进度:',i/len(flow_list)*100,'%')
#end for
fileCreate(dicts,p,flow_list) #保存txt
''' 创建每个流的pcap '''
#对每个五元组创建一个文件
t_name1=p.split('.pcap') #t_name1[0] ./****/***
t_name2=t_name1[0].split('./dataset') #t_name2[1] /***
os.mkdir('./dataset'+t_name2[1])
total=len(dicts) #统计流的数目
for key in dicts.keys():
#创建写入器
writer = PcapWriter('./dataset'+t_name2[1]+t_name2[1]+'%d'%total+'.pcap', append = True)
total = total-1
if total%100 == 0:
print('流切分进度:',(1-(total/len(dicts)))*100,'%') #打印当前进度
for data in packets:
if data.payload.name == 'IP':
try:
#读取当前数据包的信息
t_tuple = "{}:{} {}:{} {}".format(data[IP].src,data.sport,data[IP].dst,data.dport,data.proto)
#比较信息
if t_tuple == key:
writer.write(data) #写入流
writer.flush()
except AttributeError:
wrong_data = wrong_data+1
#print("AttributeError")
writer.close()
#end for
'''将五元组信息统计到文件中'''
def fileCreate(dicts,p,flow_list):
#拆分拼接字符串 创建txt文件
tlist=p.split('pcap')
txt_name=tlist[0]+'txt'
f = open(txt_name,'w')
json_dicts = json.dumps(dicts,indent=1)
f.write(json_dicts+"\n"+"the number of flows:"+str(len(dicts))+"\n"+"the number of packets:"+str(len(flow_list)))
f.close()
#flowProcess函数用于将pcap按流切分成小pcap
#packetRead函数用于读取pcap中所有数据的五元组信息,可用于切分后验证。
#fileCreate函数保存统计好的五元组信息
#目录结构 ./dataset
if __name__=='__main__':
#文件夹下数据集列表
'''
dirct = './dataset'
dirList=[]
fileList=[]
files=os.listdir(dirct)
print('files:',files)
print()
'''
#选择要切分的流的位置
p ="./dataset/AppStore-web.pcap"
pp="./dataset/AppStore-web/AppStore-web6.pcap"
flow_list=flowProcess(p)
packetRead(pp)