我是靠谱客的博主 糟糕豌豆,最近开发中收集的这篇文章主要介绍icmp协议_UDP/IP硬件协议栈设计(六):ICMP,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

920adfa871365469edac3f8c9e065a4f.png

前面耽搁了几天搞了个大东西(又离毕业远了一步),似乎前面已经完成了ARP的应答ARP表的设计,终于可以使用IP协议进行通信了。

接下来是根据协议栈设计(二)分类中得到的ICMP数据进行处理,不过本文只针对回显请求(ping功能)进行处理,即“我叫你一声,你敢答应吗?”。

4841e98c55e60d1b2415f1eba37bb5f7.png
我叫你一声,你敢答应吗

前文说过ICMP实现在IP系统间传递差错和管理报文,但是就目前的设计需要而言,只想完成“你叫上千声,我就答应你千声!”,所以只处理其回显请求,你敢ping我,我就敢答应。

  • 看看ping都干了些什么

我们时常在同一局域网的不同主机之间的通信中会遇到数据无法传输的问题,总会习惯性的调出CMD,熟练的敲上“ping 192.168.xxx.xxx”,然后惊叹一句“竟然ping不通!”,当然也会发出“为什么ping通了,还是连不上?”

为了探清楚这一过程,进行这一操作如下图所示, 一共ping两次,一次默认,另一次设padding长度为500字节,如果能ping通,则会受到来自对方地址的回复。

b69be5ac6e7a479cb5547fbbefb47c0b.png
ping两次,一次默认,一次设长度为500

同时再打开wireshark去捕获上述的过程如下图所示:

0447ad817086d0ef6c5eea197da4b37f.png
wireshark捕获上述两次ping操作

从上两张图中可以看到:

  1. 在本主机的CMD中输入"ping 192.168.208.1"指令后,将会发出四条回显请求(Echo request)的ICMP帧;
  2. 对方主机每次收到回显请求的ICMP帧后,会回复一条回显应答(Echo reply)的ICMP帧;
  3. 本主机每收到一条来自对方主机的回显应答帧后,解析其内容(padding字节长度、TTL等信息),并在CMD栏中显示出来;
  4. 如果ping的时候,将padding字节长度设为500字节,则收到对方的应答帧的padding长度也将是500字节;
  • ICMP报文结构分析

对ping的过程有所了解后,再来看看其报文结构,也就是前文分类中处理得到的ICMP数据报文,如下图所示:

8a00752feb618ace764ca39cfc07b612.png
ICMP回显请求和回显应答报文格式

b39b1149a83d6c4795c947bf3f041973.png
wireshark捕获的一次ICMP报文(Echo request)

1d40a033d3ca748fc24e4d92c1348816.png
wireshark捕获的一次ICMP报文(Echo reply)

可见其字段分别为:

  1. 类型(Type):1字节,8'h00表示reply,8'h08表示request;
  2. 代码(Code):1字节,在回显请求和应答中填8'h00;
  3. 校验和(Checksum):2字节,校验一文中说过,这里不再多说;
  4. 标识符(Identifier):2字节,用于标识,reply帧需与对应的request帧一致;
  5. 序号(Sequence):2字节,在request中,每请求一次,该字段加一,reply帧需与对应的request帧一致;
  6. 数据(Data):至少32字节,即ICMP报文中需填充的数据,reply帧需与对应的request帧一致;
  • 回显请求的处理

前面介绍了回显请求应答的过程和报文结构,可以发现,如果要处理一条ICMP Echo request帧,需要:

  1. 解析其类型字段,仅当为8'h08时,才进行应答,其他时予以舍弃处理;
  2. 当可以应答时,将应答帧(reply)的类型字段置为8'h00;
  3. 当可以应答时,保证应答帧(reply)的标识符、序号、数据字段的数据与请求帧(request)一致;
  4. 当可以应答时,校验和字段需重新按照前文所说的方式计算;

以上的处理过程用verilog可表示为:

对前文分类中解析得到的ICMP数据进行移位寄存处理

    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            icmp_data_valid_shift   <=  16'h0;
            icmp_data_shift         <=  80'h0;
        end
        else begin
            icmp_data_valid_shift   <=  {icmp_data_valid_shift[14:0],icmp_data_valid};
            icmp_data_shift         <=  {icmp_data_shift[71:0],icmp_data};
        end
    end

