一、ICMP的功能以及报文格式
ICMP报文的功能
CMP是 Internet Control Message Protocol 的缩写,即互联网控制消息协议。它是互联网协议族的核心协议之一。它用于 TCP/IP 网络中发送控制消息,提供可能发生在通信环境中的各种问题反馈,通过这些信息,使网络管理者可以对所发生的问题作出诊断,然后采取适当的措施解决问题。
虽然 ICMP 是网络层协议,但是它不像 IP 协议和 ARP 协议一样直接传递给数据链路层,而是先封装成 IP 数据包然后再传递给数据链路层。所以在 IP 数据包中如果协议类型字段的值是 1 的话,就表示 IP 数据是 ICMP 报文。
ICMP字段含义说明
各字段说明
- 类型:占一字节,标识ICMP报文的类型,目前已定义了14种,从类型值来看ICMP报文可以分为两大类。第一类是取值为1~127的差错报文,第2类是取值128以上的信息报文。如前所述
- 代码:占一字节,标识对应ICMP报文的代码。它与类型字段一起共同标识了ICMP报文的详细类型。如前所述
- 校验和:这是对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错。其计算方法与在我们介绍IP报头中的校验和计算方法是一样的。
- 标识:占两字节,用于标识本ICMP进程,但仅适用于回显请求和应答ICMP报文,对于目标不可达ICMP报文和超时ICMP报文等,该字段的值为0。
二、代码实现
参考FPGA奇哥网课
架构
ICMP_RX模块
ICMP_RX功能:判断由IP层上传过来的数据包是否为ICMP数据包,如果是,则解析出标识符和序列号,并给到ICMP_TX一个触发信号。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/05/18 16:06:11
// Design Name:
// Module Name: ICMP_RX
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ICMP_RX(
input i_clk ,
input i_rst ,
input [63:0] s_axis_ip_data ,
input [54:0] s_axis_ip_user ,//1'bMF,16'dlen,1'bsplit,8'dtype,13'doffset,16'dID
input [7 :0] s_axis_ip_keep ,
input s_axis_ip_last ,
input s_axis_ip_valid ,
output [15:0] o_Identifier ,
output [15:0] o_Sequence ,
output o_trigger
);
reg [63:0] rs_axis_ip_data ;
reg [54:0] rs_axis_ip_user ;
reg [7 :0] rs_axis_ip_keep ;
reg rs_axis_ip_last ;
reg rs_axis_ip_valid ;
reg [15:0] r_cnt ;
reg r_request ;
reg [15:0] ro_Identifier ;
reg [15:0] ro_Sequence ;
reg ro_trigger ;
reg r_icmp_flag ;
assign o_Identifier = ro_Identifier ;
assign o_Sequence = ro_Sequence ;
assign o_trigger = ro_trigger ;
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
rs_axis_ip_data <= 'd0;
rs_axis_ip_user <= 'd0;
rs_axis_ip_keep <= 'd0;
rs_axis_ip_last <= 'd0;
rs_axis_ip_valid <= 'd0;
end else begin
rs_axis_ip_data <= s_axis_ip_data ;
rs_axis_ip_user <= s_axis_ip_user ;
rs_axis_ip_keep <= s_axis_ip_keep ;
rs_axis_ip_last <= s_axis_ip_last ;
rs_axis_ip_valid <= s_axis_ip_valid;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_cnt <= 'd0;
else if(rs_axis_ip_valid)
r_cnt <= r_cnt + 1;
else
r_cnt <= 'd0;
end
//判断是否为ICMP请求回显的数据包
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_request <= 'd0;
else if(rs_axis_ip_valid && r_cnt == 0 && rs_axis_ip_data[63:48] == 16'h0800)
r_request <= 'd1;
else if(rs_axis_ip_valid && r_cnt == 0 && rs_axis_ip_data[63:48] != 16'h0800)
r_request <= 'd0;
else
r_request <= r_request;
end
//解析出标识符
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_Identifier <= 'd0;
else if(rs_axis_ip_valid && r_cnt == 0)
ro_Identifier <= rs_axis_ip_data[31:16];
else
ro_Identifier <= ro_Identifier;
end
//解析出序列值
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_Sequence <= 'd0;
else if(rs_axis_ip_valid && r_cnt == 0)
ro_Sequence <= rs_axis_ip_data[15:0];
else
ro_Sequence <= ro_Sequence;
end
//当收到ICMP请求,并且此帧已经接收完毕,那么就拉高触发信号,通知ICMP_TX模块应答
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ro_trigger <= 'd0;
else if(r_request && !s_axis_ip_valid && rs_axis_ip_valid && r_icmp_flag)
ro_trigger <= 'd1;
else
ro_trigger <= 'd0;
end
//检测是否为ICMP报文
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_icmp_flag <= 'd0;
else if(s_axis_ip_valid && !rs_axis_ip_valid && s_axis_ip_user[36:29] != 1)
r_icmp_flag <= 'd0;
else if(s_axis_ip_valid && !rs_axis_ip_valid && s_axis_ip_user[36:29] == 1)
r_icmp_flag <= 'd1;
else
r_icmp_flag <= r_icmp_flag;
end
endmodule
ICMP_TX模块
功能:接收到ICMP_RX模块传递过来的触发信号后,组成与ICMP请求报文相同标识符和序列号的响应报文。在其中的校验和的计算方式上,采用与IP首部校验和相同的方式。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/05/18 16:06:11
// Design Name:
// Module Name: ICMP_TX
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ICMP_TX(
input i_clk ,
input i_rst ,
output [63:0] m_axis_ip_data ,
output [70:0] m_axis_ip_user ,//16'dByteLen,1'bMF,16'dlen,1'bsplit,8'dtype,13'doffset,16'dID
output [7 :0] m_axis_ip_keep ,
output m_axis_ip_last ,
output m_axis_ip_valid ,
input m_axis_ip_ready ,
input [15:0] i_Identifier ,
input [15:0] i_Sequence ,
input i_trigger
);
reg [63:0] rm_axis_ip_data ;
reg [70:0] rm_axis_ip_user ;
reg [7 :0] rm_axis_ip_keep ;
reg rm_axis_ip_last ;
reg rm_axis_ip_valid ;
reg [15:0] r_cnt ;
reg [15:0] ri_Identifier ;
reg [15:0] ri_Sequence ;
reg ri_trigger ;
reg ri_trigger_1d ;
reg [31:0] r_header_check ;
assign m_axis_ip_data = rm_axis_ip_data ;
assign m_axis_ip_user = rm_axis_ip_user ;
assign m_axis_ip_keep = rm_axis_ip_keep ;
assign m_axis_ip_last = rm_axis_ip_last ;
assign m_axis_ip_valid = rm_axis_ip_valid ;
//接收ICMP_RX模块传递过来的触发信号、标识符、序列值
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst) begin
ri_trigger <= 'd0;
end else begin
ri_trigger <= i_trigger ;
ri_trigger_1d <= ri_trigger ;
end
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ri_Identifier <= 'd0;
else if(i_trigger)
ri_Identifier <= i_Identifier ;
else
ri_Identifier <= ri_Identifier ;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
ri_Sequence <= 'd0;
else if(i_trigger)
ri_Sequence <= i_Sequence ;
else
ri_Sequence <= ri_Sequence ;
end
//进行首部CRC校验,与IP报文一致,都是以2字节为单位,累加求和(把进位加在低位),然后取反。
//此处先进行累加求和
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_header_check <= 'd0;
else if(i_trigger)
r_header_check <= i_Identifier + i_Sequence + 16'h6162 + 16'h6364 + 16'h6566 +16'h6768 + 16'h696a
+ 16'h6b6c + 16'h6d6e + 16'h6f70 + 16'h7172 + 16'h7374 + 16'h7576 + 16'h7761 + 16'h6263 + 16'h6465
+ 16'h6667 + 16'h6869;
else if(ri_trigger)
r_header_check <= r_header_check[31:16] + r_header_check[15:0];
else
r_header_check <= r_header_check;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
r_cnt <= 'd0;
else if(r_cnt == 4)
r_cnt <= 'd0;
else if(ri_trigger_1d || r_cnt)
r_cnt <= r_cnt + 'd1;
else
r_cnt <= r_cnt;
end
//发送ICMP报文
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_ip_data <= 'd0;
else case(r_cnt)
0 :rm_axis_ip_data <= {16'h0000,~r_header_check[15:0],ri_Identifier,ri_Sequence};//16‘h0000代表是响应报文,~r_header_check[15:0]对累加和取反,ri_Identifier,ri_Sequence:要与请求报文的标识符和序列值相同
1 :rm_axis_ip_data <= {64'h6162636465666768};//之后全部为填充数据
2 :rm_axis_ip_data <= {64'h696a6b6c6d6e6f70};
3 :rm_axis_ip_data <= {64'h7172737475767761};
4 :rm_axis_ip_data <= {64'h6263646566676869};
default :rm_axis_ip_data <= {64'h0000000000000000};
endcase
end
//16'dByteLen,1'bMF,16'dlen,1'bsplit,8'dtype,13'doffset,16'dID
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_ip_user <= 'd0;
else
rm_axis_ip_user <= {16'd40,1'b0,16'd5,1'b0,8'd1,13'd0,16'd1};
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_ip_keep <= 8'b1111_1111;
else
rm_axis_ip_keep <= 8'b1111_1111;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_ip_last <= 'd0;
else if(r_cnt == 4)
rm_axis_ip_last <= 'd1;
else
rm_axis_ip_last <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
rm_axis_ip_valid <= 'd0;
else if(rm_axis_ip_last)
rm_axis_ip_valid <= 'd0;
else if(ri_trigger_1d)
rm_axis_ip_valid <= 'd1;
else
rm_axis_ip_valid <= rm_axis_ip_valid;
end
endmodule
三、仿真
向ICMP模块发送一个ICMP请求包,查看ICMP_RX和ICMP_TX的执行情况
ICMP_RX模块
ICMP_TX模块