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; } 输出文件数据截图如下: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |