icmp
What
ICMP是Internet Control Message Protocol(互联网控制报文协议)的缩写,它是在TCP/IP协议栈中的一种网络层协议。ICMP主要用于在IP网络中传输控制消息,以便在网络节点之间进行错误报告、诊断和路由选择。
ICMP消息通常由网络设备(如路由器或主机)生成并发送给目标设备。常见的ICMP消息类型包括:
Echo Request和Echo Reply:用于Ping命令来测试两个设备之间的可达性和延迟。
Destination Unreachable:用于向源设备报告目标不可达的情况。
Time Exceeded:当数据包在传输过程中超过生存时间(TTL)或分片重组超时时,发送此消息。
Redirect:当一个路由器认为数据包更好地通过另一个路径发送时,发送此消息来提供新的下一跳地址。
Parameter Problem:用于指示IP头部或选项字段中出现问题。
Why
错误报告和故障排除:ICMP允许网络设备生成和发送错误消息,如目标不可达、超时等,以便通知源设备发生了问题。这对于诊断网络连接问题和排除故障非常关键。
网络可达性检测:通过使用ICMP Echo Request和Echo Reply消息(也称为Ping),可以测试两台设备之间的连通性和延迟。这对于管理员来说是一种简单而有效的方式,可以确定目标设备是否可达。
路由选择:ICMP Redirect消息允许路由器向源设备提供更好的下一跳路由信息。当一个路由器认为数据包应该通过另一个路径转发时,它会发送Redirect消息,从而改变源设备的下一跳路由。
生存时间限制:ICMP Time Exceeded消息用于指示数据包在传输过程中超过了生存时间(TTL),或者分片重组超时。这有助于确保IP数据包不会无限循环,并且能够及时丢弃损坏或失效的分片。
附加功能支持:ICMP还支持其他功能,如MTU探测(发现链路最大传输单元)、流量控制、广播地址的验证等。
How
ICMP的功能是检错而不是纠错;
它将出错的报文返回给发送方的设备,发送方根据ICMP报文确定「错误类型」,从而更好的重发错误的数据包。
我们用来测试网络连通性的 ping 命令,就是ICMP的工作过程。
Type:占1字节,标识ICMP报文的类型,从类型值来看ICMP报文可以分为两大类。第一类是取值为1~127的差错报文,第2类是取值128以上的信息报文。
Code:占1字节,标识对应ICMP报文的代码。它与类型字段一起共同标识了ICMP报文的详细类型。
Checksum:占2字节,这是对包括ICMP报文数据部分在内的整个ICMP数据报的校验和,以检验报文在传输过程中是否出现了差错。
主要增加部分
#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
之前框起来的地方,我转换了网络字节序:
ichdr->icmp_ident = htons(id);
ichdr->icmp_seq_nb = htons(seq);
这样写是无法接收到数据的,因为在主函数中直接将没有转换的id
和seq
作为参数传了进来,再转换一次就出错了。
结果展示
收到icmp数据包消息,并且正常回复。
评论