[ovs-dev,v10,09/14] dp-packet: Add support for data "linearization".

Message ID 1538151315-117132-10-git-send-email-tiago.lam@intel.com
State Superseded
Headers show
Series
  • Support multi-segment mbufs
Related show

Commit Message

Lam, Tiago Sept. 28, 2018, 4:15 p.m.
Previous commits have added support to the dp_packet API to handle
multi-segmented packets, where data is not stored contiguously in
memory. However, in some cases, it is inevitable and data must be
provided contiguously. Examples of such cases are when performing csums
over the entire packet data, or when write()'ing to a file descriptor
(for a tap interface, for example). For such cases, the dp_packet API
has been extended to provide a way to transform a multi-segmented
DPBUF_DPDK packet into a DPBUF_MALLOC system packet (at the expense of a
copy of memory). If the packet's data is already stored in memory
contigously then there's no need to convert the packet.

Thus, the main use cases that were assuming that a dp_packet's data is
always held contiguously in memory were changed to make use of the new
"linear functions" in the dp_packet API when there's a need to traverse
the entire's packet data. Per the example above, when the packet's data
needs to be write() to the tap's file descriptor, or when the conntrack
module needs to verify a packet's checksum, the data is now linearized.

Additionally, the layer functions, such as dp_packet_l3() and variants,
have been modified to check if there's enough data in the packet before
returning a pointer to the data (and callers have been modified
accordingly). This requirement is needed to guarantee that a caller
doesn't read beyond the available memory.

Signed-off-by: Tiago Lam <tiago.lam@intel.com>
---
 lib/bfd.c                     |   3 +-
 lib/cfm.c                     |   5 +-
 lib/conntrack-icmp.c          |   4 +-
 lib/conntrack-private.h       |   4 +-
 lib/conntrack-tcp.c           |   6 +-
 lib/conntrack.c               | 109 +++++++++++++--------
 lib/dp-packet.c               |  18 ++++
 lib/dp-packet.h               | 217 +++++++++++++++++++++++++++---------------
 lib/dpif-netdev.c             |   5 +
 lib/dpif-netlink.c            |   5 +
 lib/dpif.c                    |   9 ++
 lib/flow.c                    |  29 +++---
 lib/lacp.c                    |   3 +-
 lib/mcast-snooping.c          |   8 +-
 lib/netdev-bsd.c              |   5 +
 lib/netdev-dummy.c            |  13 ++-
 lib/netdev-linux.c            |  13 ++-
 lib/netdev-native-tnl.c       |  39 +++++---
 lib/odp-execute.c             |  28 ++++--
 lib/ofp-print.c               |  10 +-
 lib/ovs-lldp.c                |   3 +-
 lib/packets.c                 |  81 +++++++++-------
 lib/pcap-file.c               |   2 +-
 ofproto/ofproto-dpif-upcall.c |  20 +++-
 ofproto/ofproto-dpif-xlate.c  |  42 ++++++--
 ovn/controller/pinctrl.c      |  29 +++---
 tests/test-conntrack.c        |   2 +-
 tests/test-rstp.c             |   8 +-
 tests/test-stp.c              |   8 +-
 29 files changed, 483 insertions(+), 245 deletions(-)

Comments

Flavio Leitner Oct. 3, 2018, 6:26 p.m. | #1
Hi Tiago,

There is a lot to digest in this patch, so I only have a general
comment about it for now.

See below.

On Fri, Sep 28, 2018 at 05:15:10PM +0100, Tiago Lam wrote:
> Previous commits have added support to the dp_packet API to handle
> multi-segmented packets, where data is not stored contiguously in
> memory. However, in some cases, it is inevitable and data must be
> provided contiguously. Examples of such cases are when performing csums
> over the entire packet data, or when write()'ing to a file descriptor
> (for a tap interface, for example). For such cases, the dp_packet API
> has been extended to provide a way to transform a multi-segmented
> DPBUF_DPDK packet into a DPBUF_MALLOC system packet (at the expense of a
> copy of memory). If the packet's data is already stored in memory
> contigously then there's no need to convert the packet.
> 
> Thus, the main use cases that were assuming that a dp_packet's data is
> always held contiguously in memory were changed to make use of the new
> "linear functions" in the dp_packet API when there's a need to traverse
> the entire's packet data. Per the example above, when the packet's data
> needs to be write() to the tap's file descriptor, or when the conntrack
> module needs to verify a packet's checksum, the data is now linearized.
> 
> Additionally, the layer functions, such as dp_packet_l3() and variants,
> have been modified to check if there's enough data in the packet before
> returning a pointer to the data (and callers have been modified
> accordingly). This requirement is needed to guarantee that a caller
> doesn't read beyond the available memory.
> 
> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
> ---
>  lib/bfd.c                     |   3 +-
>  lib/cfm.c                     |   5 +-
>  lib/conntrack-icmp.c          |   4 +-
>  lib/conntrack-private.h       |   4 +-
>  lib/conntrack-tcp.c           |   6 +-
>  lib/conntrack.c               | 109 +++++++++++++--------
>  lib/dp-packet.c               |  18 ++++
>  lib/dp-packet.h               | 217 +++++++++++++++++++++++++++---------------
>  lib/dpif-netdev.c             |   5 +
>  lib/dpif-netlink.c            |   5 +
>  lib/dpif.c                    |   9 ++
>  lib/flow.c                    |  29 +++---
>  lib/lacp.c                    |   3 +-
>  lib/mcast-snooping.c          |   8 +-
>  lib/netdev-bsd.c              |   5 +
>  lib/netdev-dummy.c            |  13 ++-
>  lib/netdev-linux.c            |  13 ++-
>  lib/netdev-native-tnl.c       |  39 +++++---
>  lib/odp-execute.c             |  28 ++++--
>  lib/ofp-print.c               |  10 +-
>  lib/ovs-lldp.c                |   3 +-
>  lib/packets.c                 |  81 +++++++++-------
>  lib/pcap-file.c               |   2 +-
>  ofproto/ofproto-dpif-upcall.c |  20 +++-
>  ofproto/ofproto-dpif-xlate.c  |  42 ++++++--
>  ovn/controller/pinctrl.c      |  29 +++---
>  tests/test-conntrack.c        |   2 +-
>  tests/test-rstp.c             |   8 +-
>  tests/test-stp.c              |   8 +-
>  29 files changed, 483 insertions(+), 245 deletions(-)
> 
> diff --git a/lib/bfd.c b/lib/bfd.c
> index 5308262..d50d2da 100644
> --- a/lib/bfd.c
> +++ b/lib/bfd.c
> @@ -722,7 +722,8 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
>      if (!msg) {
>          VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
>                       "%"PRIdPTR" bytes long, at least %d required).",
> -                     bfd->name, (uint8_t *) dp_packet_tail(p) - l7,
> +                     bfd->name, dp_packet_size(p) -
> +                     (l7 - (uint8_t *) dp_packet_data(p)),
>                       BFD_PACKET_LEN);
>          goto out;
>      }
> diff --git a/lib/cfm.c b/lib/cfm.c
> index 71d2c02..83baf2a 100644
> --- a/lib/cfm.c
> +++ b/lib/cfm.c
> @@ -584,7 +584,7 @@ cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet,
>  
>      atomic_read_relaxed(&cfm->extended, &extended);
>  
> -    ccm = dp_packet_l3(packet);
> +    ccm = dp_packet_l3(packet, sizeof(*ccm));
>      ccm->mdlevel_version = 0;
>      ccm->opcode = CCM_OPCODE;
>      ccm->tlv_offset = 70;
> @@ -759,8 +759,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p)
>      atomic_read_relaxed(&cfm->extended, &extended);
>  
>      eth = dp_packet_eth(p);
> -    ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
> -                    CCM_ACCEPT_LEN);
> +    ccm = dp_packet_l3(p, CCM_ACCEPT_LEN);
>  
>      if (!ccm) {
>          VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.",
> diff --git a/lib/conntrack-icmp.c b/lib/conntrack-icmp.c
> index 40fd1d8..0575d0e 100644
> --- a/lib/conntrack-icmp.c
> +++ b/lib/conntrack-icmp.c
> @@ -63,7 +63,7 @@ icmp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>  static bool
>  icmp4_valid_new(struct dp_packet *pkt)
>  {
> -    struct icmp_header *icmp = dp_packet_l4(pkt);
> +    struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
>  
>      return icmp->icmp_type == ICMP4_ECHO_REQUEST
>             || icmp->icmp_type == ICMP4_INFOREQUEST
> @@ -73,7 +73,7 @@ icmp4_valid_new(struct dp_packet *pkt)
>  static bool
>  icmp6_valid_new(struct dp_packet *pkt)
>  {
> -    struct icmp6_header *icmp6 = dp_packet_l4(pkt);
> +    struct icmp6_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
>  
>      return icmp6->icmp6_type == ICMP6_ECHO_REQUEST;
>  }
> diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
> index a344801..2191aa6 100644
> --- a/lib/conntrack-private.h
> +++ b/lib/conntrack-private.h
> @@ -159,8 +159,8 @@ tcp_payload_length(struct dp_packet *pkt)
>  {
>      const char *tcp_payload = dp_packet_get_tcp_payload(pkt);
>      if (tcp_payload) {
> -        return ((char *) dp_packet_tail(pkt) - dp_packet_l2_pad_size(pkt)
> -                - tcp_payload);
> +        return dp_packet_l4_size(pkt) -
> +               (tcp_payload - (char *) dp_packet_l4(pkt, 0));
>      } else {
>          return 0;
>      }
> diff --git a/lib/conntrack-tcp.c b/lib/conntrack-tcp.c
> index 86d313d..5450971 100644
> --- a/lib/conntrack-tcp.c
> +++ b/lib/conntrack-tcp.c
> @@ -149,7 +149,7 @@ tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>                  struct dp_packet *pkt, bool reply, long long now)
>  {
>      struct conn_tcp *conn = conn_tcp_cast(conn_);
> -    struct tcp_header *tcp = dp_packet_l4(pkt);
> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>      /* The peer that sent 'pkt' */
>      struct tcp_peer *src = &conn->peer[reply ? 1 : 0];
>      /* The peer that should receive 'pkt' */
> @@ -394,7 +394,7 @@ tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>  static bool
>  tcp_valid_new(struct dp_packet *pkt)
>  {
> -    struct tcp_header *tcp = dp_packet_l4(pkt);
> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>      uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
>  
>      if (tcp_invalid_flags(tcp_flags)) {
> @@ -416,7 +416,7 @@ tcp_new_conn(struct conntrack_bucket *ctb, struct dp_packet *pkt,
>               long long now)
>  {
>      struct conn_tcp* newconn = NULL;
> -    struct tcp_header *tcp = dp_packet_l4(pkt);
> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>      struct tcp_peer *src, *dst;
>      uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
>  
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index 974f985..f024186 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -450,10 +450,10 @@ get_ip_proto(const struct dp_packet *pkt)
>      uint8_t ip_proto;
>      struct eth_header *l2 = dp_packet_eth(pkt);
>      if (l2->eth_type == htons(ETH_TYPE_IPV6)) {
> -        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>          ip_proto = nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
>      } else {
> -        struct ip_header *l3_hdr = dp_packet_l3(pkt);
> +        struct ip_header *l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
>          ip_proto = l3_hdr->ip_proto;
>      }
>  
> @@ -476,8 +476,8 @@ get_alg_ctl_type(const struct dp_packet *pkt, ovs_be16 tp_src, ovs_be16 tp_dst,
>      enum { CT_IPPORT_FTP = 21 };
>      enum { CT_IPPORT_TFTP = 69 };
>      uint8_t ip_proto = get_ip_proto(pkt);
> -    struct udp_header *uh = dp_packet_l4(pkt);
> -    struct tcp_header *th = dp_packet_l4(pkt);
> +    struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>      ovs_be16 ftp_src_port = htons(CT_IPPORT_FTP);
>      ovs_be16 ftp_dst_port = htons(CT_IPPORT_FTP);
>      ovs_be16 tftp_dst_port = htons(CT_IPPORT_TFTP);
> @@ -530,18 +530,18 @@ pat_packet(struct dp_packet *pkt, const struct conn *conn)
>  {
>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th = dp_packet_l4(pkt);
> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>              packet_set_tcp_port(pkt, conn->rev_key.dst.port, th->tcp_dst);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh = dp_packet_l4(pkt);
> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>              packet_set_udp_port(pkt, conn->rev_key.dst.port, uh->udp_dst);
>          }
>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th = dp_packet_l4(pkt);
> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>              packet_set_tcp_port(pkt, th->tcp_src, conn->rev_key.src.port);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh = dp_packet_l4(pkt);
> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>              packet_set_udp_port(pkt, uh->udp_src, conn->rev_key.src.port);
>          }
>      }
> @@ -553,11 +553,11 @@ nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>          pkt->md.ct_state |= CS_SRC_NAT;
>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
> -            struct ip_header *nh = dp_packet_l3(pkt);
> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>              packet_set_ipv4_addr(pkt, &nh->ip_src,
>                                   conn->rev_key.dst.addr.ipv4_aligned);
>          } else {
> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>                                   nh6->ip6_src.be32,
>                                   &conn->rev_key.dst.addr.ipv6_aligned,
> @@ -569,11 +569,11 @@ nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>          pkt->md.ct_state |= CS_DST_NAT;
>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
> -            struct ip_header *nh = dp_packet_l3(pkt);
> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>              packet_set_ipv4_addr(pkt, &nh->ip_dst,
>                                   conn->rev_key.src.addr.ipv4_aligned);
>          } else {
> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>                                   nh6->ip6_dst.be32,
>                                   &conn->rev_key.src.addr.ipv6_aligned,
> @@ -590,18 +590,18 @@ un_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>  {
>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th = dp_packet_l4(pkt);
> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>              packet_set_tcp_port(pkt, th->tcp_src, conn->key.src.port);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh = dp_packet_l4(pkt);
> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>              packet_set_udp_port(pkt, uh->udp_src, conn->key.src.port);
>          }
>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th = dp_packet_l4(pkt);
> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>              packet_set_tcp_port(pkt, conn->key.dst.port, th->tcp_dst);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh = dp_packet_l4(pkt);
> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>              packet_set_udp_port(pkt, conn->key.dst.port, uh->udp_dst);
>          }
>      }
> @@ -612,21 +612,21 @@ reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>  {
>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th_in = dp_packet_l4(pkt);
> +            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
>              packet_set_tcp_port(pkt, conn->key.src.port,
>                                  th_in->tcp_dst);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh_in = dp_packet_l4(pkt);
> +            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
>              packet_set_udp_port(pkt, conn->key.src.port,
>                                  uh_in->udp_dst);
>          }
>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>          if (conn->key.nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *th_in = dp_packet_l4(pkt);
> +            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
>              packet_set_tcp_port(pkt, th_in->tcp_src,
>                                  conn->key.dst.port);
>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
> -            struct udp_header *uh_in = dp_packet_l4(pkt);
> +            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
>              packet_set_udp_port(pkt, uh_in->udp_src,
>                                  conn->key.dst.port);
>          }
> @@ -636,16 +636,26 @@ reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>  static void
>  reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
>  {
> -    char *tail = dp_packet_tail(pkt);
> -    char pad = dp_packet_l2_pad_size(pkt);
> +    char *tail;
> +    char pad;
>      struct conn_key inner_key;
>      const char *inner_l4 = NULL;
> -    uint16_t orig_l3_ofs = pkt->l3_ofs;
> -    uint16_t orig_l4_ofs = pkt->l4_ofs;
> +    uint16_t orig_l3_ofs;
> +    uint16_t orig_l4_ofs;
> +
> +    /* We need the whole packet to parse the packet below */
> +    if (!dp_packet_is_linear(pkt)) {
> +        dp_packet_linearize(pkt);
> +    }

Could you elaborate on why we need the whole packet?

> +
> +    tail = dp_packet_tail(pkt);
> +    pad = dp_packet_l2_pad_size(pkt);
> +    orig_l3_ofs = pkt->l3_ofs;
> +    orig_l4_ofs = pkt->l4_ofs;
>  
>      if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
> -        struct ip_header *nh = dp_packet_l3(pkt);
> -        struct icmp_header *icmp = dp_packet_l4(pkt);
> +        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
> +        struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
>          struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1);
>          extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad,
>                          &inner_l4, false);
> @@ -664,8 +674,8 @@ reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
>          icmp->icmp_csum = 0;
>          icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad);
>      } else {
> -        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> -        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt);
> +        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
> +        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
>          struct ovs_16aligned_ip6_hdr *inner_l3_6 =
>              (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
>          extract_l3_ipv6(&inner_key, inner_l3_6,
> @@ -702,11 +712,11 @@ un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>          pkt->md.ct_state |= CS_DST_NAT;
>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
> -            struct ip_header *nh = dp_packet_l3(pkt);
> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>              packet_set_ipv4_addr(pkt, &nh->ip_dst,
>                                   conn->key.src.addr.ipv4_aligned);
>          } else {
> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>                                   nh6->ip6_dst.be32,
>                                   &conn->key.src.addr.ipv6_aligned, true);
> @@ -720,11 +730,11 @@ un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>          pkt->md.ct_state |= CS_SRC_NAT;
>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
> -            struct ip_header *nh = dp_packet_l3(pkt);
> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>              packet_set_ipv4_addr(pkt, &nh->ip_src,
>                                   conn->key.dst.addr.ipv4_aligned);
>          } else {
> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>                                   nh6->ip6_src.be32,
>                                   &conn->key.dst.addr.ipv6_aligned, true);
> @@ -1320,6 +1330,7 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
>              write_ct_md(packet, zone, NULL, NULL, NULL);
>              continue;
>          }
> +
>          process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
>                      setlabel, nat_action_info, tp_src, tp_dst, helper);
>      }
> @@ -1901,9 +1912,18 @@ static bool
>  conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,
>                   struct conn_lookup_ctx *ctx, uint16_t zone)
>  {
> -    const struct eth_header *l2 = dp_packet_eth(pkt);
> -    const struct ip_header *l3 = dp_packet_l3(pkt);
> -    const char *l4 = dp_packet_l4(pkt);
> +    const struct eth_header *l2;
> +    const struct ip_header *l3;
> +    const char *l4;
> +
> +    /* We need the whole packet to parse the packet below */
> +    if (!dp_packet_is_linear(pkt)) {
> +        dp_packet_linearize(pkt);
> +    }

Same here. This is a heavy operation that most probably will affect
conntrack performance.


> +
> +    l2 = dp_packet_eth(pkt);
> +    l3 = dp_packet_l3(pkt, sizeof *l3);
> +    l4 = dp_packet_l4(pkt, sizeof *l4);
>  
>      memset(ctx, 0, sizeof *ctx);
>  
> @@ -2846,7 +2866,7 @@ terminate_number_str(char *str, uint8_t max_digits)
>  static void
>  get_ftp_ctl_msg(struct dp_packet *pkt, char *ftp_msg)
>  {
> -    struct tcp_header *th = dp_packet_l4(pkt);
> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>      char *tcp_hdr = (char *) th;
>      uint32_t tcp_payload_len = tcp_payload_length(pkt);
>      size_t tcp_payload_of_interest = MIN(tcp_payload_len,
> @@ -2888,7 +2908,7 @@ process_ftp_ctl_v4(struct conntrack *ct,
>                     char **ftp_data_v4_start,
>                     size_t *addr_offset_from_ftp_data_start)
>  {
> -    struct tcp_header *th = dp_packet_l4(pkt);
> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>      size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
>      char *tcp_hdr = (char *) th;
>      *ftp_data_v4_start = tcp_hdr + tcp_hdr_len;
> @@ -3034,7 +3054,7 @@ process_ftp_ctl_v6(struct conntrack *ct,
>                     size_t *addr_offset_from_ftp_data_start,
>                     size_t *addr_size, enum ct_alg_mode *mode)
>  {
> -    struct tcp_header *th = dp_packet_l4(pkt);
> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>      size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
>      char *tcp_hdr = (char *) th;
>      char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0};
> @@ -3167,7 +3187,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>                 const struct conn *conn_for_expectation,
>                 long long now, enum ftp_ctl_pkt ftp_ctl, bool nat)
>  {
> -    struct ip_header *l3_hdr = dp_packet_l3(pkt);
> +    struct ip_header *l3_hdr;
>      ovs_be32 v4_addr_rep = 0;
>      struct ct_addr v6_addr_rep;
>      size_t addr_offset_from_ftp_data_start;
> @@ -3176,6 +3196,13 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>      bool do_seq_skew_adj = true;
>      enum ct_alg_mode mode = CT_FTP_MODE_ACTIVE;
>  
> +    /* We need the whole packet to parse the packet below */
> +    if (!dp_packet_is_linear(pkt)) {
> +        dp_packet_linearize(pkt);
> +    }
> +
> +    l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
> +
>      if (detect_ftp_ctl_type(ctx, pkt) != ftp_ctl) {
>          return;
>      }
> @@ -3184,7 +3211,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>          do_seq_skew_adj = false;
>      }
>  
> -    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
> +    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>      int64_t seq_skew = 0;
>  
>      if (ftp_ctl == CT_FTP_CTL_OTHER) {
> @@ -3240,7 +3267,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>          OVS_NOT_REACHED();
>      }
>  
> -    struct tcp_header *th = dp_packet_l4(pkt);
> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>  
>      if (do_seq_skew_adj && seq_skew != 0) {
>          if (ctx->reply != conn_for_expectation->seq_skew_dir) {
> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
> index 806640b..b8f5242 100644
> --- a/lib/dp-packet.c
> +++ b/lib/dp-packet.c
> @@ -118,6 +118,9 @@ void
>  dp_packet_init_dpdk(struct dp_packet *b)
>  {
>      b->source = DPBUF_DPDK;
> +#ifdef DPDK_NETDEV
> +    b->mstate = NULL;
> +#endif
>  }
>  
>  /* Initializes 'b' as an empty dp_packet with an initial capacity of 'size'
> @@ -135,6 +138,21 @@ dp_packet_uninit(struct dp_packet *b)
>      if (b) {
>          if (b->source == DPBUF_MALLOC) {
>              free(dp_packet_base(b));
> +
> +#ifdef DPDK_NETDEV
> +            /* Packet has been "linearized" */
> +            if (b->mstate) {
> +                b->source = DPBUF_DPDK;
> +                b->mbuf.buf_addr = b->mstate->addr;
> +                b->mbuf.buf_len = b->mstate->len;
> +                b->mbuf.data_off = b->mstate->off;
> +
> +                free(b->mstate);
> +                b->mstate = NULL;
> +
> +                free_dpdk_buf((struct dp_packet *) b);
> +            }
> +#endif
>          } else if (b->source == DPBUF_DPDK) {
>  #ifdef DPDK_NETDEV
>              /* If this dp_packet was allocated by DPDK it must have been
> diff --git a/lib/dp-packet.h b/lib/dp-packet.h
> index cbf002c..9d42cbf 100644
> --- a/lib/dp-packet.h
> +++ b/lib/dp-packet.h
> @@ -27,7 +27,6 @@
>  
>  #include "netdev-dpdk.h"
>  #include "openvswitch/list.h"
> -#include "packets.h"
>  #include "util.h"
>  #include "flow.h"
>  
> @@ -46,6 +45,16 @@ enum OVS_PACKED_ENUM dp_packet_source {
>  
>  #define DP_PACKET_CONTEXT_SIZE 64
>  
> +#ifdef DPDK_NETDEV
> +/* Struct to save data for when a DPBUF_DPDK packet is converted to
> + * DPBUF_MALLOC. */
> +struct mbuf_state {
> +    void *addr;
> +    uint16_t len;
> +    uint16_t off;
> +};
> +#endif
> +
>  /* Buffer for holding packet data.  A dp_packet is automatically reallocated
>   * as necessary if it grows too large for the available memory.
>   * By default the packet type is set to Ethernet (PT_ETH).
> @@ -53,6 +62,7 @@ enum OVS_PACKED_ENUM dp_packet_source {
>  struct dp_packet {
>  #ifdef DPDK_NETDEV
>      struct rte_mbuf mbuf;       /* DPDK mbuf */
> +    struct mbuf_state *mstate;  /* Used when packet has been "linearized" */
>  #else
>      void *base_;                /* First byte of allocated space. */
>      uint16_t allocated_;        /* Number of bytes allocated. */
> @@ -85,6 +95,8 @@ struct dp_packet {
>      (char *) (((char *) BUF_ADDR) + BUF_LEN)
>  #endif
>  
> +static inline bool dp_packet_is_linear(const struct dp_packet *);
> +static inline void dp_packet_linearize(struct dp_packet *);
>  static inline void *dp_packet_data(const struct dp_packet *);
>  static inline void dp_packet_set_data(struct dp_packet *, void *);
>  static inline void *dp_packet_base(const struct dp_packet *);
> @@ -102,11 +114,12 @@ static inline void *dp_packet_eth(const struct dp_packet *);
>  static inline void dp_packet_reset_offsets(struct dp_packet *);
>  static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *);
>  static inline void dp_packet_set_l2_pad_size(struct dp_packet *, uint8_t);
> -static inline void *dp_packet_l2_5(const struct dp_packet *);
> +static inline void *dp_packet_l2_5(const struct dp_packet *, uint16_t size);
>  static inline void dp_packet_set_l2_5(struct dp_packet *, void *);
> -static inline void *dp_packet_l3(const struct dp_packet *);
> +static inline void *dp_packet_l3(const struct dp_packet *, uint16_t size);
>  static inline void dp_packet_set_l3(struct dp_packet *, void *);
> -static inline void *dp_packet_l4(const struct dp_packet *);
> +static inline size_t dp_packet_l3_size(const struct dp_packet *);
> +static inline void *dp_packet_l4(const struct dp_packet *, uint16_t size);
>  static inline void dp_packet_set_l4(struct dp_packet *, void *);
>  static inline size_t dp_packet_l4_size(const struct dp_packet *);
>  static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *);
> @@ -181,20 +194,18 @@ static inline void
>  dp_packet_delete(struct dp_packet *b)
>  {
>      if (b) {
> -        if (b->source == DPBUF_DPDK) {
> -            /* If this dp_packet was allocated by DPDK it must have been
> -             * created as a dp_packet */
> -            free_dpdk_buf((struct dp_packet*) b);
> -            return;
> -        }
> -
>          dp_packet_uninit(b);
> -        free(b);
> +
> +        if (b->source != DPBUF_DPDK) {
> +            free(b);
> +        }
>      }
>  }
>  
>  /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to
> - * byte 'offset'.  Otherwise, returns a null pointer. */
> + * byte 'offset'.  Otherwise, returns a null pointer. For DPDK packets, this
> + * means the 'offset' + 'size' must fall within the same mbuf (not necessarily
> + * the first mbuf), otherwise null is returned */
>  static inline void *
>  dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
>  {
> @@ -212,10 +223,13 @@ dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
>              buf = buf->next;
>          }
>  
> -        return buf ? rte_pktmbuf_mtod_offset(buf, char *, offset) : NULL;
> +        if (!buf || offset + size > buf->data_len) {
> +            return NULL;
> +        }
> +
> +        return rte_pktmbuf_mtod_offset(buf, char *, offset);
>      }
>  #endif
> -
>      return (char *) dp_packet_data(b) + offset;
>  }
>  
> @@ -314,20 +328,24 @@ dp_packet_pull(struct dp_packet *b, size_t size)
>      return data;
>  }
>  
> -#ifdef DPDK_NETDEV
>  /* Similar to dp_packet_try_pull() but doesn't actually pull any data, only
> - * checks if it could and returns true or false accordingly.
> - *
> - * Valid for dp_packets carrying mbufs only. */
> + * checks if it could and returns 'true' or 'false', accordingly. For DPDK
> + * packets, 'true' is only returned in case the 'offset' + 'size' falls within
> + * the first mbuf, otherwise 'false' is returned */
>  static inline bool
> -dp_packet_mbuf_may_pull(const struct dp_packet *b, size_t size) {
> -    if (size > b->mbuf.data_len) {
> +dp_packet_may_pull(const struct dp_packet *b, uint16_t offset, size_t size)
> +{
> +    if (offset == UINT16_MAX) {
> +        return false;
> +    }
> +#ifdef DPDK_NETDEV
> +    /* Offset needs to be within the first mbuf */
> +    if (offset + size > b->mbuf.data_len) {
>          return false;
>      }
> -
> -    return true;
> -}
>  #endif
> +    return (offset + size > dp_packet_size(b)) ? false : true;
> +}
>  
>  /* If 'b' has at least 'size' bytes of data, removes that many bytes from the
>   * head end of 'b' and returns the first byte removed.  Otherwise, returns a
> @@ -336,7 +354,7 @@ static inline void *
>  dp_packet_try_pull(struct dp_packet *b, size_t size)
>  {
>  #ifdef DPDK_NETDEV
> -    if (!dp_packet_mbuf_may_pull(b, size)) {
> +    if (!dp_packet_may_pull(b, 0, size)) {
>          return NULL;
>      }
>  #endif
> @@ -385,17 +403,13 @@ dp_packet_set_l2_pad_size(struct dp_packet *b, uint8_t pad_size)
>  }
>  
>  static inline void *
> -dp_packet_l2_5(const struct dp_packet *b)
> +dp_packet_l2_5(const struct dp_packet *b, uint16_t size)
>  {
> -#ifdef DPDK_NETDEV
> -    if (!dp_packet_mbuf_may_pull(b, b->l2_5_ofs)) {
> +    if (!dp_packet_may_pull(b, b->l2_5_ofs, size)) {
>          return NULL;
>      }
> -#endif
>  
> -    return b->l2_5_ofs != UINT16_MAX
> -           ? (char *) dp_packet_data(b) + b->l2_5_ofs
> -           : NULL;
> +    return (char *) dp_packet_data(b) + b->l2_5_ofs;
>  }
>  
>  static inline void
> @@ -407,17 +421,13 @@ dp_packet_set_l2_5(struct dp_packet *b, void *l2_5)
>  }
>  
>  static inline void *
> -dp_packet_l3(const struct dp_packet *b)
> +dp_packet_l3(const struct dp_packet *b, uint16_t size)
>  {
> -#ifdef DPDK_NETDEV
> -    if (!dp_packet_mbuf_may_pull(b, b->l3_ofs)) {
> +    if (!dp_packet_may_pull(b, b->l3_ofs, size)) {
>          return NULL;
>      }
> -#endif
>  
> -    return b->l3_ofs != UINT16_MAX
> -           ? (char *) dp_packet_data(b) + b->l3_ofs
> -           : NULL;
> +    return (char *) dp_packet_data(b) + b->l3_ofs;
>  }
>  
>  static inline void
> @@ -426,18 +436,34 @@ dp_packet_set_l3(struct dp_packet *b, void *l3)
>      b->l3_ofs = l3 ? (char *) l3 - (char *) dp_packet_data(b) : UINT16_MAX;
>  }
>  
> +/* Returns the size of the l3 header. Caller must make sure both l3_ofs and
> + * l4_ofs are set*/
> +static inline size_t
> +dp_packet_l3h_size(const struct dp_packet *b)
> +{
> +    return b->l4_ofs - b->l3_ofs;
> +}

Does that work on multi-seg? Or we assume L3 and L4 are always in
the same segment?

> +
> +static inline size_t
> +dp_packet_l3_size(const struct dp_packet *b)
> +{
> +    if (!dp_packet_may_pull(b, b->l3_ofs, 0)) {
> +        return 0;
> +    }
> +
> +    size_t l3_size = dp_packet_size(b) - b->l3_ofs;
> +
> +    return l3_size - dp_packet_l2_pad_size(b);
> +}
> +
>  static inline void *
> -dp_packet_l4(const struct dp_packet *b)
> +dp_packet_l4(const struct dp_packet *b, uint16_t size)
>  {
> -#ifdef DPDK_NETDEV
> -    if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
> +    if (!dp_packet_may_pull(b, b->l4_ofs, size)) {
>          return NULL;
>      }
> -#endif
>  
> -    return b->l4_ofs != UINT16_MAX
> -           ? (char *) dp_packet_data(b) + b->l4_ofs
> -           : NULL;
> +    return (char *) dp_packet_data(b) + b->l4_ofs;
>  }
>  
>  static inline void
> @@ -449,31 +475,13 @@ dp_packet_set_l4(struct dp_packet *b, void *l4)
>  static inline size_t
>  dp_packet_l4_size(const struct dp_packet *b)
>  {
> -#ifdef DPDK_NETDEV
> -    if (b->source == DPBUF_DPDK) {
> -        if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
> -            return 0;
> -        }
> -
> -        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
> -        size_t l4_size = mbuf->data_len - b->l4_ofs;
> -
> -        mbuf = mbuf->next;
> -        while (mbuf) {
> -            l4_size += mbuf->data_len;
> -
> -            mbuf = mbuf->next;
> -        }
> +    if (!dp_packet_may_pull(b, b->l4_ofs, 0)) {
> +        return 0;
> +    }
>  
> -        l4_size -= dp_packet_l2_pad_size(b);
> +    size_t l4_size = dp_packet_size(b) - b->l4_ofs;
>  
> -        return l4_size;
> -    }
> -#endif
> -    return b->l4_ofs != UINT16_MAX
> -        ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b)
> -        - dp_packet_l2_pad_size(b)
> -        : 0;
> +    return l4_size - dp_packet_l2_pad_size(b);
>  }
>  
>  static inline const void *
> @@ -482,11 +490,12 @@ dp_packet_get_tcp_payload(const struct dp_packet *b)
>      size_t l4_size = dp_packet_l4_size(b);
>  
>      if (OVS_LIKELY(l4_size >= TCP_HEADER_LEN)) {
> -        struct tcp_header *tcp = dp_packet_l4(b);
> +        struct tcp_header *tcp = dp_packet_l4(b, sizeof *tcp);
>          int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
>  
>          if (OVS_LIKELY(tcp_len >= TCP_HEADER_LEN && tcp_len <= l4_size)) {
> -            return (const char *)tcp + tcp_len;
> +            tcp = dp_packet_at(b, b->l4_ofs, tcp_len);
> +            return (tcp == NULL) ? NULL : tcp + tcp_len;
>          }
>      }
>      return NULL;
> @@ -496,28 +505,31 @@ static inline const void *
>  dp_packet_get_udp_payload(const struct dp_packet *b)
>  {
>      return OVS_LIKELY(dp_packet_l4_size(b) >= UDP_HEADER_LEN)
> -        ? (const char *)dp_packet_l4(b) + UDP_HEADER_LEN : NULL;
> +        ? (const char *) dp_packet_l4(b, UDP_HEADER_LEN) + UDP_HEADER_LEN
> +        : NULL;
>  }
>  
>  static inline const void *
>  dp_packet_get_sctp_payload(const struct dp_packet *b)
>  {
>      return OVS_LIKELY(dp_packet_l4_size(b) >= SCTP_HEADER_LEN)
> -        ? (const char *)dp_packet_l4(b) + SCTP_HEADER_LEN : NULL;
> +        ? (const char *) dp_packet_l4(b, SCTP_HEADER_LEN) + SCTP_HEADER_LEN
> +        : NULL;
>  }
>  
>  static inline const void *
>  dp_packet_get_icmp_payload(const struct dp_packet *b)
>  {
>      return OVS_LIKELY(dp_packet_l4_size(b) >= ICMP_HEADER_LEN)
> -        ? (const char *)dp_packet_l4(b) + ICMP_HEADER_LEN : NULL;
> +        ? (const char *) dp_packet_l4(b, ICMP_HEADER_LEN) + ICMP_HEADER_LEN
> +        : NULL;
>  }
>  
>  static inline const void *
>  dp_packet_get_nd_payload(const struct dp_packet *b)
>  {
>      return OVS_LIKELY(dp_packet_l4_size(b) >= ND_MSG_LEN)
> -        ? (const char *)dp_packet_l4(b) + ND_MSG_LEN : NULL;
> +        ? (const char *)dp_packet_l4(b, ND_MSG_LEN) + ND_MSG_LEN : NULL;
>  }
>  
>  #ifdef DPDK_NETDEV
> @@ -760,6 +772,60 @@ dp_packet_data(const struct dp_packet *b)
>             ? (char *) dp_packet_base(b) + __packet_data(b) : NULL;
>  }
>  
> +static inline bool
> +dp_packet_is_linear(const struct dp_packet *b OVS_UNUSED)
> +{
> +#ifdef DPDK_NETDEV
> +    if (b->source == DPBUF_DPDK) {
> +        return rte_pktmbuf_is_contiguous(&b->mbuf);
> +    }
> +#endif
> +    return true;
> +}
> +
> +/* Linearizes the data on packet 'b', if and only if 'b' is a DPBUF_DPDK
> + * packet, by copying the data into system's memory. After this the packet is
> + * effectively a DPBUF_MALLOC packet.
> + *
> + * This is an expensive operation which should only be performed as a last
> + * resort, when multi-segments are under use but data must be accessed
> + * linearly. */
> +static inline void
> +dp_packet_linearize(struct dp_packet *b OVS_UNUSED)
> +{
> +#ifdef DPDK_NETDEV
> +    if (!dp_packet_is_linear(b)) {
> +        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
> +        struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
> +        uint32_t pkt_len = dp_packet_size(pkt);
> +        struct mbuf_state *mstate = NULL;
> +        void *dst = xmalloc(pkt_len);
> +
> +        /* Copy packet's data to system's memory */
> +        if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
> +            return;
> +        }
> +
> +        /* Free all mbufs except for the first */
> +        dp_packet_clear(pkt);
> +
> +        /* Save mbuf's buf_addr to restore later */
> +        mstate = xmalloc(sizeof(*mstate));
> +        mstate->addr = pkt->mbuf.buf_addr;
> +        mstate->len = pkt->mbuf.buf_len;
> +        mstate->off = pkt->mbuf.data_off;
> +        pkt->mstate = mstate;
> +
> +        /* Tranform DPBUF_DPDK packet into a DPBUF_MALLOC packet */
> +        pkt->source = DPBUF_MALLOC;
> +        pkt->mbuf.buf_addr = dst;
> +        pkt->mbuf.buf_len = pkt_len;
> +        pkt->mbuf.data_off = 0;
> +        dp_packet_set_size(pkt, pkt_len);
> +    }


