ICMPV6遇到的坑

关于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(&eth_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(&eth_hdr_new->d_addr, &eth_hdr_old->s_addr, sizeof(struct rte_ether_addr));
    rte_memcpy(&eth_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(&eth_hdr->d_addr, &result_entry.mac_address, MAC_ADDR_LEN); 
    rte_memcpy(&eth_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(&eth_hdr_new->d_addr, &eth_hdr_old->s_addr, sizeof(struct rte_ether_addr)); 
    rte_memcpy(&eth_hdr_new->s_addr, &eth_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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写一封情书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值