--- a/src/core/ipv6/icmp6.c
+++ b/src/core/ipv6/icmp6.c
@@ -57,9 +57,9 @@
 
 #include <string.h>
 
-#if LWIP_ICMP6_DATASIZE == 0
+#if !LWIP_ICMP6_DATASIZE || (LWIP_ICMP6_DATASIZE > (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN))
 #undef LWIP_ICMP6_DATASIZE
-#define LWIP_ICMP6_DATASIZE   8
+#define LWIP_ICMP6_DATASIZE   (IP6_MIN_MTU_LENGTH - IP6_HLEN - ICMP6_HLEN)
 #endif
 
 /* Forward declarations */
@@ -387,26 +387,35 @@
 {
   struct pbuf *q;
   struct icmp6_hdr *icmp6hdr;
+  u16_t datalen = LWIP_MIN(p->tot_len, LWIP_ICMP6_DATASIZE);
+  u16_t offset;
 
-  /* ICMPv6 header + IPv6 header + data */
-  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
+  /* ICMPv6 header + datalen (as much of the offending packet as possible) */
+  q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + datalen,
                  PBUF_RAM);
   if (q == NULL) {
     LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
     ICMP6_STATS_INC(icmp6.memerr);
     return;
   }
-  LWIP_ASSERT("check that first pbuf can hold icmp 6message",
-             (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
+  LWIP_ASSERT("check that first pbuf can hold icmp6 header",
+             (q->len >= (sizeof(struct icmp6_hdr))));
 
   icmp6hdr = (struct icmp6_hdr *)q->payload;
   icmp6hdr->type = type;
   icmp6hdr->code = code;
   icmp6hdr->data = lwip_htonl(data);
 
-  /* copy fields from original packet */
-  SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
-          IP6_HLEN + LWIP_ICMP6_DATASIZE);
+  /* copy fields from original packet (which may be a chain of pbufs) */
+  offset = sizeof(struct icmp6_hdr);
+  while (p && datalen) {
+    u16_t len = LWIP_MIN(datalen, p->len);
+    err_t res = pbuf_take_at(q, p->payload, len, offset);
+    if (res != ERR_OK) break;
+    datalen -= len;
+    offset += len;
+    p = p->next;
+  }
 
   /* calculate checksum */
   icmp6hdr->chksum = 0;
--- a/src/core/ipv6/nd6.c
+++ b/src/core/ipv6/nd6.c
@@ -693,7 +693,7 @@
         }
         mtu_opt = (struct mtu_option *)buffer;
         mtu32 = lwip_htonl(mtu_opt->mtu);
-        if ((mtu32 >= 1280) && (mtu32 <= 0xffff)) {
+        if ((mtu32 >= IP6_MIN_MTU_LENGTH) && (mtu32 <= 0xffff)) {
 #if LWIP_ND6_ALLOW_RA_UPDATES
           if (inp->mtu) {
             /* don't set the mtu for IPv6 higher than the netif driver supports */
@@ -2300,7 +2300,7 @@
     return netif_mtu6(netif);
   }
 
-  return 1280; /* Minimum MTU */
+  return IP6_MIN_MTU_LENGTH; /* Minimum MTU */
 }
 
 
--- a/src/core/pbuf.c
+++ b/src/core/pbuf.c
@@ -960,54 +960,88 @@
 err_t
 pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
 {
-  size_t offset_to = 0, offset_from = 0, len;
-
   LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
               (const void *)p_to, (const void *)p_from));
 
+  LWIP_ERROR("pbuf_copy: invalid source", p_from != NULL, return ERR_ARG;);
+  return pbuf_copy_partial_pbuf(p_to, p_from, p_from->tot_len, 0);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy part or all of one packet buffer into another, to a specified offset.
+ *
+ * @note Only data in one packet is copied, no packet queue!
+ * @note Argument order is shared with pbuf_copy, but different than pbuf_copy_partial.
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ * @param copy_len number of bytes to copy
+ * @param offset offset in destination pbuf where to copy to
+ *
+ * @return ERR_OK if copy_len bytes were copied
+ *         ERR_ARG if one of the pbufs is NULL or p_from is shorter than copy_len
+ *                 or p_to is not big enough to hold copy_len at offset
+ *         ERR_VAL if any of the pbufs are part of a queue
+ */
+err_t
+pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset)
+{
+  size_t offset_to = offset, offset_from = 0, len_calc;
+  u16_t len;
+
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf(%p, %p, %"U16_F", %"U16_F")\n",
+              (const void *)p_to, (const void *)p_from, copy_len, offset));
+
+  /* is the copy_len in range? */
+  LWIP_ERROR("pbuf_copy_partial_pbuf: copy_len bigger than source", ((p_from != NULL) &&
+             (p_from->tot_len >= copy_len)), return ERR_ARG;);
   /* is the target big enough to hold the source? */
