* How to calculate ICMPv6 checksum?
@ 2025-08-07 15:32 Gábor LENCSE
2025-08-07 17:57 ` Stephen Hemminger
0 siblings, 1 reply; 3+ messages in thread
From: Gábor LENCSE @ 2025-08-07 15:32 UTC (permalink / raw)
To: users
Dear All,
I am working on adding ARP/NDP support to my SIIT / Stateful NAT64
benchmarking tool, siitperf [1]. (So far, the ARP / NDP table entries
had to be set manually at the device under test, as siitperf was not
able to reply to ARP / NDP requests).
The ARP reply functionality seems to work fine, but I have a problem
with NDP. As ICMPv6 messages contain checksum, I would need a function
that computes it. However, I only found the rte_ipv6_udptcp_cksum()
function, but I did not find a similar one for calculating ICMPv6 checksum.
I have been checking the functions shown here:
https://doc.dpdk.org/api/rte__ip6_8h.html
Could you please advise me about the function to use for ICMPv6 checksum
calculation?
Best regards,
Gábor
[1] https://github.com/lencsegabor/siitperf
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: How to calculate ICMPv6 checksum?
2025-08-07 15:32 How to calculate ICMPv6 checksum? Gábor LENCSE
@ 2025-08-07 17:57 ` Stephen Hemminger
2025-08-08 18:56 ` Gábor LENCSE
0 siblings, 1 reply; 3+ messages in thread
From: Stephen Hemminger @ 2025-08-07 17:57 UTC (permalink / raw)
To: Gábor LENCSE; +Cc: users
On Thu, 7 Aug 2025 17:32:02 +0200
Gábor LENCSE <lencse@hit.bme.hu> wrote:
> Dear All,
>
> I am working on adding ARP/NDP support to my SIIT / Stateful NAT64
> benchmarking tool, siitperf [1]. (So far, the ARP / NDP table entries
> had to be set manually at the device under test, as siitperf was not
> able to reply to ARP / NDP requests).
>
> The ARP reply functionality seems to work fine, but I have a problem
> with NDP. As ICMPv6 messages contain checksum, I would need a function
> that computes it. However, I only found the rte_ipv6_udptcp_cksum()
> function, but I did not find a similar one for calculating ICMPv6 checksum.
>
> I have been checking the functions shown here:
> https://doc.dpdk.org/api/rte__ip6_8h.html
>
> Could you please advise me about the function to use for ICMPv6 checksum
> calculation?
>
> Best regards,
>
> Gábor
>
> [1] https://github.com/lencsegabor/siitperf
The pseudo-header part is different.
https://www.rfc-editor.org/rfc/rfc4443
2.3. Message Checksum Calculation
The checksum is the 16-bit one's complement of the one's complement
sum of the entire ICMPv6 message, starting with the ICMPv6 message
type field, and prepended with a "pseudo-header" of IPv6 header
fields, as specified in [IPv6, Section 8.1]. The Next Header value
used in the pseudo-header is 58. (The inclusion of a pseudo-header
in the ICMPv6 checksum is a change from IPv4; see [IPv6] for the
rationale for this change.)
For computing the checksum, the checksum field is first set to zero.
https://www.rfc-editor.org/rfc/rfc2460#section-8.1
8.1 Upper-Layer Checksums
Any transport or other upper-layer protocol that includes the
addresses from the IP header in its checksum computation must be
modified for use over IPv6, to include the 128-bit IPv6 addresses
instead of 32-bit IPv4 addresses. In particular, the following
illustration shows the TCP and UDP "pseudo-header" for IPv6:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Upper-Layer Packet Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero | Next Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
o If the IPv6 packet contains a Routing header, the Destination
Address used in the pseudo-header is that of the final
destination. At the originating node, that address will be in
the last element of the Routing header; at the recipient(s),
that address will be in the Destination Address field of the
IPv6 header.
o The Next Header value in the pseudo-header identifies the
upper-layer protocol (e.g., 6 for TCP, or 17 for UDP). It will
differ from the Next Header value in the IPv6 header if there
are extension headers between the IPv6 header and the upper-
layer header.
o The Upper-Layer Packet Length in the pseudo-header is the
length of the upper-layer header and data (e.g., TCP header
plus TCP data). Some upper-layer protocols carry their own
length information (e.g., the Length field in the UDP header);
for such protocols, that is the length used in the pseudo-
header. Other protocols (such as TCP) do not carry their own
length information, in which case the length used in the
pseudo-header is the Payload Length from the IPv6 header, minus
the length of any extension headers present between the IPv6
header and the upper-layer header.
o Unlike IPv4, when UDP packets are originated by an IPv6 node,
the UDP checksum is not optional. That is, whenever
originating a UDP packet, an IPv6 node must compute a UDP
checksum over the packet and the pseudo-header, and, if that
computation yields a result of zero, it must be changed to hex
FFFF for placement in the UDP header. IPv6 receivers must
discard UDP packets containing a zero checksum, and should log
the error.
The IPv6 version of ICMP [ICMPv6] includes the above pseudo-header in
its checksum computation; this is a change from the IPv4 version of
ICMP, which does not include a pseudo-header in its checksum. The
reason for the change is to protect ICMP from misdelivery or
corruption of those fields of the IPv6 header on which it depends,
which, unlike IPv4, are not covered by an internet-layer checksum.
The Next Header field in the pseudo-header for ICMP contains the
value 58, which identifies the IPv6 version of ICMP.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: How to calculate ICMPv6 checksum?
2025-08-07 17:57 ` Stephen Hemminger
@ 2025-08-08 18:56 ` Gábor LENCSE
0 siblings, 0 replies; 3+ messages in thread
From: Gábor LENCSE @ 2025-08-08 18:56 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: users
[-- Attachment #1: Type: text/plain, Size: 8121 bytes --]
Dear Stephen,
Thank you very much for your answer. It helps me a lot, but I have
further questions. Please see my comments inline.
> The pseudo-header part is different.
If I understand it correctly, then it means that I need to write the
ICMPv6 checksum function myself. To that end, I reviewed the source code
of the "rte_ipv6_udptcp_cksum()" function so that I can learn from it.
However, I did not find where it differs from the one that I need. I
took the below source code from here:
https://doc.dpdk.org/api/rte__ip6_8h_source.html#l00610
rte_ipv6_udptcp_cksum(const struct rte_ipv6_hdr *ipv6_hdr, const void
*l4_hdr) { uint16_t cksum = __rte_ipv6_udptcp_cksum(ipv6_hdr, l4_hdr);
cksum = ~cksum; /* * Per RFC 768: If the computed checksum is zero for
UDP, * it is transmitted as all ones * (the equivalent in one's
complement arithmetic). */ if (cksum == 0 && ipv6_hdr->proto ==
IPPROTO_UDP) cksum = 0xffff; return cksum; } It is the highest level. It
calls an internal function and at the end it considers the protocol
number (with other words, the next header field of the IPv6 header) when
it handles UDP specific things, thus I think that this time it does not
cause any problem in the case of ICMPv6.
This is the source code of the internal function:
static inline uint16_t
__rte_ipv6_udptcp_cksum(const struct rte_ipv6_hdr *ipv6_hdr, const void
*l4_hdr)
{
uint32_t cksum;
uint32_t l4_len;
l4_len = rte_be_to_cpu_16(ipv6_hdr->payload_len);
cksum = rte_raw_cksum(l4_hdr, l4_len);
cksum += rte_ipv6_phdr_cksum(ipv6_hdr, 0);
cksum = ((cksum & 0xffff0000) >> 16) + (cksum & 0xffff);
return (uint16_t)cksum;
}
It calculates the checksum for the L4 part and also for the
pseudo-header separately. The latter could be different than what I need
for ICMPv6.
I also checked the source code of "rte_ipv6_phdr_cksum(ipv6_hdr, 0)",
please see it below the figure from RFC 2460.
> https://www.rfc-editor.org/rfc/rfc4443
>
> 2.3. Message Checksum Calculation
>
> The checksum is the 16-bit one's complement of the one's complement
> sum of the entire ICMPv6 message, starting with the ICMPv6 message
> type field, and prepended with a "pseudo-header" of IPv6 header
> fields, as specified in [IPv6, Section 8.1]. The Next Header value
> used in the pseudo-header is 58. (The inclusion of a pseudo-header
> in the ICMPv6 checksum is a change from IPv4; see [IPv6] for the
> rationale for this change.)
>
> For computing the checksum, the checksum field is first set to zero.
>
> https://www.rfc-editor.org/rfc/rfc2460#section-8.1
>
> 8.1 Upper-Layer Checksums
>
> Any transport or other upper-layer protocol that includes the
> addresses from the IP header in its checksum computation must be
> modified for use over IPv6, to include the 128-bit IPv6 addresses
> instead of 32-bit IPv4 addresses. In particular, the following
> illustration shows the TCP and UDP "pseudo-header" for IPv6:
>
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> | |
> + +
> | |
> + Source Address +
> | |
> + +
> | |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> | |
> + +
> | |
> + Destination Address +
> | |
> + +
> | |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> | Upper-Layer Packet Length |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> | zero | Next Header |
> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
So this is what I need. And it seems to me, that the below source code
does exactly the same:
static inline uint16_t
rte_ipv6_phdr_cksum(const struct rte_ipv6_hdr *ipv6_hdr, uint64_t ol_flags)
{
uint32_t sum;
struct {
rte_be32_t len; /* L4 length. */
rte_be32_t proto; /* L4 protocol - top 3 bytes must be zero */
} psd_hdr;
psd_hdr.proto = (uint32_t)(ipv6_hdr->proto << 24);
if (ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG))
psd_hdr.len = 0;
else
psd_hdr.len = ipv6_hdr->payload_len;
sum = __rte_raw_cksum(&ipv6_hdr->src_addr,
sizeof(ipv6_hdr->src_addr) + sizeof(ipv6_hdr->dst_addr),
0);
sum = __rte_raw_cksum(&psd_hdr, sizeof(psd_hdr), sum);
return __rte_raw_cksum_reduce(sum);
}
As required, it handles length field on 32 bits, and shifts the protocol
field (containing the value of 58) to the left by 24 bit, which means
the same as the "next header" field is at the topmost 8 bits of a 32 bit
number in the drawing.
Then it does a "trick" that it uses the source and destination IPv6
addresses from the IPv6 packet (likely to spare their copying).
Thus, I did not find anything what I would need to do differently.
However, on the other hand, _there should be something_, because I tried
using the "rte_ipv6_udptcp_cksum()" function (of course, I set the
checksum field to 0 before using it), but Wireshark said that the
checksum was incorrect. Both tshark and Wireshark decodes my NA message
perfectly, but the Linux kernel of the device under test does not accept
it, this is why it sends further NS messages.
This is a tshark capture on the device under test:
root@dut:~# tshark -i eno1
Running as user "root" and group "root". This could be dangerous.
Capturing on 'eno1'
1 0.000000000 fe80::baca:3aff:fe5e:25a8 → ff02::16 ICMPv6 170
Multicast Listener Report Message v2
2 0.379986848 fe80::baca:3aff:fe5e:25a8 → ff02::16 ICMPv6 170
Multicast Listener Report Message v2
3 4.156047617 2001:2::2 → 2001:2:0:8000::2 UDP 80 58488 → 27971
Len=18
4 4.156066982 fe80::baca:3aff:fe5e:25a8 → ff02::1:ff00:2 ICMPv6 86
Neighbor Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
5 4.156092949 2001:2::2 → fe80::baca:3aff:fe5e:25a8 ICMPv6 86
Neighbor Advertisement 2001:2::2 (ovr) is at 24:6e:96:3c:3f:40
6 5.183987802 fe80::baca:3aff:fe5e:25a8 → ff02::1:ff00:2 ICMPv6 86
Neighbor Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
7 5.184007499 2001:2::2 → fe80::baca:3aff:fe5e:25a8 ICMPv6 86
Neighbor Advertisement 2001:2::2 (ovr) is at 24:6e:96:3c:3f:40
8 6.203987286 fe80::baca:3aff:fe5e:25a8 → ff02::1:ff00:2 ICMPv6 86
Neighbor Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
9 6.204007429 2001:2::2 → fe80::baca:3aff:fe5e:25a8 ICMPv6 86
Neighbor Advertisement 2001:2::2 (ovr) is at 24:6e:96:3c:3f:40
10 7.232005250 2001:2::1 → ff02::1:ff00:2 ICMPv6 86 Neighbor
Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
11 8.251987771 fe80::baca:3aff:fe5e:25a8 → ff02::1:ff00:2 ICMPv6 86
Neighbor Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
12 9.275986860 fe80::baca:3aff:fe5e:25a8 → ff02::1:ff00:2 ICMPv6 86
Neighbor Solicitation for 2001:2::2 from b8:ca:3a:5e:25:a8
And Wireshark says: "Checksum: 0x1baf incorrect, should be 0x035d".
Could you please advise me, what I could overlook?
Best regards,
Gábor
[-- Attachment #2: Type: text/html, Size: 10310 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-08-08 18:56 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-07 15:32 How to calculate ICMPv6 checksum? Gábor LENCSE
2025-08-07 17:57 ` Stephen Hemminger
2025-08-08 18:56 ` Gábor LENCSE
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).