当ICMP的type为request才进行应答

    assign icmp_reply_valid     =   (icmp_type == 8'h08);  //当type为08时才应答

对于应答帧中的Checksum计算,因为需要首部和数据均参与计算,而本协议栈在分类解析时就对接收的ICMP帧进行过计算了,为降低延迟,可在解析过程中单独对其数据部分进行累加,累加结果可随着ICMP报文传递至该处理模块中,这样,在计算应答帧的Checksum时可直接加上数据部分的累加值,如此一来,Checksum的计算可在短时间内完成:

    assign icmp_checksum_clac_en    =   (icmp_data_valid_shift[5] && (!icmp_data_valid_shift[6])) || 
                                        (icmp_data_valid_shift[7] && (!icmp_data_valid_shift[8]));
    assign icmp_data_shift2         =   icmp_data_shift[15:0];

    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            icmp_checksum_clac_32   <=  32'h0;
        end
        else if (icmp_type_en) begin
            icmp_checksum_clac_32   <=  current_frame_data_checksum;  //数据部分的累加值由前级传递而来
        end
        else if (icmp_checksum_clac_en) begin
            icmp_checksum_clac_32   <=  icmp_checksum_clac_32 + icmp_data_shift2;
        end
    end

    always @ (posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            icmp_checksum_clac_temp <=  32'h0;
            icmp_checksum           <=  16'h0;
        end
        else begin
            icmp_checksum_clac_temp <=  icmp_checksum_clac_32[31:16] + icmp_checksum_clac_32[15:0];
            icmp_checksum           <=  ~{{15'h0,icmp_checksum_clac_temp[16]} + icmp_checksum_clac_temp[15:0]};
        end
    end

其他就是按照报文结构依次组成ICMP的Echo reply帧(仅包括ICMP报文部分),存入FIFO中,待发送模块进行发送处理:

    //reply type and code
    assign reply_type_code_valid        =   icmp_reply_valid && (icmp_data_valid_shift[8] && (!icmp_data_valid_shift[8+2]));
    assign reply_type_code              =   9'h0;

    //reply icmp checksum
    assign reply_checksum_valid         =   icmp_reply_valid && (icmp_data_valid_shift[10] && (!icmp_data_valid_shift[10+2]));
    assign reply_checksum_h             =   (icmp_reply_valid && (icmp_data_valid_shift[10] && (!icmp_data_valid_shift[11])))?{1'b0,icmp_checksum[15:8]}:9'h0;
    assign reply_checksum_l             =   (icmp_reply_valid && (icmp_data_valid_shift[11] && (!icmp_data_valid_shift[12])))?{1'b0,icmp_checksum[7:0]}:9'h0;

    //reply identifier/sequence num/data
    assign reply_id_seqnum_data_valid   =   icmp_reply_valid && icmp_data_valid_shift[8] & icmp_data_valid_shift[10+2];
    assign reply_id_seqnum_data_last    =   icmp_reply_valid && (~icmp_data_valid_shift[7]) & icmp_data_valid_shift[8];
    assign reply_id_seqnum_data         =   reply_id_seqnum_data_valid?{reply_id_seqnum_data_last,icmp_data_shift[71:64]}:9'h0;

    //reply data
    assign icmp_reply_data_valid        =   reply_type_code_valid | reply_checksum_valid | reply_id_seqnum_data_valid;
    assign icmp_reply_data              =   reply_type_code | reply_checksum_h | reply_checksum_l | reply_id_seqnum_data;

最后的仿真结果如下图所示:

96dc62133e4004468403873ca0c27362.png

以上即ICMP中回显请求的处理过程,针对ICMP中的其他功能由于暂时使用不到,故不作处理,这样一来,协议栈完成了地址的解析、网络通达的检测,之后就可以传数据了,且听下回分解。

若有错误还望批评指正!


十二点过九分:UDP/IP硬件协议栈设计(七):UDP​zhuanlan.zhihu.com
da65269b3b1e27cdc2574617c4994f2a.png

最后

以上就是糟糕豌豆为你收集整理的icmp协议_UDP/IP硬件协议栈设计(六):ICMP的全部内容,希望文章能够帮你解决icmp协议_UDP/IP硬件协议栈设计(六):ICMP所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部