From 8b50271204ad95b95665b83b1c0c3e8747187bda Mon Sep 17 00:00:00 2001 From: Roman Stolyarchuk Date: Thu, 8 Jan 2026 11:41:26 +0300 Subject: [PATCH 1/2] refactor: rename ipv4 and ipv6 header methods for consistency - Made relevant methods constexpr for better compile-time evaluation. - Updated method names from ipv4_header() to ip4_header() and ipv6_header() to ip6_header() in BufferView class. - Adjusted corresponding test cases to reflect the new method names. --- tests/test_buffer_view.cxx | 12 +++---- tests/test_ip4_header.cxx | 26 +++++++-------- tests/test_ip6_header.cxx | 20 ++++++------ tests/test_tcp_header.cxx | 12 +++---- tests/test_udp_header.cxx | 2 +- vbvx/buffer_view.hxx | 65 ++++++++++++++++++++------------------ vbvx/ether.hxx | 46 ++++++++++++++++----------- vbvx/flags_view.hxx | 5 +-- vbvx/srv6_header.hxx | 7 ++-- vbvx/udp_header.hxx | 17 +++++++--- 10 files changed, 118 insertions(+), 94 deletions(-) diff --git a/tests/test_buffer_view.cxx b/tests/test_buffer_view.cxx index 68e7ea5..6fd1b43 100644 --- a/tests/test_buffer_view.cxx +++ b/tests/test_buffer_view.cxx @@ -34,12 +34,12 @@ TEST(BufferViewTest, EmptyBufferNullptr) { EXPECT_FALSE(buf.ether_header()); EXPECT_FALSE(buf.ether_type().has_value()); EXPECT_FALSE(buf.vlan_header()); - EXPECT_FALSE(buf.ipv4_header()); - EXPECT_FALSE(buf.ipv6_header()); + EXPECT_FALSE(buf.ip4_header()); + EXPECT_FALSE(buf.ip6_header()); EXPECT_FALSE(buf.arp_header()); EXPECT_FALSE(buf.tcp_header()); EXPECT_FALSE(buf.udp_header()); - EXPECT_FALSE(buf.icmp_header()); + EXPECT_FALSE(buf.icmp4_header()); } TEST(BufferViewTest, TruncatedEtherHeader) { @@ -108,9 +108,9 @@ TEST(BufferViewTest, IPv4IhlTooSmall) { std::memcpy(data.data() + sizeof(EtherHeader), &ip, sizeof(ip)); BufferView buf(data.data(), static_cast(data.size())); - auto ip_hdr = buf.ipv4_header(); + auto ip_hdr = buf.ip4_header(); ASSERT_TRUE(ip_hdr); // header struct is present (bounds ok) - EXPECT_FALSE(buf.ipv4_ihl_bytes().has_value()); + EXPECT_FALSE(buf.ip4_ihl_bytes().has_value()); EXPECT_FALSE(buf.l4_offset().has_value()); EXPECT_FALSE(buf.tcp_header()); } @@ -214,7 +214,7 @@ TEST(BufferViewTest, IPv6Icmpv6) { BufferView buf(buf_bytes.data(), static_cast(buf_bytes.size())); EXPECT_EQ(buf.ip_protocol().value(), IpProtocol::ICMPv6); - auto ic = buf.icmpv6_header(); + auto ic = buf.icmp6_header(); ASSERT_TRUE(ic); EXPECT_EQ(ic->type_u8(), static_cast(ICMPv4Type::EchoRequest)); EXPECT_EQ(ic->checksum(), 0x1234u); diff --git a/tests/test_ip4_header.cxx b/tests/test_ip4_header.cxx index 4ec8fdd..041bbd7 100644 --- a/tests/test_ip4_header.cxx +++ b/tests/test_ip4_header.cxx @@ -258,7 +258,7 @@ TEST(BufferViewIPv4MalformedTest, Ipv4HeaderTooShort) { eth->type_be = autoswap(std::to_underlying(EtherType::IPv4)); BufferView buf(raw.data(), static_cast(raw.size())); - EXPECT_FALSE(buf.ipv4_header()); + EXPECT_FALSE(buf.ip4_header()); EXPECT_FALSE(buf.l4_offset()); EXPECT_FALSE(buf.ip_protocol().has_value()); } @@ -279,7 +279,7 @@ TEST(BufferViewIPv4MalformedTest, Ipv4HeaderPresentAndL4Offset) { autoswap(static_cast(sizeof(IPv4Header) + sizeof(UDPHeader))); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); ASSERT_TRUE(buf.ip_protocol().has_value()); EXPECT_EQ(buf.ip_protocol().value(), IpProtocol::UDP); ASSERT_TRUE(buf.l4_offset()); @@ -333,7 +333,7 @@ TEST(BufferViewIPv4MalformedTest, TcpOptionsTruncated) { tcp->data_offset = static_cast((10u << 4) & 0xF0u); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto th = buf.tcp_header(); ASSERT_TRUE(th); EXPECT_EQ(th->header_words(), 10u); @@ -341,7 +341,7 @@ TEST(BufferViewIPv4MalformedTest, TcpOptionsTruncated) { const auto l3 = buf.l3_offset(); ASSERT_EQ(l3, static_cast(sizeof(EtherHeader))); - const auto ihl = buf.ipv4_ihl_bytes(); + const auto ihl = buf.ip4_ihl_bytes(); ASSERT_TRUE(ihl.has_value()); const auto avail_transport = static_cast(raw.size()) - (l3 + *ihl); // available transport bytes are smaller than declared header_bytes -> @@ -370,7 +370,7 @@ TEST(BufferViewIPv4MalformedTest, TcpDataOffsetTooSmall) { tcp->data_offset = static_cast((4u << 4) & 0xF0u); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto th = buf.tcp_header(); // header_at requires at least 20 bytes so HeaderView exists, but data_offset // indicates invalid header size @@ -399,7 +399,7 @@ TEST(BufferViewIPv4MalformedTest, UdpLengthTooSmallAndTooLarge) { uh->length_be = autoswap(static_cast(4u)); // less than 8 BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto u = buf.udp_header(); ASSERT_TRUE(u); EXPECT_LT(u->length(), static_cast(sizeof(UDPHeader))); @@ -427,12 +427,12 @@ TEST(BufferViewIPv4MalformedTest, UdpLengthTooSmallAndTooLarge) { static_cast(512u)); // claims much larger than available BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto u = buf.udp_header(); ASSERT_TRUE(u); const auto l3 = buf.l3_offset(); - const auto ihl = buf.ipv4_ihl_bytes(); + const auto ihl = buf.ip4_ihl_bytes(); ASSERT_TRUE(ihl.has_value()); const auto ip_payload_bytes = static_cast( ip->total_length_be ? ip->total_length_be / 1 : 0); @@ -458,7 +458,7 @@ TEST(BufferViewIPv4MalformedTest, VlanIpv4Offset) { BufferView buf(raw.data(), static_cast(raw.size())); EXPECT_EQ(buf.l3_offset(), static_cast(sizeof(EtherHeader) + sizeof(VlanHeader))); - EXPECT_TRUE(buf.ipv4_header()); + EXPECT_TRUE(buf.ip4_header()); } TEST(BufferViewIPv4MalformedTest, IpProtocolNullOnNonIpFrames) { @@ -467,8 +467,8 @@ TEST(BufferViewIPv4MalformedTest, IpProtocolNullOnNonIpFrames) { eth->type_be = autoswap(std::to_underlying(EtherType::ARP)); BufferView buf(raw.data(), static_cast(raw.size())); - EXPECT_FALSE(buf.ipv4_header()); - EXPECT_FALSE(buf.ipv6_header()); + EXPECT_FALSE(buf.ip4_header()); + EXPECT_FALSE(buf.ip6_header()); EXPECT_FALSE(buf.ip_protocol().has_value()); } @@ -485,8 +485,8 @@ TEST(BufferViewIPv4MalformedTest, Ipv4HeaderIhlTooSmall) { ip->protocol = static_cast(IpProtocol::TCP); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); - EXPECT_FALSE(buf.ipv4_ihl_bytes()); + ASSERT_TRUE(buf.ip4_header()); + EXPECT_FALSE(buf.ip4_ihl_bytes()); EXPECT_FALSE(buf.l4_offset()); // protocol is still readable from the header itself ASSERT_TRUE(buf.ip_protocol().has_value()); diff --git a/tests/test_ip6_header.cxx b/tests/test_ip6_header.cxx index 8770818..32bcc53 100644 --- a/tests/test_ip6_header.cxx +++ b/tests/test_ip6_header.cxx @@ -211,7 +211,7 @@ TEST(BufferViewIPv6MalformedTest, Ipv6HeaderTooShort) { eth->type_be = autoswap(std::to_underlying(EtherType::IPv6)); BufferView buf(raw.data(), static_cast(raw.size())); - EXPECT_FALSE(buf.ipv6_header()); + EXPECT_FALSE(buf.ip6_header()); EXPECT_FALSE(buf.l4_offset()); EXPECT_FALSE(buf.ip_protocol().has_value()); } @@ -230,7 +230,7 @@ TEST(BufferViewIPv6MalformedTest, Ipv6HeaderPresentAndL4Offset) { ip->payload_length_be = autoswap(static_cast(sizeof(UDPHeader))); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv6_header()); + ASSERT_TRUE(buf.ip6_header()); ASSERT_TRUE(buf.ip_protocol().has_value()); EXPECT_EQ(buf.ip_protocol().value(), IpProtocol::UDP); ASSERT_TRUE(buf.l4_offset()); @@ -276,7 +276,7 @@ TEST(BufferViewIPv6MalformedTest, VlanIpv6Offset) { BufferView buf(raw.data(), static_cast(raw.size())); EXPECT_EQ(buf.l3_offset(), static_cast(sizeof(EtherHeader) + sizeof(VlanHeader))); - EXPECT_TRUE(buf.ipv6_header()); + EXPECT_TRUE(buf.ip6_header()); } TEST(BufferViewIPv6MalformedTest, IpProtocolNullOnNonIpFrames) { @@ -285,8 +285,8 @@ TEST(BufferViewIPv6MalformedTest, IpProtocolNullOnNonIpFrames) { eth->type_be = autoswap(std::to_underlying(EtherType::ARP)); BufferView buf(raw.data(), static_cast(raw.size())); - EXPECT_FALSE(buf.ipv4_header()); - EXPECT_FALSE(buf.ipv6_header()); + EXPECT_FALSE(buf.ip4_header()); + EXPECT_FALSE(buf.ip6_header()); EXPECT_FALSE(buf.ip_protocol().has_value()); } @@ -310,7 +310,7 @@ TEST(BufferViewIPv6MalformedTest, TcpOptionsTruncated) { tcp->data_offset = static_cast((10u << 4) & 0xF0u); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv6_header()); + ASSERT_TRUE(buf.ip6_header()); auto th = buf.tcp_header(); ASSERT_TRUE(th); EXPECT_EQ(th->header_words(), 10u); @@ -344,7 +344,7 @@ TEST(BufferViewIPv6MalformedTest, TcpDataOffsetTooSmall) { tcp->data_offset = static_cast((4u << 4) & 0xF0u); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv6_header()); + ASSERT_TRUE(buf.ip6_header()); auto th = buf.tcp_header(); // header_at requires at least 20 bytes so HeaderView exists, but data_offset // indicates invalid header size @@ -371,7 +371,7 @@ TEST(BufferViewIPv6MalformedTest, UdpLengthTooSmallAndTooLarge) { uh->length_be = autoswap(static_cast(4u)); // less than 8 BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv6_header()); + ASSERT_TRUE(buf.ip6_header()); auto u = buf.udp_header(); ASSERT_TRUE(u); EXPECT_LT(u->length(), static_cast(sizeof(UDPHeader))); @@ -397,10 +397,10 @@ TEST(BufferViewIPv6MalformedTest, UdpLengthTooSmallAndTooLarge) { static_cast(sizeof(UDPHeader) + 10u)); // larger than payload BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv6_header()); + ASSERT_TRUE(buf.ip6_header()); auto u = buf.udp_header(); ASSERT_TRUE(u); // UDP length > IPv6 payload length - EXPECT_GT(u->length(), buf.ipv6_header()->payload_length()); + EXPECT_GT(u->length(), buf.ip6_header()->payload_length()); } } diff --git a/tests/test_tcp_header.cxx b/tests/test_tcp_header.cxx index 4f8baf0..1ee9c81 100644 --- a/tests/test_tcp_header.cxx +++ b/tests/test_tcp_header.cxx @@ -145,7 +145,7 @@ TEST(TCPHeader, TcpOptionsExactlyFit) { static_cast((6u << 4) & 0xF0u); // 6 words -> 24 bytes BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto th = buf.tcp_header(); ASSERT_TRUE(th); EXPECT_EQ(th->header_words(), 6u); @@ -153,7 +153,7 @@ TEST(TCPHeader, TcpOptionsExactlyFit) { const auto l3 = buf.l3_offset(); ASSERT_EQ(l3, static_cast(sizeof(EtherHeader))); - const auto ihl = buf.ipv4_ihl_bytes(); + const auto ihl = buf.ip4_ihl_bytes(); ASSERT_TRUE(ihl.has_value()); const auto avail_transport = static_cast(raw.size()) - (l3 + *ihl); EXPECT_EQ(avail_transport, th->header_bytes()); @@ -180,7 +180,7 @@ TEST(TCPHeader, TcpMaxDataOffsetTruncated) { static_cast((15u << 4) & 0xF0u); // 15 words -> 60 bytes BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto th = buf.tcp_header(); ASSERT_TRUE(th); EXPECT_EQ(th->header_words(), 15u); @@ -188,7 +188,7 @@ TEST(TCPHeader, TcpMaxDataOffsetTruncated) { const auto l3 = buf.l3_offset(); ASSERT_EQ(l3, static_cast(sizeof(EtherHeader))); - const auto ihl = buf.ipv4_ihl_bytes(); + const auto ihl = buf.ip4_ihl_bytes(); ASSERT_TRUE(ihl.has_value()); const auto avail_transport = static_cast(raw.size()) - (l3 + *ihl); EXPECT_LT(avail_transport, th->header_bytes()); @@ -215,7 +215,7 @@ TEST(TCPHeader, DataOffsetLowNibbleIgnored) { tcp->data_offset = static_cast(((7u << 4) & 0xF0u) | 0x0Fu); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); auto th = buf.tcp_header(); ASSERT_TRUE(th); EXPECT_EQ(th->header_words(), 7u); @@ -224,7 +224,7 @@ TEST(TCPHeader, DataOffsetLowNibbleIgnored) { EXPECT_TRUE(th->valid_min_size()); const auto l3 = buf.l3_offset(); ASSERT_EQ(l3, static_cast(sizeof(EtherHeader))); - const auto ihl = buf.ipv4_ihl_bytes(); + const auto ihl = buf.ip4_ihl_bytes(); ASSERT_TRUE(ihl.has_value()); const auto avail_transport = static_cast(raw.size()) - (l3 + *ihl); EXPECT_EQ(avail_transport, th->header_bytes()); diff --git a/tests/test_udp_header.cxx b/tests/test_udp_header.cxx index e1f0ec7..bf7d036 100644 --- a/tests/test_udp_header.cxx +++ b/tests/test_udp_header.cxx @@ -100,7 +100,7 @@ TEST(UDPHeader, BufferViewIntegration) { uh->set_dst_port(4321); BufferView buf(raw.data(), static_cast(raw.size())); - ASSERT_TRUE(buf.ipv4_header()); + ASSERT_TRUE(buf.ip4_header()); ASSERT_TRUE(buf.ip_protocol().has_value()); EXPECT_EQ(buf.ip_protocol().value(), IpProtocol::UDP); diff --git a/vbvx/buffer_view.hxx b/vbvx/buffer_view.hxx index 19b3f4c..9856ff0 100644 --- a/vbvx/buffer_view.hxx +++ b/vbvx/buffer_view.hxx @@ -28,23 +28,23 @@ class BufferView { using buffer_t = uint8_t; public: - BufferView(const void* data, uint16_t length) noexcept + constexpr BufferView(const void* data, uint16_t length) noexcept : data_{static_cast(data)}, length_{length} {} - auto data() const noexcept -> std::span { + constexpr auto data() const noexcept -> std::span { if (!data_) { return {}; } return {data_, length_}; } - auto length() const noexcept -> uint16_t { return length_; } + constexpr auto length() const noexcept -> uint16_t { return length_; } - auto ether_header() const noexcept -> HeaderView { + constexpr auto ether_header() const noexcept -> HeaderView { return header_at(0); } - auto ether_type() const noexcept -> std::optional { + constexpr auto ether_type() const noexcept -> std::optional { auto eth = ether_header(); if (!eth) { return std::nullopt; @@ -62,7 +62,7 @@ public: return static_cast(type); } - auto vlan_header() const noexcept -> HeaderView { + constexpr auto vlan_header() const noexcept -> HeaderView { auto eth = ether_header(); if (!eth) { return {}; @@ -76,7 +76,7 @@ public: return header_at(sizeof(EtherHeader)); } - auto vlan_id() const noexcept -> std::optional { + constexpr auto vlan_id() const noexcept -> std::optional { auto vlan = vlan_header(); if (!vlan) { return std::nullopt; @@ -84,7 +84,7 @@ public: return static_cast(vlan->tci() & 0x0FFFu); } - auto l3_offset() const noexcept -> uint16_t { + constexpr auto l3_offset() const noexcept -> uint16_t { auto eth = ether_header(); if (!eth) { return 0; @@ -95,7 +95,7 @@ public: : static_cast(sizeof(EtherHeader)); } - auto arp_header() const noexcept -> HeaderView { + constexpr auto arp_header() const noexcept -> HeaderView { auto et = ether_type(); if (!et || *et != EtherType::ARP) { return {}; @@ -103,7 +103,7 @@ public: return header_at(l3_offset()); } - auto ipv4_header() const noexcept -> HeaderView { + constexpr auto ip4_header() const noexcept -> HeaderView { auto et = ether_type(); if (!et || *et != EtherType::IPv4) { return {}; @@ -111,7 +111,7 @@ public: return header_at(l3_offset()); } - auto ipv6_header() const noexcept -> HeaderView { + constexpr auto ip6_header() const noexcept -> HeaderView { auto et = ether_type(); if (!et || *et != EtherType::IPv6) { return {}; @@ -119,8 +119,8 @@ public: return header_at(l3_offset()); } - auto ipv4_ihl_bytes() const noexcept -> std::optional { - auto ip = ipv4_header(); + constexpr auto ip4_ihl_bytes() const noexcept -> std::optional { + auto ip = ip4_header(); if (!ip) { return std::nullopt; } @@ -132,31 +132,31 @@ public: return bytes; } - auto ip_protocol() const noexcept -> std::optional { - if (auto ip4 = ipv4_header()) { + constexpr auto ip_protocol() const noexcept -> std::optional { + if (auto ip4 = ip4_header()) { return static_cast(ip4->protocol); } - if (auto ip6 = ipv6_header()) { + if (auto ip6 = ip6_header()) { return static_cast(ip6->next_header); } return std::nullopt; } - auto l4_offset() const noexcept -> std::optional { - if (ipv4_header()) { - auto ihl = ipv4_ihl_bytes(); + constexpr auto l4_offset() const noexcept -> std::optional { + if (ip4_header()) { + auto ihl = ip4_ihl_bytes(); if (!ihl) { return std::nullopt; } return l3_offset() + *ihl; } - if (ipv6_header()) { + if (ip6_header()) { return l3_offset() + static_cast(sizeof(IPv6Header)); } return std::nullopt; } - auto tcp_header() const noexcept -> HeaderView { + constexpr auto tcp_header() const noexcept -> HeaderView { auto proto = ip_protocol(); auto off = l4_offset(); if (!proto || !off) { @@ -168,7 +168,7 @@ public: return header_at(*off); } - auto udp_header() const noexcept -> HeaderView { + constexpr auto udp_header() const noexcept -> HeaderView { auto proto = ip_protocol(); auto off = l4_offset(); if (!proto || !off) { @@ -180,7 +180,7 @@ public: return header_at(*off); } - auto icmp_header() const noexcept -> HeaderView { + constexpr auto icmp4_header() const noexcept -> HeaderView { auto proto = ip_protocol(); auto off = l4_offset(); if (!proto || !off) { @@ -192,7 +192,7 @@ public: return header_at(*off); } - auto icmpv6_header() const noexcept -> HeaderView { + constexpr auto icmp6_header() const noexcept -> HeaderView { auto proto = ip_protocol(); auto off = l4_offset(); if (!proto || !off || *proto != IpProtocol::ICMPv6) { @@ -201,25 +201,28 @@ public: return header_at(*off); } - // Return an SRv6 Segment Routing Header if present as the first routing - // header after the IPv6 header. This checks that the IPv6 Next Header is - // Routing (43) and that the routing type is SRH (4). - auto srv6_header() const noexcept -> HeaderView { - auto ip6 = ipv6_header(); + /** + * @brief Get SRv6 Header view if present. + * + * @return HeaderView if SRv6 header is present, empty optional + * otherwise. + */ + constexpr auto srv6_header() const noexcept -> HeaderView { + auto ip6 = ip6_header(); if (!ip6) { return {}; } if (ip6->next_header != static_cast(IpProtocol::IPv6_Route)) { return {}; } - // SRH appears immediately after the IPv6 header in the first extension. + auto offset = static_cast(l3_offset() + sizeof(IPv6Header)); return header_at(offset); } private: template - auto header_at(uint16_t offset) const noexcept -> HeaderView { + constexpr auto header_at(uint16_t offset) const noexcept -> HeaderView { auto _data = data(); if (offset + sizeof(H) > _data.size()) { return {}; diff --git a/vbvx/ether.hxx b/vbvx/ether.hxx index b62f80b..1bf1025 100644 --- a/vbvx/ether.hxx +++ b/vbvx/ether.hxx @@ -8,11 +8,13 @@ namespace vbvx { -/** @brief Ethernet frame EtherType values (network byte order). - * IANA registry (EtherType / Ethernet Numbers): - * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml - * Related IETF RFC: RFC894 (Ethernet encapsulation of IP datagrams) - * https://datatracker.ietf.org/doc/html/rfc894 +/** + * @brief Ethernet frame EtherType values (network byte order). + * + * @see IANA registry (EtherType / Ethernet Numbers): + * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml + * @see RFC894 (Ethernet encapsulation of IP datagrams): + * https://datatracker.ietf.org/doc/html/rfc894 */ enum class EtherType : uint16_t { IPv4 = 0x0800, @@ -48,10 +50,13 @@ struct [[gnu::packed]] EtherHeader { static_assert(sizeof(EtherHeader) == 14, "Wrong Ethernet header size"); static_assert(alignof(EtherHeader) == 1, "Wrong Ethernet header alignment"); -/** @brief VLAN Priority Code Point (PCP) values (3 bits). See IEEE 802.1Q and - * IANA Ethernet numbers. IANA: - * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml IEEE - * 802.1Q: https://standards.ieee.org/standard/802_1Q-2018.html +/** + * @brief VLAN Priority Code Point (PCP) values (3 bits). + * + * @see IEEE 802.1Q and IANA Ethernet numbers. IANA: + * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml + * @see IEEE 802.1Q: + * https://standards.ieee.org/standard/802_1Q-2018.html */ enum class VlanPcp : uint8_t { p0 = 0, @@ -64,11 +69,13 @@ enum class VlanPcp : uint8_t { p7 = 7 }; -/** @brief VLAN Tag Control Information (TCI) helpers and layout. - * See IEEE 802.1Q for 802.1Q VLAN tag format and IANA Ethernet numbers - * (EtherType 0x8100). IEEE 802.1Q: - * https://standards.ieee.org/standard/802_1Q-2018.html IANA: - * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml +/** + * @brief VLAN Tag Control Information (TCI) helpers and layout. + * + * @see IANA (EtherType 802.1Q / 0x8100): + * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml + * @see (EtherType 0x8100). IEEE 802.1Q: + * https://standards.ieee.org/standard/802_1Q-2018.html */ struct [[gnu::packed]] VlanTci { uint16_t raw{}; @@ -96,10 +103,13 @@ struct [[gnu::packed]] VlanTci { } }; -/** @brief VLAN (802.1Q) header (4 bytes after Ethernet header). - * IANA (EtherType 802.1Q / 0x8100): - * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml - * IEEE 802.1Q: https://standards.ieee.org/standard/802_1Q-2018.html +/** + * @brief VLAN (802.1Q) header (4 bytes after Ethernet header). + * + * @see IANA (EtherType 802.1Q / 0x8100): + * https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml + * @see IEEE 802.1Q for 802.1Q VLAN tag format: + * https://standards.ieee.org/standard/802_1Q-2018.html */ struct [[gnu::packed]] VlanHeader { uint16_t tci_be; diff --git a/vbvx/flags_view.hxx b/vbvx/flags_view.hxx index 3b3bdd6..173ba89 100644 --- a/vbvx/flags_view.hxx +++ b/vbvx/flags_view.hxx @@ -101,7 +101,7 @@ template requires std::is_enum_v class FlagsView { public: - explicit FlagsView(BitmaskEnum& flags) : flags_ref_(flags) {} + constexpr explicit FlagsView(BitmaskEnum& flags) : flags_ref_(flags) {} constexpr auto set(BitmaskEnum mask) -> FlagsView& { flags_ref_ |= mask; @@ -151,7 +151,8 @@ template requires std::is_enum_v class ConstFlagsView { public: - explicit ConstFlagsView(const BitmaskEnum& flags) : flags_ref_(flags) {} + constexpr explicit ConstFlagsView(const BitmaskEnum& flags) + : flags_ref_(flags) {} constexpr bool has(BitmaskEnum mask) const { return (flags_ref_ & mask) != static_cast(0); diff --git a/vbvx/srv6_header.hxx b/vbvx/srv6_header.hxx index 0c4a6d1..656637e 100644 --- a/vbvx/srv6_header.hxx +++ b/vbvx/srv6_header.hxx @@ -91,10 +91,10 @@ struct SRv6Tlv { /** @brief Iterator over TLVs in an SRH's TLV area. Does not allocate. */ class SRv6TlvIterator { public: - SRv6TlvIterator(const uint8_t* ptr, size_t len) noexcept + constexpr SRv6TlvIterator(const uint8_t* ptr, size_t len) noexcept : ptr_{ptr}, len_{len}, pos_{0} {} - bool next(SRv6Tlv& out) noexcept { + constexpr bool next(SRv6Tlv& out) noexcept { if (pos_ >= len_) { return false; } @@ -127,7 +127,8 @@ private: size_t pos_; }; -/** @brief HMAC TLV view for type==5 (HMAC). The 'value' pointer is the TLV +/** + * @brief HMAC TLV view for type==5 (HMAC). The 'value' pointer is the TLV * variable data where the first two bytes are D/reserved, followed by a 4-octet * HMAC Key ID, then the HMAC bytes. */ diff --git a/vbvx/udp_header.hxx b/vbvx/udp_header.hxx index 7756cb6..9362170 100644 --- a/vbvx/udp_header.hxx +++ b/vbvx/udp_header.hxx @@ -36,10 +36,19 @@ struct [[gnu::packed]] UDPHeader { return autoswap(checksum_be); } - void set_src_port(uint16_t v) noexcept { src_port_be = autoswap(v); } - void set_dst_port(uint16_t v) noexcept { dst_port_be = autoswap(v); } - void set_length(uint16_t v) noexcept { length_be = autoswap(v); } - void set_checksum(uint16_t v) noexcept { checksum_be = autoswap(v); } + constexpr void set_src_port(uint16_t v) noexcept { + src_port_be = autoswap(v); + } + + constexpr void set_dst_port(uint16_t v) noexcept { + dst_port_be = autoswap(v); + } + + constexpr void set_length(uint16_t v) noexcept { length_be = autoswap(v); } + + constexpr void set_checksum(uint16_t v) noexcept { + checksum_be = autoswap(v); + } }; static_assert(sizeof(UDPHeader) == 8, "Wrong UDP header size"); From b41388b4444cc21b26787a35b84daf38fbc0ee80 Mon Sep 17 00:00:00 2001 From: Roman Stolyarchuk Date: Thu, 8 Jan 2026 11:49:23 +0300 Subject: [PATCH 2/2] chore: add build and test badge to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2aede24..e435dac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # VBVX - VPP Buffer View eXtensions +[![Build and Test](https://github.com/llmxio/vbvx/actions/workflows/ci.yml/badge.svg)](https://github.com/llmxio/vbvx/actions/workflows/ci.yml) + VBVX (VPP Buffer View eXtensions) is a small, header-only C++23 library for **zero-copy** parsing of packet buffers. It provides views over common on-wire headers (Ethernet, VLAN, ARP, IPv4/v6, TCP/UDP, ICMP, SRv6) without copying. ## About