-  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
-             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+  LWIP_ERROR("pbuf_copy_partial_pbuf: target not big enough", ((p_to != NULL) &&
+             (p_to->tot_len >= (offset + copy_len))), return ERR_ARG;);
 
   /* iterate through pbuf chain */
   do {
     /* copy one part of the original chain */
     if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
       /* complete current p_from fits into current p_to */
-      len = p_from->len - offset_from;
+      len_calc = p_from->len - offset_from;
     } else {
       /* current p_from does not fit into current p_to */
-      len = p_to->len - offset_to;
+      len_calc = p_to->len - offset_to;
     }
+    len = (u16_t)LWIP_MIN(copy_len, len_calc);
     MEMCPY((u8_t *)p_to->payload + offset_to, (u8_t *)p_from->payload + offset_from, len);
     offset_to += len;
     offset_from += len;
+    copy_len -= len;
     LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
     LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
     if (offset_from >= p_from->len) {
       /* on to next p_from (if any) */
       offset_from = 0;
       p_from = p_from->next;
+      LWIP_ERROR("p_from != NULL", (p_from != NULL) || (copy_len == 0), return ERR_ARG;);
     }
     if (offset_to == p_to->len) {
       /* on to next p_to (if any) */
       offset_to = 0;
       p_to = p_to->next;
-      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL), return ERR_ARG;);
+      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (copy_len == 0), return ERR_ARG;);
     }
 
     if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
       /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
                  (p_from->next == NULL), return ERR_VAL;);
     }
     if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
       /* don't copy more than one packet! */
-      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+      LWIP_ERROR("pbuf_copy_partial_pbuf() does not allow packet queues!",
                  (p_to->next == NULL), return ERR_VAL;);
     }
-  } while (p_from);
-  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+  } while (copy_len);
+  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy_partial_pbuf: copy complete.\n"));
   return ERR_OK;
 }
 
--- a/src/include/lwip/opt.h
+++ b/src/include/lwip/opt.h
@@ -2502,10 +2502,12 @@
 
 /**
  * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
- * ICMPv6 error messages.
+ * ICMPv6 error messages (0 = default of IP6_MIN_MTU_LENGTH)
+ * ATTENTION: RFC4443 section 2.4 says IP6_MIN_MTU_LENGTH is a MUST,
+ * so override this only if you absolutely have to!
  */
 #if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__
-#define LWIP_ICMP6_DATASIZE             8
+#define LWIP_ICMP6_DATASIZE             0
 #endif
 
 /**
--- a/src/include/lwip/pbuf.h
+++ b/src/include/lwip/pbuf.h
@@ -293,6 +293,7 @@
 void pbuf_chain(struct pbuf *head, struct pbuf *tail);
 struct pbuf *pbuf_dechain(struct pbuf *p);
 err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from);
+err_t pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset);
 u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
 void *pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset);
 err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
--- a/src/include/lwip/prot/icmp6.h
+++ b/src/include/lwip/prot/icmp6.h
@@ -146,6 +146,8 @@
 #  include "arch/epstruct.h"
 #endif
 
+#define ICMP6_HLEN 8
+
 /** This is the ICMP6 header adapted for echo req/resp. */
 #ifdef PACK_STRUCT_USE_INCLUDES
 #  include "arch/bpstruct.h"
--- a/src/include/lwip/prot/ip6.h
+++ b/src/include/lwip/prot/ip6.h
@@ -44,6 +44,8 @@
 extern "C" {
 #endif
 
+#define IP6_MIN_MTU_LENGTH 1280
+
 /** This is the packed version of ip6_addr_t,
     used in network headers that are itself packed */
 #ifdef PACK_STRUCT_USE_INCLUDES
--- a/src/netif/lowpan6.c
+++ b/src/netif/lowpan6.c
@@ -881,7 +881,7 @@
   MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
 
   /* maximum transfer unit */
-  netif->mtu = 1280;
+  netif->mtu = IP6_MIN_MTU_LENGTH;
 
   /* broadcast capability */
   netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */;
--- a/src/netif/lowpan6_ble.c
+++ b/src/netif/lowpan6_ble.c
@@ -417,7 +417,7 @@
   MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
 
   /* maximum transfer unit, set according to RFC7668 ch2.4 */
-  netif->mtu = 1280;
+  netif->mtu = IP6_MIN_MTU_LENGTH;
 
   /* no flags set (no broadcast, ethernet,...)*/
   netif->flags = 0;
--- a/src/netif/zepif.c
+++ b/src/netif/zepif.c
@@ -201,7 +201,7 @@
   state->seqno++;
   zep->len = (u8_t)p->tot_len;
 
-  err = pbuf_take_at(q, p->payload, p->tot_len, sizeof(struct zep_hdr));
+  err = pbuf_copy_partial_pbuf(q, p, p->tot_len, sizeof(struct zep_hdr));
   if (err == ERR_OK) {
 #if ZEPIF_LOOPBACK
     zepif_udp_recv(netif, state->pcb, pbuf_clone(PBUF_RAW, PBUF_RAM, q), NULL, 0);
