* [PATCH] net: revert add IPv6 traffic class and flow label fields
@ 2026-09-22 16:36 Morten Brørup
2026-09-22 18:14 ` Stephen Hemminger
2026-09-29 7:36 ` [PATCH v2] " Morten Brørup
0 siblings, 2 replies; 5+ messages in thread
From: Morten Brørup @ 2026-09-22 16:36 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, stable, Maxime Leroy
The IPv6 header bitfields "version", "ds", "ecn", and "flow_label"
are not organized correctly on little endian architectures.
Reverted the patch introducing them.
The core problem is little endian's wrapping of the "ds" field in the
IPv6 header when the field crosses a byte border.
Let's consider a simplified struct for illustration:
struct example {
union {
rte_be32_t vtc_flow;
struct {
uint32_t version:4;
uint32_t ds:6;
uint32_t after:22;
uint32_t after:22;
uint32_t ds:6;
uint32_t version:4;
};
};
};
struct example e;
e.vtc_flow = 0x00000000;
e.ds = 0x3F; // binary: 111111
With big endian:
Value of e: 0000 111111 0000000000000000000000 = 0F C0 00 00
Memory at e's location: 0F C0 00 00
As expected!
With little endian:
Memory at e's location: 00 00 C0 0F
The reason being that the bytes are filled with bits starting with the
LSB, so when crossing a byte border, the "ds" field doesn't continue at
the following bits, i.e. the MSB of the next byte, but at the four LSB
of the next byte.
This wrapping cannot even be fixed by having separate, dedicated structs
for each field (with each their own "before" and "after" filler fields)
in the vtc_flow union.
There is a GCC attribute to fix this, but nothing similar is offered by
Clang or MSVC:
__attribute__((scalar_storage_order("big-endian")))
Bugzilla ID: 1679
Fixes: cba27998dc81 ("net: add IPv6 traffic class and flow label fields")
Cc: stable@dpdk.org
Reported-by: Maxime Leroy <maxime@leroys.fr>
Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
---
doc/guides/rel_notes/release_26_11.rst | 3 +++
lib/net/rte_ip6.h | 18 +-----------------
2 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 4b3e5d995c..6b412ffaa6 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -79,6 +79,9 @@ Removed Items
``rte_rib6_is_equal``
* table: ``RTE_LPM_IPV6_ADDR_SIZE``
+* Removed defect bitfields in IPv6 header (``struct rte_ipv6_hdr``):
+ ``version``, ``ds``, ``ecn``, ``flow_label``
+
API Changes
-----------
diff --git a/lib/net/rte_ip6.h b/lib/net/rte_ip6.h
index d1abf1f5d5..25be328955 100644
--- a/lib/net/rte_ip6.h
+++ b/lib/net/rte_ip6.h
@@ -467,23 +467,7 @@ rte_ether_mcast_from_ipv6(struct rte_ether_addr *mac, const struct rte_ipv6_addr
* IPv6 Header
*/
struct __rte_aligned(2) __rte_packed_begin rte_ipv6_hdr {
- union {
- rte_be32_t vtc_flow; /**< IP version, traffic class & flow label. */
- __extension__
- struct {
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
- uint32_t flow_label:20; /**< Flow label */
- uint32_t ecn:2; /**< ECN */
- uint32_t ds:6; /**< Differentiated services */
- uint32_t version:4; /**< Version */
-#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN
- uint32_t version:4; /**< Version */
- uint32_t ds:6; /**< Differentiated services */
- uint32_t ecn:2; /**< ECN */
- uint32_t flow_label:20; /**< Flow label */
-#endif
- };
- };
+ rte_be32_t vtc_flow; /**< IP version, traffic class & flow label. */
rte_be16_t payload_len; /**< IP payload size, including ext. headers */
uint8_t proto; /**< Protocol, next header. */
uint8_t hop_limits; /**< Hop limits. */
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] net: revert add IPv6 traffic class and flow label fields
2026-09-22 16:36 [PATCH] net: revert add IPv6 traffic class and flow label fields Morten Brørup
@ 2026-09-22 18:14 ` Stephen Hemminger
2026-09-28 14:15 ` Thomas Monjalon
2026-09-29 7:36 ` [PATCH v2] " Morten Brørup
1 sibling, 1 reply; 5+ messages in thread
From: Stephen Hemminger @ 2026-09-22 18:14 UTC (permalink / raw)
To: Morten Brørup; +Cc: dev, stable, Maxime Leroy
On Tue, 22 Sep 2026 16:36:40 +0000
Morten Brørup <mb@smartsharesystems.com> wrote:
> IPv6 header when the field crosses a byte border.
>
> Let's consider a simplified struct for illustration:
>
> struct example {
> union {
> rte_be32_t vtc_flow;
> struct {
> uint32_t version:4;
> uint32_t ds:6;
> uint32_t after:22;
> uint32_t after:22;
> uint32_t ds:6;
> uint32_t version:4;
> };
> };
> };
>
> struct example e;
> e.vtc_flow = 0x00000000;
> e.ds = 0x3F; // binary: 111111
>
> With big endian:
> Value of e: 0000 111111 0000000000000000000000 = 0F C0 00 00
> Memory at e's location: 0F C0 00 00
> As expected!
>
> With little endian:
> Memory at e's location: 00 00 C0 0F
> The reason being that the bytes are filled with bits starting with the
> LSB, so when crossing a byte border, the "ds" field doesn't continue at
> the following bits, i.e. the MSB of the next byte, but at the four LSB
> of the next byte.
>
> This wrapping cannot even be fixed by having separate, dedicated structs
> for each field (with each their own "before" and "after" filler fields)
> in the vtc_flow union.
>
> There is a GCC attribute to fix this, but nothing similar is offered by
> Clang or MSVC:
> __attribute__((scalar_storage_order("big-endian")))
>
> Bugzilla ID: 1679
> Fixes: cba27998dc81 ("net: add IPv6 traffic class and flow label fields")
> Cc: stable@dpdk.org
>
> Reported-by: Maxime Leroy <maxime@leroys.fr>
> Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
> ---
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Long form AI review. This may need addressing before merge.
The revert is correct and complete. cba27998dc81 only touched
rte_ip6.h, there are no in-tree users of the removed fields, and the
struct layout is unchanged so there is no ABI impact.
Keeping Cc: stable is right. The fields shipped in 24.11 and 25.11
but access the wrong bits on every little endian target, so any
direct user is already broken and a build failure is the better
outcome. Please add a note for the stable release notes pointing to
vtc_flow and the RTE_IPV6_HDR_*_MASK macros. Code that byte swaps
vtc_flow in place before using the fields does work on little endian
today and will stop compiling.
The commit message needs rework before this goes in.
The example struct does not compile: after, ds and version are each
declared twice with no #if between the two orderings. Since it is
the justification for the revert, either fix it or drop it and show
the actual result.
The root cause is understated. It is not only ds crossing a byte
boundary; every field is wrong on little endian, including version.
A uint32_t bitfield is allocated against the host order value of
the word, while vtc_flow holds network order. The little endian
layout is only correct after byte swapping vtc_flow.
On x86_64 with the removed layout:
header bytes 6b 91 23 45 (ver 6, DSCP EF, ECT(1), fl 0x12345)
-> version=4 ds=0x14 ecn=2 flow_label=0x3916b
version = 6 -> bytes 00 00 00 60
ds = 0x3f -> bytes 00 00 c0 0f
Suggested wording:
On little endian, a 32-bit bitfield is allocated from the least
significant bit of the host order word, but vtc_flow is stored in
network order. All four fields therefore read and write the wrong
bits. For example, setting version to 6 writes 0x60 into the last
byte of the word.
ds and flow_label are not contiguous in memory on little endian,
so no reordering of bitfields can express them.
For reference, neither Linux nor FreeBSD tries this. Linux uses a u8
bitfield only for version and the upper nibble of traffic class,
then flow_lbl[3], with big endian masks (IPV6_FLOWINFO_MASK,
IPV6_FLOWLABEL_MASK) for the rest. FreeBSD has no bitfields: a raw
ip6_flow word, the ip6_vfc byte for version, and per byte order
masks. DPDK already has the equivalent in rte_ipv6_check_version()
and RTE_IPV6_HDR_*_MASK, so nothing is lost.
Release note: "defect bitfields" reads oddly and gives no migration
path. Suggest:
* net: Removed ``version``, ``ds``, ``ecn`` and ``flow_label``
bitfields from ``struct rte_ipv6_hdr``. They accessed the wrong
bits on little endian. Use ``vtc_flow`` with the
``RTE_IPV6_HDR_*_MASK`` and ``RTE_IPV6_HDR_*_SHIFT`` macros.
Minor: "Reverted the patch introducing them." should be imperative,
"Revert the patch that introduced them."
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] net: revert add IPv6 traffic class and flow label fields
2026-09-22 18:14 ` Stephen Hemminger
@ 2026-09-28 14:15 ` Thomas Monjalon
2026-09-28 14:20 ` Morten Brørup
0 siblings, 1 reply; 5+ messages in thread
From: Thomas Monjalon @ 2026-09-28 14:15 UTC (permalink / raw)
To: Morten Brørup; +Cc: dev, stable, Maxime Leroy, Stephen Hemminger
Hello, are we waiting for a v2?
22/09/2026 20:14, Stephen Hemminger:
> On Tue, 22 Sep 2026 16:36:40 +0000
> Morten Brørup <mb@smartsharesystems.com> wrote:
>
> > IPv6 header when the field crosses a byte border.
> >
> > Let's consider a simplified struct for illustration:
> >
> > struct example {
> > union {
> > rte_be32_t vtc_flow;
> > struct {
> > uint32_t version:4;
> > uint32_t ds:6;
> > uint32_t after:22;
> > uint32_t after:22;
> > uint32_t ds:6;
> > uint32_t version:4;
> > };
> > };
> > };
> >
> > struct example e;
> > e.vtc_flow = 0x00000000;
> > e.ds = 0x3F; // binary: 111111
> >
> > With big endian:
> > Value of e: 0000 111111 0000000000000000000000 = 0F C0 00 00
> > Memory at e's location: 0F C0 00 00
> > As expected!
> >
> > With little endian:
> > Memory at e's location: 00 00 C0 0F
> > The reason being that the bytes are filled with bits starting with the
> > LSB, so when crossing a byte border, the "ds" field doesn't continue at
> > the following bits, i.e. the MSB of the next byte, but at the four LSB
> > of the next byte.
> >
> > This wrapping cannot even be fixed by having separate, dedicated structs
> > for each field (with each their own "before" and "after" filler fields)
> > in the vtc_flow union.
> >
> > There is a GCC attribute to fix this, but nothing similar is offered by
> > Clang or MSVC:
> > __attribute__((scalar_storage_order("big-endian")))
> >
> > Bugzilla ID: 1679
> > Fixes: cba27998dc81 ("net: add IPv6 traffic class and flow label fields")
> > Cc: stable@dpdk.org
> >
> > Reported-by: Maxime Leroy <maxime@leroys.fr>
> > Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
> > ---
>
> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
>
> Long form AI review. This may need addressing before merge.
>
> The revert is correct and complete. cba27998dc81 only touched
> rte_ip6.h, there are no in-tree users of the removed fields, and the
> struct layout is unchanged so there is no ABI impact.
>
> Keeping Cc: stable is right. The fields shipped in 24.11 and 25.11
> but access the wrong bits on every little endian target, so any
> direct user is already broken and a build failure is the better
> outcome. Please add a note for the stable release notes pointing to
> vtc_flow and the RTE_IPV6_HDR_*_MASK macros. Code that byte swaps
> vtc_flow in place before using the fields does work on little endian
> today and will stop compiling.
>
> The commit message needs rework before this goes in.
>
> The example struct does not compile: after, ds and version are each
> declared twice with no #if between the two orderings. Since it is
> the justification for the revert, either fix it or drop it and show
> the actual result.
>
> The root cause is understated. It is not only ds crossing a byte
> boundary; every field is wrong on little endian, including version.
> A uint32_t bitfield is allocated against the host order value of
> the word, while vtc_flow holds network order. The little endian
> layout is only correct after byte swapping vtc_flow.
>
> On x86_64 with the removed layout:
>
> header bytes 6b 91 23 45 (ver 6, DSCP EF, ECT(1), fl 0x12345)
> -> version=4 ds=0x14 ecn=2 flow_label=0x3916b
> version = 6 -> bytes 00 00 00 60
> ds = 0x3f -> bytes 00 00 c0 0f
>
> Suggested wording:
>
> On little endian, a 32-bit bitfield is allocated from the least
> significant bit of the host order word, but vtc_flow is stored in
> network order. All four fields therefore read and write the wrong
> bits. For example, setting version to 6 writes 0x60 into the last
> byte of the word.
>
> ds and flow_label are not contiguous in memory on little endian,
> so no reordering of bitfields can express them.
>
> For reference, neither Linux nor FreeBSD tries this. Linux uses a u8
> bitfield only for version and the upper nibble of traffic class,
> then flow_lbl[3], with big endian masks (IPV6_FLOWINFO_MASK,
> IPV6_FLOWLABEL_MASK) for the rest. FreeBSD has no bitfields: a raw
> ip6_flow word, the ip6_vfc byte for version, and per byte order
> masks. DPDK already has the equivalent in rte_ipv6_check_version()
> and RTE_IPV6_HDR_*_MASK, so nothing is lost.
>
> Release note: "defect bitfields" reads oddly and gives no migration
> path. Suggest:
>
> * net: Removed ``version``, ``ds``, ``ecn`` and ``flow_label``
> bitfields from ``struct rte_ipv6_hdr``. They accessed the wrong
> bits on little endian. Use ``vtc_flow`` with the
> ``RTE_IPV6_HDR_*_MASK`` and ``RTE_IPV6_HDR_*_SHIFT`` macros.
>
> Minor: "Reverted the patch introducing them." should be imperative,
> "Revert the patch that introduced them."
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2] net: revert add IPv6 traffic class and flow label fields
2026-09-22 16:36 [PATCH] net: revert add IPv6 traffic class and flow label fields Morten Brørup
2026-09-22 18:14 ` Stephen Hemminger
@ 2026-09-29 7:36 ` Morten Brørup
1 sibling, 0 replies; 5+ messages in thread
From: Morten Brørup @ 2026-09-29 7:36 UTC (permalink / raw)
To: dev, Thomas Monjalon
Cc: Morten Brørup, stable, Maxime Leroy, Stephen Hemminger
The IPv6 header bitfields "version", "ds", "ecn", and "flow_label"
are not organized correctly on little endian architectures.
On little endian, a 32-bit bitfield is allocated from the least
significant bit of the host order word, but "vtc_flow" is stored in
network order. All four fields therefore read and write the wrong
bits. For example, setting "version" to 6 writes 0x60 into the last
byte of the word.
"ds" and "flow_label" are not contiguous in memory on little endian,
so no reordering of bitfields can express them.
Reverted the patch introducing these bitfields.
PS: There is a GCC attribute to fix this, but nothing similar is
offered by Clang or MSVC:
__attribute__((scalar_storage_order("big-endian")))
Bugzilla ID: 1679
Fixes: cba27998dc81 ("net: add IPv6 traffic class and flow label fields")
Cc: stable@dpdk.org
Reported-by: Maxime Leroy <maxime@leroys.fr>
Signed-off-by: Morten Brørup <mb@smartsharesystems.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
v2: Updated commit message and release notes. (AI advanced model)
---
doc/guides/rel_notes/release_26_11.rst | 5 +++++
lib/net/rte_ip6.h | 18 +-----------------
2 files changed, 6 insertions(+), 17 deletions(-)
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 4b3e5d995c..52ccc8f787 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -79,6 +79,11 @@ Removed Items
``rte_rib6_is_equal``
* table: ``RTE_LPM_IPV6_ADDR_SIZE``
+ * net: Removed ``version``, ``ds``, ``ecn`` and ``flow_label``
+ bitfields from ``struct rte_ipv6_hdr``. They accessed the wrong
+ bits on little endian. Use ``vtc_flow`` with the
+ ``RTE_IPV6_HDR_*_MASK`` and ``RTE_IPV6_HDR_*_SHIFT`` macros.
+
API Changes
-----------
diff --git a/lib/net/rte_ip6.h b/lib/net/rte_ip6.h
index d1abf1f5d5..25be328955 100644
--- a/lib/net/rte_ip6.h
+++ b/lib/net/rte_ip6.h
@@ -467,23 +467,7 @@ rte_ether_mcast_from_ipv6(struct rte_ether_addr *mac, const struct rte_ipv6_addr
* IPv6 Header
*/
struct __rte_aligned(2) __rte_packed_begin rte_ipv6_hdr {
- union {
- rte_be32_t vtc_flow; /**< IP version, traffic class & flow label. */
- __extension__
- struct {
-#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
- uint32_t flow_label:20; /**< Flow label */
- uint32_t ecn:2; /**< ECN */
- uint32_t ds:6; /**< Differentiated services */
- uint32_t version:4; /**< Version */
-#elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN
- uint32_t version:4; /**< Version */
- uint32_t ds:6; /**< Differentiated services */
- uint32_t ecn:2; /**< ECN */
- uint32_t flow_label:20; /**< Flow label */
-#endif
- };
- };
+ rte_be32_t vtc_flow; /**< IP version, traffic class & flow label. */
rte_be16_t payload_len; /**< IP payload size, including ext. headers */
uint8_t proto; /**< Protocol, next header. */
uint8_t hop_limits; /**< Hop limits. */
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-29 7:36 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-22 16:36 [PATCH] net: revert add IPv6 traffic class and flow label fields Morten Brørup
2026-09-22 18:14 ` Stephen Hemminger
2026-09-28 14:15 ` Thomas Monjalon
2026-09-28 14:20 ` Morten Brørup
2026-09-29 7:36 ` [PATCH v2] " Morten Brørup
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox