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

Flavio Leitner fbl at sysclose.org
Wed Oct 3 18:26:53 UTC 2018


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 at 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 at openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

-- 
Flavio



More information about the dev mailing list