From: Alejandro Pacheco <[email protected]>
The set-field and NAT setters in packets.c and odp-execute.c cast
dp_packet_l3() or dp_packet_l4() to a header struct and rely on their
callers to check that the packet holds the header.  Some callers do,
some only assert that the offset is set, as set_arp() did.

Add dp_packet_l3_hdr() and dp_packet_l4_hdr(), which return the header
only when the offset is set and the packet holds the given number of
bytes from it, and use them in the IPv4, IPv6, ARP, TCP, UDP, SCTP,
ICMP and IGMP setters and in packet_get_ip_proto().  A packet that is
too short is left unchanged.  packet_rh_present() is bounded by the L3
size, and by the L4 offset when it is set.

Assisted-by: claude-opus-5-5, Claude Code
Signed-off-by: Alejandro Pacheco <[email protected]>
---
 lib/dp-packet.h      |  31 ++++++++
 lib/odp-execute.c    |  42 +++++++----
 lib/packets.c        | 140 +++++++++++++++++++++++-----------
 tests/test-packets.c | 173 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 330 insertions(+), 56 deletions(-)

diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index e4dff1436..78f90d380 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -507,6 +507,37 @@ dp_packet_l4_size(const struct dp_packet *b)
         : 0;
 }
 
+/* Returns the data at offset 'ofs' of 'b' if at least 'size' bytes follow
+ * it before the L2 padding, otherwise NULL.  'ofs' may be unset. */
+static inline void *
+dp_packet_hdr_at__(const struct dp_packet *b, uint16_t ofs, size_t size)
+{
+    size_t len = dp_packet_size(b);
+    size_t pad = dp_packet_l2_pad_size(b);
+    size_t end = pad <= len ? len - pad : 0;
+
+    return ofs != UINT16_MAX && ofs <= end && size <= end - ofs
+           ? (char *) dp_packet_data(b) + ofs
+           : NULL;
+}
+
+/* Returns the L3 header of 'b' if the packet holds at least 'size' bytes
+ * of it, otherwise NULL.  The L3 offset may be set for a header that flow
+ * extraction found too short. */
+static inline void *
+dp_packet_l3_hdr(const struct dp_packet *b, size_t size)
+{
+    return dp_packet_hdr_at__(b, b->l3_ofs, size);
+}
+
+/* Returns the L4 header of 'b' if the packet holds at least 'size' bytes
+ * of it, otherwise NULL. */
+static inline void *
+dp_packet_l4_hdr(const struct dp_packet *b, size_t size)
+{
+    return dp_packet_hdr_at__(b, b->l4_ofs, size);
+}
+
 static inline void *
 dp_packet_inner_l3(const struct dp_packet *b)
 {
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 29691d5c2..80f8cba6b 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -154,7 +154,7 @@ static void
 odp_set_ipv4(struct dp_packet *packet, const struct ovs_key_ipv4 *key,
              const struct ovs_key_ipv4 *mask)
 {
-    struct ip_header *nh = dp_packet_l3(packet);
+    struct ip_header *nh = dp_packet_l3_hdr(packet, IP_HEADER_LEN);
     ovs_be32 ip_src_nh;
     ovs_be32 ip_dst_nh;
     ovs_be32 new_ip_src;
@@ -162,7 +162,9 @@ odp_set_ipv4(struct dp_packet *packet, const struct 
ovs_key_ipv4 *key,
     uint8_t new_tos;
     uint8_t new_ttl;
 
-    ovs_assert(nh);
+    if (!nh) {
+        return;
+    }
 
     if (mask->ipv4_src) {
         ip_src_nh = get_16aligned_be32(&nh->ip_src);
@@ -237,10 +239,17 @@ static void
 odp_set_ipv6(struct dp_packet *packet, const struct ovs_key_ipv6 *key,
              const struct ovs_key_ipv6 *mask)
 {
-    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3_hdr(packet,
+                                                        IPV6_HEADER_LEN);
     struct in6_addr sbuf, dbuf;
-    uint8_t old_tc = ntohl(get_16aligned_be32(&nh->ip6_flow)) >> 20;
-    ovs_be32 old_fl = get_16aligned_be32(&nh->ip6_flow) & htonl(0xfffff);
+    uint8_t old_tc;
+    ovs_be32 old_fl;
+
+    if (!nh) {
+        return;
+    }
+    old_tc = ntohl(get_16aligned_be32(&nh->ip6_flow)) >> 20;
+    old_fl = get_16aligned_be32(&nh->ip6_flow) & htonl(0xfffff);
 
     packet_set_ipv6(
         packet,
@@ -257,7 +266,7 @@ static void
 odp_set_tcp(struct dp_packet *packet, const struct ovs_key_tcp *key,
              const struct ovs_key_tcp *mask)
 {
-    struct tcp_header *th = dp_packet_l4(packet);
+    struct tcp_header *th = dp_packet_l4_hdr(packet, TCP_HEADER_LEN);
 
     if (OVS_LIKELY(th && dp_packet_get_tcp_payload(packet))) {
         packet_set_tcp_port(packet,
@@ -270,9 +279,9 @@ static void
 odp_set_udp(struct dp_packet *packet, const struct ovs_key_udp *key,
              const struct ovs_key_udp *mask)
 {
-    struct udp_header *uh = dp_packet_l4(packet);
+    struct udp_header *uh = dp_packet_l4_hdr(packet, UDP_HEADER_LEN);
 
-    if (OVS_LIKELY(uh && dp_packet_get_udp_payload(packet))) {
+    if (OVS_LIKELY(uh)) {
         packet_set_udp_port(packet,
                             key->udp_src | (uh->udp_src & ~mask->udp_src),
                             key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
@@ -283,9 +292,9 @@ static void
 odp_set_sctp(struct dp_packet *packet, const struct ovs_key_sctp *key,
              const struct ovs_key_sctp *mask)
 {
-    struct sctp_header *sh = dp_packet_l4(packet);
+    struct sctp_header *sh = dp_packet_l4_hdr(packet, SCTP_HEADER_LEN);
 
-    if (OVS_LIKELY(sh && dp_packet_get_sctp_payload(packet))) {
+    if (OVS_LIKELY(sh)) {
         packet_set_sctp_port(packet,
                              key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
                              key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
@@ -302,9 +311,9 @@ static void
 set_arp(struct dp_packet *packet, const struct ovs_key_arp *key,
         const struct ovs_key_arp *mask)
 {
-    struct arp_eth_header *arp = dp_packet_l3(packet);
+    struct arp_eth_header *arp = dp_packet_l3_hdr(packet, ARP_ETH_HEADER_LEN);
 
-    if (!arp || dp_packet_l3_size(packet) < ARP_ETH_HEADER_LEN) {
+    if (!arp) {
         return;
     }
 
@@ -332,10 +341,15 @@ static void
 odp_set_nd_ext(struct dp_packet *packet, const struct ovs_key_nd_extensions
         *key, const struct ovs_key_nd_extensions *mask)
 {
-    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
-    ovs_16aligned_be32 reserved = ns->rso_flags;
+    const struct ovs_nd_msg *ns = dp_packet_l4_hdr(packet, sizeof *ns);
+    ovs_16aligned_be32 reserved;
     uint8_t opt_type = 0;
 
+    if (!ns) {
+        return;
+    }
+    reserved = ns->rso_flags;
+
     if (dp_packet_l4_size(packet) >= sizeof *ns + ND_LLA_OPT_LEN) {
         opt_type = ns->options[0].type;
     }
diff --git a/lib/packets.c b/lib/packets.c
index b0090fc44..4a01e172b 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1147,20 +1147,26 @@ void
 packet_set_ipv4_addr(struct dp_packet *packet,
                      ovs_16aligned_be32 *addr, ovs_be32 new_addr)
 {
-    struct ip_header *nh = dp_packet_l3(packet);
-    ovs_be32 old_addr = get_16aligned_be32(addr);
-    size_t l4_size = dp_packet_l4_size(packet);
+    struct ip_header *nh = dp_packet_l3_hdr(packet, IP_HEADER_LEN);
+    ovs_be32 old_addr;
+
+    if (!nh) {
+        return;
+    }
+    old_addr = get_16aligned_be32(addr);
 
     pkt_metadata_init_conn(&packet->md);
 
-    if (nh->ip_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
+    if (nh->ip_proto == IPPROTO_TCP
+        && dp_packet_l4_hdr(packet, TCP_HEADER_LEN)) {
         if (dp_packet_l4_checksum_valid(packet)) {
             dp_packet_l4_checksum_set_partial(packet);
         } else {
             struct tcp_header *th = dp_packet_l4(packet);
             th->tcp_csum = recalc_csum32(th->tcp_csum, old_addr, new_addr);
         }
-    } else if (nh->ip_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN ) {
+    } else if (nh->ip_proto == IPPROTO_UDP
+               && dp_packet_l4_hdr(packet, UDP_HEADER_LEN)) {
         if (dp_packet_l4_checksum_valid(packet)) {
             dp_packet_l4_checksum_set_partial(packet);
         } else {
@@ -1185,16 +1191,23 @@ packet_set_ipv4_addr(struct dp_packet *packet,
 /* Returns true, if packet contains at least one routing header where
  * segements_left > 0.
  *
- * This function assumes that L3 and L4 offsets are set in the packet. */
+ * The extension headers are looked for between the L3 header and the L4
+ * offset, or the end of the L3 payload if the L4 offset is not set. */
 bool
 packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr, bool *first_frag)
 {
     const struct ovs_16aligned_ip6_hdr *nh;
     size_t len;
     size_t remaining;
-    uint8_t *data = dp_packet_l3(packet);
+    uint8_t *data = dp_packet_l3_hdr(packet, sizeof *nh);
 
-    remaining = packet->l4_ofs - packet->l3_ofs;
+    if (!data) {
+        return false;
+    }
+    remaining = dp_packet_l3_size(packet);
+    if (dp_packet_l4(packet) && packet->l4_ofs >= packet->l3_ofs) {
+        remaining = MIN(remaining, packet->l4_ofs - packet->l3_ofs);
+    }
     if (remaining < sizeof *nh) {
         return false;
     }
@@ -1272,9 +1285,7 @@ packet_update_csum128(struct dp_packet *packet, uint8_t 
proto,
                       ovs_16aligned_be32 addr[4],
                       const struct in6_addr *new_addr)
 {
-    size_t l4_size = dp_packet_l4_size(packet);
-
-    if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
+    if (proto == IPPROTO_TCP && dp_packet_l4_hdr(packet, TCP_HEADER_LEN)) {
         if (dp_packet_l4_checksum_valid(packet)) {
             dp_packet_l4_checksum_set_partial(packet);
         } else {
@@ -1282,7 +1293,8 @@ packet_update_csum128(struct dp_packet *packet, uint8_t 
proto,
 
             th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
         }
-    } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
+    } else if (proto == IPPROTO_UDP
+               && dp_packet_l4_hdr(packet, UDP_HEADER_LEN)) {
         if (dp_packet_l4_checksum_valid(packet)) {
             dp_packet_l4_checksum_set_partial(packet);
         } else {
@@ -1295,11 +1307,13 @@ packet_update_csum128(struct dp_packet *packet, uint8_t 
proto,
                 }
             }
         }
-    } else if (proto == IPPROTO_ICMPV6 &&
-               l4_size >= sizeof(struct icmp6_header)) {
-        struct icmp6_header *icmp = dp_packet_l4(packet);
+    } else if (proto == IPPROTO_ICMPV6) {
+        struct icmp6_header *icmp = dp_packet_l4_hdr(packet, sizeof *icmp);
 
-        icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr, new_addr);
+        if (icmp) {
+            icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr,
+                                               new_addr);
+        }
     }
 }
 
@@ -1340,7 +1354,11 @@ void
 packet_set_ipv4(struct dp_packet *packet, ovs_be32 src, ovs_be32 dst,
                 uint8_t tos, uint8_t ttl)
 {
-    struct ip_header *nh = dp_packet_l3(packet);
+    struct ip_header *nh = dp_packet_l3_hdr(packet, IP_HEADER_LEN);
+
+    if (!nh) {
+        return;
+    }
 
     if (get_16aligned_be32(&nh->ip_src) != src) {
         packet_set_ipv4_addr(packet, &nh->ip_src, src);
@@ -1386,11 +1404,16 @@ packet_set_ipv6(struct dp_packet *packet, const struct 
in6_addr *src,
                 const struct in6_addr *dst, uint8_t key_tc, ovs_be32 key_fl,
                 uint8_t key_hl)
 {
-    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3_hdr(packet,
+                                                        IPV6_HEADER_LEN);
     bool recalc_csum = true;
     uint8_t proto = 0;
     bool rh_present;
 
+    if (!nh) {
+        return;
+    }
+
     rh_present = packet_rh_present(packet, &proto, &recalc_csum);
 
     if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
@@ -1426,9 +1449,14 @@ packet_set_port(ovs_be16 *port, ovs_be16 new_port, 
ovs_be16 *csum)
 void
 packet_set_tcp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst)
 {
-    struct tcp_header *th = dp_packet_l4(packet);
+    size_t ports_len = offsetof(struct tcp_header, tcp_seq);
+    struct tcp_header *th = dp_packet_l4_hdr(packet, ports_len);
     ovs_be16 *csum = NULL;
 
+    if (!th) {
+        return;
+    }
+
     if (dp_packet_l4_checksum_valid(packet)) {
         dp_packet_l4_checksum_set_partial(packet);
     } else if (dp_packet_l4_size(packet) >= TCP_HEADER_LEN) {
@@ -1446,7 +1474,11 @@ packet_set_tcp_port(struct dp_packet *packet, ovs_be16 
src, ovs_be16 dst)
 void
 packet_set_udp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst)
 {
-    struct udp_header *uh = dp_packet_l4(packet);
+    struct udp_header *uh = dp_packet_l4_hdr(packet, UDP_HEADER_LEN);
+
+    if (!uh) {
+        return;
+    }
 
     if (dp_packet_l4_checksum_valid(packet)) {
         dp_packet_l4_checksum_set_partial(packet);
@@ -1473,7 +1505,12 @@ packet_set_udp_port(struct dp_packet *packet, ovs_be16 
src, ovs_be16 dst)
 void
 packet_set_sctp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst)
 {
-    struct sctp_header *sh = dp_packet_l4(packet);
+    size_t ports_len = offsetof(struct sctp_header, sctp_vtag);
+    struct sctp_header *sh = dp_packet_l4_hdr(packet, ports_len);
+
+    if (!sh) {
+        return;
+    }
 
     if (dp_packet_l4_checksum_valid(packet)) {
         dp_packet_l4_checksum_set_partial(packet);
@@ -1507,9 +1544,14 @@ packet_set_sctp_port(struct dp_packet *packet, ovs_be16 
src, ovs_be16 dst)
 void
 packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
 {
-    struct icmp_header *ih = dp_packet_l4(packet);
-    ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
-    ovs_be16 new_tc = htons(type << 8 | code);
+    struct icmp_header *ih = dp_packet_l4_hdr(packet, ICMP_HEADER_LEN);
+    ovs_be16 orig_tc, new_tc;
+
+    if (!ih) {
+        return;
+    }
+    orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
+    new_tc = htons(type << 8 | code);
 
     if (orig_tc != new_tc) {
         ih->icmp_type = type;
@@ -1526,9 +1568,9 @@ packet_set_icmp(struct dp_packet *packet, uint8_t type, 
uint8_t code)
 void
 packet_set_icmp_id(struct dp_packet *packet, ovs_be16 icmp_id)
 {
-    struct icmp_header *ih = dp_packet_l4(packet);
+    struct icmp_header *ih = dp_packet_l4_hdr(packet, ICMP_HEADER_LEN);
 
-    if (!ih || dp_packet_l4_size(packet) < ICMP_HEADER_LEN) {
+    if (!ih) {
         return;
     }
 
@@ -1545,9 +1587,9 @@ packet_set_icmp_id(struct dp_packet *packet, ovs_be16 
icmp_id)
 uint8_t
 packet_get_icmp_type(const struct dp_packet *packet)
 {
-    struct icmp_header *ih = dp_packet_l4(packet);
+    struct icmp_header *ih = dp_packet_l4_hdr(packet, ICMP_HEADER_LEN);
 
-    if (!ih || dp_packet_l4_size(packet) < ICMP_HEADER_LEN) {
+    if (!ih) {
         return 0;
     }
 
@@ -1561,11 +1603,14 @@ packet_get_ip_proto(const struct dp_packet *packet)
     uint8_t ip_proto;
 
     if (l2->eth_type == htons(ETH_TYPE_IPV6)) {
-        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(packet);
-        ip_proto = nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
+        struct ovs_16aligned_ip6_hdr *nh6;
+
+        nh6 = dp_packet_l3_hdr(packet, IPV6_HEADER_LEN);
+        ip_proto = nh6 ? nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt : 0;
     } else {
-        struct ip_header *l3_hdr = dp_packet_l3(packet);
-        ip_proto = l3_hdr->ip_proto;
+        struct ip_header *l3_hdr = dp_packet_l3_hdr(packet, IP_HEADER_LEN);
+
+        ip_proto = l3_hdr ? l3_hdr->ip_proto : 0;
     }
 
     return ip_proto;
@@ -1602,11 +1647,15 @@ void
 packet_set_igmp3_query(struct dp_packet *packet, uint8_t max_resp,
                        ovs_be32 group, bool srs, uint8_t qrv, uint8_t qqic)
 {
-    struct igmpv3_query_header *igh = dp_packet_l4(packet);
-    ovs_be16 orig_type_max_resp =
-        htons(igh->type << 8 | igh->max_resp);
-    ovs_be16 new_type_max_resp =
-        htons(IGMP_HOST_MEMBERSHIP_QUERY << 8 | max_resp);
+    struct igmpv3_query_header *igh;
+    ovs_be16 orig_type_max_resp, new_type_max_resp;
+
+    igh = dp_packet_l4_hdr(packet, IGMPV3_QUERY_HEADER_LEN);
+    if (!igh) {
+        return;
+    }
+    orig_type_max_resp = htons(igh->type << 8 | igh->max_resp);
+    new_type_max_resp = htons(IGMP_HOST_MEMBERSHIP_QUERY << 8 | max_resp);
 
     if (orig_type_max_resp != new_type_max_resp) {
         igh->type = IGMP_HOST_MEMBERSHIP_QUERY;
@@ -1645,7 +1694,8 @@ packet_set_nd_ext(struct dp_packet *packet, const 
ovs_16aligned_be32 rso_flags,
     struct ovs_nd_msg *ns;
     struct ovs_nd_lla_opt *opt;
     int bytes_remain = dp_packet_l4_size(packet);
-    struct ovs_16aligned_ip6_hdr * nh = dp_packet_l3(packet);
+    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3_hdr(packet,
+                                                        IPV6_HEADER_LEN);
     uint32_t pseudo_hdr_csum = 0;
 
     if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
@@ -2080,15 +2130,21 @@ void
 IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6)
 {
     if (is_ipv6) {
-        ovs_16aligned_be32 *ip6 = dp_packet_l3(pkt);
+        ovs_16aligned_be32 *ip6 = dp_packet_l3_hdr(pkt, IPV6_HEADER_LEN);
 
+        if (!ip6) {
+            return;
+        }
         put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
                                 htonl(IP_ECN_CE << 20));
     } else {
-        struct ip_header *nh = dp_packet_l3(pkt);
-        uint8_t tos = nh->ip_tos;
+        struct ip_header *nh = dp_packet_l3_hdr(pkt, IP_HEADER_LEN);
+        uint8_t tos;
 
-        tos |= IP_ECN_CE;
+        if (!nh) {
+            return;
+        }
+        tos = nh->ip_tos | IP_ECN_CE;
         if (nh->ip_tos != tos) {
             if (dp_packet_ip_checksum_valid(pkt)) {
                 dp_packet_ip_checksum_set_partial(pkt);
diff --git a/tests/test-packets.c b/tests/test-packets.c
index 8efc2cdc3..31a0a227c 100644
--- a/tests/test-packets.c
+++ b/tests/test-packets.c
@@ -243,6 +243,175 @@ test_set_nd(void)
     check_set_nd(tll_sll, sizeof tll_sll, 0);
 }
 
+static void
+test_hdr_accessors(void)
+{
+    uint8_t data[24] = { 0 };
+    struct dp_packet *packet = dp_packet_clone_data(data, sizeof data);
+
+    ovs_assert(!dp_packet_l3_hdr(packet, 0));
+    ovs_assert(!dp_packet_l4_hdr(packet, 0));
+
+    dp_packet_set_l3(packet, dp_packet_data(packet));
+    dp_packet_set_l4(packet, (char *) dp_packet_data(packet) + 20);
+    ovs_assert(dp_packet_l3_hdr(packet, 24) == dp_packet_data(packet));
+    ovs_assert(!dp_packet_l3_hdr(packet, 25));
+    ovs_assert(dp_packet_l4_hdr(packet, 4) == dp_packet_l4(packet));
+    ovs_assert(!dp_packet_l4_hdr(packet, 5));
+
+    dp_packet_set_l2_pad_size(packet, 4);
+    ovs_assert(dp_packet_l3_hdr(packet, 20));
+    ovs_assert(!dp_packet_l3_hdr(packet, 21));
+    ovs_assert(dp_packet_l4_hdr(packet, 0));
+    ovs_assert(!dp_packet_l4_hdr(packet, 1));
+
+    dp_packet_set_l2_pad_size(packet, 0);
+    dp_packet_set_size(packet, 16);
+    ovs_assert(dp_packet_l3_hdr(packet, 16));
+    ovs_assert(!dp_packet_l4_hdr(packet, 0));
+
+    dp_packet_delete(packet);
+}
+
+/* Runs 'set' on a packet that holds the first 'n' bytes of 'hdr' as its L3
+ * header, or as its L4 header if 'l4', and returns true if 'set' changed the
+ * packet's data. */
+static bool
+check_set_hdr(const uint8_t *hdr, size_t n, bool l4,
+              void (*set)(struct dp_packet *))
+{
+    struct dp_packet *packet = dp_packet_clone_data(hdr, n);
+    bool changed;
+
+    if (l4) {
+        dp_packet_set_l4(packet, dp_packet_data(packet));
+    } else {
+        dp_packet_set_l3(packet, dp_packet_data(packet));
+    }
+    set(packet);
+    changed = memcmp(dp_packet_data(packet), hdr, n) != 0;
+
+    dp_packet_delete(packet);
+    return changed;
+}
+
+static void
+set_ipv4(struct dp_packet *packet)
+{
+    packet_set_ipv4(packet, htonl(0x0a000009), 0, 0, 0);
+}
+
+static void
+set_ipv6(struct dp_packet *packet)
+{
+    struct in6_addr src = IN6ADDR_LOOPBACK_INIT;
+
+    packet_set_ipv6(packet, &src, &in6addr_any, 0, 0, 0);
+}
+
+static void
+set_tcp_port(struct dp_packet *packet)
+{
+    packet_set_tcp_port(packet, htons(1), htons(2));
+}
+
+static void
+set_udp_port(struct dp_packet *packet)
+{
+    packet_set_udp_port(packet, htons(1), htons(2));
+}
+
+static void
+set_sctp_port(struct dp_packet *packet)
+{
+    packet_set_sctp_port(packet, htons(1), htons(2));
+}
+
+static void
+set_icmp(struct dp_packet *packet)
+{
+    packet_set_icmp(packet, ICMP4_DST_UNREACH, 1);
+}
+
+static void
+set_igmp3_query(struct dp_packet *packet)
+{
+    packet_set_igmp3_query(packet, 10, htonl(0xe0000001), false, 2, 125);
+}
+
+static void
+test_set_short_hdr(void)
+{
+    uint8_t hdr[IPV6_HEADER_LEN + 4] = { 0 };
+
+    ovs_assert(check_set_hdr(hdr, IP_HEADER_LEN, false, set_ipv4));
+    ovs_assert(!check_set_hdr(hdr, IP_HEADER_LEN - 1, false, set_ipv4));
+    ovs_assert(check_set_hdr(hdr, IPV6_HEADER_LEN, false, set_ipv6));
+    ovs_assert(!check_set_hdr(hdr, IPV6_HEADER_LEN - 1, false, set_ipv6));
+    ovs_assert(check_set_hdr(hdr, 4, true, set_tcp_port));
+    ovs_assert(!check_set_hdr(hdr, 3, true, set_tcp_port));
+    ovs_assert(check_set_hdr(hdr, UDP_HEADER_LEN, true, set_udp_port));
+    ovs_assert(!check_set_hdr(hdr, UDP_HEADER_LEN - 1, true, set_udp_port));
+    ovs_assert(check_set_hdr(hdr, 4, true, set_sctp_port));
+    ovs_assert(!check_set_hdr(hdr, 3, true, set_sctp_port));
+    ovs_assert(check_set_hdr(hdr, ICMP_HEADER_LEN, true, set_icmp));
+    ovs_assert(!check_set_hdr(hdr, ICMP_HEADER_LEN - 1, true, set_icmp));
+    ovs_assert(check_set_hdr(hdr, IGMPV3_QUERY_HEADER_LEN, true,
+                             set_igmp3_query));
+    ovs_assert(!check_set_hdr(hdr, IGMPV3_QUERY_HEADER_LEN - 1, true,
+                              set_igmp3_query));
+}
+
+static void
+test_rh_present_no_l4(void)
+{
+    struct ovs_16aligned_ip6_hdr *nh;
+    uint8_t hdr[IPV6_HEADER_LEN + 4] = { 0 };
+    struct dp_packet *packet;
+    bool first_frag = false;
+    uint8_t nexthdr;
+
+    /* A hop-by-hop options header cut after 4 bytes, so flow extraction
+     * leaves the L4 offset unset. */
+    nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, hdr);
+    nh->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_HOPOPTS;
+    packet = dp_packet_clone_data(hdr, sizeof hdr);
+    dp_packet_set_l3(packet, dp_packet_data(packet));
+
+    ovs_assert(!packet_rh_present(packet, &nexthdr, &first_frag));
+    ovs_assert(nexthdr == IPPROTO_HOPOPTS);
+
+    dp_packet_delete(packet);
+}
+
+static void
+test_set_ipv6_addr_short_icmp6(void)
+{
+    struct ovs_16aligned_ip6_hdr *nh;
+    uint8_t hdr[IPV6_HEADER_LEN + 2] = { 0 };
+    struct in6_addr src = IN6ADDR_LOOPBACK_INIT;
+    struct dp_packet *packet;
+    uint8_t *l4;
+
+    nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, hdr);
+    nh->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
+    hdr[IPV6_HEADER_LEN] = 0x12;
+    hdr[IPV6_HEADER_LEN + 1] = 0x34;
+    packet = dp_packet_clone_data(hdr, sizeof hdr);
+    dp_packet_set_l3(packet, dp_packet_data(packet));
+    dp_packet_set_l4(packet, (char *) dp_packet_data(packet)
+                             + IPV6_HEADER_LEN);
+
+    packet_set_ipv6(packet, &src, &in6addr_any, 0, 0, 0);
+
+    nh = dp_packet_l3(packet);
+    ovs_assert(!memcmp(&nh->ip6_src, &src, sizeof src));
+    l4 = dp_packet_l4(packet);
+    ovs_assert(l4[0] == 0x12 && l4[1] == 0x34);
+
+    dp_packet_delete(packet);
+}
+
 static void
 test_packets_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
@@ -252,6 +421,10 @@ test_packets_main(int argc OVS_UNUSED, char *argv[] 
OVS_UNUSED)
     test_ipv6_masking();
     test_ipv6_parsing();
     test_set_nd();
+    test_hdr_accessors();
+    test_set_short_hdr();
+    test_rh_present_no_l4();
+    test_set_ipv6_addr_short_icmp6();
 }
 
 OVSTEST_REGISTER("test-packets", test_packets_main);
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to