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/conntrack.c b/lib/conntrack.c
index 974f985..15d1ed2 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -636,6 +636,8 @@ reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
 static void
 reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
+    void *l3 = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
+    void *l4 = dp_packet_linear_ofs(pkt, pkt->l4_ofs);
     char *tail = dp_packet_tail(pkt);
     char pad = dp_packet_l2_pad_size(pkt);
     struct conn_key inner_key;
@@ -644,8 +646,8 @@ reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
     uint16_t 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 = l3;
+        struct icmp_header *icmp = l4;
         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 +666,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 = l3;
+        struct icmp6_error_header *icmp6 = l4;
         struct ovs_16aligned_ip6_hdr *inner_l3_6 =
             (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
         extract_l3_ipv6(&inner_key, inner_l3_6,
@@ -1320,6 +1322,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);
     }
@@ -1902,8 +1905,8 @@ 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 ip_header *l3 = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
+    const char *l4 = dp_packet_linear_ofs(pkt, pkt->l4_ofs);
 
     memset(ctx, 0, sizeof *ctx);
 
@@ -3167,7 +3170,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 = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
     ovs_be32 v4_addr_rep = 0;
     struct ct_addr v6_addr_rep;
     size_t addr_offset_from_ftp_data_start;
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 022e420..7f7b5f5 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -46,6 +46,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 +63,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. */
@@ -86,6 +97,7 @@ struct dp_packet {
 #endif
 
 static inline void *dp_packet_data(const struct dp_packet *);
+static inline void *dp_packet_linear_data(const struct dp_packet *b);
 static inline void dp_packet_set_data(struct dp_packet *, void *);
 static inline void *dp_packet_base(const struct dp_packet *);
 static inline void dp_packet_set_base(struct dp_packet *, void *);
@@ -139,6 +151,8 @@ static inline void dp_packet_delete(struct dp_packet *);
 
 static inline void *dp_packet_at(const struct dp_packet *, size_t offset,
                                  size_t size);
+static inline void *dp_packet_linear_ofs(const struct dp_packet *b,
+                                         uint16_t ofs);
 static inline void *dp_packet_at_assert(const struct dp_packet *,
                                         size_t offset, size_t size);
 #ifdef DPDK_NETDEV
@@ -181,15 +195,11 @@ 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);
+        }
     }
 }
 
@@ -747,6 +757,68 @@ dp_packet_data(const struct dp_packet *b)
            ? (char *) dp_packet_base(b) + __packet_data(b) : NULL;
 }
 
+/* Linearizes the data held by 'b', if and only if its content is
+ * non-contiguous, and returns a pointer to the byte 'ofs' within linearized
+ * 'b', if 'ofs' has been set (!= UINT16_MAX).  Otherwise, returns a null
+ * pointer. */
+static inline void *
+dp_packet_linear_ofs(const struct dp_packet *b, uint16_t ofs)
+{
+    /* "Linearize" the data in the packet, iff needed */
+    dp_packet_linear_data(b);
+
+    return ofs != UINT16_MAX
+           ? (char *) dp_packet_data(b) + ofs
+           : NULL;
+}
+
+/* Copies the content of the DPDK packet 'b', if and only if its content is
+ * distributed amongst multiple segments, into system's memory, so that data is
+ * stored linearly. A pointer to the newly allocated (copied) data is returned.
+ *
+ * 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. Otherwise dp_packet_data() should be used instead. */
+static inline void *
+dp_packet_linear_data(const struct dp_packet *b)
+{
+    if (b->source == DPBUF_DPDK) {
+#ifdef DPDK_NETDEV
+        if (!rte_pktmbuf_is_contiguous(&b->mbuf)) {
+            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 NULL;
+            }
+
+            /* 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
+    }
+
+    return dp_packet_data(b);
+}
+
 static inline void
 dp_packet_set_data(struct dp_packet *b, void *data)
 {
@@ -825,6 +897,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-netlink.c b/lib/dpif-netlink.c
index e6d5a6e..14e6b3e 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1875,7 +1875,7 @@ dpif_netlink_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec,
     k_exec->dp_ifindex = dp_ifindex;
 
     nl_msg_put_unspec(buf, OVS_PACKET_ATTR_PACKET,
-                      dp_packet_data(d_exec->packet),
+                      dp_packet_linear_data(d_exec->packet),
                       dp_packet_size(d_exec->packet));
 
     key_ofs = nl_msg_start_nested(buf, OVS_PACKET_ATTR_KEY);
diff --git a/lib/dpif.c b/lib/dpif.c
index d799f97..9cd0d07 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1830,7 +1830,7 @@ log_execute_message(const struct dpif *dpif,
         uint64_t stub[1024 / 8];
         struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub);
 
-        packet = ofp_packet_to_string(dp_packet_data(execute->packet),
+        packet = ofp_packet_to_string(dp_packet_linear_data(execute->packet),
                                       dp_packet_size(execute->packet),
                                       execute->packet->packet_type);
         odp_key_from_dp_packet(&md, execute->packet);
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index a153aa2..71dc87f 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -701,7 +701,7 @@ netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
     }
 
     for (i = 0; i < batch->count; i++) {
-        const void *data = dp_packet_data(batch->packets[i]);
+        const void *data = dp_packet_data_linear(batch->packets[i]);
         size_t size = dp_packet_size(batch->packets[i]);
 
         while (!error) {
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index d498467..eef169c 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -233,7 +233,8 @@ 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));
+        retval = stream_send(s->stream, dp_packet_linear_data(txbuf),
+                             dp_packet_size(txbuf));
 
         if (retval > 0) {
             dp_packet_pull(txbuf, retval);
@@ -1088,7 +1089,7 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH(i, packet, batch) {
-        const void *buffer = dp_packet_data(packet);
+        const void *buffer = dp_packet_linear_data(packet);
         size_t size = dp_packet_size(packet);
 
         if (batch->packets[i]->packet_type != htonl(PT_ETH)) {
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index e16ea58..7581467 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1380,7 +1380,7 @@ netdev_linux_sock_batch_send(int sock, int ifindex,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-        iov[i].iov_base = dp_packet_data(packet);
+        iov[i].iov_base = dp_packet_linear_data(packet);
         iov[i].iov_len = dp_packet_size(packet);
         mmsg[i].msg_hdr = (struct msghdr) { .msg_name = &sll,
                                             .msg_namelen = sizeof sll,
@@ -1434,7 +1434,8 @@ netdev_linux_tap_batch_send(struct netdev *netdev_,
         int error;
 
         do {
-            retval = write(netdev->tap_fd, dp_packet_data(packet), size);
+            retval = write(netdev->tap_fd, dp_packet_linear_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..0b6be43 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -87,6 +87,7 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
         ovs_be32 ip_src, ip_dst;
 
         if (OVS_UNLIKELY(!dp_packet_ip_checksum_valid(packet))) {
+            ip = dp_packet_linear_ofs(packet, packet->l3_ofs);
             if (csum(ip, IP_IHL(ip->ip_ihl_ver) * 4)) {
                 VLOG_WARN_RL(&err_rl, "ip packet has invalid checksum");
                 return NULL;
@@ -196,6 +197,8 @@ udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
                 csum = packet_csum_pseudoheader(dp_packet_l3(packet));
             }
 
+            udp = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
             csum = csum_continue(csum, udp, dp_packet_size(packet) -
                                  ((const unsigned char *)udp -
                                   (const unsigned char *)dp_packet_eth(packet)
@@ -236,6 +239,8 @@ netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
             csum = packet_csum_pseudoheader(netdev_tnl_ip_hdr(dp_packet_data(packet)));
         }
 
+        udp = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
         csum = csum_continue(csum, udp, ip_tot_size);
         udp->udp_csum = csum_finish(csum);
 
@@ -373,6 +378,8 @@ parse_gre_header(struct dp_packet *packet,
     if (greh->flags & htons(GRE_CSUM)) {
         ovs_be16 pkt_csum;
 
+        greh = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
         pkt_csum = csum(greh, dp_packet_size(packet) -
                               ((const unsigned char *)greh -
                                (const unsigned char *)dp_packet_eth(packet)));
@@ -448,7 +455,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_linear_ofs(packet, packet->l4_ofs);
+        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..e4d2604 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -231,7 +231,7 @@ 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_msg *ns = dp_packet_linear_ofs(packet, packet->l4_ofs);
     const struct ovs_nd_lla_opt *lla_opt = dp_packet_get_nd_payload(packet);
 
     if (OVS_LIKELY(ns && lla_opt)) {
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index cf93d2e..459e59f 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -111,7 +111,7 @@ ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
 char *
 ofp_dp_packet_to_string(const struct dp_packet *packet)
 {
-    return ofp_packet_to_string(dp_packet_data(packet),
+    return ofp_packet_to_string(dp_packet_linear_data(packet),
                                 dp_packet_size(packet),
                                 packet->packet_type);
 }
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index 05c1dd4..21605c6 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_linear_data(p),
+                   dp_packet_size(p));
     }
 }
 
diff --git a/lib/packets.c b/lib/packets.c
index 38bfb60..2cb8da4 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1180,7 +1180,8 @@ 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_linear_ofs(packet,
+                                                            packet->l3_ofs);
     uint8_t proto = 0;
     bool rh_present;
 
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index d17d7a8..3167ee4 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1319,7 +1319,7 @@ dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
     header->stripped = 4;
     header->header_length = MIN(dp_packet_size(packet),
                                 sampler->sFlowFsMaximumHeaderSize);
-    header->header_bytes = dp_packet_data(packet);
+    header->header_bytes = dp_packet_linear_data(packet);
 
     /* Add extended switch element. */
     memset(&switchElem, 0, sizeof(switchElem));
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 6222207..ec55e69 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1455,7 +1455,7 @@ process_upcall(struct udpif *udpif, struct upcall *upcall,
                 .pin = {
                     .up = {
                         .base = {
-                            .packet = xmemdup(dp_packet_data(packet),
+                            .packet = xmemdup(dp_packet_linear_data(packet),
                                               dp_packet_size(packet)),
                             .packet_len = dp_packet_size(packet),
                             .reason = cookie->controller.reason,
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index e26f6c8..a9d2547 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_linear_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_linear_data(&payload),
                                 dp_packet_size(&payload));
     }
 }
@@ -2559,7 +2561,8 @@ update_mcast_snooping_table4__(const struct xlate_ctx *ctx,
     size_t offset;
     ovs_be32 ip4 = flow->igmp_group_ip4;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    offset = (char *) dp_packet_linear_ofs(packet, packet->l4_ofs) -
+        (char *) dp_packet_linear_data(packet);
     igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
     if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
         xlate_report_debug(ctx, OFT_DETAIL,
@@ -2618,7 +2621,8 @@ update_mcast_snooping_table6__(const struct xlate_ctx *ctx,
     int count;
     size_t offset;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    offset = (char *) dp_packet_linear_ofs(packet, packet->l4_ofs) -
+        (char *) dp_packet_linear_data(packet);
     mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
 
     if (!mld ||