I suggest to invert the logic there:
       if (dp_packet_is_linear(b)) {
           return
       }

       struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
       struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
       uint32_t pkt_len = dp_packet_size(pkt);
       struct mbuf_state *mstate = NULL;
       void *dst = xmalloc(pkt_len);

       /* Copy packet's data to system's memory */
       if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
           return;
       }

Or even document the requirement and take it out because all callers
are doing like this:
       if (!dp_packet_is_linear()) {
           dp_packet_linearize();
       }    

> +#endif
> +}
> +
>  static inline void
>  dp_packet_set_data(struct dp_packet *b, void *data)
>  {
> @@ -837,6 +903,7 @@ dp_packet_mbuf_init(struct dp_packet *p OVS_UNUSED)
>      mbuf->ol_flags = mbuf->tx_offload = mbuf->packet_type = 0;
>      mbuf->nb_segs = 1;
>      mbuf->next = NULL;
> +    p->mstate = NULL;
>  #endif
>  }
>  
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index e322f55..43cddb6 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -5698,6 +5698,11 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_,
>              .support = dp_netdev_support,
>          };
>  
> +        /* Gather the whole data for printing the packet (if debug enabled) */
> +        if (!dp_packet_is_linear(packet_)) {
> +            dp_packet_linearize(packet_);
> +        }
> +
>          ofpbuf_init(&key, 0);
>          odp_flow_key_from_flow(&odp_parms, &key);
>          packet_str = ofp_dp_packet_to_string(packet_);
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index 2131503..ee11d51 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -1810,6 +1810,11 @@ dpif_netlink_operate__(struct dpif_netlink *dpif,
>                  }
>                  n_ops = i;
>              } else {
> +                /* We will need to pass the whole to encode the message */
> +                if (!dp_packet_is_linear(op->execute.packet)) {
> +                    dp_packet_linearize(op->execute.packet);
> +                }
> +
>                  dpif_netlink_encode_execute(dpif->dp_ifindex, &op->execute,
>                                              &aux->request);
>              }
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 4697a4d..514fae5 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1243,6 +1243,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
>          execute.probe = false;
>          execute.mtu = 0;
>          aux->error = dpif_execute(aux->dpif, &execute);
> +
>          log_execute_message(aux->dpif, &this_module, &execute,
>                              true, aux->error);
>  
> @@ -1395,6 +1396,7 @@ dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
>  
>                  case DPIF_OP_EXECUTE:
>                      COVERAGE_INC(dpif_execute);
> +
>                      log_execute_message(dpif, &this_module, &op->execute,
>                                          false, error);
>                      break;
> @@ -1822,6 +1824,13 @@ log_execute_message(const struct dpif *dpif,
>          uint64_t stub[1024 / 8];
>          struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub);
>  
> +        /* We will need the whole data for logging */
> +        struct dp_packet *p = CONST_CAST(struct dp_packet *,
> +                                         execute->packet);
> +        if (!dp_packet_is_linear(p)) {
> +            dp_packet_linearize(p);
> +        }
> +
>          packet = ofp_packet_to_string(dp_packet_data(execute->packet),
>                                        dp_packet_size(execute->packet),
>                                        execute->packet->packet_type);
> diff --git a/lib/flow.c b/lib/flow.c
> index bffee70..aa904d4 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -2958,34 +2958,35 @@ static void
>  flow_compose_l4_csum(struct dp_packet *p, const struct flow *flow,
>                       uint32_t pseudo_hdr_csum)
>  {
> -    size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
> +    //size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
> +    size_t l4_len = dp_packet_l4_size(p);
>  
>      if (!(flow->nw_frag & FLOW_NW_FRAG_ANY)
>          || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
>          if (flow->nw_proto == IPPROTO_TCP) {
> -            struct tcp_header *tcp = dp_packet_l4(p);
> +            struct tcp_header *tcp = dp_packet_l4(p, sizeof *tcp);
>  
>              tcp->tcp_csum = 0;
>              tcp->tcp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
>                                                        tcp, l4_len));
>          } else if (flow->nw_proto == IPPROTO_UDP) {
> -            struct udp_header *udp = dp_packet_l4(p);
> +            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
>  
>              udp->udp_csum = 0;
>              udp->udp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
>                                                        udp, l4_len));
>          } else if (flow->nw_proto == IPPROTO_ICMP) {
> -            struct icmp_header *icmp = dp_packet_l4(p);
> +            struct icmp_header *icmp = dp_packet_l4(p, sizeof *icmp);
>  
>              icmp->icmp_csum = 0;
>              icmp->icmp_csum = csum(icmp, l4_len);
>          } else if (flow->nw_proto == IPPROTO_IGMP) {
> -            struct igmp_header *igmp = dp_packet_l4(p);
> +            struct igmp_header *igmp = dp_packet_l4(p, sizeof *igmp);
>  
>              igmp->igmp_csum = 0;
>              igmp->igmp_csum = csum(igmp, l4_len);
>          } else if (flow->nw_proto == IPPROTO_ICMPV6) {
> -            struct icmp6_hdr *icmp = dp_packet_l4(p);
> +            struct icmp6_hdr *icmp = dp_packet_l4(p, sizeof *icmp);
>  
>              icmp->icmp6_cksum = 0;
>              icmp->icmp6_cksum = (OVS_FORCE uint16_t)
> @@ -3015,18 +3016,18 @@ packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
>          eth->eth_type = htons(dp_packet_size(p));
>      } else if (dl_type_is_ip_any(flow->dl_type)) {
>          uint32_t pseudo_hdr_csum;
> -        size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
> +        size_t l4_len = dp_packet_l4_size(p);
>  
>          if (flow->dl_type == htons(ETH_TYPE_IP)) {
> -            struct ip_header *ip = dp_packet_l3(p);
> +            struct ip_header *ip = dp_packet_l3(p, sizeof *ip);
>  
> -            ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
> +            ip->ip_tot_len = htons(dp_packet_l3_size(p));
>              ip->ip_csum = 0;
>              ip->ip_csum = csum(ip, sizeof *ip);
>  
>              pseudo_hdr_csum = packet_csum_pseudoheader(ip);
>          } else { /* ETH_TYPE_IPV6 */
> -            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p);
> +            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p, sizeof *nh);
>  
>              nh->ip6_plen = htons(l4_len);
>              pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
> @@ -3035,7 +3036,7 @@ packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
>          if ((!(flow->nw_frag & FLOW_NW_FRAG_ANY)
>               || !(flow->nw_frag & FLOW_NW_FRAG_LATER))
>              && flow->nw_proto == IPPROTO_UDP) {
> -            struct udp_header *udp = dp_packet_l4(p);
> +            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
>  
>              udp->udp_len = htons(l4_len + extra_size);
>          }
> @@ -3103,8 +3104,8 @@ flow_compose(struct dp_packet *p, const struct flow *flow,
>  
>          l4_len = flow_compose_l4(p, flow, l7, l7_len);
>  
> -        ip = dp_packet_l3(p);
> -        ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
> +        ip = dp_packet_l3(p, sizeof *ip);
> +        ip->ip_tot_len = htons(dp_packet_l3_size(p));
>          /* Checksum has already been zeroed by put_zeros call. */
>          ip->ip_csum = csum(ip, sizeof *ip);
>  
> @@ -3126,7 +3127,7 @@ flow_compose(struct dp_packet *p, const struct flow *flow,
>  
>          l4_len = flow_compose_l4(p, flow, l7, l7_len);
>  
> -        nh = dp_packet_l3(p);
> +        nh = dp_packet_l3(p, sizeof *nh);
>          nh->ip6_plen = htons(l4_len);
>  
>          pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
> diff --git a/lib/lacp.c b/lib/lacp.c
> index d6b36aa..ec92202 100644
> --- a/lib/lacp.c
> +++ b/lib/lacp.c
> @@ -190,8 +190,7 @@ parse_lacp_packet(const struct dp_packet *p)
>  {
>      const struct lacp_pdu *pdu;
>  
> -    pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
> -                    LACP_PDU_LEN);
> +    pdu = dp_packet_l3(p, LACP_PDU_LEN);
>  
>      if (pdu && pdu->subtype == 1
>          && pdu->actor_type == 1 && pdu->actor_len == 20
> diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
> index 6730301..af0cadb 100644
> --- a/lib/mcast-snooping.c
> +++ b/lib/mcast-snooping.c
> @@ -450,11 +450,11 @@ mcast_snooping_add_report(struct mcast_snooping *ms,
>      int count = 0;
>      int ngrp;
>  
> -    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
> -    igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
> +    igmpv3 = dp_packet_l4(p, IGMPV3_HEADER_LEN);
>      if (!igmpv3) {
>          return 0;
>      }
> +    offset = (char *) igmpv3 - (char *) dp_packet_data(p);
>      ngrp = ntohs(igmpv3->ngrp);
>      offset += IGMPV3_HEADER_LEN;
>      while (ngrp--) {
> @@ -502,11 +502,11 @@ mcast_snooping_add_mld(struct mcast_snooping *ms,
>      int ngrp;
>      bool ret;
>  
> -    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
> -    mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
> +    mld = dp_packet_l4(p, MLD_HEADER_LEN);
>      if (!mld) {
>          return 0;
>      }
> +    offset = (char *) mld - (char *) dp_packet_data(p);
>      ngrp = ntohs(mld->ngrp);
>      offset += MLD_HEADER_LEN;
>      addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
> diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
> index a153aa2..a545e7e 100644
> --- a/lib/netdev-bsd.c
> +++ b/lib/netdev-bsd.c
> @@ -701,6 +701,11 @@ netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
>      }
>  
>      for (i = 0; i < batch->count; i++) {
> +        /* We need the whole data to send the packet on the device */
> +        if (!dp_packet_is_linear(batch->packets[i])) {
> +            dp_packet_linearize(batch->packets[i]);
> +        }
> +


Perhaps for this case and the upcall too, we could have a dp_packet
function that provides data from the packet. E.g.:

/* buf must have at least 'size' bytes or NULL to return only the
   amount of bytes linearly available */
ssize_t dp_packet_data_read(packet, offset, size, &ptr, buf)
{
    if dp_packet_mbuf_may_pull(packet, offset, size) {
        /* zero copy */
        ptr = dp_packet_at(packet, offset, size);
        return len;
    }

    if (buf == NULL) {
        ptr = dp_packet_at(packet, offset, size);
        return <remaining bytes in the seg>;
    }

    ptr = buf;
    return dp_packet_copy(packet, offset, size, buf)
}
       
The callers can iterate and send chunks of data.


>          const void *data = dp_packet_data(batch->packets[i]);
>          size_t size = dp_packet_size(batch->packets[i]);
>  
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 2cf0563..9682fc3 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -233,7 +233,13 @@ dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s)
>  
>          ASSIGN_CONTAINER(txbuf_node, ovs_list_front(&s->txq), list_node);
>          txbuf = txbuf_node->pkt;
> -        retval = stream_send(s->stream, dp_packet_data(txbuf), dp_packet_size(txbuf));
> +
> +        if (!dp_packet_is_linear(txbuf)) {
> +            dp_packet_linearize(txbuf);
> +        }
> +
> +        retval = stream_send(s->stream, dp_packet_data(txbuf),
> +                             dp_packet_size(txbuf));
>  
>          if (retval > 0) {
>              dp_packet_pull(txbuf, retval);
> @@ -1088,6 +1094,11 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
>  
>      struct dp_packet *packet;
>      DP_PACKET_BATCH_FOR_EACH(i, packet, batch) {
> +        /* We need the whole data to send the packet on the device */
> +        if (!dp_packet_is_linear(packet)) {
> +            dp_packet_linearize(packet);
> +        }
> +
>          const void *buffer = dp_packet_data(packet);
>          size_t size = dp_packet_size(packet);
>  
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index c55aad8..a477bab 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -1380,6 +1380,11 @@ netdev_linux_sock_batch_send(int sock, int ifindex,
>  
>      struct dp_packet *packet;
>      DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
> +        /* We need the whole data to send the packet on the device */
> +        if (!dp_packet_is_linear(packet)) {
> +            dp_packet_linearize(packet);
> +        }
> +
>          iov[i].iov_base = dp_packet_data(packet);
>          iov[i].iov_len = dp_packet_size(packet);
>          mmsg[i].msg_hdr = (struct msghdr) { .msg_name = &sll,
> @@ -1433,8 +1438,14 @@ netdev_linux_tap_batch_send(struct netdev *netdev_,
>          ssize_t retval;
>          int error;
>  
> +        /* We need the whole data to send the packet on the device */
> +        if (!dp_packet_is_linear(packet)) {
> +            dp_packet_linearize(packet);
> +        }
> +
>          do {
> -            retval = write(netdev->tap_fd, dp_packet_data(packet), size);
> +            retval = write(netdev->tap_fd, dp_packet_data(packet),
> +                           size);
>              error = retval < 0 ? errno : 0;
>          } while (error == EINTR);
>  
> diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
> index 56baaa2..24c847d 100644
> --- a/lib/netdev-native-tnl.c
> +++ b/lib/netdev-native-tnl.c
> @@ -65,13 +65,18 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>      void *nh;
>      struct ip_header *ip;
>      struct ovs_16aligned_ip6_hdr *ip6;
> -    void *l4;
> +    char *l4;
>      int l3_size;
>  
> -    nh = dp_packet_l3(packet);
> +    /* We will need to perform the checksum on the whole data below */
> +    if (!dp_packet_is_linear(packet)) {
> +        dp_packet_linearize(packet);
> +    }
> +
> +    nh = dp_packet_l3(packet, sizeof *ip);
>      ip = nh;
>      ip6 = nh;
> -    l4 = dp_packet_l4(packet);
> +    l4 = dp_packet_l4(packet, sizeof *l4);
>  
>      if (!nh || !l4) {
>          return NULL;
> @@ -79,8 +84,7 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>  
>      *hlen = sizeof(struct eth_header);
>  
> -    l3_size = dp_packet_size(packet) -
> -              ((char *)nh - (char *)dp_packet_data(packet));
> +    l3_size = dp_packet_l3_size(packet);
>  
>      if (IP_VER(ip->ip_ihl_ver) == 4) {
>  
> @@ -151,6 +155,11 @@ netdev_tnl_push_ip_header(struct dp_packet *packet,
>      struct ip_header *ip;
>      struct ovs_16aligned_ip6_hdr *ip6;
>  
> +    /* We will need to perform the checksum on the whole data below */
> +    if (!dp_packet_is_linear(packet)) {
> +        dp_packet_linearize(packet);
> +    }
> +
>      eth = dp_packet_push_uninit(packet, size);
>      *ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
>  
> @@ -191,15 +200,16 @@ udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>          if (OVS_UNLIKELY(!dp_packet_l4_checksum_valid(packet))) {
>              uint32_t csum;
>              if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
> -                csum = packet_csum_pseudoheader6(dp_packet_l3(packet));
> +                csum = packet_csum_pseudoheader6(
> +                        dp_packet_l3(packet,
> +                                     sizeof(struct ip6_hdr)));
>              } else {
> -                csum = packet_csum_pseudoheader(dp_packet_l3(packet));
> +                csum = packet_csum_pseudoheader(
> +                        dp_packet_l3(packet,
> +                                     sizeof(struct ip_header)));
>              }
>  
> -            csum = csum_continue(csum, udp, dp_packet_size(packet) -
> -                                 ((const unsigned char *)udp -
> -                                  (const unsigned char *)dp_packet_eth(packet)
> -                                 ));
> +            csum = csum_continue(csum, udp, dp_packet_l4_size(packet));
>              if (csum_finish(csum)) {
>                  return NULL;
>              }
> @@ -373,9 +383,7 @@ parse_gre_header(struct dp_packet *packet,
>      if (greh->flags & htons(GRE_CSUM)) {
>          ovs_be16 pkt_csum;
>  
> -        pkt_csum = csum(greh, dp_packet_size(packet) -
> -                              ((const unsigned char *)greh -
> -                               (const unsigned char *)dp_packet_eth(packet)));
> +        pkt_csum = csum(greh, dp_packet_l4_size(packet));
>          if (pkt_csum) {
>              return -EINVAL;
>          }
> @@ -448,7 +456,8 @@ netdev_gre_push_header(const struct netdev *netdev,
>      greh = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
>  
>      if (greh->flags & htons(GRE_CSUM)) {
> -        ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
> +        greh = dp_packet_l4(packet, sizeof *greh);
> +        ovs_be16 *csum_opt = (ovs_be16 *) greh;
>          *csum_opt = csum(greh, ip_tot_size);
>      }
>  
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> index 5831d1f..8fdc4f9 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -70,7 +70,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(packet, sizeof(*nh));
>      ovs_be32 ip_src_nh;
>      ovs_be32 ip_dst_nh;
>      ovs_be32 new_ip_src;
> @@ -140,7 +140,7 @@ 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(packet, sizeof *nh);
>      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);
> @@ -160,7 +160,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(packet, sizeof *th);
>  
>      if (OVS_LIKELY(th && dp_packet_get_tcp_payload(packet))) {
>          packet_set_tcp_port(packet,
> @@ -173,7 +173,7 @@ 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(packet, sizeof *uh);
>  
>      if (OVS_LIKELY(uh && dp_packet_get_udp_payload(packet))) {
>          packet_set_udp_port(packet,
> @@ -186,7 +186,7 @@ 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(packet, sizeof *sh);
>  
>      if (OVS_LIKELY(sh && dp_packet_get_sctp_payload(packet))) {
>          packet_set_sctp_port(packet,
> @@ -205,7 +205,7 @@ 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(packet, sizeof *arp);
>  
>      if (!mask) {
>          arp->ar_op = key->arp_op;
> @@ -231,8 +231,16 @@ static void
>  odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
>             const struct ovs_key_nd *mask)
>  {
> -    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
> -    const struct ovs_nd_lla_opt *lla_opt = dp_packet_get_nd_payload(packet);
> +    const struct ovs_nd_msg *ns;
> +    const struct ovs_nd_lla_opt *lla_opt;
> +
> +    /* To orocess neighbor discovery options, we need the whole packet */
> +    if (!dp_packet_is_linear(packet)) {
> +        dp_packet_linearize(packet);
> +    }
> +
> +    ns = dp_packet_l4(packet, sizeof *ns);
> +    lla_opt = dp_packet_get_nd_payload(packet);
>  
>      if (OVS_LIKELY(ns && lla_opt)) {
>          int bytes_remain = dp_packet_l4_size(packet) - sizeof(*ns);
> @@ -275,7 +283,7 @@ static void
>  odp_set_nsh(struct dp_packet *packet, const struct nlattr *a, bool has_mask)
>  {
>      struct ovs_key_nsh key, mask;
> -    struct nsh_hdr *nsh = dp_packet_l3(packet);
> +    struct nsh_hdr *nsh = dp_packet_l3(packet, sizeof *nsh);
>      uint8_t mdtype = nsh_md_type(nsh);
>      ovs_be32 path_hdr;
>  
> @@ -522,7 +530,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
>          break;
>  
>      case OVS_KEY_ATTR_MPLS:
> -        mh = dp_packet_l2_5(packet);
> +        mh = dp_packet_l2_5(packet, sizeof *mh);
>          if (mh) {
>              put_16aligned_be32(&mh->mpls_lse, nl_attr_get_be32(a)
>                                 | (get_16aligned_be32(&mh->mpls_lse)
> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> index e05a969..37db260 100644
> --- a/lib/ofp-print.c
> +++ b/lib/ofp-print.c
> @@ -84,21 +84,21 @@ ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
>      l4_size = dp_packet_l4_size(&buf);
>  
>      if (flow.nw_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
> -        struct tcp_header *th = dp_packet_l4(&buf);
> +        struct tcp_header *th = dp_packet_l4(&buf, sizeof *th);
>          ds_put_format(&ds, " tcp_csum:%"PRIx16, ntohs(th->tcp_csum));
>      } else if (flow.nw_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
> -        struct udp_header *uh = dp_packet_l4(&buf);
> +        struct udp_header *uh = dp_packet_l4(&buf, sizeof *uh);
>          ds_put_format(&ds, " udp_csum:%"PRIx16, ntohs(uh->udp_csum));
>      } else if (flow.nw_proto == IPPROTO_SCTP && l4_size >= SCTP_HEADER_LEN) {
> -        struct sctp_header *sh = dp_packet_l4(&buf);
> +        struct sctp_header *sh = dp_packet_l4(&buf, sizeof *sh);
>          ds_put_format(&ds, " sctp_csum:%"PRIx32,
>                        ntohl(get_16aligned_be32(&sh->sctp_csum)));
>      } else if (flow.nw_proto == IPPROTO_ICMP && l4_size >= ICMP_HEADER_LEN) {
> -        struct icmp_header *icmph = dp_packet_l4(&buf);
> +        struct icmp_header *icmph = dp_packet_l4(&buf, sizeof *icmph);
>          ds_put_format(&ds, " icmp_csum:%"PRIx16,
>                        ntohs(icmph->icmp_csum));
>      } else if (flow.nw_proto == IPPROTO_ICMPV6 && l4_size >= ICMP6_HEADER_LEN) {
> -        struct icmp6_header *icmp6h = dp_packet_l4(&buf);
> +        struct icmp6_header *icmp6h = dp_packet_l4(&buf, sizeof *icmp6h);
>          ds_put_format(&ds, " icmp6_csum:%"PRIx16,
>                        ntohs(icmp6h->icmp6_cksum));
>      }
> diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
> index 05c1dd4..39d677a 100644
> --- a/lib/ovs-lldp.c
> +++ b/lib/ovs-lldp.c
> @@ -668,7 +668,8 @@ lldp_process_packet(struct lldp *lldp, const struct dp_packet *p)
>  {
>      if (lldp) {
>          lldpd_recv(lldp->lldpd, lldpd_first_hardware(lldp->lldpd),
> -                   (char *) dp_packet_data(p), dp_packet_size(p));
> +                   (char *) dp_packet_data(p),
> +                   dp_packet_size(p));
>      }
>  }
>  
> diff --git a/lib/packets.c b/lib/packets.c
> index 38bfb60..6ae5118 100644
> --- a/lib/packets.c
> +++ b/lib/packets.c
> @@ -260,8 +260,8 @@ push_eth(struct dp_packet *packet, const struct eth_addr *dst,
>  void
>  pop_eth(struct dp_packet *packet)
>  {
> -    char *l2_5 = dp_packet_l2_5(packet);
> -    char *l3 = dp_packet_l3(packet);
> +    char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
> +    char *l3 = dp_packet_l3(packet, sizeof *l3);
>      ovs_be16 ethertype;
>      int increment;
>  
> @@ -292,10 +292,10 @@ set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
>  
>      if (eth_type_vlan(eh->eth_type)) {
>          ovs_be16 *p;
> -        char *l2_5 = dp_packet_l2_5(packet);
> +        char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
>  
>          p = ALIGNED_CAST(ovs_be16 *,
> -                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet)) - 2);
> +                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet, -2)));
>          *p = eth_type;
>      } else {
>          eh->eth_type = eth_type;
> @@ -359,7 +359,7 @@ set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse)
>  {
>      /* Packet type should be MPLS to set label stack entry. */
>      if (is_mpls(packet)) {
> -        struct mpls_hdr *mh = dp_packet_l2_5(packet);
> +        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
>  
>          /* Update mpls label stack entry. */
>          put_16aligned_be32(&mh->mpls_lse, mpls_lse);
> @@ -401,7 +401,7 @@ void
>  pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
>  {
>      if (is_mpls(packet)) {
> -        struct mpls_hdr *mh = dp_packet_l2_5(packet);
> +        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
>          size_t len = packet->l2_5_ofs;
>  
>          set_ethertype(packet, ethtype);
> @@ -449,7 +449,7 @@ push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)
>  bool
>  pop_nsh(struct dp_packet *packet)
>  {
> -    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
> +    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet, sizeof *nsh);
>      size_t length;
>      uint32_t next_pt;
>  
> @@ -975,16 +975,16 @@ 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);
> +    struct ip_header *nh = dp_packet_l3(packet, sizeof *nh);
>      ovs_be32 old_addr = get_16aligned_be32(addr);
>      size_t l4_size = dp_packet_l4_size(packet);
>  
>      if (nh->ip_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
> -        struct tcp_header *th = dp_packet_l4(packet);
> +        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>  
>          th->tcp_csum = recalc_csum32(th->tcp_csum, old_addr, new_addr);
>      } else if (nh->ip_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN ) {
> -        struct udp_header *uh = dp_packet_l4(packet);
> +        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
>  
>          if (uh->udp_csum) {
>              uh->udp_csum = recalc_csum32(uh->udp_csum, old_addr, new_addr);
> @@ -1007,12 +1007,20 @@ packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr)
>      const struct ovs_16aligned_ip6_hdr *nh;
>      size_t len;
>      size_t remaining;
> -    uint8_t *data = dp_packet_l3(packet);
> +    uint8_t *data;
>  
> -    remaining = packet->l4_ofs - packet->l3_ofs;
> +    remaining = dp_packet_l3h_size(packet);
>      if (remaining < sizeof *nh) {
>          return false;
>      }
> +
> +    /* We will need the whole data for processing the headers below */
> +    if (!dp_packet_is_linear(packet)) {
> +        dp_packet_linearize(packet);
> +    }
> +
> +    data = dp_packet_l3(packet, sizeof *nh);
> +
>      nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
>      data += sizeof *nh;
>      remaining -= sizeof *nh;
> @@ -1088,11 +1096,11 @@ packet_update_csum128(struct dp_packet *packet, uint8_t proto,
>      size_t l4_size = dp_packet_l4_size(packet);
>  
>      if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
> -        struct tcp_header *th = dp_packet_l4(packet);
> +        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>  
>          th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
>      } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
> -        struct udp_header *uh = dp_packet_l4(packet);
> +        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
>  
>          if (uh->udp_csum) {
>              uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
> @@ -1102,7 +1110,7 @@ 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);
> +        struct icmp6_header *icmp = dp_packet_l4(packet, sizeof *icmp);
>  
>          icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr, new_addr);
>      }
> @@ -1144,7 +1152,7 @@ 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(packet, sizeof *nh);
>  
>      if (get_16aligned_be32(&nh->ip_src) != src) {
>          packet_set_ipv4_addr(packet, &nh->ip_src, src);
> @@ -1180,7 +1188,7 @@ 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(packet, sizeof *nh);
>      uint8_t proto = 0;
>      bool rh_present;
>  
> @@ -1215,7 +1223,7 @@ 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);
> +    struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>  
>      packet_set_port(&th->tcp_src, src, &th->tcp_csum);
>      packet_set_port(&th->tcp_dst, dst, &th->tcp_csum);
> @@ -1227,7 +1235,7 @@ 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(packet, sizeof *uh);
>  
>      if (uh->udp_csum) {
>          packet_set_port(&uh->udp_src, src, &uh->udp_csum);
> @@ -1248,7 +1256,7 @@ 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);
> +    struct sctp_header *sh = dp_packet_l4(packet, sizeof *sh);
>      ovs_be32 old_csum, old_correct_csum, new_csum;
>      uint16_t tp_len = dp_packet_l4_size(packet);
>  
> @@ -1269,7 +1277,7 @@ 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);
> +    struct icmp_header *ih = dp_packet_l4(packet, sizeof(*ih));
>      ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
>      ovs_be16 new_tc = htons(type << 8 | code);
>  
> @@ -1293,7 +1301,12 @@ packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
>          return;
>      }
>  
> -    ns = dp_packet_l4(packet);
> +    /* To process neighbor discovery options, we need the whole packet */
> +    if (!dp_packet_is_linear(packet)) {
> +        dp_packet_linearize(packet);
> +    }
> +
> +    ns = dp_packet_l4(packet, sizeof *ns);
>      opt = &ns->options[0];
>      bytes_remain -= sizeof(*ns);
>  
> @@ -1431,7 +1444,7 @@ compose_arp(struct dp_packet *b, uint16_t arp_op,
>      eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
>      eth->eth_src = arp_sha;
>  
> -    struct arp_eth_header *arp = dp_packet_l3(b);
> +    struct arp_eth_header *arp = dp_packet_l3(b, sizeof *arp);
>      arp->ar_op = htons(arp_op);
>      arp->ar_sha = arp_sha;
>      arp->ar_tha = arp_tha;
> @@ -1475,7 +1488,7 @@ compose_ipv6(struct dp_packet *packet, uint8_t proto,
>      struct ip6_hdr *nh;
>      void *data;
>  
> -    nh = dp_packet_l3(packet);
> +    nh = dp_packet_l3(packet, sizeof *nh);
>      nh->ip6_vfc = 0x60;
>      nh->ip6_nxt = proto;
>      nh->ip6_plen = htons(size);
> @@ -1514,7 +1527,8 @@ compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
>      packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
>  
>      ns->icmph.icmp6_cksum = 0;
> -    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
> +    icmp_csum = packet_csum_pseudoheader6(
> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>      ns->icmph.icmp6_cksum = csum_finish(
>          csum_continue(icmp_csum, ns, ND_MSG_LEN + ND_LLA_OPT_LEN));
>  }
> @@ -1545,7 +1559,8 @@ compose_nd_na(struct dp_packet *b,
>      packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
>  
>      na->icmph.icmp6_cksum = 0;
> -    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
> +    icmp_csum = packet_csum_pseudoheader6(
> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>      na->icmph.icmp6_cksum = csum_finish(csum_continue(
>          icmp_csum, na, ND_MSG_LEN + ND_LLA_OPT_LEN));
>  }
> @@ -1596,7 +1611,8 @@ compose_nd_ra(struct dp_packet *b,
>      }
>  
>      ra->icmph.icmp6_cksum = 0;
> -    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
> +    uint32_t icmp_csum = packet_csum_pseudoheader6(
> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>          icmp_csum, ra, RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_len));
>  }
> @@ -1610,10 +1626,10 @@ packet_put_ra_prefix_opt(struct dp_packet *b,
>                           const ovs_be128 prefix)
>  {
>      size_t prev_l4_size = dp_packet_l4_size(b);
> -    struct ip6_hdr *nh = dp_packet_l3(b);
> +    struct ip6_hdr *nh = dp_packet_l3(b, sizeof *nh);
>      nh->ip6_plen = htons(prev_l4_size + ND_PREFIX_OPT_LEN);
>  
> -    struct ovs_ra_msg *ra = dp_packet_l4(b);
> +    struct ovs_ra_msg *ra = dp_packet_l4(b, sizeof *ra);
>      struct ovs_nd_prefix_opt *prefix_opt =
>          dp_packet_put_uninit(b, sizeof *prefix_opt);
>      prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
> @@ -1626,7 +1642,8 @@ packet_put_ra_prefix_opt(struct dp_packet *b,
>      memcpy(prefix_opt->prefix.be32, prefix.be32, sizeof(ovs_be32[4]));
>  
>      ra->icmph.icmp6_cksum = 0;
> -    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
> +    uint32_t icmp_csum = packet_csum_pseudoheader6(
> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>          icmp_csum, ra, prev_l4_size + ND_PREFIX_OPT_LEN));
>  }
> @@ -1684,12 +1701,12 @@ 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(pkt, sizeof *ip6);
>  
>          put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
>                                  htonl(IP_ECN_CE << 20));
>      } else {
> -        struct ip_header *nh = dp_packet_l3(pkt);
> +        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>          uint8_t tos = nh->ip_tos;
>  
>          tos |= IP_ECN_CE;
> diff --git a/lib/pcap-file.c b/lib/pcap-file.c
> index ea5cfa3..6340862 100644
> --- a/lib/pcap-file.c
> +++ b/lib/pcap-file.c
> @@ -315,7 +315,7 @@ tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
>          || !l7) {
>          return NULL;
>      }
> -    tcp = dp_packet_l4(packet);
> +    tcp = dp_packet_l4(packet, sizeof *tcp);
>      flags = TCP_FLAGS(tcp->tcp_ctl);
>      l7_length = (char *) dp_packet_tail(packet) - l7;
>      seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 0cc964a..220b8c0 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1381,12 +1381,18 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>      case SFLOW_UPCALL:
>          if (upcall->sflow) {
>              struct dpif_sflow_actions sflow_actions;
> +            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
>  
>              memset(&sflow_actions, 0, sizeof sflow_actions);
>  
>              actions_len = dpif_read_actions(udpif, upcall, flow,
>                                              upcall->type, &sflow_actions);
> -            dpif_sflow_received(upcall->sflow, packet, flow,
> +            /* Gather the whole data */
> +            if (!dp_packet_is_linear(p)) {
> +                dp_packet_linearize(p);
> +            }
> +
> +            dpif_sflow_received(upcall->sflow, p, flow,
>                                  flow->in_port.odp_port, &upcall->cookie,
>                                  actions_len > 0 ? &sflow_actions : NULL);
>          }
> @@ -1447,6 +1453,12 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>  
>              const struct frozen_state *state = &recirc_node->state;
>  
> +            /* Gather the whole data */
> +            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
> +            if (!dp_packet_is_linear(p)) {
> +                dp_packet_linearize(p);
> +            }
> +
>              struct ofproto_async_msg *am = xmalloc(sizeof *am);
>              *am = (struct ofproto_async_msg) {
>                  .controller_id = cookie->controller.controller_id,
> @@ -1454,9 +1466,9 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>                  .pin = {
>                      .up = {
>                          .base = {
> -                            .packet = xmemdup(dp_packet_data(packet),
> -                                              dp_packet_size(packet)),
> -                            .packet_len = dp_packet_size(packet),
> +                            .packet = xmemdup(dp_packet_data(p),
> +                                              dp_packet_size(p)),
> +                            .packet_len = dp_packet_size(p),
>                              .reason = cookie->controller.reason,
>                              .table_id = state->table_id,
>                              .cookie = get_32aligned_be64(
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 84cce81..26e3437 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -1735,7 +1735,8 @@ stp_process_packet(const struct xport *xport, const struct dp_packet *packet)
>      }
>  
>      if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
> -        stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload));
> +        stp_received_bpdu(sp, dp_packet_data(&payload),
> +                          dp_packet_size(&payload));
>      }
>  }
>  
> @@ -1786,7 +1787,8 @@ rstp_process_packet(const struct xport *xport, const struct dp_packet *packet)
>      }
>  
>      if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
> -        rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload),
> +        rstp_port_received_bpdu(xport->rstp_port,
> +                                dp_packet_data(&payload),
>                                  dp_packet_size(&payload));
>      }
>  }
> @@ -2556,11 +2558,10 @@ update_mcast_snooping_table4__(const struct xlate_ctx *ctx,
>  {
>      const struct igmp_header *igmp;
>      int count;
> -    size_t offset;
>      ovs_be32 ip4 = flow->igmp_group_ip4;
>  
> -    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
> -    igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
> +    igmp = dp_packet_l4(packet, IGMP_HEADER_LEN);
> +
>      if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
>          xlate_report_debug(ctx, OFT_DETAIL,
>                             "multicast snooping received bad IGMP "
> @@ -2616,13 +2617,11 @@ update_mcast_snooping_table6__(const struct xlate_ctx *ctx,
>  {
>      const struct mld_header *mld;
>      int count;
> -    size_t offset;
>  
> -    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
> -    mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
> +    mld = dp_packet_l4(packet, MLD_HEADER_LEN);
>  
>      if (!mld ||
> -        packet_csum_upperlayer6(dp_packet_l3(packet),
> +        packet_csum_upperlayer6(dp_packet_l3(packet, sizeof(struct ip6_hdr)),
>                                  mld, IPPROTO_ICMPV6,
>                                  dp_packet_l4_size(packet)) != 0) {
>          xlate_report_debug(ctx, OFT_DETAIL, "multicast snooping received "
> @@ -2925,6 +2924,13 @@ xlate_normal(struct xlate_ctx *ctx)
>          && is_ip_any(flow)) {
>          struct mcast_snooping *ms = ctx->xbridge->ms;
>          struct mcast_group *grp = NULL;
> +        struct dp_packet *p = CONST_CAST(struct dp_packet *,
> +                                         ctx->xin->packet);
> +
> +        /* We will need the whole data for processing the packet below */
> +        if (!dp_packet_is_linear(p)) {
> +            dp_packet_linearize(p);
> +        }
>  
>          if (is_igmp(flow, wc)) {
>              /*
> @@ -3213,7 +3219,8 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>      const struct flow *flow = &ctx->xin->flow;
>      struct flow_wildcards *wc = ctx->wc;
>      const struct xbridge *xbridge = ctx->xbridge;
> -    const struct dp_packet *packet = ctx->xin->packet;
> +    struct dp_packet *packet = CONST_CAST(struct dp_packet *,
> +                                          ctx->xin->packet);
>      enum slow_path_reason slow;
>  
>      if (!xport) {
> @@ -3225,6 +3232,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>          slow = SLOW_CFM;
>      } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
>          if (packet) {
> +            /* Gather the whole data for further processing */
> +            if (!dp_packet_is_linear(packet)) {
> +                dp_packet_linearize(packet);
> +            }
> +
>              bfd_process_packet(xport->bfd, flow, packet);
>              /* If POLL received, immediately sends FINAL back. */
>              if (bfd_should_send_packet(xport->bfd)) {
> @@ -3241,6 +3253,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>      } else if ((xbridge->stp || xbridge->rstp) &&
>                 stp_should_process_flow(flow, wc)) {
>          if (packet) {
> +            /* Gather the whole data for further processing */
> +            if (!dp_packet_is_linear(packet)) {
> +                dp_packet_linearize(packet);
> +            }
> +
>              xbridge->stp
>                  ? stp_process_packet(xport, packet)
>                  : rstp_process_packet(xport, packet);
> @@ -3248,6 +3265,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>          slow = SLOW_STP;
>      } else if (xport->lldp && lldp_should_process_flow(xport->lldp, flow)) {
>          if (packet) {
> +            /* Gather the whole data for further processing */
> +            if (!dp_packet_is_linear(packet)) {
> +                dp_packet_linearize(packet);
> +            }
> +
>              lldp_process_packet(xport->lldp, packet);
>          }
>          slow = SLOW_LLDP;
> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
> index 8ae4c9e..cda1135 100644
> --- a/ovn/controller/pinctrl.c
> +++ b/ovn/controller/pinctrl.c
> @@ -213,7 +213,7 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
>      eth->eth_dst = ip_flow->dl_dst;
>      eth->eth_src = ip_flow->dl_src;
>  
> -    struct arp_eth_header *arp = dp_packet_l3(&packet);
> +    struct arp_eth_header *arp = dp_packet_l3(&packet, sizeof(*arp));
>      arp->ar_op = htons(ARP_OP_REQUEST);
>      arp->ar_sha = ip_flow->dl_src;
>      put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
> @@ -291,9 +291,10 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
>          ih->icmp6_base.icmp6_cksum = 0;
>  
>          uint8_t *data = dp_packet_put_zeros(&packet, sizeof *nh);
> -        memcpy(data, dp_packet_l3(pkt_in), sizeof(*nh));
> +        memcpy(data, dp_packet_l3(pkt_in, sizeof(*nh)), sizeof(*nh));
>  
> -        icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
> +        icmpv6_csum = packet_csum_pseudoheader6(
> +            dp_packet_l3(&packet, sizeof(struct ovs_16aligned_ip6_hdr)));
>          ih->icmp6_base.icmp6_cksum = csum_finish(
>              csum_continue(icmpv6_csum, ih,
>                            sizeof(*nh) + ICMP6_ERROR_HEADER_LEN));
> @@ -355,7 +356,7 @@ pinctrl_handle_tcp_reset(const struct flow *ip_flow, struct dp_packet *pkt_in,
>      }
>  
>      struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
> -    struct tcp_header *tcp_in = dp_packet_l4(pkt_in);
> +    struct tcp_header *tcp_in = dp_packet_l4(pkt_in, sizeof(*tcp_in));
>      dp_packet_set_l4(&packet, th);
>      th->tcp_ctl = TCP_CTL(TCP_RST, 5);
>      if (ip_flow->tcp_flags & htons(TCP_ACK)) {
> @@ -534,7 +535,7 @@ pinctrl_handle_put_dhcp_opts(
>  
>      udp->udp_len = htons(new_l4_size);
>  
> -    struct ip_header *out_ip = dp_packet_l3(&pkt_out);
> +    struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
>      out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
>      udp->udp_csum = 0;
>      /* Checksum needs to be initialized to zero. */
> @@ -709,7 +710,7 @@ pinctrl_handle_put_dhcpv6_opts(
>          goto exit;
>      }
>  
> -    struct udp_header *in_udp = dp_packet_l4(pkt_in);
> +    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof(*in_udp));
>      const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in);
>      if (!in_udp || !in_dhcpv6_data) {
>          VLOG_WARN_RL(&rl, "truncated dhcpv6 packet");
> @@ -824,11 +825,13 @@ pinctrl_handle_put_dhcpv6_opts(
>      out_udp->udp_len = htons(new_l4_size);
>      out_udp->udp_csum = 0;
>  
> -    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out);
> +    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out,
> +                                                         sizeof *out_ip6);
>      out_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = out_udp->udp_len;
>  
>      uint32_t csum;
> -    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out));
> +    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out,
> +        sizeof(struct ovs_16aligned_ip6_hdr)));
>      csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) -
>                           ((const unsigned char *)out_udp -
>                           (const unsigned char *)dp_packet_eth(&pkt_out)));
> @@ -916,7 +919,7 @@ pinctrl_handle_dns_lookup(
>          goto exit;
>      }
>  
> -    struct udp_header *in_udp = dp_packet_l4(pkt_in);
> +    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof *in_udp);
>      size_t udp_len = ntohs(in_udp->udp_len);
>      size_t l4_len = dp_packet_l4_size(pkt_in);
>      uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
> @@ -1081,14 +1084,14 @@ pinctrl_handle_dns_lookup(
>  
>      struct eth_header *eth = dp_packet_data(&pkt_out);
>      if (eth->eth_type == htons(ETH_TYPE_IP)) {
> -        struct ip_header *out_ip = dp_packet_l3(&pkt_out);
> +        struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
>          out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs
>                                     + new_l4_size);
>          /* Checksum needs to be initialized to zero. */
>          out_ip->ip_csum = 0;
>          out_ip->ip_csum = csum(out_ip, sizeof *out_ip);
>      } else {
> -        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
> +        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
>          nh->ip6_plen = htons(new_l4_size);
>  
>          /* IPv6 needs UDP checksum calculated */
> @@ -2487,9 +2490,9 @@ pinctrl_handle_put_nd_ra_opts(
>      dp_packet_put(&pkt_out, userdata->data, userdata->size);
>  
>      /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
> -    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
> +    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
>      nh->ip6_plen = htons(userdata->size);
> -    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
> +    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out, sizeof *ra);
>      ra->icmph.icmp6_cksum = 0;
>      uint32_t icmp_csum = packet_csum_pseudoheader6(nh);
>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
> diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c
> index 12017ea..72d4ecc 100644
> --- a/tests/test-conntrack.c
> +++ b/tests/test-conntrack.c
> @@ -46,7 +46,7 @@ prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
>          dp_packet_put_hex(pkt, payload, NULL);
>          flow_extract(pkt, &flow);
>  
> -        udp = dp_packet_l4(pkt);
> +        udp = dp_packet_l4(pkt, sizeof *udp);
>          udp->udp_src = htons(ntohs(udp->udp_src) + tid);
>  
>          if (change) {
> diff --git a/tests/test-rstp.c b/tests/test-rstp.c
> index 01aeaf8..a0125d1 100644
> --- a/tests/test-rstp.c
> +++ b/tests/test-rstp.c
> @@ -86,8 +86,12 @@ send_bpdu(struct dp_packet *pkt, void *port_, void *b_)
>      assert(port_no < b->n_ports);
>      lan = b->ports[port_no];
>      if (lan) {
> -        const void *data = dp_packet_l3(pkt);
> -        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
> +        if (!dp_packet_is_linear(pkt)) {
> +            dp_packet_linearize(pkt);
> +        }
> +
> +        const char *data = dp_packet_l3(pkt, sizeof *data);
> +        size_t size = dp_packet_size(pkt);
>          int i;
>  
>          for (i = 0; i < lan->n_conns; i++) {
> diff --git a/tests/test-stp.c b/tests/test-stp.c
> index c85c99d..c0f566c 100644
> --- a/tests/test-stp.c
> +++ b/tests/test-stp.c
> @@ -94,8 +94,12 @@ send_bpdu(struct dp_packet *pkt, int port_no, void *b_)
>      assert(port_no < b->n_ports);
>      lan = b->ports[port_no];
>      if (lan) {
> -        const void *data = dp_packet_l3(pkt);
> -        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
> +        if (!dp_packet_is_linear(pkt)) {
> +            dp_packet_linearize(pkt);
> +        }
> +
> +        const char *data = dp_packet_l3(pkt, sizeof *data);
> +        size_t size = dp_packet_size(pkt);
>          int i;
>  
>          for (i = 0; i < lan->n_conns; i++) {
> -- 
> 2.7.4
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Lam, Tiago Oct. 5, 2018, 2:51 p.m. | #2
On 03/10/2018 19:26, Flavio Leitner wrote:
> 
> Hi Tiago,
> 
> There is a lot to digest in this patch, so I only have a general
> comment about it for now.
> 
> See below.
> 
> On Fri, Sep 28, 2018 at 05:15:10PM +0100, Tiago Lam wrote:
>> Previous commits have added support to the dp_packet API to handle
>> multi-segmented packets, where data is not stored contiguously in
>> memory. However, in some cases, it is inevitable and data must be
>> provided contiguously. Examples of such cases are when performing csums
>> over the entire packet data, or when write()'ing to a file descriptor
>> (for a tap interface, for example). For such cases, the dp_packet API
>> has been extended to provide a way to transform a multi-segmented
>> DPBUF_DPDK packet into a DPBUF_MALLOC system packet (at the expense of a
>> copy of memory). If the packet's data is already stored in memory
>> contigously then there's no need to convert the packet.
>>
>> Thus, the main use cases that were assuming that a dp_packet's data is
>> always held contiguously in memory were changed to make use of the new
>> "linear functions" in the dp_packet API when there's a need to traverse
>> the entire's packet data. Per the example above, when the packet's data
>> needs to be write() to the tap's file descriptor, or when the conntrack
>> module needs to verify a packet's checksum, the data is now linearized.
>>
>> Additionally, the layer functions, such as dp_packet_l3() and variants,
>> have been modified to check if there's enough data in the packet before
>> returning a pointer to the data (and callers have been modified
>> accordingly). This requirement is needed to guarantee that a caller
>> doesn't read beyond the available memory.
>>
>> Signed-off-by: Tiago Lam <tiago.lam@intel.com>
>> ---
>>  lib/bfd.c                     |   3 +-
>>  lib/cfm.c                     |   5 +-
>>  lib/conntrack-icmp.c          |   4 +-
>>  lib/conntrack-private.h       |   4 +-
>>  lib/conntrack-tcp.c           |   6 +-
>>  lib/conntrack.c               | 109 +++++++++++++--------
>>  lib/dp-packet.c               |  18 ++++
>>  lib/dp-packet.h               | 217 +++++++++++++++++++++++++++---------------
>>  lib/dpif-netdev.c             |   5 +
>>  lib/dpif-netlink.c            |   5 +
>>  lib/dpif.c                    |   9 ++
>>  lib/flow.c                    |  29 +++---
>>  lib/lacp.c                    |   3 +-
>>  lib/mcast-snooping.c          |   8 +-
>>  lib/netdev-bsd.c              |   5 +
>>  lib/netdev-dummy.c            |  13 ++-
>>  lib/netdev-linux.c            |  13 ++-
>>  lib/netdev-native-tnl.c       |  39 +++++---
>>  lib/odp-execute.c             |  28 ++++--
>>  lib/ofp-print.c               |  10 +-
>>  lib/ovs-lldp.c                |   3 +-
>>  lib/packets.c                 |  81 +++++++++-------
>>  lib/pcap-file.c               |   2 +-
>>  ofproto/ofproto-dpif-upcall.c |  20 +++-
>>  ofproto/ofproto-dpif-xlate.c  |  42 ++++++--
>>  ovn/controller/pinctrl.c      |  29 +++---
>>  tests/test-conntrack.c        |   2 +-
>>  tests/test-rstp.c             |   8 +-
>>  tests/test-stp.c              |   8 +-
>>  29 files changed, 483 insertions(+), 245 deletions(-)
>>
>> diff --git a/lib/bfd.c b/lib/bfd.c
>> index 5308262..d50d2da 100644
>> --- a/lib/bfd.c
>> +++ b/lib/bfd.c
>> @@ -722,7 +722,8 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
>>      if (!msg) {
>>          VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
>>                       "%"PRIdPTR" bytes long, at least %d required).",
>> -                     bfd->name, (uint8_t *) dp_packet_tail(p) - l7,
>> +                     bfd->name, dp_packet_size(p) -
>> +                     (l7 - (uint8_t *) dp_packet_data(p)),
>>                       BFD_PACKET_LEN);
>>          goto out;
>>      }
>> diff --git a/lib/cfm.c b/lib/cfm.c
>> index 71d2c02..83baf2a 100644
>> --- a/lib/cfm.c
>> +++ b/lib/cfm.c
>> @@ -584,7 +584,7 @@ cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet,
>>  
>>      atomic_read_relaxed(&cfm->extended, &extended);
>>  
>> -    ccm = dp_packet_l3(packet);
>> +    ccm = dp_packet_l3(packet, sizeof(*ccm));
>>      ccm->mdlevel_version = 0;
>>      ccm->opcode = CCM_OPCODE;
>>      ccm->tlv_offset = 70;
>> @@ -759,8 +759,7 @@ cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p)
>>      atomic_read_relaxed(&cfm->extended, &extended);
>>  
>>      eth = dp_packet_eth(p);
>> -    ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
>> -                    CCM_ACCEPT_LEN);
>> +    ccm = dp_packet_l3(p, CCM_ACCEPT_LEN);
>>  
>>      if (!ccm) {
>>          VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.",
>> diff --git a/lib/conntrack-icmp.c b/lib/conntrack-icmp.c
>> index 40fd1d8..0575d0e 100644
>> --- a/lib/conntrack-icmp.c
>> +++ b/lib/conntrack-icmp.c
>> @@ -63,7 +63,7 @@ icmp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>>  static bool
>>  icmp4_valid_new(struct dp_packet *pkt)
>>  {
>> -    struct icmp_header *icmp = dp_packet_l4(pkt);
>> +    struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
>>  
>>      return icmp->icmp_type == ICMP4_ECHO_REQUEST
>>             || icmp->icmp_type == ICMP4_INFOREQUEST
>> @@ -73,7 +73,7 @@ icmp4_valid_new(struct dp_packet *pkt)
>>  static bool
>>  icmp6_valid_new(struct dp_packet *pkt)
>>  {
>> -    struct icmp6_header *icmp6 = dp_packet_l4(pkt);
>> +    struct icmp6_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
>>  
>>      return icmp6->icmp6_type == ICMP6_ECHO_REQUEST;
>>  }
>> diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
>> index a344801..2191aa6 100644
>> --- a/lib/conntrack-private.h
>> +++ b/lib/conntrack-private.h
>> @@ -159,8 +159,8 @@ tcp_payload_length(struct dp_packet *pkt)
>>  {
>>      const char *tcp_payload = dp_packet_get_tcp_payload(pkt);
>>      if (tcp_payload) {
>> -        return ((char *) dp_packet_tail(pkt) - dp_packet_l2_pad_size(pkt)
>> -                - tcp_payload);
>> +        return dp_packet_l4_size(pkt) -
>> +               (tcp_payload - (char *) dp_packet_l4(pkt, 0));
>>      } else {
>>          return 0;
>>      }
>> diff --git a/lib/conntrack-tcp.c b/lib/conntrack-tcp.c
>> index 86d313d..5450971 100644
>> --- a/lib/conntrack-tcp.c
>> +++ b/lib/conntrack-tcp.c
>> @@ -149,7 +149,7 @@ tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>>                  struct dp_packet *pkt, bool reply, long long now)
>>  {
>>      struct conn_tcp *conn = conn_tcp_cast(conn_);
>> -    struct tcp_header *tcp = dp_packet_l4(pkt);
>> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>>      /* The peer that sent 'pkt' */
>>      struct tcp_peer *src = &conn->peer[reply ? 1 : 0];
>>      /* The peer that should receive 'pkt' */
>> @@ -394,7 +394,7 @@ tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
>>  static bool
>>  tcp_valid_new(struct dp_packet *pkt)
>>  {
>> -    struct tcp_header *tcp = dp_packet_l4(pkt);
>> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>>      uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
>>  
>>      if (tcp_invalid_flags(tcp_flags)) {
>> @@ -416,7 +416,7 @@ tcp_new_conn(struct conntrack_bucket *ctb, struct dp_packet *pkt,
>>               long long now)
>>  {
>>      struct conn_tcp* newconn = NULL;
>> -    struct tcp_header *tcp = dp_packet_l4(pkt);
>> +    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
>>      struct tcp_peer *src, *dst;
>>      uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
>>  
>> diff --git a/lib/conntrack.c b/lib/conntrack.c
>> index 974f985..f024186 100644
>> --- a/lib/conntrack.c
>> +++ b/lib/conntrack.c
>> @@ -450,10 +450,10 @@ get_ip_proto(const struct dp_packet *pkt)
>>      uint8_t ip_proto;
>>      struct eth_header *l2 = dp_packet_eth(pkt);
>>      if (l2->eth_type == htons(ETH_TYPE_IPV6)) {
>> -        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>          ip_proto = nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
>>      } else {
>> -        struct ip_header *l3_hdr = dp_packet_l3(pkt);
>> +        struct ip_header *l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
>>          ip_proto = l3_hdr->ip_proto;
>>      }
>>  
>> @@ -476,8 +476,8 @@ get_alg_ctl_type(const struct dp_packet *pkt, ovs_be16 tp_src, ovs_be16 tp_dst,
>>      enum { CT_IPPORT_FTP = 21 };
>>      enum { CT_IPPORT_TFTP = 69 };
>>      uint8_t ip_proto = get_ip_proto(pkt);
>> -    struct udp_header *uh = dp_packet_l4(pkt);
>> -    struct tcp_header *th = dp_packet_l4(pkt);
>> +    struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>      ovs_be16 ftp_src_port = htons(CT_IPPORT_FTP);
>>      ovs_be16 ftp_dst_port = htons(CT_IPPORT_FTP);
>>      ovs_be16 tftp_dst_port = htons(CT_IPPORT_TFTP);
>> @@ -530,18 +530,18 @@ pat_packet(struct dp_packet *pkt, const struct conn *conn)
>>  {
>>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th = dp_packet_l4(pkt);
>> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>              packet_set_tcp_port(pkt, conn->rev_key.dst.port, th->tcp_dst);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh = dp_packet_l4(pkt);
>> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>>              packet_set_udp_port(pkt, conn->rev_key.dst.port, uh->udp_dst);
>>          }
>>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th = dp_packet_l4(pkt);
>> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>              packet_set_tcp_port(pkt, th->tcp_src, conn->rev_key.src.port);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh = dp_packet_l4(pkt);
>> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>>              packet_set_udp_port(pkt, uh->udp_src, conn->rev_key.src.port);
>>          }
>>      }
>> @@ -553,11 +553,11 @@ nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
>>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>>          pkt->md.ct_state |= CS_SRC_NAT;
>>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
>> -            struct ip_header *nh = dp_packet_l3(pkt);
>> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>>              packet_set_ipv4_addr(pkt, &nh->ip_src,
>>                                   conn->rev_key.dst.addr.ipv4_aligned);
>>          } else {
>> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>>                                   nh6->ip6_src.be32,
>>                                   &conn->rev_key.dst.addr.ipv6_aligned,
>> @@ -569,11 +569,11 @@ nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
>>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>>          pkt->md.ct_state |= CS_DST_NAT;
>>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
>> -            struct ip_header *nh = dp_packet_l3(pkt);
>> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>>              packet_set_ipv4_addr(pkt, &nh->ip_dst,
>>                                   conn->rev_key.src.addr.ipv4_aligned);
>>          } else {
>> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>>                                   nh6->ip6_dst.be32,
>>                                   &conn->rev_key.src.addr.ipv6_aligned,
>> @@ -590,18 +590,18 @@ un_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>>  {
>>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th = dp_packet_l4(pkt);
>> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>              packet_set_tcp_port(pkt, th->tcp_src, conn->key.src.port);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh = dp_packet_l4(pkt);
>> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>>              packet_set_udp_port(pkt, uh->udp_src, conn->key.src.port);
>>          }
>>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th = dp_packet_l4(pkt);
>> +            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>              packet_set_tcp_port(pkt, conn->key.dst.port, th->tcp_dst);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh = dp_packet_l4(pkt);
>> +            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
>>              packet_set_udp_port(pkt, conn->key.dst.port, uh->udp_dst);
>>          }
>>      }
>> @@ -612,21 +612,21 @@ reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>>  {
>>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th_in = dp_packet_l4(pkt);
>> +            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
>>              packet_set_tcp_port(pkt, conn->key.src.port,
>>                                  th_in->tcp_dst);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh_in = dp_packet_l4(pkt);
>> +            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
>>              packet_set_udp_port(pkt, conn->key.src.port,
>>                                  uh_in->udp_dst);
>>          }
>>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>>          if (conn->key.nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *th_in = dp_packet_l4(pkt);
>> +            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
>>              packet_set_tcp_port(pkt, th_in->tcp_src,
>>                                  conn->key.dst.port);
>>          } else if (conn->key.nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *uh_in = dp_packet_l4(pkt);
>> +            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
>>              packet_set_udp_port(pkt, uh_in->udp_src,
>>                                  conn->key.dst.port);
>>          }
>> @@ -636,16 +636,26 @@ reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
>>  static void
>>  reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
>>  {
>> -    char *tail = dp_packet_tail(pkt);
>> -    char pad = dp_packet_l2_pad_size(pkt);
>> +    char *tail;
>> +    char pad;
>>      struct conn_key inner_key;
>>      const char *inner_l4 = NULL;
>> -    uint16_t orig_l3_ofs = pkt->l3_ofs;
>> -    uint16_t orig_l4_ofs = pkt->l4_ofs;
>> +    uint16_t orig_l3_ofs;
>> +    uint16_t orig_l4_ofs;
>> +
>> +    /* We need the whole packet to parse the packet below */
>> +    if (!dp_packet_is_linear(pkt)) {
>> +        dp_packet_linearize(pkt);
>> +    }
> 
> Could you elaborate on why we need the whole packet?

This one in specific passes a void* pointer to extract_l3_ipv4(), that
it uses to iterate through the data contiguously. But in some other
places it is doing pointer arithmetic, assuming that memory is
contiguous. I'm sure a lot of these cases can be improved though, I just
haven't gone over them yet.

On a broader note, I haven't added it to this patchset yet but we could
add a dp_packet function to help calculate the checksum in a segmented
packet. This would avoid having to call dp_packet_linearization() just
for calculating the checksums, which would help in a lot of cases. That
new function would return different amounts of data, depending if the
packet is fragmented or not.

> 
>> +
>> +    tail = dp_packet_tail(pkt);
>> +    pad = dp_packet_l2_pad_size(pkt);
>> +    orig_l3_ofs = pkt->l3_ofs;
>> +    orig_l4_ofs = pkt->l4_ofs;
>>  
>>      if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
>> -        struct ip_header *nh = dp_packet_l3(pkt);
>> -        struct icmp_header *icmp = dp_packet_l4(pkt);
>> +        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>> +        struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
>>          struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1);
>>          extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad,
>>                          &inner_l4, false);
>> @@ -664,8 +674,8 @@ reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
>>          icmp->icmp_csum = 0;
>>          icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad);
>>      } else {
>> -        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> -        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt);
>> +        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>> +        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
>>          struct ovs_16aligned_ip6_hdr *inner_l3_6 =
>>              (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
>>          extract_l3_ipv6(&inner_key, inner_l3_6,
>> @@ -702,11 +712,11 @@ un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
>>      if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
>>          pkt->md.ct_state |= CS_DST_NAT;
>>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
>> -            struct ip_header *nh = dp_packet_l3(pkt);
>> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>>              packet_set_ipv4_addr(pkt, &nh->ip_dst,
>>                                   conn->key.src.addr.ipv4_aligned);
>>          } else {
>> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>>                                   nh6->ip6_dst.be32,
>>                                   &conn->key.src.addr.ipv6_aligned, true);
>> @@ -720,11 +730,11 @@ un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
>>      } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
>>          pkt->md.ct_state |= CS_SRC_NAT;
>>          if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
>> -            struct ip_header *nh = dp_packet_l3(pkt);
>> +            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>>              packet_set_ipv4_addr(pkt, &nh->ip_src,
>>                                   conn->key.dst.addr.ipv4_aligned);
>>          } else {
>> -            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>              packet_set_ipv6_addr(pkt, conn->key.nw_proto,
>>                                   nh6->ip6_src.be32,
>>                                   &conn->key.dst.addr.ipv6_aligned, true);
>> @@ -1320,6 +1330,7 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
>>              write_ct_md(packet, zone, NULL, NULL, NULL);
>>              continue;
>>          }
>> +
>>          process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
>>                      setlabel, nat_action_info, tp_src, tp_dst, helper);
>>      }
>> @@ -1901,9 +1912,18 @@ static bool
>>  conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,
>>                   struct conn_lookup_ctx *ctx, uint16_t zone)
>>  {
>> -    const struct eth_header *l2 = dp_packet_eth(pkt);
>> -    const struct ip_header *l3 = dp_packet_l3(pkt);
>> -    const char *l4 = dp_packet_l4(pkt);
>> +    const struct eth_header *l2;
>> +    const struct ip_header *l3;
>> +    const char *l4;
>> +
>> +    /* We need the whole packet to parse the packet below */
>> +    if (!dp_packet_is_linear(pkt)) {
>> +        dp_packet_linearize(pkt);
>> +    }
> 
> Same here. This is a heavy operation that most probably will affect
> conntrack performance.

It will, at least the way it is right now. Improvements can be done to
remove this though, like the checksum improvement I mentioned above.

Just to make it clear though, if not using multi-segments mbufs then no
performance drop is expected.

> 
> 
>> +
>> +    l2 = dp_packet_eth(pkt);
>> +    l3 = dp_packet_l3(pkt, sizeof *l3);
>> +    l4 = dp_packet_l4(pkt, sizeof *l4);
>>  
>>      memset(ctx, 0, sizeof *ctx);
>>  
>> @@ -2846,7 +2866,7 @@ terminate_number_str(char *str, uint8_t max_digits)
>>  static void
>>  get_ftp_ctl_msg(struct dp_packet *pkt, char *ftp_msg)
>>  {
>> -    struct tcp_header *th = dp_packet_l4(pkt);
>> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>      char *tcp_hdr = (char *) th;
>>      uint32_t tcp_payload_len = tcp_payload_length(pkt);
>>      size_t tcp_payload_of_interest = MIN(tcp_payload_len,
>> @@ -2888,7 +2908,7 @@ process_ftp_ctl_v4(struct conntrack *ct,
>>                     char **ftp_data_v4_start,
>>                     size_t *addr_offset_from_ftp_data_start)
>>  {
>> -    struct tcp_header *th = dp_packet_l4(pkt);
>> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>      size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
>>      char *tcp_hdr = (char *) th;
>>      *ftp_data_v4_start = tcp_hdr + tcp_hdr_len;
>> @@ -3034,7 +3054,7 @@ process_ftp_ctl_v6(struct conntrack *ct,
>>                     size_t *addr_offset_from_ftp_data_start,
>>                     size_t *addr_size, enum ct_alg_mode *mode)
>>  {
>> -    struct tcp_header *th = dp_packet_l4(pkt);
>> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>      size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
>>      char *tcp_hdr = (char *) th;
>>      char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0};
>> @@ -3167,7 +3187,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>>                 const struct conn *conn_for_expectation,
>>                 long long now, enum ftp_ctl_pkt ftp_ctl, bool nat)
>>  {
>> -    struct ip_header *l3_hdr = dp_packet_l3(pkt);
>> +    struct ip_header *l3_hdr;
>>      ovs_be32 v4_addr_rep = 0;
>>      struct ct_addr v6_addr_rep;
>>      size_t addr_offset_from_ftp_data_start;
>> @@ -3176,6 +3196,13 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>>      bool do_seq_skew_adj = true;
>>      enum ct_alg_mode mode = CT_FTP_MODE_ACTIVE;
>>  
>> +    /* We need the whole packet to parse the packet below */
>> +    if (!dp_packet_is_linear(pkt)) {
>> +        dp_packet_linearize(pkt);
>> +    }
>> +
>> +    l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
>> +
>>      if (detect_ftp_ctl_type(ctx, pkt) != ftp_ctl) {
>>          return;
>>      }
>> @@ -3184,7 +3211,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>>          do_seq_skew_adj = false;
>>      }
>>  
>> -    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>> +    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
>>      int64_t seq_skew = 0;
>>  
>>      if (ftp_ctl == CT_FTP_CTL_OTHER) {
>> @@ -3240,7 +3267,7 @@ handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
>>          OVS_NOT_REACHED();
>>      }
>>  
>> -    struct tcp_header *th = dp_packet_l4(pkt);
>> +    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
>>  
>>      if (do_seq_skew_adj && seq_skew != 0) {
>>          if (ctx->reply != conn_for_expectation->seq_skew_dir) {
>> diff --git a/lib/dp-packet.c b/lib/dp-packet.c
>> index 806640b..b8f5242 100644
>> --- a/lib/dp-packet.c
>> +++ b/lib/dp-packet.c
>> @@ -118,6 +118,9 @@ void
>>  dp_packet_init_dpdk(struct dp_packet *b)
>>  {
>>      b->source = DPBUF_DPDK;
>> +#ifdef DPDK_NETDEV
>> +    b->mstate = NULL;
>> +#endif
>>  }
>>  
>>  /* Initializes 'b' as an empty dp_packet with an initial capacity of 'size'
>> @@ -135,6 +138,21 @@ dp_packet_uninit(struct dp_packet *b)
>>      if (b) {
>>          if (b->source == DPBUF_MALLOC) {
>>              free(dp_packet_base(b));
>> +
>> +#ifdef DPDK_NETDEV
>> +            /* Packet has been "linearized" */
>> +            if (b->mstate) {
>> +                b->source = DPBUF_DPDK;
>> +                b->mbuf.buf_addr = b->mstate->addr;
>> +                b->mbuf.buf_len = b->mstate->len;
>> +                b->mbuf.data_off = b->mstate->off;
>> +
>> +                free(b->mstate);
>> +                b->mstate = NULL;
>> +
>> +                free_dpdk_buf((struct dp_packet *) b);
>> +            }
>> +#endif
>>          } else if (b->source == DPBUF_DPDK) {
>>  #ifdef DPDK_NETDEV
>>              /* If this dp_packet was allocated by DPDK it must have been
>> diff --git a/lib/dp-packet.h b/lib/dp-packet.h
>> index cbf002c..9d42cbf 100644
>> --- a/lib/dp-packet.h
>> +++ b/lib/dp-packet.h
>> @@ -27,7 +27,6 @@
>>  
>>  #include "netdev-dpdk.h"
>>  #include "openvswitch/list.h"
>> -#include "packets.h"
>>  #include "util.h"
>>  #include "flow.h"
>>  
>> @@ -46,6 +45,16 @@ enum OVS_PACKED_ENUM dp_packet_source {
>>  
>>  #define DP_PACKET_CONTEXT_SIZE 64
>>  
>> +#ifdef DPDK_NETDEV
>> +/* Struct to save data for when a DPBUF_DPDK packet is converted to
>> + * DPBUF_MALLOC. */
>> +struct mbuf_state {
>> +    void *addr;
>> +    uint16_t len;
>> +    uint16_t off;
>> +};
>> +#endif
>> +
>>  /* Buffer for holding packet data.  A dp_packet is automatically reallocated
>>   * as necessary if it grows too large for the available memory.
>>   * By default the packet type is set to Ethernet (PT_ETH).
>> @@ -53,6 +62,7 @@ enum OVS_PACKED_ENUM dp_packet_source {
>>  struct dp_packet {
>>  #ifdef DPDK_NETDEV
>>      struct rte_mbuf mbuf;       /* DPDK mbuf */
>> +    struct mbuf_state *mstate;  /* Used when packet has been "linearized" */
>>  #else
>>      void *base_;                /* First byte of allocated space. */
>>      uint16_t allocated_;        /* Number of bytes allocated. */
>> @@ -85,6 +95,8 @@ struct dp_packet {
>>      (char *) (((char *) BUF_ADDR) + BUF_LEN)
>>  #endif
>>  
>> +static inline bool dp_packet_is_linear(const struct dp_packet *);
>> +static inline void dp_packet_linearize(struct dp_packet *);
>>  static inline void *dp_packet_data(const struct dp_packet *);
>>  static inline void dp_packet_set_data(struct dp_packet *, void *);
>>  static inline void *dp_packet_base(const struct dp_packet *);
>> @@ -102,11 +114,12 @@ static inline void *dp_packet_eth(const struct dp_packet *);
>>  static inline void dp_packet_reset_offsets(struct dp_packet *);
>>  static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *);
>>  static inline void dp_packet_set_l2_pad_size(struct dp_packet *, uint8_t);
>> -static inline void *dp_packet_l2_5(const struct dp_packet *);
>> +static inline void *dp_packet_l2_5(const struct dp_packet *, uint16_t size);
>>  static inline void dp_packet_set_l2_5(struct dp_packet *, void *);
>> -static inline void *dp_packet_l3(const struct dp_packet *);
>> +static inline void *dp_packet_l3(const struct dp_packet *, uint16_t size);
>>  static inline void dp_packet_set_l3(struct dp_packet *, void *);
>> -static inline void *dp_packet_l4(const struct dp_packet *);
>> +static inline size_t dp_packet_l3_size(const struct dp_packet *);
>> +static inline void *dp_packet_l4(const struct dp_packet *, uint16_t size);
>>  static inline void dp_packet_set_l4(struct dp_packet *, void *);
>>  static inline size_t dp_packet_l4_size(const struct dp_packet *);
>>  static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *);
>> @@ -181,20 +194,18 @@ static inline void
>>  dp_packet_delete(struct dp_packet *b)
>>  {
>>      if (b) {
>> -        if (b->source == DPBUF_DPDK) {
>> -            /* If this dp_packet was allocated by DPDK it must have been
>> -             * created as a dp_packet */
>> -            free_dpdk_buf((struct dp_packet*) b);
>> -            return;
>> -        }
>> -
>>          dp_packet_uninit(b);
>> -        free(b);
>> +
>> +        if (b->source != DPBUF_DPDK) {
>> +            free(b);
>> +        }
>>      }
>>  }
>>  
>>  /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to
>> - * byte 'offset'.  Otherwise, returns a null pointer. */
>> + * byte 'offset'.  Otherwise, returns a null pointer. For DPDK packets, this
>> + * means the 'offset' + 'size' must fall within the same mbuf (not necessarily
>> + * the first mbuf), otherwise null is returned */
>>  static inline void *
>>  dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
>>  {
>> @@ -212,10 +223,13 @@ dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
>>              buf = buf->next;
>>          }
>>  
>> -        return buf ? rte_pktmbuf_mtod_offset(buf, char *, offset) : NULL;
>> +        if (!buf || offset + size > buf->data_len) {
>> +            return NULL;
>> +        }
>> +
>> +        return rte_pktmbuf_mtod_offset(buf, char *, offset);
>>      }
>>  #endif
>> -
>>      return (char *) dp_packet_data(b) + offset;
>>  }
>>  
>> @@ -314,20 +328,24 @@ dp_packet_pull(struct dp_packet *b, size_t size)
>>      return data;
>>  }
>>  
>> -#ifdef DPDK_NETDEV
>>  /* Similar to dp_packet_try_pull() but doesn't actually pull any data, only
>> - * checks if it could and returns true or false accordingly.
>> - *
>> - * Valid for dp_packets carrying mbufs only. */
>> + * checks if it could and returns 'true' or 'false', accordingly. For DPDK
>> + * packets, 'true' is only returned in case the 'offset' + 'size' falls within
>> + * the first mbuf, otherwise 'false' is returned */
>>  static inline bool
>> -dp_packet_mbuf_may_pull(const struct dp_packet *b, size_t size) {
>> -    if (size > b->mbuf.data_len) {
>> +dp_packet_may_pull(const struct dp_packet *b, uint16_t offset, size_t size)
>> +{
>> +    if (offset == UINT16_MAX) {
>> +        return false;
>> +    }
>> +#ifdef DPDK_NETDEV
>> +    /* Offset needs to be within the first mbuf */
>> +    if (offset + size > b->mbuf.data_len) {
>>          return false;
>>      }
>> -
>> -    return true;
>> -}
>>  #endif
>> +    return (offset + size > dp_packet_size(b)) ? false : true;
>> +}
>>  
>>  /* If 'b' has at least 'size' bytes of data, removes that many bytes from the
>>   * head end of 'b' and returns the first byte removed.  Otherwise, returns a
>> @@ -336,7 +354,7 @@ static inline void *
>>  dp_packet_try_pull(struct dp_packet *b, size_t size)
>>  {
>>  #ifdef DPDK_NETDEV
>> -    if (!dp_packet_mbuf_may_pull(b, size)) {
>> +    if (!dp_packet_may_pull(b, 0, size)) {
>>          return NULL;
>>      }
>>  #endif
>> @@ -385,17 +403,13 @@ dp_packet_set_l2_pad_size(struct dp_packet *b, uint8_t pad_size)
>>  }
>>  
>>  static inline void *
>> -dp_packet_l2_5(const struct dp_packet *b)
>> +dp_packet_l2_5(const struct dp_packet *b, uint16_t size)
>>  {
>> -#ifdef DPDK_NETDEV
>> -    if (!dp_packet_mbuf_may_pull(b, b->l2_5_ofs)) {
>> +    if (!dp_packet_may_pull(b, b->l2_5_ofs, size)) {
>>          return NULL;
>>      }
>> -#endif
>>  
>> -    return b->l2_5_ofs != UINT16_MAX
>> -           ? (char *) dp_packet_data(b) + b->l2_5_ofs
>> -           : NULL;
>> +    return (char *) dp_packet_data(b) + b->l2_5_ofs;
>>  }
>>  
>>  static inline void
>> @@ -407,17 +421,13 @@ dp_packet_set_l2_5(struct dp_packet *b, void *l2_5)
>>  }
>>  
>>  static inline void *
>> -dp_packet_l3(const struct dp_packet *b)
>> +dp_packet_l3(const struct dp_packet *b, uint16_t size)
>>  {
>> -#ifdef DPDK_NETDEV
>> -    if (!dp_packet_mbuf_may_pull(b, b->l3_ofs)) {
>> +    if (!dp_packet_may_pull(b, b->l3_ofs, size)) {
>>          return NULL;
>>      }
>> -#endif
>>  
>> -    return b->l3_ofs != UINT16_MAX
>> -           ? (char *) dp_packet_data(b) + b->l3_ofs
>> -           : NULL;
>> +    return (char *) dp_packet_data(b) + b->l3_ofs;
>>  }
>>  
>>  static inline void
>> @@ -426,18 +436,34 @@ dp_packet_set_l3(struct dp_packet *b, void *l3)
>>      b->l3_ofs = l3 ? (char *) l3 - (char *) dp_packet_data(b) : UINT16_MAX;
>>  }
>>  
>> +/* Returns the size of the l3 header. Caller must make sure both l3_ofs and
>> + * l4_ofs are set*/
>> +static inline size_t
>> +dp_packet_l3h_size(const struct dp_packet *b)
>> +{
>> +    return b->l4_ofs - b->l3_ofs;
>> +}
> 
> Does that work on multi-seg? Or we assume L3 and L4 are always in
> the same segment?

Right now there's an assumption that the headers will be on the same
segment, because of the miniflow extraction, but I'd rather not build
dp_packet under that assumption. I think it should be fine here since we
are subtracting from the offsets directly (uint16_t's), not doing
pointer arithmetic.

> 
>> +
>> +static inline size_t
>> +dp_packet_l3_size(const struct dp_packet *b)
>> +{
>> +    if (!dp_packet_may_pull(b, b->l3_ofs, 0)) {
>> +        return 0;
>> +    }
>> +
>> +    size_t l3_size = dp_packet_size(b) - b->l3_ofs;
>> +
>> +    return l3_size - dp_packet_l2_pad_size(b);
>> +}
>> +
>>  static inline void *
>> -dp_packet_l4(const struct dp_packet *b)
>> +dp_packet_l4(const struct dp_packet *b, uint16_t size)
>>  {
>> -#ifdef DPDK_NETDEV
>> -    if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
>> +    if (!dp_packet_may_pull(b, b->l4_ofs, size)) {
>>          return NULL;
>>      }
>> -#endif
>>  
>> -    return b->l4_ofs != UINT16_MAX
>> -           ? (char *) dp_packet_data(b) + b->l4_ofs
>> -           : NULL;
>> +    return (char *) dp_packet_data(b) + b->l4_ofs;
>>  }
>>  
>>  static inline void
>> @@ -449,31 +475,13 @@ dp_packet_set_l4(struct dp_packet *b, void *l4)
>>  static inline size_t
>>  dp_packet_l4_size(const struct dp_packet *b)
>>  {
>> -#ifdef DPDK_NETDEV
>> -    if (b->source == DPBUF_DPDK) {
>> -        if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
>> -            return 0;
>> -        }
>> -
>> -        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
>> -        size_t l4_size = mbuf->data_len - b->l4_ofs;
>> -
>> -        mbuf = mbuf->next;
>> -        while (mbuf) {
>> -            l4_size += mbuf->data_len;
>> -
>> -            mbuf = mbuf->next;
>> -        }
>> +    if (!dp_packet_may_pull(b, b->l4_ofs, 0)) {
>> +        return 0;
>> +    }
>>  
>> -        l4_size -= dp_packet_l2_pad_size(b);
>> +    size_t l4_size = dp_packet_size(b) - b->l4_ofs;
>>  
>> -        return l4_size;
>> -    }
>> -#endif
>> -    return b->l4_ofs != UINT16_MAX
>> -        ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b)
>> -        - dp_packet_l2_pad_size(b)
>> -        : 0;
>> +    return l4_size - dp_packet_l2_pad_size(b);
>>  }
>>  
>>  static inline const void *
>> @@ -482,11 +490,12 @@ dp_packet_get_tcp_payload(const struct dp_packet *b)
>>      size_t l4_size = dp_packet_l4_size(b);
>>  
>>      if (OVS_LIKELY(l4_size >= TCP_HEADER_LEN)) {
>> -        struct tcp_header *tcp = dp_packet_l4(b);
>> +        struct tcp_header *tcp = dp_packet_l4(b, sizeof *tcp);
>>          int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
>>  
>>          if (OVS_LIKELY(tcp_len >= TCP_HEADER_LEN && tcp_len <= l4_size)) {
>> -            return (const char *)tcp + tcp_len;
>> +            tcp = dp_packet_at(b, b->l4_ofs, tcp_len);
>> +            return (tcp == NULL) ? NULL : tcp + tcp_len;
>>          }
>>      }
>>      return NULL;
>> @@ -496,28 +505,31 @@ static inline const void *
>>  dp_packet_get_udp_payload(const struct dp_packet *b)
>>  {
>>      return OVS_LIKELY(dp_packet_l4_size(b) >= UDP_HEADER_LEN)
>> -        ? (const char *)dp_packet_l4(b) + UDP_HEADER_LEN : NULL;
>> +        ? (const char *) dp_packet_l4(b, UDP_HEADER_LEN) + UDP_HEADER_LEN
>> +        : NULL;
>>  }
>>  
>>  static inline const void *
>>  dp_packet_get_sctp_payload(const struct dp_packet *b)
>>  {
>>      return OVS_LIKELY(dp_packet_l4_size(b) >= SCTP_HEADER_LEN)
>> -        ? (const char *)dp_packet_l4(b) + SCTP_HEADER_LEN : NULL;
>> +        ? (const char *) dp_packet_l4(b, SCTP_HEADER_LEN) + SCTP_HEADER_LEN
>> +        : NULL;
>>  }
>>  
>>  static inline const void *
>>  dp_packet_get_icmp_payload(const struct dp_packet *b)
>>  {
>>      return OVS_LIKELY(dp_packet_l4_size(b) >= ICMP_HEADER_LEN)
>> -        ? (const char *)dp_packet_l4(b) + ICMP_HEADER_LEN : NULL;
>> +        ? (const char *) dp_packet_l4(b, ICMP_HEADER_LEN) + ICMP_HEADER_LEN
>> +        : NULL;
>>  }
>>  
>>  static inline const void *
>>  dp_packet_get_nd_payload(const struct dp_packet *b)
>>  {
>>      return OVS_LIKELY(dp_packet_l4_size(b) >= ND_MSG_LEN)
>> -        ? (const char *)dp_packet_l4(b) + ND_MSG_LEN : NULL;
>> +        ? (const char *)dp_packet_l4(b, ND_MSG_LEN) + ND_MSG_LEN : NULL;
>>  }
>>  
>>  #ifdef DPDK_NETDEV
>> @@ -760,6 +772,60 @@ dp_packet_data(const struct dp_packet *b)
>>             ? (char *) dp_packet_base(b) + __packet_data(b) : NULL;
>>  }
>>  
>> +static inline bool
>> +dp_packet_is_linear(const struct dp_packet *b OVS_UNUSED)
>> +{
>> +#ifdef DPDK_NETDEV
>> +    if (b->source == DPBUF_DPDK) {
>> +        return rte_pktmbuf_is_contiguous(&b->mbuf);
>> +    }
>> +#endif
>> +    return true;
>> +}
>> +
>> +/* Linearizes the data on packet 'b', if and only if 'b' is a DPBUF_DPDK
>> + * packet, by copying the data into system's memory. After this the packet is
>> + * effectively a DPBUF_MALLOC packet.
>> + *
>> + * This is an expensive operation which should only be performed as a last
>> + * resort, when multi-segments are under use but data must be accessed
>> + * linearly. */
>> +static inline void
>> +dp_packet_linearize(struct dp_packet *b OVS_UNUSED)
>> +{
>> +#ifdef DPDK_NETDEV
>> +    if (!dp_packet_is_linear(b)) {
>> +        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
>> +        struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
>> +        uint32_t pkt_len = dp_packet_size(pkt);
>> +        struct mbuf_state *mstate = NULL;
>> +        void *dst = xmalloc(pkt_len);
>> +
>> +        /* Copy packet's data to system's memory */
>> +        if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
>> +            return;
>> +        }
>> +
>> +        /* Free all mbufs except for the first */
>> +        dp_packet_clear(pkt);
>> +
>> +        /* Save mbuf's buf_addr to restore later */
>> +        mstate = xmalloc(sizeof(*mstate));
>> +        mstate->addr = pkt->mbuf.buf_addr;
>> +        mstate->len = pkt->mbuf.buf_len;
>> +        mstate->off = pkt->mbuf.data_off;
>> +        pkt->mstate = mstate;
>> +
>> +        /* Tranform DPBUF_DPDK packet into a DPBUF_MALLOC packet */
>> +        pkt->source = DPBUF_MALLOC;
>> +        pkt->mbuf.buf_addr = dst;
>> +        pkt->mbuf.buf_len = pkt_len;
>> +        pkt->mbuf.data_off = 0;
>> +        dp_packet_set_size(pkt, pkt_len);
>> +    }
> 
> 
> I suggest to invert the logic there:
>        if (dp_packet_is_linear(b)) {
>            return
>        }
> 
>        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
>        struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
>        uint32_t pkt_len = dp_packet_size(pkt);
>        struct mbuf_state *mstate = NULL;
>        void *dst = xmalloc(pkt_len);
> 
>        /* Copy packet's data to system's memory */
>        if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
>            return;
>        }
> 
> Or even document the requirement and take it out because all callers
> are doing like this:
>        if (!dp_packet_is_linear()) {
>            dp_packet_linearize();
>        }    
> 

I must have missed this after refactoring the callers. Thanks for
pointing it out, I'll use the latter and update the function comment.

>> +#endif
>> +}
>> +
>>  static inline void
>>  dp_packet_set_data(struct dp_packet *b, void *data)
>>  {
>> @@ -837,6 +903,7 @@ dp_packet_mbuf_init(struct dp_packet *p OVS_UNUSED)
>>      mbuf->ol_flags = mbuf->tx_offload = mbuf->packet_type = 0;
>>      mbuf->nb_segs = 1;
>>      mbuf->next = NULL;
>> +    p->mstate = NULL;
>>  #endif
>>  }
>>  
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index e322f55..43cddb6 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -5698,6 +5698,11 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_,
>>              .support = dp_netdev_support,
>>          };
>>  
>> +        /* Gather the whole data for printing the packet (if debug enabled) */
>> +        if (!dp_packet_is_linear(packet_)) {
>> +            dp_packet_linearize(packet_);
>> +        }
>> +
>>          ofpbuf_init(&key, 0);
>>          odp_flow_key_from_flow(&odp_parms, &key);
>>          packet_str = ofp_dp_packet_to_string(packet_);
>> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
>> index 2131503..ee11d51 100644
>> --- a/lib/dpif-netlink.c
>> +++ b/lib/dpif-netlink.c
>> @@ -1810,6 +1810,11 @@ dpif_netlink_operate__(struct dpif_netlink *dpif,
>>                  }
>>                  n_ops = i;
>>              } else {
>> +                /* We will need to pass the whole to encode the message */
>> +                if (!dp_packet_is_linear(op->execute.packet)) {
>> +                    dp_packet_linearize(op->execute.packet);
>> +                }
>> +
>>                  dpif_netlink_encode_execute(dpif->dp_ifindex, &op->execute,
>>                                              &aux->request);
>>              }
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index 4697a4d..514fae5 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1243,6 +1243,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
>>          execute.probe = false;
>>          execute.mtu = 0;
>>          aux->error = dpif_execute(aux->dpif, &execute);
>> +
>>          log_execute_message(aux->dpif, &this_module, &execute,
>>                              true, aux->error);
>>  
>> @@ -1395,6 +1396,7 @@ dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
>>  
>>                  case DPIF_OP_EXECUTE:
>>                      COVERAGE_INC(dpif_execute);
>> +
>>                      log_execute_message(dpif, &this_module, &op->execute,
>>                                          false, error);
>>                      break;
>> @@ -1822,6 +1824,13 @@ log_execute_message(const struct dpif *dpif,
>>          uint64_t stub[1024 / 8];
>>          struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub);
>>  
>> +        /* We will need the whole data for logging */
>> +        struct dp_packet *p = CONST_CAST(struct dp_packet *,
>> +                                         execute->packet);
>> +        if (!dp_packet_is_linear(p)) {
>> +            dp_packet_linearize(p);
>> +        }
>> +
>>          packet = ofp_packet_to_string(dp_packet_data(execute->packet),
>>                                        dp_packet_size(execute->packet),
>>                                        execute->packet->packet_type);
>> diff --git a/lib/flow.c b/lib/flow.c
>> index bffee70..aa904d4 100644
>> --- a/lib/flow.c
>> +++ b/lib/flow.c
>> @@ -2958,34 +2958,35 @@ static void
>>  flow_compose_l4_csum(struct dp_packet *p, const struct flow *flow,
>>                       uint32_t pseudo_hdr_csum)
>>  {
>> -    size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
>> +    //size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
>> +    size_t l4_len = dp_packet_l4_size(p);
>>  
>>      if (!(flow->nw_frag & FLOW_NW_FRAG_ANY)
>>          || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
>>          if (flow->nw_proto == IPPROTO_TCP) {
>> -            struct tcp_header *tcp = dp_packet_l4(p);
>> +            struct tcp_header *tcp = dp_packet_l4(p, sizeof *tcp);
>>  
>>              tcp->tcp_csum = 0;
>>              tcp->tcp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
>>                                                        tcp, l4_len));
>>          } else if (flow->nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *udp = dp_packet_l4(p);
>> +            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
>>  
>>              udp->udp_csum = 0;
>>              udp->udp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
>>                                                        udp, l4_len));
>>          } else if (flow->nw_proto == IPPROTO_ICMP) {
>> -            struct icmp_header *icmp = dp_packet_l4(p);
>> +            struct icmp_header *icmp = dp_packet_l4(p, sizeof *icmp);
>>  
>>              icmp->icmp_csum = 0;
>>              icmp->icmp_csum = csum(icmp, l4_len);
>>          } else if (flow->nw_proto == IPPROTO_IGMP) {
>> -            struct igmp_header *igmp = dp_packet_l4(p);
>> +            struct igmp_header *igmp = dp_packet_l4(p, sizeof *igmp);
>>  
>>              igmp->igmp_csum = 0;
>>              igmp->igmp_csum = csum(igmp, l4_len);
>>          } else if (flow->nw_proto == IPPROTO_ICMPV6) {
>> -            struct icmp6_hdr *icmp = dp_packet_l4(p);
>> +            struct icmp6_hdr *icmp = dp_packet_l4(p, sizeof *icmp);
>>  
>>              icmp->icmp6_cksum = 0;
>>              icmp->icmp6_cksum = (OVS_FORCE uint16_t)
>> @@ -3015,18 +3016,18 @@ packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
>>          eth->eth_type = htons(dp_packet_size(p));
>>      } else if (dl_type_is_ip_any(flow->dl_type)) {
>>          uint32_t pseudo_hdr_csum;
>> -        size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
>> +        size_t l4_len = dp_packet_l4_size(p);
>>  
>>          if (flow->dl_type == htons(ETH_TYPE_IP)) {
>> -            struct ip_header *ip = dp_packet_l3(p);
>> +            struct ip_header *ip = dp_packet_l3(p, sizeof *ip);
>>  
>> -            ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
>> +            ip->ip_tot_len = htons(dp_packet_l3_size(p));
>>              ip->ip_csum = 0;
>>              ip->ip_csum = csum(ip, sizeof *ip);
>>  
>>              pseudo_hdr_csum = packet_csum_pseudoheader(ip);
>>          } else { /* ETH_TYPE_IPV6 */
>> -            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p);
>> +            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p, sizeof *nh);
>>  
>>              nh->ip6_plen = htons(l4_len);
>>              pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
>> @@ -3035,7 +3036,7 @@ packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
>>          if ((!(flow->nw_frag & FLOW_NW_FRAG_ANY)
>>               || !(flow->nw_frag & FLOW_NW_FRAG_LATER))
>>              && flow->nw_proto == IPPROTO_UDP) {
>> -            struct udp_header *udp = dp_packet_l4(p);
>> +            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
>>  
>>              udp->udp_len = htons(l4_len + extra_size);
>>          }
>> @@ -3103,8 +3104,8 @@ flow_compose(struct dp_packet *p, const struct flow *flow,
>>  
>>          l4_len = flow_compose_l4(p, flow, l7, l7_len);
>>  
>> -        ip = dp_packet_l3(p);
>> -        ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
>> +        ip = dp_packet_l3(p, sizeof *ip);
>> +        ip->ip_tot_len = htons(dp_packet_l3_size(p));
>>          /* Checksum has already been zeroed by put_zeros call. */
>>          ip->ip_csum = csum(ip, sizeof *ip);
>>  
>> @@ -3126,7 +3127,7 @@ flow_compose(struct dp_packet *p, const struct flow *flow,
>>  
>>          l4_len = flow_compose_l4(p, flow, l7, l7_len);
>>  
>> -        nh = dp_packet_l3(p);
>> +        nh = dp_packet_l3(p, sizeof *nh);
>>          nh->ip6_plen = htons(l4_len);
>>  
>>          pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
>> diff --git a/lib/lacp.c b/lib/lacp.c
>> index d6b36aa..ec92202 100644
>> --- a/lib/lacp.c
>> +++ b/lib/lacp.c
>> @@ -190,8 +190,7 @@ parse_lacp_packet(const struct dp_packet *p)
>>  {
>>      const struct lacp_pdu *pdu;
>>  
>> -    pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
>> -                    LACP_PDU_LEN);
>> +    pdu = dp_packet_l3(p, LACP_PDU_LEN);
>>  
>>      if (pdu && pdu->subtype == 1
>>          && pdu->actor_type == 1 && pdu->actor_len == 20
>> diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
>> index 6730301..af0cadb 100644
>> --- a/lib/mcast-snooping.c
>> +++ b/lib/mcast-snooping.c
>> @@ -450,11 +450,11 @@ mcast_snooping_add_report(struct mcast_snooping *ms,
>>      int count = 0;
>>      int ngrp;
>>  
>> -    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
>> -    igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
>> +    igmpv3 = dp_packet_l4(p, IGMPV3_HEADER_LEN);
>>      if (!igmpv3) {
>>          return 0;
>>      }
>> +    offset = (char *) igmpv3 - (char *) dp_packet_data(p);
>>      ngrp = ntohs(igmpv3->ngrp);
>>      offset += IGMPV3_HEADER_LEN;
>>      while (ngrp--) {
>> @@ -502,11 +502,11 @@ mcast_snooping_add_mld(struct mcast_snooping *ms,
>>      int ngrp;
>>      bool ret;
>>  
>> -    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
>> -    mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
>> +    mld = dp_packet_l4(p, MLD_HEADER_LEN);
>>      if (!mld) {
>>          return 0;
>>      }
>> +    offset = (char *) mld - (char *) dp_packet_data(p);
>>      ngrp = ntohs(mld->ngrp);
>>      offset += MLD_HEADER_LEN;
>>      addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
>> diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
>> index a153aa2..a545e7e 100644
>> --- a/lib/netdev-bsd.c
>> +++ b/lib/netdev-bsd.c
>> @@ -701,6 +701,11 @@ netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
>>      }
>>  
>>      for (i = 0; i < batch->count; i++) {
>> +        /* We need the whole data to send the packet on the device */
>> +        if (!dp_packet_is_linear(batch->packets[i])) {
>> +            dp_packet_linearize(batch->packets[i]);
>> +        }
>> +
> 
> 
> Perhaps for this case and the upcall too, we could have a dp_packet
> function that provides data from the packet. E.g.:
> 
> /* buf must have at least 'size' bytes or NULL to return only the
>    amount of bytes linearly available */
> ssize_t dp_packet_data_read(packet, offset, size, &ptr, buf)
> {
>     if dp_packet_mbuf_may_pull(packet, offset, size) {
>         /* zero copy */
>         ptr = dp_packet_at(packet, offset, size);
>         return len;
>     }
> 
>     if (buf == NULL) {
>         ptr = dp_packet_at(packet, offset, size);
>         return <remaining bytes in the seg>;
>     }
> 
>     ptr = buf;
>     return dp_packet_copy(packet, offset, size, buf)
> }
>        
> The callers can iterate and send chunks of data.

Nice! I hadn't considered it to this case, to be honest, only for the
checksums. But seems to be the way forward.

I'll sit on this for the next iteration and will send it next week.

> 
> 
>>          const void *data = dp_packet_data(batch->packets[i]);
>>          size_t size = dp_packet_size(batch->packets[i]);
>>  
>> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
>> index 2cf0563..9682fc3 100644
>> --- a/lib/netdev-dummy.c
>> +++ b/lib/netdev-dummy.c
>> @@ -233,7 +233,13 @@ dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s)
>>  
>>          ASSIGN_CONTAINER(txbuf_node, ovs_list_front(&s->txq), list_node);
>>          txbuf = txbuf_node->pkt;
>> -        retval = stream_send(s->stream, dp_packet_data(txbuf), dp_packet_size(txbuf));
>> +
>> +        if (!dp_packet_is_linear(txbuf)) {
>> +            dp_packet_linearize(txbuf);
>> +        }
>> +
>> +        retval = stream_send(s->stream, dp_packet_data(txbuf),
>> +                             dp_packet_size(txbuf));
>>  
>>          if (retval > 0) {
>>              dp_packet_pull(txbuf, retval);
>> @@ -1088,6 +1094,11 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
>>  
>>      struct dp_packet *packet;
>>      DP_PACKET_BATCH_FOR_EACH(i, packet, batch) {
>> +        /* We need the whole data to send the packet on the device */
>> +        if (!dp_packet_is_linear(packet)) {
>> +            dp_packet_linearize(packet);
>> +        }
>> +
>>          const void *buffer = dp_packet_data(packet);
>>          size_t size = dp_packet_size(packet);
>>  
>> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
>> index c55aad8..a477bab 100644
>> --- a/lib/netdev-linux.c
>> +++ b/lib/netdev-linux.c
>> @@ -1380,6 +1380,11 @@ netdev_linux_sock_batch_send(int sock, int ifindex,
>>  
>>      struct dp_packet *packet;
>>      DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
>> +        /* We need the whole data to send the packet on the device */
>> +        if (!dp_packet_is_linear(packet)) {
>> +            dp_packet_linearize(packet);
>> +        }
>> +
>>          iov[i].iov_base = dp_packet_data(packet);
>>          iov[i].iov_len = dp_packet_size(packet);
>>          mmsg[i].msg_hdr = (struct msghdr) { .msg_name = &sll,
>> @@ -1433,8 +1438,14 @@ netdev_linux_tap_batch_send(struct netdev *netdev_,
>>          ssize_t retval;
>>          int error;
>>  
>> +        /* We need the whole data to send the packet on the device */
>> +        if (!dp_packet_is_linear(packet)) {
>> +            dp_packet_linearize(packet);
>> +        }
>> +
>>          do {
>> -            retval = write(netdev->tap_fd, dp_packet_data(packet), size);
>> +            retval = write(netdev->tap_fd, dp_packet_data(packet),
>> +                           size);
>>              error = retval < 0 ? errno : 0;
>>          } while (error == EINTR);
>>  
>> diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
>> index 56baaa2..24c847d 100644
>> --- a/lib/netdev-native-tnl.c
>> +++ b/lib/netdev-native-tnl.c
>> @@ -65,13 +65,18 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>>      void *nh;
>>      struct ip_header *ip;
>>      struct ovs_16aligned_ip6_hdr *ip6;
>> -    void *l4;
>> +    char *l4;
>>      int l3_size;
>>  
>> -    nh = dp_packet_l3(packet);
>> +    /* We will need to perform the checksum on the whole data below */
>> +    if (!dp_packet_is_linear(packet)) {
>> +        dp_packet_linearize(packet);
>> +    }
>> +
>> +    nh = dp_packet_l3(packet, sizeof *ip);
>>      ip = nh;
>>      ip6 = nh;
>> -    l4 = dp_packet_l4(packet);
>> +    l4 = dp_packet_l4(packet, sizeof *l4);
>>  
>>      if (!nh || !l4) {
>>          return NULL;
>> @@ -79,8 +84,7 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>>  
>>      *hlen = sizeof(struct eth_header);
>>  
>> -    l3_size = dp_packet_size(packet) -
>> -              ((char *)nh - (char *)dp_packet_data(packet));
>> +    l3_size = dp_packet_l3_size(packet);
>>  
>>      if (IP_VER(ip->ip_ihl_ver) == 4) {
>>  
>> @@ -151,6 +155,11 @@ netdev_tnl_push_ip_header(struct dp_packet *packet,
>>      struct ip_header *ip;
>>      struct ovs_16aligned_ip6_hdr *ip6;
>>  
>> +    /* We will need to perform the checksum on the whole data below */
>> +    if (!dp_packet_is_linear(packet)) {
>> +        dp_packet_linearize(packet);
>> +    }
>> +
>>      eth = dp_packet_push_uninit(packet, size);
>>      *ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
>>  
>> @@ -191,15 +200,16 @@ udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
>>          if (OVS_UNLIKELY(!dp_packet_l4_checksum_valid(packet))) {
>>              uint32_t csum;
>>              if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
>> -                csum = packet_csum_pseudoheader6(dp_packet_l3(packet));
>> +                csum = packet_csum_pseudoheader6(
>> +                        dp_packet_l3(packet,
>> +                                     sizeof(struct ip6_hdr)));
>>              } else {
>> -                csum = packet_csum_pseudoheader(dp_packet_l3(packet));
>> +                csum = packet_csum_pseudoheader(
>> +                        dp_packet_l3(packet,
>> +                                     sizeof(struct ip_header)));
>>              }
>>  
>> -            csum = csum_continue(csum, udp, dp_packet_size(packet) -
>> -                                 ((const unsigned char *)udp -
>> -                                  (const unsigned char *)dp_packet_eth(packet)
>> -                                 ));
>> +            csum = csum_continue(csum, udp, dp_packet_l4_size(packet));
>>              if (csum_finish(csum)) {
>>                  return NULL;
>>              }
>> @@ -373,9 +383,7 @@ parse_gre_header(struct dp_packet *packet,
>>      if (greh->flags & htons(GRE_CSUM)) {
>>          ovs_be16 pkt_csum;
>>  
>> -        pkt_csum = csum(greh, dp_packet_size(packet) -
>> -                              ((const unsigned char *)greh -
>> -                               (const unsigned char *)dp_packet_eth(packet)));
>> +        pkt_csum = csum(greh, dp_packet_l4_size(packet));
>>          if (pkt_csum) {
>>              return -EINVAL;
>>          }
>> @@ -448,7 +456,8 @@ netdev_gre_push_header(const struct netdev *netdev,
>>      greh = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
>>  
>>      if (greh->flags & htons(GRE_CSUM)) {
>> -        ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
>> +        greh = dp_packet_l4(packet, sizeof *greh);
>> +        ovs_be16 *csum_opt = (ovs_be16 *) greh;
>>          *csum_opt = csum(greh, ip_tot_size);
>>      }
>>  
>> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
>> index 5831d1f..8fdc4f9 100644
>> --- a/lib/odp-execute.c
>> +++ b/lib/odp-execute.c
>> @@ -70,7 +70,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(packet, sizeof(*nh));
>>      ovs_be32 ip_src_nh;
>>      ovs_be32 ip_dst_nh;
>>      ovs_be32 new_ip_src;
>> @@ -140,7 +140,7 @@ 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(packet, sizeof *nh);
>>      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);
>> @@ -160,7 +160,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(packet, sizeof *th);
>>  
>>      if (OVS_LIKELY(th && dp_packet_get_tcp_payload(packet))) {
>>          packet_set_tcp_port(packet,
>> @@ -173,7 +173,7 @@ 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(packet, sizeof *uh);
>>  
>>      if (OVS_LIKELY(uh && dp_packet_get_udp_payload(packet))) {
>>          packet_set_udp_port(packet,
>> @@ -186,7 +186,7 @@ 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(packet, sizeof *sh);
>>  
>>      if (OVS_LIKELY(sh && dp_packet_get_sctp_payload(packet))) {
>>          packet_set_sctp_port(packet,
>> @@ -205,7 +205,7 @@ 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(packet, sizeof *arp);
>>  
>>      if (!mask) {
>>          arp->ar_op = key->arp_op;
>> @@ -231,8 +231,16 @@ static void
>>  odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
>>             const struct ovs_key_nd *mask)
>>  {
>> -    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
>> -    const struct ovs_nd_lla_opt *lla_opt = dp_packet_get_nd_payload(packet);
>> +    const struct ovs_nd_msg *ns;
>> +    const struct ovs_nd_lla_opt *lla_opt;
>> +
>> +    /* To orocess neighbor discovery options, we need the whole packet */
>> +    if (!dp_packet_is_linear(packet)) {
>> +        dp_packet_linearize(packet);
>> +    }
>> +
>> +    ns = dp_packet_l4(packet, sizeof *ns);
>> +    lla_opt = dp_packet_get_nd_payload(packet);
>>  
>>      if (OVS_LIKELY(ns && lla_opt)) {
>>          int bytes_remain = dp_packet_l4_size(packet) - sizeof(*ns);
>> @@ -275,7 +283,7 @@ static void
>>  odp_set_nsh(struct dp_packet *packet, const struct nlattr *a, bool has_mask)
>>  {
>>      struct ovs_key_nsh key, mask;
>> -    struct nsh_hdr *nsh = dp_packet_l3(packet);
>> +    struct nsh_hdr *nsh = dp_packet_l3(packet, sizeof *nsh);
>>      uint8_t mdtype = nsh_md_type(nsh);
>>      ovs_be32 path_hdr;
>>  
>> @@ -522,7 +530,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
>>          break;
>>  
>>      case OVS_KEY_ATTR_MPLS:
>> -        mh = dp_packet_l2_5(packet);
>> +        mh = dp_packet_l2_5(packet, sizeof *mh);
>>          if (mh) {
>>              put_16aligned_be32(&mh->mpls_lse, nl_attr_get_be32(a)
>>                                 | (get_16aligned_be32(&mh->mpls_lse)
>> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
>> index e05a969..37db260 100644
>> --- a/lib/ofp-print.c
>> +++ b/lib/ofp-print.c
>> @@ -84,21 +84,21 @@ ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
>>      l4_size = dp_packet_l4_size(&buf);
>>  
>>      if (flow.nw_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
>> -        struct tcp_header *th = dp_packet_l4(&buf);
>> +        struct tcp_header *th = dp_packet_l4(&buf, sizeof *th);
>>          ds_put_format(&ds, " tcp_csum:%"PRIx16, ntohs(th->tcp_csum));
>>      } else if (flow.nw_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
>> -        struct udp_header *uh = dp_packet_l4(&buf);
>> +        struct udp_header *uh = dp_packet_l4(&buf, sizeof *uh);
>>          ds_put_format(&ds, " udp_csum:%"PRIx16, ntohs(uh->udp_csum));
>>      } else if (flow.nw_proto == IPPROTO_SCTP && l4_size >= SCTP_HEADER_LEN) {
>> -        struct sctp_header *sh = dp_packet_l4(&buf);
>> +        struct sctp_header *sh = dp_packet_l4(&buf, sizeof *sh);
>>          ds_put_format(&ds, " sctp_csum:%"PRIx32,
>>                        ntohl(get_16aligned_be32(&sh->sctp_csum)));
>>      } else if (flow.nw_proto == IPPROTO_ICMP && l4_size >= ICMP_HEADER_LEN) {
>> -        struct icmp_header *icmph = dp_packet_l4(&buf);
>> +        struct icmp_header *icmph = dp_packet_l4(&buf, sizeof *icmph);
>>          ds_put_format(&ds, " icmp_csum:%"PRIx16,
>>                        ntohs(icmph->icmp_csum));
>>      } else if (flow.nw_proto == IPPROTO_ICMPV6 && l4_size >= ICMP6_HEADER_LEN) {
>> -        struct icmp6_header *icmp6h = dp_packet_l4(&buf);
>> +        struct icmp6_header *icmp6h = dp_packet_l4(&buf, sizeof *icmp6h);
>>          ds_put_format(&ds, " icmp6_csum:%"PRIx16,
>>                        ntohs(icmp6h->icmp6_cksum));
>>      }
>> diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
>> index 05c1dd4..39d677a 100644
>> --- a/lib/ovs-lldp.c
>> +++ b/lib/ovs-lldp.c
>> @@ -668,7 +668,8 @@ lldp_process_packet(struct lldp *lldp, const struct dp_packet *p)
>>  {
>>      if (lldp) {
>>          lldpd_recv(lldp->lldpd, lldpd_first_hardware(lldp->lldpd),
>> -                   (char *) dp_packet_data(p), dp_packet_size(p));
>> +                   (char *) dp_packet_data(p),
>> +                   dp_packet_size(p));
>>      }
>>  }
>>  
>> diff --git a/lib/packets.c b/lib/packets.c
>> index 38bfb60..6ae5118 100644
>> --- a/lib/packets.c
>> +++ b/lib/packets.c
>> @@ -260,8 +260,8 @@ push_eth(struct dp_packet *packet, const struct eth_addr *dst,
>>  void
>>  pop_eth(struct dp_packet *packet)
>>  {
>> -    char *l2_5 = dp_packet_l2_5(packet);
>> -    char *l3 = dp_packet_l3(packet);
>> +    char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
>> +    char *l3 = dp_packet_l3(packet, sizeof *l3);
>>      ovs_be16 ethertype;
>>      int increment;
>>  
>> @@ -292,10 +292,10 @@ set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
>>  
>>      if (eth_type_vlan(eh->eth_type)) {
>>          ovs_be16 *p;
>> -        char *l2_5 = dp_packet_l2_5(packet);
>> +        char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
>>  
>>          p = ALIGNED_CAST(ovs_be16 *,
>> -                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet)) - 2);
>> +                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet, -2)));
>>          *p = eth_type;
>>      } else {
>>          eh->eth_type = eth_type;
>> @@ -359,7 +359,7 @@ set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse)
>>  {
>>      /* Packet type should be MPLS to set label stack entry. */
>>      if (is_mpls(packet)) {
>> -        struct mpls_hdr *mh = dp_packet_l2_5(packet);
>> +        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
>>  
>>          /* Update mpls label stack entry. */
>>          put_16aligned_be32(&mh->mpls_lse, mpls_lse);
>> @@ -401,7 +401,7 @@ void
>>  pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
>>  {
>>      if (is_mpls(packet)) {
>> -        struct mpls_hdr *mh = dp_packet_l2_5(packet);
>> +        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
>>          size_t len = packet->l2_5_ofs;
>>  
>>          set_ethertype(packet, ethtype);
>> @@ -449,7 +449,7 @@ push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)
>>  bool
>>  pop_nsh(struct dp_packet *packet)
>>  {
>> -    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
>> +    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet, sizeof *nsh);
>>      size_t length;
>>      uint32_t next_pt;
>>  
>> @@ -975,16 +975,16 @@ 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);
>> +    struct ip_header *nh = dp_packet_l3(packet, sizeof *nh);
>>      ovs_be32 old_addr = get_16aligned_be32(addr);
>>      size_t l4_size = dp_packet_l4_size(packet);
>>  
>>      if (nh->ip_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
>> -        struct tcp_header *th = dp_packet_l4(packet);
>> +        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>>  
>>          th->tcp_csum = recalc_csum32(th->tcp_csum, old_addr, new_addr);
>>      } else if (nh->ip_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN ) {
>> -        struct udp_header *uh = dp_packet_l4(packet);
>> +        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
>>  
>>          if (uh->udp_csum) {
>>              uh->udp_csum = recalc_csum32(uh->udp_csum, old_addr, new_addr);
>> @@ -1007,12 +1007,20 @@ packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr)
>>      const struct ovs_16aligned_ip6_hdr *nh;
>>      size_t len;
>>      size_t remaining;
>> -    uint8_t *data = dp_packet_l3(packet);
>> +    uint8_t *data;
>>  
>> -    remaining = packet->l4_ofs - packet->l3_ofs;
>> +    remaining = dp_packet_l3h_size(packet);
>>      if (remaining < sizeof *nh) {
>>          return false;
>>      }
>> +
>> +    /* We will need the whole data for processing the headers below */
>> +    if (!dp_packet_is_linear(packet)) {
>> +        dp_packet_linearize(packet);
>> +    }
>> +
>> +    data = dp_packet_l3(packet, sizeof *nh);
>> +
>>      nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
>>      data += sizeof *nh;
>>      remaining -= sizeof *nh;
>> @@ -1088,11 +1096,11 @@ packet_update_csum128(struct dp_packet *packet, uint8_t proto,
>>      size_t l4_size = dp_packet_l4_size(packet);
>>  
>>      if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
>> -        struct tcp_header *th = dp_packet_l4(packet);
>> +        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>>  
>>          th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
>>      } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
>> -        struct udp_header *uh = dp_packet_l4(packet);
>> +        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
>>  
>>          if (uh->udp_csum) {
>>              uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
>> @@ -1102,7 +1110,7 @@ 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);
>> +        struct icmp6_header *icmp = dp_packet_l4(packet, sizeof *icmp);
>>  
>>          icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr, new_addr);
>>      }
>> @@ -1144,7 +1152,7 @@ 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(packet, sizeof *nh);
>>  
>>      if (get_16aligned_be32(&nh->ip_src) != src) {
>>          packet_set_ipv4_addr(packet, &nh->ip_src, src);
>> @@ -1180,7 +1188,7 @@ 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(packet, sizeof *nh);
>>      uint8_t proto = 0;
>>      bool rh_present;
>>  
>> @@ -1215,7 +1223,7 @@ 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);
>> +    struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
>>  
>>      packet_set_port(&th->tcp_src, src, &th->tcp_csum);
>>      packet_set_port(&th->tcp_dst, dst, &th->tcp_csum);
>> @@ -1227,7 +1235,7 @@ 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(packet, sizeof *uh);
>>  
>>      if (uh->udp_csum) {
>>          packet_set_port(&uh->udp_src, src, &uh->udp_csum);
>> @@ -1248,7 +1256,7 @@ 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);
>> +    struct sctp_header *sh = dp_packet_l4(packet, sizeof *sh);
>>      ovs_be32 old_csum, old_correct_csum, new_csum;
>>      uint16_t tp_len = dp_packet_l4_size(packet);
>>  
>> @@ -1269,7 +1277,7 @@ 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);
>> +    struct icmp_header *ih = dp_packet_l4(packet, sizeof(*ih));
>>      ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
>>      ovs_be16 new_tc = htons(type << 8 | code);
>>  
>> @@ -1293,7 +1301,12 @@ packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
>>          return;
>>      }
>>  
>> -    ns = dp_packet_l4(packet);
>> +    /* To process neighbor discovery options, we need the whole packet */
>> +    if (!dp_packet_is_linear(packet)) {
>> +        dp_packet_linearize(packet);
>> +    }
>> +
>> +    ns = dp_packet_l4(packet, sizeof *ns);
>>      opt = &ns->options[0];
>>      bytes_remain -= sizeof(*ns);
>>  
>> @@ -1431,7 +1444,7 @@ compose_arp(struct dp_packet *b, uint16_t arp_op,
>>      eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
>>      eth->eth_src = arp_sha;
>>  
>> -    struct arp_eth_header *arp = dp_packet_l3(b);
>> +    struct arp_eth_header *arp = dp_packet_l3(b, sizeof *arp);
>>      arp->ar_op = htons(arp_op);
>>      arp->ar_sha = arp_sha;
>>      arp->ar_tha = arp_tha;
>> @@ -1475,7 +1488,7 @@ compose_ipv6(struct dp_packet *packet, uint8_t proto,
>>      struct ip6_hdr *nh;
>>      void *data;
>>  
>> -    nh = dp_packet_l3(packet);
>> +    nh = dp_packet_l3(packet, sizeof *nh);
>>      nh->ip6_vfc = 0x60;
>>      nh->ip6_nxt = proto;
>>      nh->ip6_plen = htons(size);
>> @@ -1514,7 +1527,8 @@ compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
>>      packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
>>  
>>      ns->icmph.icmp6_cksum = 0;
>> -    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
>> +    icmp_csum = packet_csum_pseudoheader6(
>> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>>      ns->icmph.icmp6_cksum = csum_finish(
>>          csum_continue(icmp_csum, ns, ND_MSG_LEN + ND_LLA_OPT_LEN));
>>  }
>> @@ -1545,7 +1559,8 @@ compose_nd_na(struct dp_packet *b,
>>      packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
>>  
>>      na->icmph.icmp6_cksum = 0;
>> -    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
>> +    icmp_csum = packet_csum_pseudoheader6(
>> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>>      na->icmph.icmp6_cksum = csum_finish(csum_continue(
>>          icmp_csum, na, ND_MSG_LEN + ND_LLA_OPT_LEN));
>>  }
>> @@ -1596,7 +1611,8 @@ compose_nd_ra(struct dp_packet *b,
>>      }
>>  
>>      ra->icmph.icmp6_cksum = 0;
>> -    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
>> +    uint32_t icmp_csum = packet_csum_pseudoheader6(
>> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>>          icmp_csum, ra, RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_len));
>>  }
>> @@ -1610,10 +1626,10 @@ packet_put_ra_prefix_opt(struct dp_packet *b,
>>                           const ovs_be128 prefix)
>>  {
>>      size_t prev_l4_size = dp_packet_l4_size(b);
>> -    struct ip6_hdr *nh = dp_packet_l3(b);
>> +    struct ip6_hdr *nh = dp_packet_l3(b, sizeof *nh);
>>      nh->ip6_plen = htons(prev_l4_size + ND_PREFIX_OPT_LEN);
>>  
>> -    struct ovs_ra_msg *ra = dp_packet_l4(b);
>> +    struct ovs_ra_msg *ra = dp_packet_l4(b, sizeof *ra);
>>      struct ovs_nd_prefix_opt *prefix_opt =
>>          dp_packet_put_uninit(b, sizeof *prefix_opt);
>>      prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
>> @@ -1626,7 +1642,8 @@ packet_put_ra_prefix_opt(struct dp_packet *b,
>>      memcpy(prefix_opt->prefix.be32, prefix.be32, sizeof(ovs_be32[4]));
>>  
>>      ra->icmph.icmp6_cksum = 0;
>> -    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
>> +    uint32_t icmp_csum = packet_csum_pseudoheader6(
>> +        dp_packet_l3(b, sizeof(struct ip6_hdr)));
>>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>>          icmp_csum, ra, prev_l4_size + ND_PREFIX_OPT_LEN));
>>  }
>> @@ -1684,12 +1701,12 @@ 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(pkt, sizeof *ip6);
>>  
>>          put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
>>                                  htonl(IP_ECN_CE << 20));
>>      } else {
>> -        struct ip_header *nh = dp_packet_l3(pkt);
>> +        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
>>          uint8_t tos = nh->ip_tos;
>>  
>>          tos |= IP_ECN_CE;
>> diff --git a/lib/pcap-file.c b/lib/pcap-file.c
>> index ea5cfa3..6340862 100644
>> --- a/lib/pcap-file.c
>> +++ b/lib/pcap-file.c
>> @@ -315,7 +315,7 @@ tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
>>          || !l7) {
>>          return NULL;
>>      }
>> -    tcp = dp_packet_l4(packet);
>> +    tcp = dp_packet_l4(packet, sizeof *tcp);
>>      flags = TCP_FLAGS(tcp->tcp_ctl);
>>      l7_length = (char *) dp_packet_tail(packet) - l7;
>>      seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index 0cc964a..220b8c0 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -1381,12 +1381,18 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>>      case SFLOW_UPCALL:
>>          if (upcall->sflow) {
>>              struct dpif_sflow_actions sflow_actions;
>> +            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
>>  
>>              memset(&sflow_actions, 0, sizeof sflow_actions);
>>  
>>              actions_len = dpif_read_actions(udpif, upcall, flow,
>>                                              upcall->type, &sflow_actions);
>> -            dpif_sflow_received(upcall->sflow, packet, flow,
>> +            /* Gather the whole data */
>> +            if (!dp_packet_is_linear(p)) {
>> +                dp_packet_linearize(p);
>> +            }
>> +
>> +            dpif_sflow_received(upcall->sflow, p, flow,
>>                                  flow->in_port.odp_port, &upcall->cookie,
>>                                  actions_len > 0 ? &sflow_actions : NULL);
>>          }
>> @@ -1447,6 +1453,12 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>>  
>>              const struct frozen_state *state = &recirc_node->state;
>>  
>> +            /* Gather the whole data */
>> +            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
>> +            if (!dp_packet_is_linear(p)) {
>> +                dp_packet_linearize(p);
>> +            }
>> +
>>              struct ofproto_async_msg *am = xmalloc(sizeof *am);
>>              *am = (struct ofproto_async_msg) {
>>                  .controller_id = cookie->controller.controller_id,
>> @@ -1454,9 +1466,9 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
>>                  .pin = {
>>                      .up = {
>>                          .base = {
>> -                            .packet = xmemdup(dp_packet_data(packet),
>> -                                              dp_packet_size(packet)),
>> -                            .packet_len = dp_packet_size(packet),
>> +                            .packet = xmemdup(dp_packet_data(p),
>> +                                              dp_packet_size(p)),
>> +                            .packet_len = dp_packet_size(p),
>>                              .reason = cookie->controller.reason,
>>                              .table_id = state->table_id,
>>                              .cookie = get_32aligned_be64(
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index 84cce81..26e3437 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -1735,7 +1735,8 @@ stp_process_packet(const struct xport *xport, const struct dp_packet *packet)
>>      }
>>  
>>      if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
>> -        stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload));
>> +        stp_received_bpdu(sp, dp_packet_data(&payload),
>> +                          dp_packet_size(&payload));
>>      }
>>  }
>>  
>> @@ -1786,7 +1787,8 @@ rstp_process_packet(const struct xport *xport, const struct dp_packet *packet)
>>      }
>>  
>>      if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
>> -        rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload),
>> +        rstp_port_received_bpdu(xport->rstp_port,
>> +                                dp_packet_data(&payload),
>>                                  dp_packet_size(&payload));
>>      }
>>  }
>> @@ -2556,11 +2558,10 @@ update_mcast_snooping_table4__(const struct xlate_ctx *ctx,
>>  {
>>      const struct igmp_header *igmp;
>>      int count;
>> -    size_t offset;
>>      ovs_be32 ip4 = flow->igmp_group_ip4;
>>  
>> -    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
>> -    igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
>> +    igmp = dp_packet_l4(packet, IGMP_HEADER_LEN);
>> +
>>      if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
>>          xlate_report_debug(ctx, OFT_DETAIL,
>>                             "multicast snooping received bad IGMP "
>> @@ -2616,13 +2617,11 @@ update_mcast_snooping_table6__(const struct xlate_ctx *ctx,
>>  {
>>      const struct mld_header *mld;
>>      int count;
>> -    size_t offset;
>>  
>> -    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
>> -    mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
>> +    mld = dp_packet_l4(packet, MLD_HEADER_LEN);
>>  
>>      if (!mld ||
>> -        packet_csum_upperlayer6(dp_packet_l3(packet),
>> +        packet_csum_upperlayer6(dp_packet_l3(packet, sizeof(struct ip6_hdr)),
>>                                  mld, IPPROTO_ICMPV6,
>>                                  dp_packet_l4_size(packet)) != 0) {
>>          xlate_report_debug(ctx, OFT_DETAIL, "multicast snooping received "
>> @@ -2925,6 +2924,13 @@ xlate_normal(struct xlate_ctx *ctx)
>>          && is_ip_any(flow)) {
>>          struct mcast_snooping *ms = ctx->xbridge->ms;
>>          struct mcast_group *grp = NULL;
>> +        struct dp_packet *p = CONST_CAST(struct dp_packet *,
>> +                                         ctx->xin->packet);
>> +
>> +        /* We will need the whole data for processing the packet below */
>> +        if (!dp_packet_is_linear(p)) {
>> +            dp_packet_linearize(p);
>> +        }
>>  
>>          if (is_igmp(flow, wc)) {
>>              /*
>> @@ -3213,7 +3219,8 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>>      const struct flow *flow = &ctx->xin->flow;
>>      struct flow_wildcards *wc = ctx->wc;
>>      const struct xbridge *xbridge = ctx->xbridge;
>> -    const struct dp_packet *packet = ctx->xin->packet;
>> +    struct dp_packet *packet = CONST_CAST(struct dp_packet *,
>> +                                          ctx->xin->packet);
>>      enum slow_path_reason slow;
>>  
>>      if (!xport) {
>> @@ -3225,6 +3232,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>>          slow = SLOW_CFM;
>>      } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
>>          if (packet) {
>> +            /* Gather the whole data for further processing */
>> +            if (!dp_packet_is_linear(packet)) {
>> +                dp_packet_linearize(packet);
>> +            }
>> +
>>              bfd_process_packet(xport->bfd, flow, packet);
>>              /* If POLL received, immediately sends FINAL back. */
>>              if (bfd_should_send_packet(xport->bfd)) {
>> @@ -3241,6 +3253,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>>      } else if ((xbridge->stp || xbridge->rstp) &&
>>                 stp_should_process_flow(flow, wc)) {
>>          if (packet) {
>> +            /* Gather the whole data for further processing */
>> +            if (!dp_packet_is_linear(packet)) {
>> +                dp_packet_linearize(packet);
>> +            }
>> +
>>              xbridge->stp
>>                  ? stp_process_packet(xport, packet)
>>                  : rstp_process_packet(xport, packet);
>> @@ -3248,6 +3265,11 @@ process_special(struct xlate_ctx *ctx, const struct xport *xport)
>>          slow = SLOW_STP;
>>      } else if (xport->lldp && lldp_should_process_flow(xport->lldp, flow)) {
>>          if (packet) {
>> +            /* Gather the whole data for further processing */
>> +            if (!dp_packet_is_linear(packet)) {
>> +                dp_packet_linearize(packet);
>> +            }
>> +
>>              lldp_process_packet(xport->lldp, packet);
>>          }
>>          slow = SLOW_LLDP;
>> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
>> index 8ae4c9e..cda1135 100644
>> --- a/ovn/controller/pinctrl.c
>> +++ b/ovn/controller/pinctrl.c
>> @@ -213,7 +213,7 @@ pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
>>      eth->eth_dst = ip_flow->dl_dst;
>>      eth->eth_src = ip_flow->dl_src;
>>  
>> -    struct arp_eth_header *arp = dp_packet_l3(&packet);
>> +    struct arp_eth_header *arp = dp_packet_l3(&packet, sizeof(*arp));
>>      arp->ar_op = htons(ARP_OP_REQUEST);
>>      arp->ar_sha = ip_flow->dl_src;
>>      put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
>> @@ -291,9 +291,10 @@ pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
>>          ih->icmp6_base.icmp6_cksum = 0;
>>  
>>          uint8_t *data = dp_packet_put_zeros(&packet, sizeof *nh);
>> -        memcpy(data, dp_packet_l3(pkt_in), sizeof(*nh));
>> +        memcpy(data, dp_packet_l3(pkt_in, sizeof(*nh)), sizeof(*nh));
>>  
>> -        icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
>> +        icmpv6_csum = packet_csum_pseudoheader6(
>> +            dp_packet_l3(&packet, sizeof(struct ovs_16aligned_ip6_hdr)));
>>          ih->icmp6_base.icmp6_cksum = csum_finish(
>>              csum_continue(icmpv6_csum, ih,
>>                            sizeof(*nh) + ICMP6_ERROR_HEADER_LEN));
>> @@ -355,7 +356,7 @@ pinctrl_handle_tcp_reset(const struct flow *ip_flow, struct dp_packet *pkt_in,
>>      }
>>  
>>      struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
>> -    struct tcp_header *tcp_in = dp_packet_l4(pkt_in);
>> +    struct tcp_header *tcp_in = dp_packet_l4(pkt_in, sizeof(*tcp_in));
>>      dp_packet_set_l4(&packet, th);
>>      th->tcp_ctl = TCP_CTL(TCP_RST, 5);
>>      if (ip_flow->tcp_flags & htons(TCP_ACK)) {
>> @@ -534,7 +535,7 @@ pinctrl_handle_put_dhcp_opts(
>>  
>>      udp->udp_len = htons(new_l4_size);
>>  
>> -    struct ip_header *out_ip = dp_packet_l3(&pkt_out);
>> +    struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
>>      out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
>>      udp->udp_csum = 0;
>>      /* Checksum needs to be initialized to zero. */
>> @@ -709,7 +710,7 @@ pinctrl_handle_put_dhcpv6_opts(
>>          goto exit;
>>      }
>>  
>> -    struct udp_header *in_udp = dp_packet_l4(pkt_in);
>> +    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof(*in_udp));
>>      const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in);
>>      if (!in_udp || !in_dhcpv6_data) {
>>          VLOG_WARN_RL(&rl, "truncated dhcpv6 packet");
>> @@ -824,11 +825,13 @@ pinctrl_handle_put_dhcpv6_opts(
>>      out_udp->udp_len = htons(new_l4_size);
>>      out_udp->udp_csum = 0;
>>  
>> -    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out);
>> +    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out,
>> +                                                         sizeof *out_ip6);
>>      out_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = out_udp->udp_len;
>>  
>>      uint32_t csum;
>> -    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out));
>> +    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out,
>> +        sizeof(struct ovs_16aligned_ip6_hdr)));
>>      csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) -
>>                           ((const unsigned char *)out_udp -
>>                           (const unsigned char *)dp_packet_eth(&pkt_out)));
>> @@ -916,7 +919,7 @@ pinctrl_handle_dns_lookup(
>>          goto exit;
>>      }
>>  
>> -    struct udp_header *in_udp = dp_packet_l4(pkt_in);
>> +    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof *in_udp);
>>      size_t udp_len = ntohs(in_udp->udp_len);
>>      size_t l4_len = dp_packet_l4_size(pkt_in);
>>      uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
>> @@ -1081,14 +1084,14 @@ pinctrl_handle_dns_lookup(
>>  
>>      struct eth_header *eth = dp_packet_data(&pkt_out);
>>      if (eth->eth_type == htons(ETH_TYPE_IP)) {
>> -        struct ip_header *out_ip = dp_packet_l3(&pkt_out);
>> +        struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
>>          out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs
>>                                     + new_l4_size);
>>          /* Checksum needs to be initialized to zero. */
>>          out_ip->ip_csum = 0;
>>          out_ip->ip_csum = csum(out_ip, sizeof *out_ip);
>>      } else {
>> -        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
>> +        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
>>          nh->ip6_plen = htons(new_l4_size);
>>  
>>          /* IPv6 needs UDP checksum calculated */
>> @@ -2487,9 +2490,9 @@ pinctrl_handle_put_nd_ra_opts(
>>      dp_packet_put(&pkt_out, userdata->data, userdata->size);
>>  
>>      /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
>> -    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
>> +    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
>>      nh->ip6_plen = htons(userdata->size);
>> -    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
>> +    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out, sizeof *ra);
>>      ra->icmph.icmp6_cksum = 0;
>>      uint32_t icmp_csum = packet_csum_pseudoheader6(nh);
>>      ra->icmph.icmp6_cksum = csum_finish(csum_continue(
>> diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c
>> index 12017ea..72d4ecc 100644
>> --- a/tests/test-conntrack.c
>> +++ b/tests/test-conntrack.c
>> @@ -46,7 +46,7 @@ prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
>>          dp_packet_put_hex(pkt, payload, NULL);
>>          flow_extract(pkt, &flow);
>>  
>> -        udp = dp_packet_l4(pkt);
>> +        udp = dp_packet_l4(pkt, sizeof *udp);
>>          udp->udp_src = htons(ntohs(udp->udp_src) + tid);
>>  
>>          if (change) {
>> diff --git a/tests/test-rstp.c b/tests/test-rstp.c
>> index 01aeaf8..a0125d1 100644
>> --- a/tests/test-rstp.c
>> +++ b/tests/test-rstp.c
>> @@ -86,8 +86,12 @@ send_bpdu(struct dp_packet *pkt, void *port_, void *b_)
>>      assert(port_no < b->n_ports);
>>      lan = b->ports[port_no];
>>      if (lan) {
>> -        const void *data = dp_packet_l3(pkt);
>> -        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
>> +        if (!dp_packet_is_linear(pkt)) {
>> +            dp_packet_linearize(pkt);
>> +        }
>> +
>> +        const char *data = dp_packet_l3(pkt, sizeof *data);
>> +        size_t size = dp_packet_size(pkt);
>>          int i;
>>  
>>          for (i = 0; i < lan->n_conns; i++) {
>> diff --git a/tests/test-stp.c b/tests/test-stp.c
>> index c85c99d..c0f566c 100644
>> --- a/tests/test-stp.c
>> +++ b/tests/test-stp.c
>> @@ -94,8 +94,12 @@ send_bpdu(struct dp_packet *pkt, int port_no, void *b_)
>>      assert(port_no < b->n_ports);
>>      lan = b->ports[port_no];
>>      if (lan) {
>> -        const void *data = dp_packet_l3(pkt);
>> -        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
>> +        if (!dp_packet_is_linear(pkt)) {
>> +            dp_packet_linearize(pkt);
>> +        }
>> +
>> +        const char *data = dp_packet_l3(pkt, sizeof *data);
>> +        size_t size = dp_packet_size(pkt);
>>          int i;
>>  
>>          for (i = 0; i < lan->n_conns; i++) {
>> -- 
>> 2.7.4
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

Patch

diff --git a/lib/bfd.c b/lib/bfd.c
index 5308262..d50d2da 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -722,7 +722,8 @@  bfd_process_packet(struct bfd *bfd, const struct flow *flow,
     if (!msg) {
         VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
                      "%"PRIdPTR" bytes long, at least %d required).",
-                     bfd->name, (uint8_t *) dp_packet_tail(p) - l7,
+                     bfd->name, dp_packet_size(p) -
+                     (l7 - (uint8_t *) dp_packet_data(p)),
                      BFD_PACKET_LEN);
         goto out;
     }
diff --git a/lib/cfm.c b/lib/cfm.c
index 71d2c02..83baf2a 100644
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -584,7 +584,7 @@  cfm_compose_ccm(struct cfm *cfm, struct dp_packet *packet,
 
     atomic_read_relaxed(&cfm->extended, &extended);
 
-    ccm = dp_packet_l3(packet);
+    ccm = dp_packet_l3(packet, sizeof(*ccm));
     ccm->mdlevel_version = 0;
     ccm->opcode = CCM_OPCODE;
     ccm->tlv_offset = 70;
@@ -759,8 +759,7 @@  cfm_process_heartbeat(struct cfm *cfm, const struct dp_packet *p)
     atomic_read_relaxed(&cfm->extended, &extended);
 
     eth = dp_packet_eth(p);
-    ccm = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
-                    CCM_ACCEPT_LEN);
+    ccm = dp_packet_l3(p, CCM_ACCEPT_LEN);
 
     if (!ccm) {
         VLOG_INFO_RL(&rl, "%s: Received an unparseable 802.1ag CCM heartbeat.",
diff --git a/lib/conntrack-icmp.c b/lib/conntrack-icmp.c
index 40fd1d8..0575d0e 100644
--- a/lib/conntrack-icmp.c
+++ b/lib/conntrack-icmp.c
@@ -63,7 +63,7 @@  icmp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
 static bool
 icmp4_valid_new(struct dp_packet *pkt)
 {
-    struct icmp_header *icmp = dp_packet_l4(pkt);
+    struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
 
     return icmp->icmp_type == ICMP4_ECHO_REQUEST
            || icmp->icmp_type == ICMP4_INFOREQUEST
@@ -73,7 +73,7 @@  icmp4_valid_new(struct dp_packet *pkt)
 static bool
 icmp6_valid_new(struct dp_packet *pkt)
 {
-    struct icmp6_header *icmp6 = dp_packet_l4(pkt);
+    struct icmp6_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
 
     return icmp6->icmp6_type == ICMP6_ECHO_REQUEST;
 }
diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
index a344801..2191aa6 100644
--- a/lib/conntrack-private.h
+++ b/lib/conntrack-private.h
@@ -159,8 +159,8 @@  tcp_payload_length(struct dp_packet *pkt)
 {
     const char *tcp_payload = dp_packet_get_tcp_payload(pkt);
     if (tcp_payload) {
-        return ((char *) dp_packet_tail(pkt) - dp_packet_l2_pad_size(pkt)
-                - tcp_payload);
+        return dp_packet_l4_size(pkt) -
+               (tcp_payload - (char *) dp_packet_l4(pkt, 0));
     } else {
         return 0;
     }
diff --git a/lib/conntrack-tcp.c b/lib/conntrack-tcp.c
index 86d313d..5450971 100644
--- a/lib/conntrack-tcp.c
+++ b/lib/conntrack-tcp.c
@@ -149,7 +149,7 @@  tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
                 struct dp_packet *pkt, bool reply, long long now)
 {
     struct conn_tcp *conn = conn_tcp_cast(conn_);
-    struct tcp_header *tcp = dp_packet_l4(pkt);
+    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
     /* The peer that sent 'pkt' */
     struct tcp_peer *src = &conn->peer[reply ? 1 : 0];
     /* The peer that should receive 'pkt' */
@@ -394,7 +394,7 @@  tcp_conn_update(struct conn *conn_, struct conntrack_bucket *ctb,
 static bool
 tcp_valid_new(struct dp_packet *pkt)
 {
-    struct tcp_header *tcp = dp_packet_l4(pkt);
+    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
     uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
 
     if (tcp_invalid_flags(tcp_flags)) {
@@ -416,7 +416,7 @@  tcp_new_conn(struct conntrack_bucket *ctb, struct dp_packet *pkt,
              long long now)
 {
     struct conn_tcp* newconn = NULL;
-    struct tcp_header *tcp = dp_packet_l4(pkt);
+    struct tcp_header *tcp = dp_packet_l4(pkt, sizeof *tcp);
     struct tcp_peer *src, *dst;
     uint16_t tcp_flags = TCP_FLAGS(tcp->tcp_ctl);
 
diff --git a/lib/conntrack.c b/lib/conntrack.c
index 974f985..f024186 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -450,10 +450,10 @@  get_ip_proto(const struct dp_packet *pkt)
     uint8_t ip_proto;
     struct eth_header *l2 = dp_packet_eth(pkt);
     if (l2->eth_type == htons(ETH_TYPE_IPV6)) {
-        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
         ip_proto = nh6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
     } else {
-        struct ip_header *l3_hdr = dp_packet_l3(pkt);
+        struct ip_header *l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
         ip_proto = l3_hdr->ip_proto;
     }
 
@@ -476,8 +476,8 @@  get_alg_ctl_type(const struct dp_packet *pkt, ovs_be16 tp_src, ovs_be16 tp_dst,
     enum { CT_IPPORT_FTP = 21 };
     enum { CT_IPPORT_TFTP = 69 };
     uint8_t ip_proto = get_ip_proto(pkt);
-    struct udp_header *uh = dp_packet_l4(pkt);
-    struct tcp_header *th = dp_packet_l4(pkt);
+    struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
+    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
     ovs_be16 ftp_src_port = htons(CT_IPPORT_FTP);
     ovs_be16 ftp_dst_port = htons(CT_IPPORT_FTP);
     ovs_be16 tftp_dst_port = htons(CT_IPPORT_TFTP);
@@ -530,18 +530,18 @@  pat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
     if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th = dp_packet_l4(pkt);
+            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
             packet_set_tcp_port(pkt, conn->rev_key.dst.port, th->tcp_dst);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh = dp_packet_l4(pkt);
+            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
             packet_set_udp_port(pkt, conn->rev_key.dst.port, uh->udp_dst);
         }
     } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th = dp_packet_l4(pkt);
+            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
             packet_set_tcp_port(pkt, th->tcp_src, conn->rev_key.src.port);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh = dp_packet_l4(pkt);
+            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
             packet_set_udp_port(pkt, uh->udp_src, conn->rev_key.src.port);
         }
     }
@@ -553,11 +553,11 @@  nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
     if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
         pkt->md.ct_state |= CS_SRC_NAT;
         if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-            struct ip_header *nh = dp_packet_l3(pkt);
+            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
             packet_set_ipv4_addr(pkt, &nh->ip_src,
                                  conn->rev_key.dst.addr.ipv4_aligned);
         } else {
-            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
             packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                  nh6->ip6_src.be32,
                                  &conn->rev_key.dst.addr.ipv6_aligned,
@@ -569,11 +569,11 @@  nat_packet(struct dp_packet *pkt, const struct conn *conn, bool related)
     } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
         pkt->md.ct_state |= CS_DST_NAT;
         if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-            struct ip_header *nh = dp_packet_l3(pkt);
+            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
             packet_set_ipv4_addr(pkt, &nh->ip_dst,
                                  conn->rev_key.src.addr.ipv4_aligned);
         } else {
-            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
             packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                  nh6->ip6_dst.be32,
                                  &conn->rev_key.src.addr.ipv6_aligned,
@@ -590,18 +590,18 @@  un_pat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
     if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th = dp_packet_l4(pkt);
+            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
             packet_set_tcp_port(pkt, th->tcp_src, conn->key.src.port);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh = dp_packet_l4(pkt);
+            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
             packet_set_udp_port(pkt, uh->udp_src, conn->key.src.port);
         }
     } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th = dp_packet_l4(pkt);
+            struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
             packet_set_tcp_port(pkt, conn->key.dst.port, th->tcp_dst);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh = dp_packet_l4(pkt);
+            struct udp_header *uh = dp_packet_l4(pkt, sizeof *uh);
             packet_set_udp_port(pkt, conn->key.dst.port, uh->udp_dst);
         }
     }
@@ -612,21 +612,21 @@  reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
     if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th_in = dp_packet_l4(pkt);
+            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
             packet_set_tcp_port(pkt, conn->key.src.port,
                                 th_in->tcp_dst);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh_in = dp_packet_l4(pkt);
+            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
             packet_set_udp_port(pkt, conn->key.src.port,
                                 uh_in->udp_dst);
         }
     } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
         if (conn->key.nw_proto == IPPROTO_TCP) {
-            struct tcp_header *th_in = dp_packet_l4(pkt);
+            struct tcp_header *th_in = dp_packet_l4(pkt, sizeof *th_in);
             packet_set_tcp_port(pkt, th_in->tcp_src,
                                 conn->key.dst.port);
         } else if (conn->key.nw_proto == IPPROTO_UDP) {
-            struct udp_header *uh_in = dp_packet_l4(pkt);
+            struct udp_header *uh_in = dp_packet_l4(pkt, sizeof *uh_in);
             packet_set_udp_port(pkt, uh_in->udp_src,
                                 conn->key.dst.port);
         }
@@ -636,16 +636,26 @@  reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
 static void
 reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
-    char *tail = dp_packet_tail(pkt);
-    char pad = dp_packet_l2_pad_size(pkt);
+    char *tail;
+    char pad;
     struct conn_key inner_key;
     const char *inner_l4 = NULL;
-    uint16_t orig_l3_ofs = pkt->l3_ofs;
-    uint16_t orig_l4_ofs = pkt->l4_ofs;
+    uint16_t orig_l3_ofs;
+    uint16_t orig_l4_ofs;
+
+    /* We need the whole packet to parse the packet below */
+    if (!dp_packet_is_linear(pkt)) {
+        dp_packet_linearize(pkt);
+    }
+
+    tail = dp_packet_tail(pkt);
+    pad = dp_packet_l2_pad_size(pkt);
+    orig_l3_ofs = pkt->l3_ofs;
+    orig_l4_ofs = pkt->l4_ofs;
 
     if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-        struct ip_header *nh = dp_packet_l3(pkt);
-        struct icmp_header *icmp = dp_packet_l4(pkt);
+        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
+        struct icmp_header *icmp = dp_packet_l4(pkt, sizeof *icmp);
         struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1);
         extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad,
                         &inner_l4, false);
@@ -664,8 +674,8 @@  reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
         icmp->icmp_csum = 0;
         icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad);
     } else {
-        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
-        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt);
+        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
+        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt, sizeof *icmp6);
         struct ovs_16aligned_ip6_hdr *inner_l3_6 =
             (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
         extract_l3_ipv6(&inner_key, inner_l3_6,
@@ -702,11 +712,11 @@  un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
     if (conn->nat_info->nat_action & NAT_ACTION_SRC) {
         pkt->md.ct_state |= CS_DST_NAT;
         if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-            struct ip_header *nh = dp_packet_l3(pkt);
+            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
             packet_set_ipv4_addr(pkt, &nh->ip_dst,
                                  conn->key.src.addr.ipv4_aligned);
         } else {
-            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
             packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                  nh6->ip6_dst.be32,
                                  &conn->key.src.addr.ipv6_aligned, true);
@@ -720,11 +730,11 @@  un_nat_packet(struct dp_packet *pkt, const struct conn *conn,
     } else if (conn->nat_info->nat_action & NAT_ACTION_DST) {
         pkt->md.ct_state |= CS_SRC_NAT;
         if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-            struct ip_header *nh = dp_packet_l3(pkt);
+            struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
             packet_set_ipv4_addr(pkt, &nh->ip_src,
                                  conn->key.dst.addr.ipv4_aligned);
         } else {
-            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
             packet_set_ipv6_addr(pkt, conn->key.nw_proto,
                                  nh6->ip6_src.be32,
                                  &conn->key.dst.addr.ipv6_aligned, true);
@@ -1320,6 +1330,7 @@  conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
             write_ct_md(packet, zone, NULL, NULL, NULL);
             continue;
         }
+
         process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
                     setlabel, nat_action_info, tp_src, tp_dst, helper);
     }
@@ -1901,9 +1912,18 @@  static bool
 conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,
                  struct conn_lookup_ctx *ctx, uint16_t zone)
 {
-    const struct eth_header *l2 = dp_packet_eth(pkt);
-    const struct ip_header *l3 = dp_packet_l3(pkt);
-    const char *l4 = dp_packet_l4(pkt);
+    const struct eth_header *l2;
+    const struct ip_header *l3;
+    const char *l4;
+
+    /* We need the whole packet to parse the packet below */
+    if (!dp_packet_is_linear(pkt)) {
+        dp_packet_linearize(pkt);
+    }
+
+    l2 = dp_packet_eth(pkt);
+    l3 = dp_packet_l3(pkt, sizeof *l3);
+    l4 = dp_packet_l4(pkt, sizeof *l4);
 
     memset(ctx, 0, sizeof *ctx);
 
@@ -2846,7 +2866,7 @@  terminate_number_str(char *str, uint8_t max_digits)
 static void
 get_ftp_ctl_msg(struct dp_packet *pkt, char *ftp_msg)
 {
-    struct tcp_header *th = dp_packet_l4(pkt);
+    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
     char *tcp_hdr = (char *) th;
     uint32_t tcp_payload_len = tcp_payload_length(pkt);
     size_t tcp_payload_of_interest = MIN(tcp_payload_len,
@@ -2888,7 +2908,7 @@  process_ftp_ctl_v4(struct conntrack *ct,
                    char **ftp_data_v4_start,
                    size_t *addr_offset_from_ftp_data_start)
 {
-    struct tcp_header *th = dp_packet_l4(pkt);
+    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
     size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
     char *tcp_hdr = (char *) th;
     *ftp_data_v4_start = tcp_hdr + tcp_hdr_len;
@@ -3034,7 +3054,7 @@  process_ftp_ctl_v6(struct conntrack *ct,
                    size_t *addr_offset_from_ftp_data_start,
                    size_t *addr_size, enum ct_alg_mode *mode)
 {
-    struct tcp_header *th = dp_packet_l4(pkt);
+    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
     size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
     char *tcp_hdr = (char *) th;
     char ftp_msg[LARGEST_FTP_MSG_OF_INTEREST + 1] = {0};
@@ -3167,7 +3187,7 @@  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
                const struct conn *conn_for_expectation,
                long long now, enum ftp_ctl_pkt ftp_ctl, bool nat)
 {
-    struct ip_header *l3_hdr = dp_packet_l3(pkt);
+    struct ip_header *l3_hdr;
     ovs_be32 v4_addr_rep = 0;
     struct ct_addr v6_addr_rep;
     size_t addr_offset_from_ftp_data_start;
@@ -3176,6 +3196,13 @@  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
     bool do_seq_skew_adj = true;
     enum ct_alg_mode mode = CT_FTP_MODE_ACTIVE;
 
+    /* We need the whole packet to parse the packet below */
+    if (!dp_packet_is_linear(pkt)) {
+        dp_packet_linearize(pkt);
+    }
+
+    l3_hdr = dp_packet_l3(pkt, sizeof *l3_hdr);
+
     if (detect_ftp_ctl_type(ctx, pkt) != ftp_ctl) {
         return;
     }
@@ -3184,7 +3211,7 @@  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
         do_seq_skew_adj = false;
     }
 
-    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
+    struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt, sizeof *nh6);
     int64_t seq_skew = 0;
 
     if (ftp_ctl == CT_FTP_CTL_OTHER) {
@@ -3240,7 +3267,7 @@  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
         OVS_NOT_REACHED();
     }
 
-    struct tcp_header *th = dp_packet_l4(pkt);
+    struct tcp_header *th = dp_packet_l4(pkt, sizeof *th);
 
     if (do_seq_skew_adj && seq_skew != 0) {
         if (ctx->reply != conn_for_expectation->seq_skew_dir) {
diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 806640b..b8f5242 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -118,6 +118,9 @@  void
 dp_packet_init_dpdk(struct dp_packet *b)
 {
     b->source = DPBUF_DPDK;
+#ifdef DPDK_NETDEV
+    b->mstate = NULL;
+#endif
 }
 
 /* Initializes 'b' as an empty dp_packet with an initial capacity of 'size'
@@ -135,6 +138,21 @@  dp_packet_uninit(struct dp_packet *b)
     if (b) {
         if (b->source == DPBUF_MALLOC) {
             free(dp_packet_base(b));
+
+#ifdef DPDK_NETDEV
+            /* Packet has been "linearized" */
+            if (b->mstate) {
+                b->source = DPBUF_DPDK;
+                b->mbuf.buf_addr = b->mstate->addr;
+                b->mbuf.buf_len = b->mstate->len;
+                b->mbuf.data_off = b->mstate->off;
+
+                free(b->mstate);
+                b->mstate = NULL;
+
+                free_dpdk_buf((struct dp_packet *) b);
+            }
+#endif
         } else if (b->source == DPBUF_DPDK) {
 #ifdef DPDK_NETDEV
             /* If this dp_packet was allocated by DPDK it must have been
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index cbf002c..9d42cbf 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -27,7 +27,6 @@ 
 
 #include "netdev-dpdk.h"
 #include "openvswitch/list.h"
-#include "packets.h"
 #include "util.h"
 #include "flow.h"
 
@@ -46,6 +45,16 @@  enum OVS_PACKED_ENUM dp_packet_source {
 
 #define DP_PACKET_CONTEXT_SIZE 64
 
+#ifdef DPDK_NETDEV
+/* Struct to save data for when a DPBUF_DPDK packet is converted to
+ * DPBUF_MALLOC. */
+struct mbuf_state {
+    void *addr;
+    uint16_t len;
+    uint16_t off;
+};
+#endif
+
 /* Buffer for holding packet data.  A dp_packet is automatically reallocated
  * as necessary if it grows too large for the available memory.
  * By default the packet type is set to Ethernet (PT_ETH).
@@ -53,6 +62,7 @@  enum OVS_PACKED_ENUM dp_packet_source {
 struct dp_packet {
 #ifdef DPDK_NETDEV
     struct rte_mbuf mbuf;       /* DPDK mbuf */
+    struct mbuf_state *mstate;  /* Used when packet has been "linearized" */
 #else
     void *base_;                /* First byte of allocated space. */
     uint16_t allocated_;        /* Number of bytes allocated. */
@@ -85,6 +95,8 @@  struct dp_packet {
     (char *) (((char *) BUF_ADDR) + BUF_LEN)
 #endif
 
+static inline bool dp_packet_is_linear(const struct dp_packet *);
+static inline void dp_packet_linearize(struct dp_packet *);
 static inline void *dp_packet_data(const struct dp_packet *);
 static inline void dp_packet_set_data(struct dp_packet *, void *);
 static inline void *dp_packet_base(const struct dp_packet *);
@@ -102,11 +114,12 @@  static inline void *dp_packet_eth(const struct dp_packet *);
 static inline void dp_packet_reset_offsets(struct dp_packet *);
 static inline uint8_t dp_packet_l2_pad_size(const struct dp_packet *);
 static inline void dp_packet_set_l2_pad_size(struct dp_packet *, uint8_t);
-static inline void *dp_packet_l2_5(const struct dp_packet *);
+static inline void *dp_packet_l2_5(const struct dp_packet *, uint16_t size);
 static inline void dp_packet_set_l2_5(struct dp_packet *, void *);
-static inline void *dp_packet_l3(const struct dp_packet *);
+static inline void *dp_packet_l3(const struct dp_packet *, uint16_t size);
 static inline void dp_packet_set_l3(struct dp_packet *, void *);
-static inline void *dp_packet_l4(const struct dp_packet *);
+static inline size_t dp_packet_l3_size(const struct dp_packet *);
+static inline void *dp_packet_l4(const struct dp_packet *, uint16_t size);
 static inline void dp_packet_set_l4(struct dp_packet *, void *);
 static inline size_t dp_packet_l4_size(const struct dp_packet *);
 static inline const void *dp_packet_get_tcp_payload(const struct dp_packet *);
@@ -181,20 +194,18 @@  static inline void
 dp_packet_delete(struct dp_packet *b)
 {
     if (b) {
-        if (b->source == DPBUF_DPDK) {
-            /* If this dp_packet was allocated by DPDK it must have been
-             * created as a dp_packet */
-            free_dpdk_buf((struct dp_packet*) b);
-            return;
-        }
-
         dp_packet_uninit(b);
-        free(b);
+
+        if (b->source != DPBUF_DPDK) {
+            free(b);
+        }
     }
 }
 
 /* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to
- * byte 'offset'.  Otherwise, returns a null pointer. */
+ * byte 'offset'.  Otherwise, returns a null pointer. For DPDK packets, this
+ * means the 'offset' + 'size' must fall within the same mbuf (not necessarily
+ * the first mbuf), otherwise null is returned */
 static inline void *
 dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
 {
@@ -212,10 +223,13 @@  dp_packet_at(const struct dp_packet *b, size_t offset, size_t size)
             buf = buf->next;
         }
 
-        return buf ? rte_pktmbuf_mtod_offset(buf, char *, offset) : NULL;
+        if (!buf || offset + size > buf->data_len) {
+            return NULL;
+        }
+
+        return rte_pktmbuf_mtod_offset(buf, char *, offset);
     }
 #endif
-
     return (char *) dp_packet_data(b) + offset;
 }
 
@@ -314,20 +328,24 @@  dp_packet_pull(struct dp_packet *b, size_t size)
     return data;
 }
 
-#ifdef DPDK_NETDEV
 /* Similar to dp_packet_try_pull() but doesn't actually pull any data, only
- * checks if it could and returns true or false accordingly.
- *
- * Valid for dp_packets carrying mbufs only. */
+ * checks if it could and returns 'true' or 'false', accordingly. For DPDK
+ * packets, 'true' is only returned in case the 'offset' + 'size' falls within
+ * the first mbuf, otherwise 'false' is returned */
 static inline bool
-dp_packet_mbuf_may_pull(const struct dp_packet *b, size_t size) {
-    if (size > b->mbuf.data_len) {
+dp_packet_may_pull(const struct dp_packet *b, uint16_t offset, size_t size)
+{
+    if (offset == UINT16_MAX) {
+        return false;
+    }
+#ifdef DPDK_NETDEV
+    /* Offset needs to be within the first mbuf */
+    if (offset + size > b->mbuf.data_len) {
         return false;
     }
-
-    return true;
-}
 #endif
+    return (offset + size > dp_packet_size(b)) ? false : true;
+}
 
 /* If 'b' has at least 'size' bytes of data, removes that many bytes from the
  * head end of 'b' and returns the first byte removed.  Otherwise, returns a
@@ -336,7 +354,7 @@  static inline void *
 dp_packet_try_pull(struct dp_packet *b, size_t size)
 {
 #ifdef DPDK_NETDEV
-    if (!dp_packet_mbuf_may_pull(b, size)) {
+    if (!dp_packet_may_pull(b, 0, size)) {
         return NULL;
     }
 #endif
@@ -385,17 +403,13 @@  dp_packet_set_l2_pad_size(struct dp_packet *b, uint8_t pad_size)
 }
 
 static inline void *
-dp_packet_l2_5(const struct dp_packet *b)
+dp_packet_l2_5(const struct dp_packet *b, uint16_t size)
 {
-#ifdef DPDK_NETDEV
-    if (!dp_packet_mbuf_may_pull(b, b->l2_5_ofs)) {
+    if (!dp_packet_may_pull(b, b->l2_5_ofs, size)) {
         return NULL;
     }
-#endif
 
-    return b->l2_5_ofs != UINT16_MAX
-           ? (char *) dp_packet_data(b) + b->l2_5_ofs
-           : NULL;
+    return (char *) dp_packet_data(b) + b->l2_5_ofs;
 }
 
 static inline void
@@ -407,17 +421,13 @@  dp_packet_set_l2_5(struct dp_packet *b, void *l2_5)
 }
 
 static inline void *
-dp_packet_l3(const struct dp_packet *b)
+dp_packet_l3(const struct dp_packet *b, uint16_t size)
 {
-#ifdef DPDK_NETDEV
-    if (!dp_packet_mbuf_may_pull(b, b->l3_ofs)) {
+    if (!dp_packet_may_pull(b, b->l3_ofs, size)) {
         return NULL;
     }
-#endif
 
-    return b->l3_ofs != UINT16_MAX
-           ? (char *) dp_packet_data(b) + b->l3_ofs
-           : NULL;
+    return (char *) dp_packet_data(b) + b->l3_ofs;
 }
 
 static inline void
@@ -426,18 +436,34 @@  dp_packet_set_l3(struct dp_packet *b, void *l3)
     b->l3_ofs = l3 ? (char *) l3 - (char *) dp_packet_data(b) : UINT16_MAX;
 }
 
+/* Returns the size of the l3 header. Caller must make sure both l3_ofs and
+ * l4_ofs are set*/
+static inline size_t
+dp_packet_l3h_size(const struct dp_packet *b)
+{
+    return b->l4_ofs - b->l3_ofs;
+}
+
+static inline size_t
+dp_packet_l3_size(const struct dp_packet *b)
+{
+    if (!dp_packet_may_pull(b, b->l3_ofs, 0)) {
+        return 0;
+    }
+
+    size_t l3_size = dp_packet_size(b) - b->l3_ofs;
+
+    return l3_size - dp_packet_l2_pad_size(b);
+}
+
 static inline void *
-dp_packet_l4(const struct dp_packet *b)
+dp_packet_l4(const struct dp_packet *b, uint16_t size)
 {
-#ifdef DPDK_NETDEV
-    if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
+    if (!dp_packet_may_pull(b, b->l4_ofs, size)) {
         return NULL;
     }
-#endif
 
-    return b->l4_ofs != UINT16_MAX
-           ? (char *) dp_packet_data(b) + b->l4_ofs
-           : NULL;
+    return (char *) dp_packet_data(b) + b->l4_ofs;
 }
 
 static inline void
@@ -449,31 +475,13 @@  dp_packet_set_l4(struct dp_packet *b, void *l4)
 static inline size_t
 dp_packet_l4_size(const struct dp_packet *b)
 {
-#ifdef DPDK_NETDEV
-    if (b->source == DPBUF_DPDK) {
-        if (!dp_packet_mbuf_may_pull(b, b->l4_ofs)) {
-            return 0;
-        }
-
-        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
-        size_t l4_size = mbuf->data_len - b->l4_ofs;
-
-        mbuf = mbuf->next;
-        while (mbuf) {
-            l4_size += mbuf->data_len;
-
-            mbuf = mbuf->next;
-        }
+    if (!dp_packet_may_pull(b, b->l4_ofs, 0)) {
+        return 0;
+    }
 
-        l4_size -= dp_packet_l2_pad_size(b);
+    size_t l4_size = dp_packet_size(b) - b->l4_ofs;
 
-        return l4_size;
-    }
-#endif
-    return b->l4_ofs != UINT16_MAX
-        ? (const char *)dp_packet_tail(b) - (const char *)dp_packet_l4(b)
-        - dp_packet_l2_pad_size(b)
-        : 0;
+    return l4_size - dp_packet_l2_pad_size(b);
 }
 
 static inline const void *
@@ -482,11 +490,12 @@  dp_packet_get_tcp_payload(const struct dp_packet *b)
     size_t l4_size = dp_packet_l4_size(b);
 
     if (OVS_LIKELY(l4_size >= TCP_HEADER_LEN)) {
-        struct tcp_header *tcp = dp_packet_l4(b);
+        struct tcp_header *tcp = dp_packet_l4(b, sizeof *tcp);
         int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
 
         if (OVS_LIKELY(tcp_len >= TCP_HEADER_LEN && tcp_len <= l4_size)) {
-            return (const char *)tcp + tcp_len;
+            tcp = dp_packet_at(b, b->l4_ofs, tcp_len);
+            return (tcp == NULL) ? NULL : tcp + tcp_len;
         }
     }
     return NULL;
@@ -496,28 +505,31 @@  static inline const void *
 dp_packet_get_udp_payload(const struct dp_packet *b)
 {
     return OVS_LIKELY(dp_packet_l4_size(b) >= UDP_HEADER_LEN)
-        ? (const char *)dp_packet_l4(b) + UDP_HEADER_LEN : NULL;
+        ? (const char *) dp_packet_l4(b, UDP_HEADER_LEN) + UDP_HEADER_LEN
+        : NULL;
 }
 
 static inline const void *
 dp_packet_get_sctp_payload(const struct dp_packet *b)
 {
     return OVS_LIKELY(dp_packet_l4_size(b) >= SCTP_HEADER_LEN)
-        ? (const char *)dp_packet_l4(b) + SCTP_HEADER_LEN : NULL;
+        ? (const char *) dp_packet_l4(b, SCTP_HEADER_LEN) + SCTP_HEADER_LEN
+        : NULL;
 }
 
 static inline const void *
 dp_packet_get_icmp_payload(const struct dp_packet *b)
 {
     return OVS_LIKELY(dp_packet_l4_size(b) >= ICMP_HEADER_LEN)
-        ? (const char *)dp_packet_l4(b) + ICMP_HEADER_LEN : NULL;
+        ? (const char *) dp_packet_l4(b, ICMP_HEADER_LEN) + ICMP_HEADER_LEN
+        : NULL;
 }
 
 static inline const void *
 dp_packet_get_nd_payload(const struct dp_packet *b)
 {
     return OVS_LIKELY(dp_packet_l4_size(b) >= ND_MSG_LEN)
-        ? (const char *)dp_packet_l4(b) + ND_MSG_LEN : NULL;
+        ? (const char *)dp_packet_l4(b, ND_MSG_LEN) + ND_MSG_LEN : NULL;
 }
 
 #ifdef DPDK_NETDEV
@@ -760,6 +772,60 @@  dp_packet_data(const struct dp_packet *b)
            ? (char *) dp_packet_base(b) + __packet_data(b) : NULL;
 }
 
+static inline bool
+dp_packet_is_linear(const struct dp_packet *b OVS_UNUSED)
+{
+#ifdef DPDK_NETDEV
+    if (b->source == DPBUF_DPDK) {
+        return rte_pktmbuf_is_contiguous(&b->mbuf);
+    }
+#endif
+    return true;
+}
+
+/* Linearizes the data on packet 'b', if and only if 'b' is a DPBUF_DPDK
+ * packet, by copying the data into system's memory. After this the packet is
+ * effectively a DPBUF_MALLOC packet.
+ *
+ * This is an expensive operation which should only be performed as a last
+ * resort, when multi-segments are under use but data must be accessed
+ * linearly. */
+static inline void
+dp_packet_linearize(struct dp_packet *b OVS_UNUSED)
+{
+#ifdef DPDK_NETDEV
+    if (!dp_packet_is_linear(b)) {
+        struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
+        struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
+        uint32_t pkt_len = dp_packet_size(pkt);
+        struct mbuf_state *mstate = NULL;
+        void *dst = xmalloc(pkt_len);
+
+        /* Copy packet's data to system's memory */
+        if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
+            return;
+        }
+
+        /* Free all mbufs except for the first */
+        dp_packet_clear(pkt);
+
+        /* Save mbuf's buf_addr to restore later */
+        mstate = xmalloc(sizeof(*mstate));
+        mstate->addr = pkt->mbuf.buf_addr;
+        mstate->len = pkt->mbuf.buf_len;
+        mstate->off = pkt->mbuf.data_off;
+        pkt->mstate = mstate;
+
+        /* Tranform DPBUF_DPDK packet into a DPBUF_MALLOC packet */
+        pkt->source = DPBUF_MALLOC;
+        pkt->mbuf.buf_addr = dst;
+        pkt->mbuf.buf_len = pkt_len;
+        pkt->mbuf.data_off = 0;
+        dp_packet_set_size(pkt, pkt_len);
+    }
+#endif
+}
+
 static inline void
 dp_packet_set_data(struct dp_packet *b, void *data)
 {
@@ -837,6 +903,7 @@  dp_packet_mbuf_init(struct dp_packet *p OVS_UNUSED)
     mbuf->ol_flags = mbuf->tx_offload = mbuf->packet_type = 0;
     mbuf->nb_segs = 1;
     mbuf->next = NULL;
+    p->mstate = NULL;
 #endif
 }
 
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index e322f55..43cddb6 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -5698,6 +5698,11 @@  dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, struct dp_packet *packet_,
             .support = dp_netdev_support,
         };
 
+        /* Gather the whole data for printing the packet (if debug enabled) */
+        if (!dp_packet_is_linear(packet_)) {
+            dp_packet_linearize(packet_);
+        }
+
         ofpbuf_init(&key, 0);
         odp_flow_key_from_flow(&odp_parms, &key);
         packet_str = ofp_dp_packet_to_string(packet_);
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 2131503..ee11d51 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1810,6 +1810,11 @@  dpif_netlink_operate__(struct dpif_netlink *dpif,
                 }
                 n_ops = i;
             } else {
+                /* We will need to pass the whole to encode the message */
+                if (!dp_packet_is_linear(op->execute.packet)) {
+                    dp_packet_linearize(op->execute.packet);
+                }
+
                 dpif_netlink_encode_execute(dpif->dp_ifindex, &op->execute,
                                             &aux->request);
             }
diff --git a/lib/dpif.c b/lib/dpif.c
index 4697a4d..514fae5 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1243,6 +1243,7 @@  dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
         execute.probe = false;
         execute.mtu = 0;
         aux->error = dpif_execute(aux->dpif, &execute);
+
         log_execute_message(aux->dpif, &this_module, &execute,
                             true, aux->error);
 
@@ -1395,6 +1396,7 @@  dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t n_ops)
 
                 case DPIF_OP_EXECUTE:
                     COVERAGE_INC(dpif_execute);
+
                     log_execute_message(dpif, &this_module, &op->execute,
                                         false, error);
                     break;
@@ -1822,6 +1824,13 @@  log_execute_message(const struct dpif *dpif,
         uint64_t stub[1024 / 8];
         struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub);
 
+        /* We will need the whole data for logging */
+        struct dp_packet *p = CONST_CAST(struct dp_packet *,
+                                         execute->packet);
+        if (!dp_packet_is_linear(p)) {
+            dp_packet_linearize(p);
+        }
+
         packet = ofp_packet_to_string(dp_packet_data(execute->packet),
                                       dp_packet_size(execute->packet),
                                       execute->packet->packet_type);
diff --git a/lib/flow.c b/lib/flow.c
index bffee70..aa904d4 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -2958,34 +2958,35 @@  static void
 flow_compose_l4_csum(struct dp_packet *p, const struct flow *flow,
                      uint32_t pseudo_hdr_csum)
 {
-    size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
+    //size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
+    size_t l4_len = dp_packet_l4_size(p);
 
     if (!(flow->nw_frag & FLOW_NW_FRAG_ANY)
         || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         if (flow->nw_proto == IPPROTO_TCP) {
-            struct tcp_header *tcp = dp_packet_l4(p);
+            struct tcp_header *tcp = dp_packet_l4(p, sizeof *tcp);
 
             tcp->tcp_csum = 0;
             tcp->tcp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
                                                       tcp, l4_len));
         } else if (flow->nw_proto == IPPROTO_UDP) {
-            struct udp_header *udp = dp_packet_l4(p);
+            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
 
             udp->udp_csum = 0;
             udp->udp_csum = csum_finish(csum_continue(pseudo_hdr_csum,
                                                       udp, l4_len));
         } else if (flow->nw_proto == IPPROTO_ICMP) {
-            struct icmp_header *icmp = dp_packet_l4(p);
+            struct icmp_header *icmp = dp_packet_l4(p, sizeof *icmp);
 
             icmp->icmp_csum = 0;
             icmp->icmp_csum = csum(icmp, l4_len);
         } else if (flow->nw_proto == IPPROTO_IGMP) {
-            struct igmp_header *igmp = dp_packet_l4(p);
+            struct igmp_header *igmp = dp_packet_l4(p, sizeof *igmp);
 
             igmp->igmp_csum = 0;
             igmp->igmp_csum = csum(igmp, l4_len);
         } else if (flow->nw_proto == IPPROTO_ICMPV6) {
-            struct icmp6_hdr *icmp = dp_packet_l4(p);
+            struct icmp6_hdr *icmp = dp_packet_l4(p, sizeof *icmp);
 
             icmp->icmp6_cksum = 0;
             icmp->icmp6_cksum = (OVS_FORCE uint16_t)
@@ -3015,18 +3016,18 @@  packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
         eth->eth_type = htons(dp_packet_size(p));
     } else if (dl_type_is_ip_any(flow->dl_type)) {
         uint32_t pseudo_hdr_csum;
-        size_t l4_len = (char *) dp_packet_tail(p) - (char *) dp_packet_l4(p);
+        size_t l4_len = dp_packet_l4_size(p);
 
         if (flow->dl_type == htons(ETH_TYPE_IP)) {
-            struct ip_header *ip = dp_packet_l3(p);
+            struct ip_header *ip = dp_packet_l3(p, sizeof *ip);
 
-            ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
+            ip->ip_tot_len = htons(dp_packet_l3_size(p));
             ip->ip_csum = 0;
             ip->ip_csum = csum(ip, sizeof *ip);
 
             pseudo_hdr_csum = packet_csum_pseudoheader(ip);
         } else { /* ETH_TYPE_IPV6 */
-            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p);
+            struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(p, sizeof *nh);
 
             nh->ip6_plen = htons(l4_len);
             pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
@@ -3035,7 +3036,7 @@  packet_expand(struct dp_packet *p, const struct flow *flow, size_t size)
         if ((!(flow->nw_frag & FLOW_NW_FRAG_ANY)
              || !(flow->nw_frag & FLOW_NW_FRAG_LATER))
             && flow->nw_proto == IPPROTO_UDP) {
-            struct udp_header *udp = dp_packet_l4(p);
+            struct udp_header *udp = dp_packet_l4(p, sizeof *udp);
 
             udp->udp_len = htons(l4_len + extra_size);
         }
@@ -3103,8 +3104,8 @@  flow_compose(struct dp_packet *p, const struct flow *flow,
 
         l4_len = flow_compose_l4(p, flow, l7, l7_len);
 
-        ip = dp_packet_l3(p);
-        ip->ip_tot_len = htons(p->l4_ofs - p->l3_ofs + l4_len);
+        ip = dp_packet_l3(p, sizeof *ip);
+        ip->ip_tot_len = htons(dp_packet_l3_size(p));
         /* Checksum has already been zeroed by put_zeros call. */
         ip->ip_csum = csum(ip, sizeof *ip);
 
@@ -3126,7 +3127,7 @@  flow_compose(struct dp_packet *p, const struct flow *flow,
 
         l4_len = flow_compose_l4(p, flow, l7, l7_len);
 
-        nh = dp_packet_l3(p);
+        nh = dp_packet_l3(p, sizeof *nh);
         nh->ip6_plen = htons(l4_len);
 
         pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
diff --git a/lib/lacp.c b/lib/lacp.c
index d6b36aa..ec92202 100644
--- a/lib/lacp.c
+++ b/lib/lacp.c
@@ -190,8 +190,7 @@  parse_lacp_packet(const struct dp_packet *p)
 {
     const struct lacp_pdu *pdu;
 
-    pdu = dp_packet_at(p, (uint8_t *)dp_packet_l3(p) - (uint8_t *)dp_packet_data(p),
-                    LACP_PDU_LEN);
+    pdu = dp_packet_l3(p, LACP_PDU_LEN);
 
     if (pdu && pdu->subtype == 1
         && pdu->actor_type == 1 && pdu->actor_len == 20
diff --git a/lib/mcast-snooping.c b/lib/mcast-snooping.c
index 6730301..af0cadb 100644
--- a/lib/mcast-snooping.c
+++ b/lib/mcast-snooping.c
@@ -450,11 +450,11 @@  mcast_snooping_add_report(struct mcast_snooping *ms,
     int count = 0;
     int ngrp;
 
-    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
-    igmpv3 = dp_packet_at(p, offset, IGMPV3_HEADER_LEN);
+    igmpv3 = dp_packet_l4(p, IGMPV3_HEADER_LEN);
     if (!igmpv3) {
         return 0;
     }
+    offset = (char *) igmpv3 - (char *) dp_packet_data(p);
     ngrp = ntohs(igmpv3->ngrp);
     offset += IGMPV3_HEADER_LEN;
     while (ngrp--) {
@@ -502,11 +502,11 @@  mcast_snooping_add_mld(struct mcast_snooping *ms,
     int ngrp;
     bool ret;
 
-    offset = (char *) dp_packet_l4(p) - (char *) dp_packet_data(p);
-    mld = dp_packet_at(p, offset, MLD_HEADER_LEN);
+    mld = dp_packet_l4(p, MLD_HEADER_LEN);
     if (!mld) {
         return 0;
     }
+    offset = (char *) mld - (char *) dp_packet_data(p);
     ngrp = ntohs(mld->ngrp);
     offset += MLD_HEADER_LEN;
     addr = dp_packet_at(p, offset, sizeof(struct in6_addr));
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index a153aa2..a545e7e 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -701,6 +701,11 @@  netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
     }
 
     for (i = 0; i < batch->count; i++) {
+        /* We need the whole data to send the packet on the device */
+        if (!dp_packet_is_linear(batch->packets[i])) {
+            dp_packet_linearize(batch->packets[i]);
+        }
+
         const void *data = dp_packet_data(batch->packets[i]);
         size_t size = dp_packet_size(batch->packets[i]);
 
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index 2cf0563..9682fc3 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -233,7 +233,13 @@  dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s)
 
         ASSIGN_CONTAINER(txbuf_node, ovs_list_front(&s->txq), list_node);
         txbuf = txbuf_node->pkt;
-        retval = stream_send(s->stream, dp_packet_data(txbuf), dp_packet_size(txbuf));
+
+        if (!dp_packet_is_linear(txbuf)) {
+            dp_packet_linearize(txbuf);
+        }
+
+        retval = stream_send(s->stream, dp_packet_data(txbuf),
+                             dp_packet_size(txbuf));
 
         if (retval > 0) {
             dp_packet_pull(txbuf, retval);
@@ -1088,6 +1094,11 @@  netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH(i, packet, batch) {
+        /* We need the whole data to send the packet on the device */
+        if (!dp_packet_is_linear(packet)) {
+            dp_packet_linearize(packet);
+        }
+
         const void *buffer = dp_packet_data(packet);
         size_t size = dp_packet_size(packet);
 
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index c55aad8..a477bab 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1380,6 +1380,11 @@  netdev_linux_sock_batch_send(int sock, int ifindex,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
+        /* We need the whole data to send the packet on the device */
+        if (!dp_packet_is_linear(packet)) {
+            dp_packet_linearize(packet);
+        }
+
         iov[i].iov_base = dp_packet_data(packet);
         iov[i].iov_len = dp_packet_size(packet);
         mmsg[i].msg_hdr = (struct msghdr) { .msg_name = &sll,
@@ -1433,8 +1438,14 @@  netdev_linux_tap_batch_send(struct netdev *netdev_,
         ssize_t retval;
         int error;
 
+        /* We need the whole data to send the packet on the device */
+        if (!dp_packet_is_linear(packet)) {
+            dp_packet_linearize(packet);
+        }
+
         do {
-            retval = write(netdev->tap_fd, dp_packet_data(packet), size);
+            retval = write(netdev->tap_fd, dp_packet_data(packet),
+                           size);
             error = retval < 0 ? errno : 0;
         } while (error == EINTR);
 
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 56baaa2..24c847d 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -65,13 +65,18 @@  netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
     void *nh;
     struct ip_header *ip;
     struct ovs_16aligned_ip6_hdr *ip6;
-    void *l4;
+    char *l4;
     int l3_size;
 
-    nh = dp_packet_l3(packet);
+    /* We will need to perform the checksum on the whole data below */
+    if (!dp_packet_is_linear(packet)) {
+        dp_packet_linearize(packet);
+    }
+
+    nh = dp_packet_l3(packet, sizeof *ip);
     ip = nh;
     ip6 = nh;
-    l4 = dp_packet_l4(packet);
+    l4 = dp_packet_l4(packet, sizeof *l4);
 
     if (!nh || !l4) {
         return NULL;
@@ -79,8 +84,7 @@  netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
 
     *hlen = sizeof(struct eth_header);
 
-    l3_size = dp_packet_size(packet) -
-              ((char *)nh - (char *)dp_packet_data(packet));
+    l3_size = dp_packet_l3_size(packet);
 
     if (IP_VER(ip->ip_ihl_ver) == 4) {
 
@@ -151,6 +155,11 @@  netdev_tnl_push_ip_header(struct dp_packet *packet,
     struct ip_header *ip;
     struct ovs_16aligned_ip6_hdr *ip6;
 
+    /* We will need to perform the checksum on the whole data below */
+    if (!dp_packet_is_linear(packet)) {
+        dp_packet_linearize(packet);
+    }
+
     eth = dp_packet_push_uninit(packet, size);
     *ip_tot_size = dp_packet_size(packet) - sizeof (struct eth_header);
 
@@ -191,15 +200,16 @@  udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
         if (OVS_UNLIKELY(!dp_packet_l4_checksum_valid(packet))) {
             uint32_t csum;
             if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
-                csum = packet_csum_pseudoheader6(dp_packet_l3(packet));
+                csum = packet_csum_pseudoheader6(
+                        dp_packet_l3(packet,
+                                     sizeof(struct ip6_hdr)));
             } else {
-                csum = packet_csum_pseudoheader(dp_packet_l3(packet));
+                csum = packet_csum_pseudoheader(
+                        dp_packet_l3(packet,
+                                     sizeof(struct ip_header)));
             }
 
-            csum = csum_continue(csum, udp, dp_packet_size(packet) -
-                                 ((const unsigned char *)udp -
-                                  (const unsigned char *)dp_packet_eth(packet)
-                                 ));
+            csum = csum_continue(csum, udp, dp_packet_l4_size(packet));
             if (csum_finish(csum)) {
                 return NULL;
             }
@@ -373,9 +383,7 @@  parse_gre_header(struct dp_packet *packet,
     if (greh->flags & htons(GRE_CSUM)) {
         ovs_be16 pkt_csum;
 
-        pkt_csum = csum(greh, dp_packet_size(packet) -
-                              ((const unsigned char *)greh -
-                               (const unsigned char *)dp_packet_eth(packet)));
+        pkt_csum = csum(greh, dp_packet_l4_size(packet));
         if (pkt_csum) {
             return -EINVAL;
         }
@@ -448,7 +456,8 @@  netdev_gre_push_header(const struct netdev *netdev,
     greh = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
 
     if (greh->flags & htons(GRE_CSUM)) {
-        ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
+        greh = dp_packet_l4(packet, sizeof *greh);
+        ovs_be16 *csum_opt = (ovs_be16 *) greh;
         *csum_opt = csum(greh, ip_tot_size);
     }
 
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 5831d1f..8fdc4f9 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -70,7 +70,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(packet, sizeof(*nh));
     ovs_be32 ip_src_nh;
     ovs_be32 ip_dst_nh;
     ovs_be32 new_ip_src;
@@ -140,7 +140,7 @@  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(packet, sizeof *nh);
     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);
@@ -160,7 +160,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(packet, sizeof *th);
 
     if (OVS_LIKELY(th && dp_packet_get_tcp_payload(packet))) {
         packet_set_tcp_port(packet,
@@ -173,7 +173,7 @@  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(packet, sizeof *uh);
 
     if (OVS_LIKELY(uh && dp_packet_get_udp_payload(packet))) {
         packet_set_udp_port(packet,
@@ -186,7 +186,7 @@  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(packet, sizeof *sh);
 
     if (OVS_LIKELY(sh && dp_packet_get_sctp_payload(packet))) {
         packet_set_sctp_port(packet,
@@ -205,7 +205,7 @@  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(packet, sizeof *arp);
 
     if (!mask) {
         arp->ar_op = key->arp_op;
@@ -231,8 +231,16 @@  static void
 odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
            const struct ovs_key_nd *mask)
 {
-    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
-    const struct ovs_nd_lla_opt *lla_opt = dp_packet_get_nd_payload(packet);
+    const struct ovs_nd_msg *ns;
+    const struct ovs_nd_lla_opt *lla_opt;
+
+    /* To orocess neighbor discovery options, we need the whole packet */
+    if (!dp_packet_is_linear(packet)) {
+        dp_packet_linearize(packet);
+    }
+
+    ns = dp_packet_l4(packet, sizeof *ns);
+    lla_opt = dp_packet_get_nd_payload(packet);
 
     if (OVS_LIKELY(ns && lla_opt)) {
         int bytes_remain = dp_packet_l4_size(packet) - sizeof(*ns);
@@ -275,7 +283,7 @@  static void
 odp_set_nsh(struct dp_packet *packet, const struct nlattr *a, bool has_mask)
 {
     struct ovs_key_nsh key, mask;
-    struct nsh_hdr *nsh = dp_packet_l3(packet);
+    struct nsh_hdr *nsh = dp_packet_l3(packet, sizeof *nsh);
     uint8_t mdtype = nsh_md_type(nsh);
     ovs_be32 path_hdr;
 
@@ -522,7 +530,7 @@  odp_execute_masked_set_action(struct dp_packet *packet,
         break;
 
     case OVS_KEY_ATTR_MPLS:
-        mh = dp_packet_l2_5(packet);
+        mh = dp_packet_l2_5(packet, sizeof *mh);
         if (mh) {
             put_16aligned_be32(&mh->mpls_lse, nl_attr_get_be32(a)
                                | (get_16aligned_be32(&mh->mpls_lse)
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index e05a969..37db260 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -84,21 +84,21 @@  ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
     l4_size = dp_packet_l4_size(&buf);
 
     if (flow.nw_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
-        struct tcp_header *th = dp_packet_l4(&buf);
+        struct tcp_header *th = dp_packet_l4(&buf, sizeof *th);
         ds_put_format(&ds, " tcp_csum:%"PRIx16, ntohs(th->tcp_csum));
     } else if (flow.nw_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
-        struct udp_header *uh = dp_packet_l4(&buf);
+        struct udp_header *uh = dp_packet_l4(&buf, sizeof *uh);
         ds_put_format(&ds, " udp_csum:%"PRIx16, ntohs(uh->udp_csum));
     } else if (flow.nw_proto == IPPROTO_SCTP && l4_size >= SCTP_HEADER_LEN) {
-        struct sctp_header *sh = dp_packet_l4(&buf);
+        struct sctp_header *sh = dp_packet_l4(&buf, sizeof *sh);
         ds_put_format(&ds, " sctp_csum:%"PRIx32,
                       ntohl(get_16aligned_be32(&sh->sctp_csum)));
     } else if (flow.nw_proto == IPPROTO_ICMP && l4_size >= ICMP_HEADER_LEN) {
-        struct icmp_header *icmph = dp_packet_l4(&buf);
+        struct icmp_header *icmph = dp_packet_l4(&buf, sizeof *icmph);
         ds_put_format(&ds, " icmp_csum:%"PRIx16,
                       ntohs(icmph->icmp_csum));
     } else if (flow.nw_proto == IPPROTO_ICMPV6 && l4_size >= ICMP6_HEADER_LEN) {
-        struct icmp6_header *icmp6h = dp_packet_l4(&buf);
+        struct icmp6_header *icmp6h = dp_packet_l4(&buf, sizeof *icmp6h);
         ds_put_format(&ds, " icmp6_csum:%"PRIx16,
                       ntohs(icmp6h->icmp6_cksum));
     }
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index 05c1dd4..39d677a 100644
--- a/lib/ovs-lldp.c
+++ b/lib/ovs-lldp.c
@@ -668,7 +668,8 @@  lldp_process_packet(struct lldp *lldp, const struct dp_packet *p)
 {
     if (lldp) {
         lldpd_recv(lldp->lldpd, lldpd_first_hardware(lldp->lldpd),
-                   (char *) dp_packet_data(p), dp_packet_size(p));
+                   (char *) dp_packet_data(p),
+                   dp_packet_size(p));
     }
 }
 
diff --git a/lib/packets.c b/lib/packets.c
index 38bfb60..6ae5118 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -260,8 +260,8 @@  push_eth(struct dp_packet *packet, const struct eth_addr *dst,
 void
 pop_eth(struct dp_packet *packet)
 {
-    char *l2_5 = dp_packet_l2_5(packet);
-    char *l3 = dp_packet_l3(packet);
+    char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
+    char *l3 = dp_packet_l3(packet, sizeof *l3);
     ovs_be16 ethertype;
     int increment;
 
@@ -292,10 +292,10 @@  set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
 
     if (eth_type_vlan(eh->eth_type)) {
         ovs_be16 *p;
-        char *l2_5 = dp_packet_l2_5(packet);
+        char *l2_5 = dp_packet_l2_5(packet, sizeof *l2_5);
 
         p = ALIGNED_CAST(ovs_be16 *,
-                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet)) - 2);
+                         (l2_5 ? l2_5 : (char *)dp_packet_l3(packet, -2)));
         *p = eth_type;
     } else {
         eh->eth_type = eth_type;
@@ -359,7 +359,7 @@  set_mpls_lse(struct dp_packet *packet, ovs_be32 mpls_lse)
 {
     /* Packet type should be MPLS to set label stack entry. */
     if (is_mpls(packet)) {
-        struct mpls_hdr *mh = dp_packet_l2_5(packet);
+        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
 
         /* Update mpls label stack entry. */
         put_16aligned_be32(&mh->mpls_lse, mpls_lse);
@@ -401,7 +401,7 @@  void
 pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
 {
     if (is_mpls(packet)) {
-        struct mpls_hdr *mh = dp_packet_l2_5(packet);
+        struct mpls_hdr *mh = dp_packet_l2_5(packet, sizeof *mh);
         size_t len = packet->l2_5_ofs;
 
         set_ethertype(packet, ethtype);
@@ -449,7 +449,7 @@  push_nsh(struct dp_packet *packet, const struct nsh_hdr *nsh_hdr_src)
 bool
 pop_nsh(struct dp_packet *packet)
 {
-    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
+    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet, sizeof *nsh);
     size_t length;
     uint32_t next_pt;
 
@@ -975,16 +975,16 @@  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);
+    struct ip_header *nh = dp_packet_l3(packet, sizeof *nh);
     ovs_be32 old_addr = get_16aligned_be32(addr);
     size_t l4_size = dp_packet_l4_size(packet);
 
     if (nh->ip_proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
-        struct tcp_header *th = dp_packet_l4(packet);
+        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
 
         th->tcp_csum = recalc_csum32(th->tcp_csum, old_addr, new_addr);
     } else if (nh->ip_proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN ) {
-        struct udp_header *uh = dp_packet_l4(packet);
+        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
 
         if (uh->udp_csum) {
             uh->udp_csum = recalc_csum32(uh->udp_csum, old_addr, new_addr);
@@ -1007,12 +1007,20 @@  packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr)
     const struct ovs_16aligned_ip6_hdr *nh;
     size_t len;
     size_t remaining;
-    uint8_t *data = dp_packet_l3(packet);
+    uint8_t *data;
 
-    remaining = packet->l4_ofs - packet->l3_ofs;
+    remaining = dp_packet_l3h_size(packet);
     if (remaining < sizeof *nh) {
         return false;
     }
+
+    /* We will need the whole data for processing the headers below */
+    if (!dp_packet_is_linear(packet)) {
+        dp_packet_linearize(packet);
+    }
+
+    data = dp_packet_l3(packet, sizeof *nh);
+
     nh = ALIGNED_CAST(struct ovs_16aligned_ip6_hdr *, data);
     data += sizeof *nh;
     remaining -= sizeof *nh;
@@ -1088,11 +1096,11 @@  packet_update_csum128(struct dp_packet *packet, uint8_t proto,
     size_t l4_size = dp_packet_l4_size(packet);
 
     if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
-        struct tcp_header *th = dp_packet_l4(packet);
+        struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
 
         th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
     } else if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
-        struct udp_header *uh = dp_packet_l4(packet);
+        struct udp_header *uh = dp_packet_l4(packet, sizeof *uh);
 
         if (uh->udp_csum) {
             uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
@@ -1102,7 +1110,7 @@  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);
+        struct icmp6_header *icmp = dp_packet_l4(packet, sizeof *icmp);
 
         icmp->icmp6_cksum = recalc_csum128(icmp->icmp6_cksum, addr, new_addr);
     }
@@ -1144,7 +1152,7 @@  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(packet, sizeof *nh);
 
     if (get_16aligned_be32(&nh->ip_src) != src) {
         packet_set_ipv4_addr(packet, &nh->ip_src, src);
@@ -1180,7 +1188,7 @@  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(packet, sizeof *nh);
     uint8_t proto = 0;
     bool rh_present;
 
@@ -1215,7 +1223,7 @@  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);
+    struct tcp_header *th = dp_packet_l4(packet, sizeof *th);
 
     packet_set_port(&th->tcp_src, src, &th->tcp_csum);
     packet_set_port(&th->tcp_dst, dst, &th->tcp_csum);
@@ -1227,7 +1235,7 @@  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(packet, sizeof *uh);
 
     if (uh->udp_csum) {
         packet_set_port(&uh->udp_src, src, &uh->udp_csum);
@@ -1248,7 +1256,7 @@  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);
+    struct sctp_header *sh = dp_packet_l4(packet, sizeof *sh);
     ovs_be32 old_csum, old_correct_csum, new_csum;
     uint16_t tp_len = dp_packet_l4_size(packet);
 
@@ -1269,7 +1277,7 @@  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);
+    struct icmp_header *ih = dp_packet_l4(packet, sizeof(*ih));
     ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
     ovs_be16 new_tc = htons(type << 8 | code);
 
@@ -1293,7 +1301,12 @@  packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
         return;
     }
 
-    ns = dp_packet_l4(packet);
+    /* To process neighbor discovery options, we need the whole packet */
+    if (!dp_packet_is_linear(packet)) {
+        dp_packet_linearize(packet);
+    }
+
+    ns = dp_packet_l4(packet, sizeof *ns);
     opt = &ns->options[0];
     bytes_remain -= sizeof(*ns);
 
@@ -1431,7 +1444,7 @@  compose_arp(struct dp_packet *b, uint16_t arp_op,
     eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
     eth->eth_src = arp_sha;
 
-    struct arp_eth_header *arp = dp_packet_l3(b);
+    struct arp_eth_header *arp = dp_packet_l3(b, sizeof *arp);
     arp->ar_op = htons(arp_op);
     arp->ar_sha = arp_sha;
     arp->ar_tha = arp_tha;
@@ -1475,7 +1488,7 @@  compose_ipv6(struct dp_packet *packet, uint8_t proto,
     struct ip6_hdr *nh;
     void *data;
 
-    nh = dp_packet_l3(packet);
+    nh = dp_packet_l3(packet, sizeof *nh);
     nh->ip6_vfc = 0x60;
     nh->ip6_nxt = proto;
     nh->ip6_plen = htons(size);
@@ -1514,7 +1527,8 @@  compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
     packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
 
     ns->icmph.icmp6_cksum = 0;
-    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    icmp_csum = packet_csum_pseudoheader6(
+        dp_packet_l3(b, sizeof(struct ip6_hdr)));
     ns->icmph.icmp6_cksum = csum_finish(
         csum_continue(icmp_csum, ns, ND_MSG_LEN + ND_LLA_OPT_LEN));
 }
@@ -1545,7 +1559,8 @@  compose_nd_na(struct dp_packet *b,
     packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
 
     na->icmph.icmp6_cksum = 0;
-    icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    icmp_csum = packet_csum_pseudoheader6(
+        dp_packet_l3(b, sizeof(struct ip6_hdr)));
     na->icmph.icmp6_cksum = csum_finish(csum_continue(
         icmp_csum, na, ND_MSG_LEN + ND_LLA_OPT_LEN));
 }
@@ -1596,7 +1611,8 @@  compose_nd_ra(struct dp_packet *b,
     }
 
     ra->icmph.icmp6_cksum = 0;
-    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    uint32_t icmp_csum = packet_csum_pseudoheader6(
+        dp_packet_l3(b, sizeof(struct ip6_hdr)));
     ra->icmph.icmp6_cksum = csum_finish(csum_continue(
         icmp_csum, ra, RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_len));
 }
@@ -1610,10 +1626,10 @@  packet_put_ra_prefix_opt(struct dp_packet *b,
                          const ovs_be128 prefix)
 {
     size_t prev_l4_size = dp_packet_l4_size(b);
-    struct ip6_hdr *nh = dp_packet_l3(b);
+    struct ip6_hdr *nh = dp_packet_l3(b, sizeof *nh);
     nh->ip6_plen = htons(prev_l4_size + ND_PREFIX_OPT_LEN);
 
-    struct ovs_ra_msg *ra = dp_packet_l4(b);
+    struct ovs_ra_msg *ra = dp_packet_l4(b, sizeof *ra);
     struct ovs_nd_prefix_opt *prefix_opt =
         dp_packet_put_uninit(b, sizeof *prefix_opt);
     prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
@@ -1626,7 +1642,8 @@  packet_put_ra_prefix_opt(struct dp_packet *b,
     memcpy(prefix_opt->prefix.be32, prefix.be32, sizeof(ovs_be32[4]));
 
     ra->icmph.icmp6_cksum = 0;
-    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+    uint32_t icmp_csum = packet_csum_pseudoheader6(
+        dp_packet_l3(b, sizeof(struct ip6_hdr)));
     ra->icmph.icmp6_cksum = csum_finish(csum_continue(
         icmp_csum, ra, prev_l4_size + ND_PREFIX_OPT_LEN));
 }
@@ -1684,12 +1701,12 @@  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(pkt, sizeof *ip6);
 
         put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
                                 htonl(IP_ECN_CE << 20));
     } else {
-        struct ip_header *nh = dp_packet_l3(pkt);
+        struct ip_header *nh = dp_packet_l3(pkt, sizeof *nh);
         uint8_t tos = nh->ip_tos;
 
         tos |= IP_ECN_CE;
diff --git a/lib/pcap-file.c b/lib/pcap-file.c
index ea5cfa3..6340862 100644
--- a/lib/pcap-file.c
+++ b/lib/pcap-file.c
@@ -315,7 +315,7 @@  tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
         || !l7) {
         return NULL;
     }
-    tcp = dp_packet_l4(packet);
+    tcp = dp_packet_l4(packet, sizeof *tcp);
     flags = TCP_FLAGS(tcp->tcp_ctl);
     l7_length = (char *) dp_packet_tail(packet) - l7;
     seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 0cc964a..220b8c0 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1381,12 +1381,18 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
     case SFLOW_UPCALL:
         if (upcall->sflow) {
             struct dpif_sflow_actions sflow_actions;
+            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
 
             memset(&sflow_actions, 0, sizeof sflow_actions);
 
             actions_len = dpif_read_actions(udpif, upcall, flow,
                                             upcall->type, &sflow_actions);
-            dpif_sflow_received(upcall->sflow, packet, flow,
+            /* Gather the whole data */
+            if (!dp_packet_is_linear(p)) {
+                dp_packet_linearize(p);
+            }
+
+            dpif_sflow_received(upcall->sflow, p, flow,
                                 flow->in_port.odp_port, &upcall->cookie,
                                 actions_len > 0 ? &sflow_actions : NULL);
         }
@@ -1447,6 +1453,12 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
 
             const struct frozen_state *state = &recirc_node->state;
 
+            /* Gather the whole data */
+            struct dp_packet *p = CONST_CAST(struct dp_packet *, packet);
+            if (!dp_packet_is_linear(p)) {
+                dp_packet_linearize(p);
+            }
+
             struct ofproto_async_msg *am = xmalloc(sizeof *am);
             *am = (struct ofproto_async_msg) {
                 .controller_id = cookie->controller.controller_id,
@@ -1454,9 +1466,9 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
                 .pin = {
                     .up = {
                         .base = {
-                            .packet = xmemdup(dp_packet_data(packet),
-                                              dp_packet_size(packet)),
-                            .packet_len = dp_packet_size(packet),
+                            .packet = xmemdup(dp_packet_data(p),
+                                              dp_packet_size(p)),
+                            .packet_len = dp_packet_size(p),
                             .reason = cookie->controller.reason,
                             .table_id = state->table_id,
                             .cookie = get_32aligned_be64(
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 84cce81..26e3437 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1735,7 +1735,8 @@  stp_process_packet(const struct xport *xport, const struct dp_packet *packet)
     }
 
     if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
-        stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload));
+        stp_received_bpdu(sp, dp_packet_data(&payload),
+                          dp_packet_size(&payload));
     }
 }
 
@@ -1786,7 +1787,8 @@  rstp_process_packet(const struct xport *xport, const struct dp_packet *packet)
     }
 
     if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
-        rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload),
+        rstp_port_received_bpdu(xport->rstp_port,
+                                dp_packet_data(&payload),
                                 dp_packet_size(&payload));
     }
 }
@@ -2556,11 +2558,10 @@  update_mcast_snooping_table4__(const struct xlate_ctx *ctx,
 {
     const struct igmp_header *igmp;
     int count;
-    size_t offset;
     ovs_be32 ip4 = flow->igmp_group_ip4;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
-    igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
+    igmp = dp_packet_l4(packet, IGMP_HEADER_LEN);
+
     if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
         xlate_report_debug(ctx, OFT_DETAIL,
                            "multicast snooping received bad IGMP "
@@ -2616,13 +2617,11 @@  update_mcast_snooping_table6__(const struct xlate_ctx *ctx,
 {
     const struct mld_header *mld;
     int count;
-    size_t offset;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
-    mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
+    mld = dp_packet_l4(packet, MLD_HEADER_LEN);
 
     if (!mld ||
-        packet_csum_upperlayer6(dp_packet_l3(packet),
+        packet_csum_upperlayer6(dp_packet_l3(packet, sizeof(struct ip6_hdr)),
                                 mld, IPPROTO_ICMPV6,
                                 dp_packet_l4_size(packet)) != 0) {
         xlate_report_debug(ctx, OFT_DETAIL, "multicast snooping received "
@@ -2925,6 +2924,13 @@  xlate_normal(struct xlate_ctx *ctx)
         && is_ip_any(flow)) {
         struct mcast_snooping *ms = ctx->xbridge->ms;
         struct mcast_group *grp = NULL;
+        struct dp_packet *p = CONST_CAST(struct dp_packet *,
+                                         ctx->xin->packet);
+
+        /* We will need the whole data for processing the packet below */
+        if (!dp_packet_is_linear(p)) {
+            dp_packet_linearize(p);
+        }
 
         if (is_igmp(flow, wc)) {
             /*
@@ -3213,7 +3219,8 @@  process_special(struct xlate_ctx *ctx, const struct xport *xport)
     const struct flow *flow = &ctx->xin->flow;
     struct flow_wildcards *wc = ctx->wc;
     const struct xbridge *xbridge = ctx->xbridge;
-    const struct dp_packet *packet = ctx->xin->packet;
+    struct dp_packet *packet = CONST_CAST(struct dp_packet *,
+                                          ctx->xin->packet);
     enum slow_path_reason slow;
 
     if (!xport) {
@@ -3225,6 +3232,11 @@  process_special(struct xlate_ctx *ctx, const struct xport *xport)
         slow = SLOW_CFM;
     } else if (xport->bfd && bfd_should_process_flow(xport->bfd, flow, wc)) {
         if (packet) {
+            /* Gather the whole data for further processing */
+            if (!dp_packet_is_linear(packet)) {
+                dp_packet_linearize(packet);
+            }
+
             bfd_process_packet(xport->bfd, flow, packet);
             /* If POLL received, immediately sends FINAL back. */
             if (bfd_should_send_packet(xport->bfd)) {
@@ -3241,6 +3253,11 @@  process_special(struct xlate_ctx *ctx, const struct xport *xport)
     } else if ((xbridge->stp || xbridge->rstp) &&
                stp_should_process_flow(flow, wc)) {
         if (packet) {
+            /* Gather the whole data for further processing */
+            if (!dp_packet_is_linear(packet)) {
+                dp_packet_linearize(packet);
+            }
+
             xbridge->stp
                 ? stp_process_packet(xport, packet)
                 : rstp_process_packet(xport, packet);
@@ -3248,6 +3265,11 @@  process_special(struct xlate_ctx *ctx, const struct xport *xport)
         slow = SLOW_STP;
     } else if (xport->lldp && lldp_should_process_flow(xport->lldp, flow)) {
         if (packet) {
+            /* Gather the whole data for further processing */
+            if (!dp_packet_is_linear(packet)) {
+                dp_packet_linearize(packet);
+            }
+
             lldp_process_packet(xport->lldp, packet);
         }
         slow = SLOW_LLDP;
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 8ae4c9e..cda1135 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -213,7 +213,7 @@  pinctrl_handle_arp(const struct flow *ip_flow, const struct match *md,
     eth->eth_dst = ip_flow->dl_dst;
     eth->eth_src = ip_flow->dl_src;
 
-    struct arp_eth_header *arp = dp_packet_l3(&packet);
+    struct arp_eth_header *arp = dp_packet_l3(&packet, sizeof(*arp));
     arp->ar_op = htons(ARP_OP_REQUEST);
     arp->ar_sha = ip_flow->dl_src;
     put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
@@ -291,9 +291,10 @@  pinctrl_handle_icmp(const struct flow *ip_flow, struct dp_packet *pkt_in,
         ih->icmp6_base.icmp6_cksum = 0;
 
         uint8_t *data = dp_packet_put_zeros(&packet, sizeof *nh);
-        memcpy(data, dp_packet_l3(pkt_in), sizeof(*nh));
+        memcpy(data, dp_packet_l3(pkt_in, sizeof(*nh)), sizeof(*nh));
 
-        icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
+        icmpv6_csum = packet_csum_pseudoheader6(
+            dp_packet_l3(&packet, sizeof(struct ovs_16aligned_ip6_hdr)));
         ih->icmp6_base.icmp6_cksum = csum_finish(
             csum_continue(icmpv6_csum, ih,
                           sizeof(*nh) + ICMP6_ERROR_HEADER_LEN));
@@ -355,7 +356,7 @@  pinctrl_handle_tcp_reset(const struct flow *ip_flow, struct dp_packet *pkt_in,
     }
 
     struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
-    struct tcp_header *tcp_in = dp_packet_l4(pkt_in);
+    struct tcp_header *tcp_in = dp_packet_l4(pkt_in, sizeof(*tcp_in));
     dp_packet_set_l4(&packet, th);
     th->tcp_ctl = TCP_CTL(TCP_RST, 5);
     if (ip_flow->tcp_flags & htons(TCP_ACK)) {
@@ -534,7 +535,7 @@  pinctrl_handle_put_dhcp_opts(
 
     udp->udp_len = htons(new_l4_size);
 
-    struct ip_header *out_ip = dp_packet_l3(&pkt_out);
+    struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
     out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
     udp->udp_csum = 0;
     /* Checksum needs to be initialized to zero. */
@@ -709,7 +710,7 @@  pinctrl_handle_put_dhcpv6_opts(
         goto exit;
     }
 
-    struct udp_header *in_udp = dp_packet_l4(pkt_in);
+    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof(*in_udp));
     const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in);
     if (!in_udp || !in_dhcpv6_data) {
         VLOG_WARN_RL(&rl, "truncated dhcpv6 packet");
@@ -824,11 +825,13 @@  pinctrl_handle_put_dhcpv6_opts(
     out_udp->udp_len = htons(new_l4_size);
     out_udp->udp_csum = 0;
 
-    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out);
+    struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out,
+                                                         sizeof *out_ip6);
     out_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = out_udp->udp_len;
 
     uint32_t csum;
-    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out));
+    csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out,
+        sizeof(struct ovs_16aligned_ip6_hdr)));
     csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) -
                          ((const unsigned char *)out_udp -
                          (const unsigned char *)dp_packet_eth(&pkt_out)));
@@ -916,7 +919,7 @@  pinctrl_handle_dns_lookup(
         goto exit;
     }
 
-    struct udp_header *in_udp = dp_packet_l4(pkt_in);
+    struct udp_header *in_udp = dp_packet_l4(pkt_in, sizeof *in_udp);
     size_t udp_len = ntohs(in_udp->udp_len);
     size_t l4_len = dp_packet_l4_size(pkt_in);
     uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
@@ -1081,14 +1084,14 @@  pinctrl_handle_dns_lookup(
 
     struct eth_header *eth = dp_packet_data(&pkt_out);
     if (eth->eth_type == htons(ETH_TYPE_IP)) {
-        struct ip_header *out_ip = dp_packet_l3(&pkt_out);
+        struct ip_header *out_ip = dp_packet_l3(&pkt_out, sizeof(*out_ip));
         out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs
                                    + new_l4_size);
         /* Checksum needs to be initialized to zero. */
         out_ip->ip_csum = 0;
         out_ip->ip_csum = csum(out_ip, sizeof *out_ip);
     } else {
-        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
+        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
         nh->ip6_plen = htons(new_l4_size);
 
         /* IPv6 needs UDP checksum calculated */
@@ -2487,9 +2490,9 @@  pinctrl_handle_put_nd_ra_opts(
     dp_packet_put(&pkt_out, userdata->data, userdata->size);
 
     /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
-    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
+    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out, sizeof(*nh));
     nh->ip6_plen = htons(userdata->size);
-    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
+    struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out, sizeof *ra);
     ra->icmph.icmp6_cksum = 0;
     uint32_t icmp_csum = packet_csum_pseudoheader6(nh);
     ra->icmph.icmp6_cksum = csum_finish(csum_continue(
diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c
index 12017ea..72d4ecc 100644
--- a/tests/test-conntrack.c
+++ b/tests/test-conntrack.c
@@ -46,7 +46,7 @@  prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
         dp_packet_put_hex(pkt, payload, NULL);
         flow_extract(pkt, &flow);
 
-        udp = dp_packet_l4(pkt);
+        udp = dp_packet_l4(pkt, sizeof *udp);
         udp->udp_src = htons(ntohs(udp->udp_src) + tid);
 
         if (change) {
diff --git a/tests/test-rstp.c b/tests/test-rstp.c
index 01aeaf8..a0125d1 100644
--- a/tests/test-rstp.c
+++ b/tests/test-rstp.c
@@ -86,8 +86,12 @@  send_bpdu(struct dp_packet *pkt, void *port_, void *b_)
     assert(port_no < b->n_ports);
     lan = b->ports[port_no];
     if (lan) {
-        const void *data = dp_packet_l3(pkt);
-        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
+        if (!dp_packet_is_linear(pkt)) {
+            dp_packet_linearize(pkt);
+        }
+
+        const char *data = dp_packet_l3(pkt, sizeof *data);
+        size_t size = dp_packet_size(pkt);
         int i;
 
         for (i = 0; i < lan->n_conns; i++) {
diff --git a/tests/test-stp.c b/tests/test-stp.c
index c85c99d..c0f566c 100644
--- a/tests/test-stp.c
+++ b/tests/test-stp.c
@@ -94,8 +94,12 @@  send_bpdu(struct dp_packet *pkt, int port_no, void *b_)
     assert(port_no < b->n_ports);
     lan = b->ports[port_no];
     if (lan) {
-        const void *data = dp_packet_l3(pkt);
-        size_t size = (char *) dp_packet_tail(pkt) - (char *) data;
+        if (!dp_packet_is_linear(pkt)) {
+            dp_packet_linearize(pkt);
+        }
+
+        const char *data = dp_packet_l3(pkt, sizeof *data);
+        size_t size = dp_packet_size(pkt);
         int i;
 
         for (i = 0; i < lan->n_conns; i++) {