关于icmpv6的介绍在其他文章中,这里就不再赘述。RFC4443中文
RFC4861中文
1、发送ICMPV6 NEIGHBOR SOLICIATION数据包
struct test_rte_flow_item_icmp6_nd_ns {
uint8_t type; /**< ICMPv6 type, normally 135. */
uint8_t code; /**< ICMPv6 code, normally 0. */
rte_be16_t checksum; /**< ICMPv6 checksum. */
rte_be32_t reserved; /**< Reserved, normally 0. */
uint8_t target_addr[16]; /**< Target address. */
uint8_t op_type; /**< ND option type, normally 1. */
uint8_t length; /**< ND option length, normally 1. */
struct rte_ether_addr sla; /**< Source Ethernet LLA. */
};
void create_neighbor_solicition(struct rte_mbuf *pkt, uint8_t *dst_ipv6_addr)
{
struct rte_ether_hdr *eth_hdr;
struct rte_ipv6_hdr *ipv6_hdr;
struct test_rte_flow_item_icmp6_nd_ns *icmp_hdr;
eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
_generate_requested_multicast_mac(dst_ipv6_addr, eth_hdr);
rte_memcpy(ð_hdr->s_addr, &responser.src_mac_addr, sizeof(struct rte_ether_addr));
eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
ipv6_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
_generate_requested_multicast_ipv6(dst_ipv6_addr, ipv6_hdr);
rte_memcpy(&ipv6_hdr->src_addr, &responser.src_ipv6_addr, IPV6_ADDR_LEN);
ipv6_hdr->vtc_flow = htonl(0x60000000);
ipv6_hdr->payload_len = htons(sizeof(struct test_rte_flow_item_icmp6_nd_ns));
ipv6_hdr->proto = IPPROTO_ICMPV6;
ipv6_hdr->hop_limits = 0xff;
icmp_hdr = (struct test_rte_flow_item_icmp6_nd_ns *)(ipv6_hdr + 1);
icmp_hdr->type = ICMPV6_NEIGHBOR_SOLICIATION;
icmp_hdr->code = 0;
icmp_hdr->checksum = 0;
icmp_hdr->reserved = 0;
rte_memcpy(&icmp_hdr->target_addr, dst_ipv6_addr, sizeof(icmp_hdr->target_addr));
icmp_hdr->op_type = ICMPV6_ND_OPT_SOURCE_LINKADDR;
icmp_hdr->length = 1; //1 == 8 bytes
rte_memcpy(&icmp_hdr->sla, &responser.src_mac_addr, sizeof(struct rte_ether_addr));
icmp_hdr->checksum =htons(calculate_icmpv6_checksum(icmp_hdr, ipv6_hdr->src_addr, ipv6_hdr->dst_addr,
sizeof(struct test_rte_flow_item_icmp6_nd_ns)));
pkt->pkt_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv6_hdr)
+ sizeof(struct test_rte_flow_item_icmp6_nd_ns);
pkt->data_len = pkt->pkt_len;
}
这里需要注意,ICMPV6 NEIGHBOR SOLICIATION
类似于IPV4中的ARP REQUEST,这时候并不知道对端mac,所以需要封装组播mac,不同于V4,这里的目的ip也需要封装为组播ip(按理说不需要封装也行,但是我的环境中不封装无法收到NA报文)
1.1 封装 NEIGHBOR SOLICIATION 组播mac
static void _generate_requested_multicast_mac(const uint8_t *dst_ipv6_addr, struct rte_ether_hdr *eth_hdr)
{
if (!dst_ipv6_addr || !eth_hdr) {
RESPONSE_ERROR("_generate_requested_multicast_mac fail. \n");
return;
}
const uint8_t fixed_mac_prefix[] = {0x33, 0x33, 0xff};
uint32_t last_32_bits = (dst_ipv6_addr[12] << 24) | (dst_ipv6_addr[13] << 16) | (dst_ipv6_addr[14] << 8) | dst_ipv6_addr[15];
uint8_t last_24_bits[3];
last_24_bits[0] = (last_32_bits >> 16) & 0xFF;
last_24_bits[1] = (last_32_bits >> 8) & 0xFF;
last_24_bits[2] = last_32_bits & 0xFF;
rte_memcpy(eth_hdr->d_addr.addr_bytes, fixed_mac_prefix, 3);
rte_memcpy(eth_hdr->d_addr.addr_bytes + 3, last_24_bits, 3);
}
1.2 封装 NEIGHBOR SOLICIATION 组播ip
static void _generate_requested_multicast_ipv6(const uint8_t *dst_ipv6_addr, struct rte_ipv6_hdr *ipv6_hdr)
{
if (!dst_ipv6_addr | !ipv6_hdr) {
RESPONSE_ERROR("_generate_requested_multicast_ipv6 fail. \n");
return;
}
ipv6_hdr->dst_addr[0] = 0xFF;
ipv6_hdr->dst_addr[1] = 0x02;
ipv6_hdr->dst_addr[2] = 0x00;
ipv6_hdr->dst_addr[3] = 0x00;
ipv6_hdr->dst_addr[4] = 0x00;
ipv6_hdr->dst_addr[5] = 0x00;
ipv6_hdr->dst_addr[6] = 0x00;
ipv6_hdr->dst_addr[7] = 0x00;
ipv6_hdr->dst_addr[8] = 0x00;
ipv6_hdr->dst_addr[9] = 0x00;
ipv6_hdr->dst_addr[10] = 0x00;
ipv6_hdr->dst_addr[11] = 0x01;
ipv6_hdr->dst_addr[12] = 0xFF;
ipv6_hdr->dst_addr[13] = dst_ipv6_addr[13];
ipv6_hdr->dst_addr[14] = dst_ipv6_addr[14];
ipv6_hdr->dst_addr[15] = dst_ipv6_addr[15];
}
2 发送ICMPV6 NEIGHBOR ADVERTISEMENT数据包
struct rte_flow_item_icmp6_nd_na {
uint8_t type; /**< ICMPv6 type, normally 136. */
uint8_t code; /**< ICMPv6 code, normally 0. */
rte_be16_t checksum; /**< ICMPv6 checksum. */
/**
* Route flag (1b), solicited flag (1b), override flag (1b),
* reserved (29b).
*/
rte_be32_t rso_reserved;
uint8_t target_addr[16]; /**< Target address. */
};
static void _response_icmpv6_NS(struct rte_mbuf *new_pkt, struct rte_mbuf *old_pkt)
{
struct rte_ether_hdr *eth_hdr_old = rte_pktmbuf_mtod(old_pkt, struct rte_ether_hdr *);
struct rte_ipv6_hdr *ipv6_hdr_old = rte_pktmbuf_mtod_offset(old_pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
struct rte_flow_item_icmp6_nd_ns *icmpv6_ns = (struct rte_flow_item_icmp6_nd_ns *)(ipv6_hdr_old + 1);
struct rte_ether_hdr *eth_hdr_new = rte_pktmbuf_mtod(new_pkt, struct rte_ether_hdr *);
rte_memcpy(ð_hdr_new->d_addr, ð_hdr_old->s_addr, sizeof(struct rte_ether_addr));
rte_memcpy(ð_hdr_new->s_addr, &responser.src_mac_addr, sizeof(struct rte_ether_addr));
eth_hdr_new->ether_type = htons(0x86DD);
struct rte_ipv6_hdr *ipv6_hdr_new = rte_pktmbuf_mtod_offset(new_pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
rte_memcpy(&ipv6_hdr_new->dst_addr, &responser.dst_ipv6_addr, sizeof(struct in6_addr));
rte_memcpy(&ipv6_hdr_new->src_addr, &responser.src_ipv6_addr, sizeof(struct in6_addr));
ipv6_hdr_new->vtc_flow = htonl(0x60000000);
ipv6_hdr_new->payload_len = htons(sizeof(struct rte_flow_item_icmp6_nd_na));
ipv6_hdr_new->proto = ipv6_hdr_old->proto;
ipv6_hdr_new->hop_limits = 0xff;
struct rte_flow_item_icmp6_nd_na *icmp_hdr_new = (struct rte_flow_item_icmp6_nd_na *)(ipv6_hdr_new + 1);
icmp_hdr_new->type = ICMPV6_NEIGHBOR_ADVERTISEMENT;
icmp_hdr_new->code = 0;
icmp_hdr_new->checksum = 0;
icmp_hdr_new->rso_reserved = rte_cpu_to_be_32(0x60000000);
rte_memcpy(&icmp_hdr_new->target_addr, &ipv6_hdr_old->src_addr, sizeof(struct in6_addr));
icmp_hdr_new->checksum =htons(calculate_icmpv6_checksum((uint8_t *)icmp_hdr_new,ipv6_hdr_new->src_addr,
ipv6_hdr_new->dst_addr,sizeof(struct rte_flow_item_icmp6_nd_na)));
new_pkt->pkt_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv6_hdr) + sizeof(struct rte_flow_item_icmp6_nd_na);
new_pkt->data_len = new_pkt->pkt_len;
RESPONSE_DEBUG("encapsulation ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT success \n");
}
3 发送ICMPV6_ECHO_REQUEST数据包
struct test_rte_icmp_hdr {
uint8_t icmp_type; /* ICMP packet type. */
uint8_t icmp_code; /* ICMP packet code. */
rte_be16_t icmp_cksum; /* ICMP packet checksum. */
rte_be16_t icmp_ident; /* ICMP packet identifier. */
rte_be16_t icmp_seq_nb; /* ICMP packet sequence number. */
uint8_t data[ICMPV6_REQUEST_DATA_LEN];
} __attribute__((__packed__));
static _create_icmpv6_request(struct rte_mbuf *pkt, uint8_t *dst_ipv6_addr, uint16_t identifier)
{
static uint16_t seq_num = 0;
struct rte_ether_hdr *eth_hdr;
struct rte_ipv6_hdr *ipv6_hdr;
struct test_rte_icmp_hdr *icmp_hdr;
struct ipv6_mac_entry result_entry;
eth_hdr = rte_pktmbuf_mtod(pkt, struct rte_ether_hdr *);
//_generate_requested_multicast_mac(dst_ipv6_addr, eth_hdr);
get_mac_by_ipv6(dst_ipv6_addr, &result_entry);
rte_memcpy(ð_hdr->d_addr, &result_entry.mac_address, MAC_ADDR_LEN);
rte_memcpy(ð_hdr->s_addr, &responser.src_mac_addr, MAC_ADDR_LEN);
eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV6);
ipv6_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
rte_memcpy(&ipv6_hdr->dst_addr, dst_ipv6_addr, IPV6_ADDR_LEN);
rte_memcpy(&ipv6_hdr->src_addr, &responser.src_ipv6_addr, IPV6_ADDR_LEN);
ipv6_hdr->vtc_flow = htonl(0x60000000);
ipv6_hdr->payload_len = htons(sizeof(struct test_rte_icmp_hdr));
ipv6_hdr->proto = IPPROTO_ICMPV6;
ipv6_hdr->hop_limits = 0xff;
icmp_hdr = (struct test_rte_icmp_hdr *)(ipv6_hdr + 1);
icmp_hdr->icmp_type = ICMPV6_ECHO_REQUEST;
icmp_hdr->icmp_code = 0;
icmp_hdr->icmp_cksum = 0;
icmp_hdr->icmp_ident = htons(identifier);
icmp_hdr->icmp_seq_nb = htons(((seq_num++) % 4) + 1);
memset(&icmp_hdr->data, 1, ICMPV6_REQUEST_DATA_LEN);
icmp_hdr->icmp_cksum =htons(calculate_icmpv6_checksum(icmp_hdr, ipv6_hdr->src_addr, ipv6_hdr->dst_addr,
sizeof(struct test_rte_icmp_hdr)));
pkt->pkt_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv6_hdr) + sizeof(struct test_rte_icmp_hdr);
pkt->data_len = pkt->pkt_len;
}
4 发送ICMPV6_ECHO_REPLY数据包
static void _response_icmpv6_request(struct rte_mbuf *new_pkt, struct rte_mbuf *old_pkt)
{
struct rte_ether_hdr *eth_hdr_old = rte_pktmbuf_mtod(old_pkt, struct rte_ether_hdr *);
struct rte_ipv6_hdr *ipv6_hdr_old = rte_pktmbuf_mtod_offset(old_pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
struct test_rte_icmp_hdr *icmp_hdr_old = (struct test_rte_icmp_hdr *)(ipv6_hdr_old + 1);
struct rte_ether_hdr *eth_hdr_new = rte_pktmbuf_mtod(new_pkt, struct rte_ether_hdr *);
rte_memcpy(ð_hdr_new->d_addr, ð_hdr_old->s_addr, sizeof(struct rte_ether_addr));
rte_memcpy(ð_hdr_new->s_addr, ð_hdr_old->d_addr, sizeof(struct rte_ether_addr));
eth_hdr_new->ether_type = htons(0x86DD);
struct rte_ipv6_hdr *ipv6_hdr_new = rte_pktmbuf_mtod_offset(new_pkt, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
rte_memcpy(&ipv6_hdr_new->dst_addr, &ipv6_hdr_old->src_addr, sizeof(struct in6_addr));
rte_memcpy(&ipv6_hdr_new->src_addr, &ipv6_hdr_old->dst_addr, sizeof(struct in6_addr));
ipv6_hdr_new->vtc_flow = htonl(0x60000000);
ipv6_hdr_new->payload_len = htons(sizeof(struct test_rte_icmp_hdr));
ipv6_hdr_new->proto = ipv6_hdr_old->proto;
ipv6_hdr_new->hop_limits = 0xff;
struct test_rte_icmp_hdr *icmp_hdr_new = (struct test_rte_icmp_hdr *)(ipv6_hdr_new + 1);
icmp_hdr_new->icmp_type = ICMPV6_ECHO_REPLY;
icmp_hdr_new->icmp_code = 0;
icmp_hdr_new->icmp_ident = htons(icmp_hdr_old->icmp_ident);
icmp_hdr_new->icmp_seq_nb = htons(icmp_hdr_old->icmp_seq_nb);
icmp_hdr_new->icmp_cksum = 0;
rte_memcpy(&icmp_hdr_new->data, &icmp_hdr_old->data, ICMPV6_REQUEST_DATA_LEN);
icmp_hdr_new->icmp_cksum = htons(calculate_icmpv6_checksum((uint8_t *)icmp_hdr_new, ipv6_hdr_new->src_addr,
ipv6_hdr_new->dst_addr, sizeof(struct test_rte_icmp_hdr)));
new_pkt->pkt_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv6_hdr) + sizeof(struct test_rte_icmp_hdr);
new_pkt->data_len = new_pkt->pkt_len;
RESPONSE_DEBUG("encapsulation ICMPV6_ECHO_REPLY success \n");
}
5 icmpv6检验和计算
这里也是踩了坑,icmpv6的校验和计算,需要自己封装一个伪报头,这个需要注意,建议写的时候先用wireshak抓个icmpv6的报文,然后用报文中的数据计算出检验和看是否能对上,如果对不上再挨着将数据打印出来检查
struct ipv6_pseudo_header {
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint32_t length;
uint8_t zero[3];
uint8_t next_header;
};
// 计算 ICMPv6 校验和的函数
uint16_t calculate_icmpv6_checksum(uint8_t *icmp_hdr, const uint8_t *src_addr, const uint8_t *dst_addr, uint32_t length)
{
uint32_t sum = 0;
size_t i;
const uint16_t *pseudo_ptr;
const uint16_t *icmp_ptr;
// 创建 IPv6 伪头部
struct ipv6_pseudo_header pseudo_header = {
.next_header = 0x3a,
.length = rte_cpu_to_be_32(length),
.zero = {0}
};
// 拷贝源地址和目标地址到伪头部
rte_memcpy(pseudo_header.src_addr, src_addr, 16);
rte_memcpy(pseudo_header.dst_addr, dst_addr, 16);
// 计算伪头部的校验和
pseudo_ptr = (const uint16_t *)&pseudo_header;
for (i = 0; i < sizeof(struct ipv6_pseudo_header) / 2; ++i) {
sum += rte_be_to_cpu_16(pseudo_ptr[i]);
}
// 计算 ICMPv6 报文的校验和
icmp_ptr = (const uint16_t *)icmp_hdr;
for (i = 0; i < length / 2; ++i) {
sum += rte_be_to_cpu_16(icmp_ptr[i]);
}
// 处理奇数长度的情况
if (length % 2 == 1) {
uint16_t last_byte = icmp_hdr[length - 1] << 8;
sum += rte_be_to_cpu_16(last_byte);
}
// 将进位加到低 16 位
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
// 取反得到校验和
return (uint16_t)(~sum);
}