- 基于udp协议
- 在上一次实现的代码上加功能
前言
基于udp协议,在实现arp功能后,本机ping完虚拟机之后,虚拟机需要将相关的icmp信息返回给本机。
一、先添加相关标识
#define ENABLE_ICMP 1
二、实现checksum
定义创建icmp包的函数前,需要自己实现checksum
#if ENABLE_ICMP
static uint16_t ng_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;
}
#endif
三、定义创建icmp包的函数
思路与send一样,只不过要发送的包不是arp包,而是icmp包
// 根据不同的code处理
#if ENABLE_ICMP
static int ng_encode_icmp_pkt(uint8_t *msg, uint8_t *dst_mac,
uint32_t sip, uint32_t dip, uint16_t id, uint16_t seqnb)
{
// 1 ether
struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;
rte_memcpy(eth->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
// 2 ip
struct rte_ipv4_hdr *ip = (struct rte_ipv4_hdr *)(msg + sizeof(struct rte_ether_hdr));
ip->version_ihl = 0x45;
ip->type_of_service = 0;
ip->total_length = htons(sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr));
ip->packet_id = 0;
ip->fragment_offset = 0;
ip->time_to_live = 64; // ttl = 64
ip->next_proto_id = IPPROTO_ICMP; //注意这里,与之前ip包的不同
ip->src_addr = sip;
ip->dst_addr = dip;
ip->hdr_checksum = 0;
ip->hdr_checksum = rte_ipv4_cksum(ip);
// 3 icmp
struct rte_icmp_hdr *icmp = (struct rte_icmp_hdr *)(msg + sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));
icmp->icmp_type = RTE_IP_ICMP_ECHO_REPLY;
icmp->icmp_code = 0;
icmp->icmp_ident = id;
icmp->icmp_seq_nb = seqnb;
icmp->icmp_cksum = 0;
icmp->icmp_cksum = ng_checksum((uint16_t *)icmp, sizeof(struct rte_icmp_hdr));
return 0;
}
#endif
四、在mbuf中存储icmp包
注意,此时参数不需要端口号,因为icmp是在网络层的协议,此时并不需要端口号。
#if ENABLE_ICMP
static struct rte_mbuf *ng_send_icmp(struct rte_mempool *mbuf_pool, uint8_t *dst_mac,
uint32_t sip, uint32_t dip, uint16_t id, uint16_t seqnb)
{
const unsigned total_length = 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, "rte_pktmbuf_alloc\n");
}
mbuf->pkt_len = total_length;
mbuf->data_len = total_length;
uint8_t *pkt_data = rte_pktmbuf_mtod(mbuf, uint8_t *);
ng_encode_icmp_pkt(pkt_data, dst_mac, sip, dip, id, seqnb);
return mbuf;
}
#endif
五、main函数中添加
!!!注意:这里代码与处理IP协议并列,代码顺序如下:
#if ENABLE_ICMP
if (IPPROTO_ICMP == iphdr->next_proto_id)
{
struct rte_icmp_hdr *icmphdr = (struct rte_icmp_hdr *)(iphdr + 1);
// 加上打印信息
struct in_addr addr;
addr.s_addr = iphdr->src_addr;
printf("icmp ---> src: %s ", inet_ntoa(addr));
if (icmphdr->icmp_type == RTE_IP_ICMP_ECHO_REQUEST)
{
// 如果是08则实现ping
addr.s_addr = iphdr->dst_addr;
printf(" local: %s , type : %d\n", inet_ntoa(addr), icmphdr->icmp_type);
struct rte_mbuf *txbuf = ng_send_icmp(mbuf_pool, ehdr->s_addr.addr_bytes,
iphdr->dst_addr, iphdr->src_addr, icmphdr->icmp_ident, icmphdr->icmp_seq_nb);
rte_eth_tx_burst(gDpdkPortId, 0, &txbuf, 1);
rte_pktmbuf_free(txbuf);
rte_pktmbuf_free(mbufs[i]);
}
}
#endif
运行结果
首先,arp已经添加好了
此时,ping虚拟机
完成了icmp功能后
./build/dpdk_icmp
再次ping,则得到:
此时,已经有相应的icmp信息了。