icmp

What

ICMP是Internet Control Message Protocol(互联网控制报文协议)的缩写,它是在TCP/IP协议栈中的一种网络层协议。ICMP主要用于在IP网络中传输控制消息,以便在网络节点之间进行错误报告、诊断和路由选择。

ICMP消息通常由网络设备(如路由器或主机)生成并发送给目标设备。常见的ICMP消息类型包括:

  1. Echo Request和Echo Reply:用于Ping命令来测试两个设备之间的可达性和延迟。

  2. Destination Unreachable:用于向源设备报告目标不可达的情况。

  3. Time Exceeded:当数据包在传输过程中超过生存时间(TTL)或分片重组超时时,发送此消息。

  4. Redirect:当一个路由器认为数据包更好地通过另一个路径发送时,发送此消息来提供新的下一跳地址。

  5. Parameter Problem:用于指示IP头部或选项字段中出现问题。

Why

  1. 错误报告和故障排除:ICMP允许网络设备生成和发送错误消息,如目标不可达、超时等,以便通知源设备发生了问题。这对于诊断网络连接问题和排除故障非常关键。

  2. 网络可达性检测:通过使用ICMP Echo Request和Echo Reply消息(也称为Ping),可以测试两台设备之间的连通性和延迟。这对于管理员来说是一种简单而有效的方式,可以确定目标设备是否可达。

  3. 路由选择:ICMP Redirect消息允许路由器向源设备提供更好的下一跳路由信息。当一个路由器认为数据包应该通过另一个路径转发时,它会发送Redirect消息,从而改变源设备的下一跳路由。

  4. 生存时间限制:ICMP Time Exceeded消息用于指示数据包在传输过程中超过了生存时间(TTL),或者分片重组超时。这有助于确保IP数据包不会无限循环,并且能够及时丢弃损坏或失效的分片。

  5. 附加功能支持:ICMP还支持其他功能,如MTU探测(发现链路最大传输单元)、流量控制、广播地址的验证等。

How

ICMP的功能是检错而不是纠错;

它将出错的报文返回给发送方的设备,发送方根据ICMP报文确定「错误类型」,从而更好的重发错误的数据包。

我们用来测试网络连通性的 ping 命令,就是ICMP的工作过程。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240719165953.png

  • Type:占1字节,标识ICMP报文的类型,从类型值来看ICMP报文可以分为两大类。第一类是取值为1~127的差错报文,第2类是取值128以上的信息报文。

  • Code:占1字节,标识对应ICMP报文的代码。它与类型字段一起共同标识了ICMP报文的详细类型。

  • Checksum:占2字节,这是对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错。

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240721235701.png

主要增加部分

#if ICMP_ENABLE


/*
 * 函数名称: ln_icmp_checksum
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-22
 * 功能描述: icmp数据包校验
 * 返 回 值: 校验结果
*/
static uint16_t ln_icmp_checksum(uint16_t* addr, int count) {

    register long sum = 0;

    while (count > 1) {

        sum += *(unsigned short*)addr++;
        count -= 2;
    }

    if (count > 0) {

        sum += *(unsigned char*)addr;
    }

    while (sum >> 16) {

        sum = (sum & 0xffff) + (sum >> 16);
    }

    return ~sum;
}


/*
 * 函数名称: ln_icmp_encode
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-22
 * 功能描述: 组织icmp数据包
 * 返 回 值: 0
*/
static int ln_icmp_encode(uint8_t* pktbuf, uint8_t* dst_mac, uint32_t sip, uint32_t dip, uint16_t id, uint16_t seq) {

    struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;
    rte_memcpy(ehdr->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
    rte_memcpy(ehdr->s_addr.addr_bytes, nSrcMac, RTE_ETHER_ADDR_LEN);
    ehdr->ether_type = htons(RTE_ETHER_TYPE_IPV4);

    struct rte_ipv4_hdr* iphdr = (struct rte_ipv4_hdr*)(ehdr + 1);
    iphdr->version_ihl = 0x45;
    iphdr->type_of_service = 0;
    iphdr->total_length = htons(sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr));
    iphdr->packet_id = 0;
    iphdr->fragment_offset = 0;
    iphdr->time_to_live = 64;
    iphdr->next_proto_id = IPPROTO_ICMP;
    iphdr->src_addr = sip;
    iphdr->dst_addr = dip;
    iphdr->hdr_checksum = 0;
    iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);

    struct rte_icmp_hdr* ichdr = (struct rte_icmp_hdr*)(iphdr + 1);
    ichdr->icmp_type = RTE_IP_ICMP_ECHO_REPLY;
    ichdr->icmp_code = 0;
    ichdr->icmp_ident = id;
    ichdr->icmp_seq_nb =seq;
    ichdr->icmp_cksum = 0;
    uint16_t icmp_ck = ln_icmp_checksum((uint16_t*) ichdr, sizeof(struct rte_icmp_hdr));
    ichdr->icmp_cksum = icmp_ck;

    return 0;
}


/*
 * 函数名称: ln_icmp_send
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-07-22
 * 功能描述: icmp数据包发送
 * 返 回 值: 存储数据包的那一段内存
*/
struct rte_mbuf* ln_icmp_send(struct rte_mempool* mbuf_pool, uint8_t* dst_mac, uint32_t sip, uint32_t dip, uint16_t id, uint16_t seq) {

    int total_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr);

    struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);
    if (!mbuf) {

        rte_exit(EXIT_FAILURE, "Alloc icmp buf failed");
    }
    mbuf->pkt_len = total_len;
    mbuf->data_len = total_len;

    uint8_t* pktdata = rte_pktmbuf_mtod(mbuf, uint8_t*);
    ln_icmp_encode(pktdata, dst_mac, sip, dip, id, seq);

    return mbuf;
}


#endif

debug

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240722102843.png

之前框起来的地方,我转换了网络字节序:

ichdr->icmp_ident = htons(id);
ichdr->icmp_seq_nb = htons(seq);

这样写是无法接收到数据的,因为在主函数中直接将没有转换的id​和seq​作为参数传了进来,再转换一次就出错了。

结果展示

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240722012024.png

收到icmp数据包消息,并且正常回复。

项目地址

roboteth-icmp