perl生成以太网帧
发布时间:2020-12-15 23:47:02 所属栏目:大数据 来源:网络整理
导读:实验室在仿真以太网相关Verilog代码的时候,有时候需要产生一定以太网帧数据供TestBench使用,手动编写64字节-1518字节的不同的以太网数据十分麻烦。参考上板调试时候使用思博伦TeseCenter网络测试仪的思想,我们可以使用Perl脚本批量生成需要的数据,参考代
|
实验室在仿真以太网相关Verilog代码的时候,有时候需要产生一定以太网帧数据供TestBench使用,手动编写64字节-1518字节的不同的以太网数据十分麻烦。参考上板调试时候使用思博伦TeseCenter网络测试仪的思想,我们可以使用Perl脚本批量生成需要的数据,参考代码如下: #######################################################################################
# main.pl #
# 段聪 #
# 2014/12/5-2014/12/8 #
# 说明: #
# 1.生成以太网帧 #
# 2.生成三个文件,分别是帧数据文件,帧头文件,帧长文件 #
# 输出文件在当前目录的output文件夹中 #
# 3.忽略4字节以太网CRC校验值(帧长60-1514) #
# 4.CRC32校验算法使用C++编写,在子目录crc中 #
# #
# 运行环境: #
# perl/ActivePerl #
#######################################################################################
# LOG #
# V2: #
#1、(已解决)可以输入设备数n,每个设备都有一个MAC地址和IP地址 #
#2、(已解决)ARP帧发送可控(开关),发送的ARP帧位设备的映射关系, #
# 暂定为只发送n个ARP,首先ARP帧 #
#3、(已解决)修改默认值问题 #
#4、(已解决)生成按字节反序的数据文件 (file_reverse_bytes.pl) #
# #
# #
#######################################################################################
=pod
承载IP帧时
+------------+---------+-----------+--------------+-----------+---------+---------+-----------------+
| 以太网头部 | IP头部 | TCP端口号 | 数据填充 | 源设备号 | 帧编号 | CRC校验 | 以太网CRC(不选) |
| 14字节 | 20字节 | 4字节 | (64~1518)-50 | 4字节 | 4字节 | 4字节 | 4字节 |
+------------+---------+-----------+--------------+-----------+---------+---------+-----------------+
注:对(TCP端口号头部~行号)计算CRC校验值
承载ARP帧时
+------------+---------+--------------------------+-----------+---------+---------+-----------------+
| 以太网头部 | ARP头部 | 数据填充 | 源设备号 | 帧编号 | CRC校验 | 以太网CRC(不选) |
| 14字节 | 28字节 | (64~1518)-54 | 4字节 | 4字节 | 4字节 | 4字节 |
+------------+---------+--------------------------+-----------+---------+---------+-----------------+
注:对(ARP头部~行号)计算CRC校验值
行号是数据帧在文件中的行号,从0计数
MAC地址产生策略
mac(0) --> mac(0)+1
mac(0)+1 --> mac(0)+2
...
mac(0)+i --> mac(0)+i+1
...
mac(0)+n --> mac(0)
=cut
#是否开启调试模式
#开启时候debug为1
$debug = 0;
#-------------------------------------------------------------
print "n","*" x 15,"以太网帧生成器","nn";
#------------------------常量定义-----------------------------
$ETHERNET_MAX_LENGTH = 1518; #以太网最长帧字节数
$ETHERNET_MIN_LENGTH = 64; #以太网最短帧字节数
$ETHERNET_CRC_LENGTH = 4; #以太网CRC字节数
#-------------------------------------------------------------
#------------------------创建文件-----------------------------
$outdir = "./output/";
$frame_file = $outdir."frame.txt";
$frame_length_file = $outdir."frame_length.txt";
$frame_header_file = $outdir."frame_header.txt";
unless (-d $outdir){
#输出文件夹不存在
mkdir $outdir;
}
open FRAME_FILE,">",$frame_file or die "打开文件失败";
open FRAME_LENGTH_FILE,$frame_length_file or die "打开文件失败";
open FRAME_HEADER_FILE,$frame_header_file or die "打开文件失败";
#-------------------------------------------------------------
print "输入帧数目(帧数目,直接回车设置为默认值10):";
$row_cnt = <STDIN>;
chomp($row_cnt);
if($row_cnt =~ /^$/){
print "帧数目设置为默认值10n";
$row_cnt = 10;
}
else{
while(!($row_cnt =~ /d+/)){
print "帧数目输入不是数字,重新输入:n";
$row_cnt = <STDIN>;
chomp($row_cnt);
}
}
print "-" x 20,"n";
#---------------------设备数目--------------------------------
print "输入设备数目(直接回车设置为默认值5):";
$mac_cnt = <STDIN>;
chomp($mac_cnt);
if($mac_cnt =~ /^$/){
print "设备数目设置为默认值5n";
$mac_cnt = 5;
}
else{
while(!($mac_cnt =~ /d+/)){
print "设备数目不是数字,重新输入:n";
$mac_cnt = <STDIN>;
chomp($mac_cnt);
}
}
print "-" x 20,"n";
#---------------------帧长产生策略----------------------------
print "选择帧长产生策略(1或2):n";
print "直接回车默认选择1自增n";
print "1. 自增n";
print "2. 随机n";
$frame_length_type = <STDIN>;
chomp($frame_length_type);
if($frame_length_type =~/^$/){
print "帧长产生策略设置为默认自增n";
$frame_length_type = 1;
}
else{
while($frame_length_type>2 || $frame_length_type<1){
print "错误的输入,选择帧长产生策略(1或2):n";
$frame_length_type = <STDIN>;
}
}
print "-" x 20,"n";
#-------------------------------------------------------------
my $base_frame_length;
my $interval;
if($frame_length_type==1){
print "n选择了帧长自动增加...n";
print "直接回车设置为默认值64:n";
print "输入帧长起始值(64-1518):";
$base_frame_length = <STDIN>;
chomp($base_frame_length);
if($base_frame_length =~/^$/){
print "输入帧长起始值设置为默认值64n";
$base_frame_length = 64;
}
else{
while($base_frame_length>1518 || $base_frame_length<64){
print "帧长不在范围内,重新输入起始值(64-1518):n";
$base_frame_length = <STDIN>;
chomp($base_frame_length);
}
}
#-----------------------------------------------
print "输入增加间隔(直接回车选择默认值1):n";
$interval = <STDIN>;
chomp($interval);
if($interval =~/^$/){
print "增加间隔设置为为默认值64n";
$interval = 1;
}
else{
while(!($interval =~ /d+/)){
print "输入错误,输入增加间隔:n";
$interval = <STDIN>;
chomp($interval);
}
}
}
print "n","-" x 20,"n";
#--------------------MAC地址产生策略---------------------------
print "MAC地址产生策略:自增n";
print "输入起始MAC地址(默认001122334455,直接回车选择默认):";
$base_mac = <STDIN>;
chomp($base_mac);
if($base_mac =~/^$/){
print "起始MAC地址设置为默认001122334455n";
$base_mac = "001122334455";
}
else{
#正则表达式判断输入格式是否正确
while(!($base_mac =~ /[0-9a-fA-F]{12}/)){
print "错误的输入,重新输入起始MAC地址(如001122334455):n";
$base_mac = <STDIN>;
chomp($base_mac);
}
}
print "n","n";
#--------------------数据填充策略------------------------------
print "填充数据产生策略:(1或2,直接回车选择默认2伪随机):n";
print "1. 固定n";
print "2. 伪随机n";
$fill_data_type = <STDIN>;
chomp($fill_data_type);
if($fill_data_type =~ /^$/){
$fill_data_type = 2;
}
else{
while($fill_data_type>3){
print "错误的输入,选择填充数据产生策略(1或2):n";
$fill_data_type = <STDIN>;
chomp($fill_data_type);
}
}
print "n","n";
my $fixed_fill_data;
if($fill_data_type==1){
print "n选择了固定数据填充...n";
print "输入填充数据(16位,例:aabb):";
$fixed_fill_data = <STDIN>;
chomp($fixed_fill_data);
}
#--------------------是否先生成ARP帧------------------------------
$gene_arp = "y";#默认生成ARP帧
print "是否先生成ARP帧(y/n,直接回车默认y生成)?";
$gene_arp = <STDIN>;
chomp($gene_arp);
if($gene_arp =~ /^$/){
print "选择默认生成ARP帧n";
$gene_arp = "y";
}
else{
while(!($gene_arp =~ /^[ynYN]$/)){
#输入错误
print "输入错误,是否生成ARP帧(y/n)n";
$gene_arp = <STDIN>;
chomp($gene_arp);
}
}
#########################################################################
#以太网头部
my $ethernet_header;
#以太网总帧长
my $frame_length;
#ARP帧长42字节=14字节以太网头部+28字节ARP头部
#每一帧都必须加入填充0以达到以太网的最小长度要求:60字节
my $arp_frame_length = 64;
#当前行号
my $current_row_str;
my $current_row = 0;
my $current_mac = $base_mac;
if($gene_arp eq "y" || $gene_arp eq "Y"){
my $ethernet_type;
my $mac_dst;
my $mac_src;
print "n";
#生成ARP帧(数目与设备数目相同)
for($i=0;$i<$mac_cnt;$i++){
print $i,"----ARP数据帧----n";
#----------------------
$ethernet_type = "0806";
#目的MAC地址0xFFFFFFFFFFFF
$mac_dst = "ffffffffffff";
#源MAC地址
#print "base_mac is $base_macn";
#print "base_mac hex is $base_mac".hex($base_mac)."n";
$mac_src = $current_mac;
$current_mac = inc_mac($current_mac);
#print $mac_src,"n";
$ethernet_header = $mac_dst.$mac_src.$ethernet_type;
#------------------------------------------------------------------------------------
#ARP帧头部
#[2B硬件类型|2B协议类型|1B硬件地址长度|1B协议地址长度|2BOP|6B发送者硬件地址
# |4B发送者IP|6B目标硬件地址|4B目标IP地址]
my $hardware_type = dec2hexStr(0x0001,4);
my $protocal_type = dec2hexStr(0x0800,4);
my $hardware_size = dec2hexStr(0x06,2);
my $protocal_size = dec2hexStr(0x04,2);
my $op = dec2hexStr(0x0001,4);
my $sender_mac = $mac_src;
my $sender_ip = dec2hexStr(192,2).dec2hexStr(168,2).dec2hexStr(0,2).dec2hexStr($i,2);
my $target_mac = dec2hexStr(0x00_00_00_00_00_00,12);
my $target_ip = dec2hexStr(192,2).dec2hexStr($i+1,2);
if($debug==1){
print "length of hardware_type $hardware_type is ".length($hardware_type)."n";
print "length of protocal_type $protocal_type is ".length($protocal_type)."n";
print "length of hardware_size $hardware_size is ".length($hardware_size)."n";
print "length of protocal_size $protocal_size is ".length($protocal_size)."n";
print "length of op $op is ".length($op)."n";
print "length of sender_mac $sender_mac is ".length($sender_mac)."n";
print "length of sender_ip $sender_ip is ".length($sender_ip)."n";
print "length of target_mac $target_mac is ".length($target_mac)."n";
print "length of target_ip $target_ip is ".length($target_ip)."n";
}
#------------------------------------------------------------------------------------
my $data_size = int($arp_frame_length - 58);#填充的字节数
my $payload .= "00" x $data_size;
#------------------------------------------------------------------------------------
#添加源设备号(MAC地址编号,32位)
$device_no = dec2hexStr($i,8);
#添加帧编号(32位)
$current_row = $i;
$current_row_str = dec2hexStr($i,8);
#------------------------------------------------------------------------------------
#ARP协议头部+数据
$arp_protocal_head = $hardware_type
.$protocal_type
.$hardware_size
.$protocal_size
.$op
.$sender_mac
.$sender_ip
.$target_mac
.$target_ip;
$arp_protocal_data = $payload
.$device_no
.$current_row_str;
$arp_protocal_all = $arp_protocal_head.$arp_protocal_data;
if($debug==1){
print "length of arp_protocal_head is ".length($arp_protocal_head)."n";
}
#添加CRC校验值
$crc = getCRC32($arp_protocal_all);
print FRAME_FILE $ethernet_header.$arp_protocal_all.$crc,"n";
print FRAME_HEADER_FILE "$mac_dst,$mac_src,$ethernet_type,$arp_protocal_head,$device_no,$current_row_str,$crcn";
print FRAME_LENGTH_FILE ($arp_frame_length-$ETHERNET_CRC_LENGTH)."n";
}
}
#########################################################################
#产生IP帧
$current_mac = $base_mac;
srand;
for($i=0;$i<$row_cnt;$i++){
print $i,"----IP数据帧----n";
if($frame_length_type==1){
#帧长自增
$frame_length = $base_frame_length + $interval*$i;
if($frame_length>$ETHERNET_MAX_LENGTH){
$frame_length = $ETHERNET_MAX_LENGTH;
}
}
else{
#产生64-1518的随机数
$frame_length = $ETHERNET_MIN_LENGTH + int(rand($ETHERNET_MAX_LENGTH-$ETHERNET_MIN_LENGTH));
}
#my $mac_src_ip = hex($base_mac)+$i%$mac_cnt;
#源MAC地址 (自增)
my $mac_src = $current_mac;
#目的MAC地址(设定为下一条数据的源MAC)
my $mac_dst = inc_mac($current_mac);
$current_mac = $mac_dst;
if(($i%$mac_cnt) == ($mac_cnt-1)){
$mac_dst = $base_mac;
$current_mac = $base_mac;
}
#------------------------------------------------------------------------------------
#IP帧 TCP/UDP
$ethernet_type = "0800";
$ethernet_header = $mac_dst.$mac_src.$ethernet_type;
#------------------------------------------------------------------------------------
#【IP头部与载荷格式】
#版本号:0100表示IPV4,0110表示IPV6.
#[4位版本号|4位首部长度|8位服务类型|16位总字节数 ]
#[16位标示 |3位标志|13位片偏移]
#[8位生存时间 |8位协议 |16位首部校验和 ]
#[32位源IP地址 ]
#[32位目的IP地址 ]
#[16位源端口号 |16位目的端口号 ]###开始TCP协议
#[数据和填充]
#[4字节数据CRC校验值]
#---------------------------IP固定头部20字节-----------------------------------------
#默认ipv4(4位)
$ip_version = dec2hexStr(0b0100,1);
#首部长度(4位,以4字节为单位,默认为5)
$head_length = dec2hexStr(0b0101,1);
#服务类型(8位,不用)
$service_type = dec2hexStr(0b00000000,2);
#首部和数据总字节数 = 帧长-18(16位)
$total_bytes = dec2hexStr($frame_length-18,4);
#标示
$identification = dec2hexStr(0b0000_0000_0000_0000,4);
#标志(不分片),偏移为0
$flag_offset = dec2hexStr(0b010_0_0000_0000_0000,4);
#生存时间(128)
$ttl = dec2hexStr(128,2);
#8位协议(默认TCP)
$protocol = dec2hexStr(0x06,2);
#首部校验和(不计算)
$head_checksum = dec2hexStr(0x0000,4);
#IP地址192.168.0.1/192.168.0.2
$src_ip = dec2hexStr(192,2).dec2hexStr($i%$mac_cnt,2);
#print $src_ip,"---源n";
$dst_ip = dec2hexStr(192,2).dec2hexStr($i%$mac_cnt+1,2);
#print $dst_ip,"---目的n";
#------------------------------------------------------------------------------------
#---------------------------TCP协议头部----------------------------------------------
$src_port = dec2hexStr(100,4);#源端口
$dst_port = dec2hexStr(80,4); #目的端口
#------------------------------------------------------------------------------------
#TCP协议其他头部和数据
my $payload;
my $data_size = int(($frame_length - 54)/2);#2字节的总数
if($fill_data_type == 1){
#固定填充
for(1..$data_size){
$payload .= $fixed_fill_data;
}
}
else{
#伪随机填充
$last_data = 0xABCD;
$payload = dec2hexStr($last_data,4);
for(2..$data_size){
$current_data = (~((($last_data & 0b0001_0000_0000_0000)<<3)^(($last_data & 0b0000_0000_0000_1000)<<12)^(($last_data & 0b0000_0000_0000_0010)<<14)^(($last_data & 0b0000_0000_0000_0001)<<15)))&(0b1000_0000_0000_0000)|(($last_data & 0b1111_1111_1111_1110)>>1);
$payload .= dec2hexStr($current_data,4);
#print dec2hexStr($current_data,4),"n";
$last_data = $current_data;
}
if(($frame_length - 50)%2==1){
#还剩单独8bit
$current_data = (~((($last_data & 0b0001_0000_0000_0000)<<3)^(($last_data & 0b0000_0000_0000_1000)<<12)^(($last_data & 0b0000_0000_0000_0010)<<14)^(($last_data & 0b0000_0000_0000_0001)<<15)))&(0b1000_0000_0000_0000)|(($last_data & 0b1111_1111_1111_1110)>>1);
$payload .= substr(dec2hexStr($current_data,2,2);#取低8位
#print substr(dec2hexStr($current_data,2),"n";
}
}
#------------------------------------------------------------------------------------
#添加源设备号(MAC地址编号,32位)
$device_no = dec2hexStr($i%$mac_cnt,8);
#添加行号(32位)
$current_row = $current_row + 1;
$current_row_str = dec2hexStr($current_row,8);
#------------------------------------------------------------------------------------
#IP协议头部+数据
$ip_protocal_head = $ip_version
.$head_length
.$service_type
.$total_bytes
.$identification
.$flag_offset
.$ttl
.$protocol
.$head_checksum
.$src_ip
.$dst_ip;
$ip_protocal_data = $src_port
.$dst_port
.$payload
.$device_no
.$current_row_str;
$ip_protocal_all = $ip_protocal_head.$ip_protocal_data;
#添加CRC校验值
$crc = getCRC32($ip_protocal_data);
print FRAME_FILE $ethernet_header.$ip_protocal_all.$crc,"n";
print FRAME_HEADER_FILE "$mac_dst,$ip_protocal_head,$crcn";
print FRAME_LENGTH_FILE ($frame_length-$ETHERNET_CRC_LENGTH)."n";
}
#########################################################################
close FRAME_FILE;
close FRAME_LENGTH_FILE;
close FRAME_HEADER_FILE;
print "n--------------运行结束------------n";
print "----输出文件为 $frame_filen";
print " $frame_header_filen";
print " $frame_length_filen";
##############################子函数######################################
#十进制转16进制
sub dec2hexStr{
my $fix_wdth = 0;
if(@_ > 1)
{
$fix_wdth = 1;
}
my $data = shift @_;
my $data16 = sprintf("%x",$data);
my $width = shift @_;
if($fix_wdth==1)
{
if(length($data16)<$width)
{
$data16 = ("0" x ($width-length($data16))).$data16;
}
}
return $data16;
}
#计算CRC校验值
sub getCRC32{
$data = shift @_;
$str = readpipe("./crc/exe/crc.exe $data");
$str = substr($str,4,8);
$str1 = substr($str,2);
$str2 = substr($str,2);
$str3 = substr($str,2);
$str4 = substr($str,6,2);
$crc = $str4.$str3.$str2.$str1;
return $crc;
}
#mac地址加一
sub inc_mac{
my $str = shift @_;
my $mac_first_4 = substr($str,4);
#printf("0x%xn",hex($mac_first_4)+1);
my $mac_last_8 = substr($str,8);
if($mac_last_8 =~ /[fF]{8}/){
$mac_first_4 = dec2hexStr(hex($mac_first_4)+1,4);
#print $mac_first_4,"n";
return $mac_first_4."00000000";
}
else{
$mac_last_8 = dec2hexStr(hex($mac_last_8)+1,8);
return $mac_first_4.$mac_last_8;
}
}
对发送和接收的文件进行对比的脚本: #######################################################################################
# compare_file.pl #
# 段聪 #
# 2014/12/5-2014/12/8 #
# 说明: #
# 对比发送的数据和接收的数据文件 #
# #
# #
#######################################################################################
print "输入接收文件名称:";
$file_send = "./output/frame.txt";
$file_receive = <STDIN>;#"./output/frame_receive.txt";
open(SEND,$file_send) or die "打开文件 $file_send 失败n";
open(RECEIVE,$file_receive) or die "打开文件 $file_receive 失败n";
#-------------------------------------------------------------------
print "n","-" x 10,"开始对比","n";
#对接收文件按照发送编号进行排序
my %send_hash;
my $send_no_str;
my $send_no;
my $row = 0;
#接收文件
while($line = <RECEIVE>){
chomp($line);
$row++;
$send_no_str = substr($line,length($line)-16,8);
$send_hash{$send_no_str} = $row.",".$line;
#print $row.",".$line,"n";
}
#按键值排序
my @keys_sort = sort keys %send_hash;
#读取发送文件
print "n";
$row = 0;
my $receive_line;
my @x,@y;
while($line = <SEND>){
chomp($line);
$row++;
$send_no_str = substr($line,8);
$receive_all = $send_hash{$send_no_str};
@receive_line_with_row = split ',',$receive_all;
$receive_line_row = $receive_line_with_row[0];
$receive_line = $receive_line_with_row[1];
if($line ne $receive_line){
#查找不同的地方
@x = split '',$line;
@y = split '',$receive_line;
$result = join '',map { $x[$_] eq $y[$_] ? "*" : $y[$_] }
0 .. $#y;
print "发送文件第 $row 行与接收文件第 $receive_line_row 行不一致,发送编号$send_non";
print "发送:$linen";
print "接收:$receive_linen";
print "差异:$resultnn"
}
else{
#print "--n";
}
}
#-------------------------------------------------------------------
close SEND;
close RECEIVE;
print "n","对比结束","n";
#######################################################################################
# file_reverse_bytes.pl #
# 段聪 #
# 2014/12/5-2014/12/8 #
# 说明: #
# 对main.pl生成的帧数据文件frame.txt按字节进行反序 #
# #
# #
#######################################################################################
$input_file = "./output/frame.txt";
$output_file = substr($input_file,length($input_file)-4)."_reverse.txt";
open(INPUT_FILE,"<",$input_file) or die "打开文件 $input_file 失败n";
open(OUTPUT_FILE,$output_file) or die "打开文件 $output_file 失败n";
print "n","*"x 30,"nn";
print "n----开始反序处理----nn";
$cnt = 0;
while($line = <INPUT_FILE>){
$cnt++;
print "正在处理第 $cnt 行...n";
chomp($line);
#遍历每一行进行处理
print OUTPUT_FILE reverse_line($line)."n";
}
close INPUT_FILE;
close OUTPUT_FILE;
print "n----处理结束----n";
print "----输出文件为 $output_filenn";
sub reverse_line{
$str = shift @_;
$str_reverse = reverse $str;
my $str_result;
for($i=0;$i<length($str)-1;$i+=2){
$str_result.= reverse substr($str_reverse,$i,2);
}
return $str_result;
}
输出文件数据截图如下:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |



