diff --git a/src/core/ipv4/icmp.c b/src/core/ipv4/icmp.c index c872cdd1..cb8aaf19 100644 --- a/src/core/ipv4/icmp.c +++ b/src/core/ipv4/icmp.c @@ -148,28 +148,25 @@ icmp_input(struct pbuf *p, struct netif *inp) * allocate a new one and copy p into it */ struct pbuf *r; - /* switch p->payload to ip header */ - if (pbuf_header(p, hlen)) { - LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0); - goto memerr; - } /* allocate new packet buffer with space for link headers */ - r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); + r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM); if (r == NULL) { LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n")); goto memerr; } LWIP_ASSERT("check that first pbuf can hold struct the ICMP header", (r->len >= hlen + sizeof(struct icmp_echo_hdr))); - /* copy the whole packet including ip header */ - if (pbuf_copy(r, p) != ERR_OK) { - LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); - goto memerr; - } + /* copy the ip header */ + MEMCPY(r->payload, iphdr, hlen); iphdr = (struct ip_hdr *)r->payload; /* switch r->payload back to icmp header */ if (pbuf_header(r, -hlen)) { - LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0); + LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0); + goto memerr; + } + /* copy the rest of the packet without ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0); goto memerr; } /* free the original p */ diff --git a/src/core/ipv4/ip4.c b/src/core/ipv4/ip4.c index 5922fcd0..9b360652 100644 --- a/src/core/ipv4/ip4.c +++ b/src/core/ipv4/ip4.c @@ -586,7 +586,7 @@ ip_input(struct pbuf *p, struct netif *inp) /* send ICMP destination protocol unreachable unless is was a broadcast */ if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && !ip_addr_ismulticast(ip_current_dest_addr())) { - pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */ p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c index cdace5c5..1c3a95e7 100644 --- a/src/core/ipv6/ip6.c +++ b/src/core/ipv6/ip6.c @@ -649,6 +649,7 @@ netif_found: options_done: /* p points to IPv6 header again. */ + /* @todo: this does not work for PBUF_REF pbufs */ pbuf_header(p, ip_data.current_ip_header_tot_len); /* send to upper layers */ diff --git a/src/core/pbuf.c b/src/core/pbuf.c index d9af29b8..0f6a1c8d 100644 --- a/src/core/pbuf.c +++ b/src/core/pbuf.c @@ -489,26 +489,17 @@ pbuf_realloc(struct pbuf *p, u16_t new_len) /** * Adjusts the payload pointer to hide or reveal headers in the payload. - * - * Adjusts the ->payload pointer so that space for a header - * (dis)appears in the pbuf payload. - * - * The ->payload, ->tot_len and ->len fields are adjusted. + * @see pbuf_header. * * @param p pbuf to change the header size. - * @param header_size_increment Number of bytes to increment header size which - * increases the size of the pbuf. New space is on the front. - * (Using a negative value decreases the header size.) - * If hdr_size_inc is 0, this function does nothing and returns successful. + * @param header_size_increment Number of bytes to increment header size. + * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types * - * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so - * the call will fail. A check is made that the increase in header size does - * not move the payload pointer in front of the start of the buffer. * @return non-zero on failure, zero on success. * */ u8_t -pbuf_header(struct pbuf *p, s16_t header_size_increment) +pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) { u16_t type; void *payload; @@ -561,6 +552,8 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment) if ((header_size_increment < 0) && (increment_magnitude <= p->len)) { /* increase payload pointer */ p->payload = (u8_t *)p->payload - header_size_increment; + } else if ((header_size_increment > 0) && force) { + p->payload = (u8_t *)p->payload - header_size_increment; } else { /* cannot expand payload to front (yet!) * bail out unsuccessfully */ @@ -581,6 +574,42 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment) return 0; } +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns successful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + return pbuf_header_impl(p, header_size_increment, 0); +} + +/** + * Same as pbuf_header but does not check if 'header_size > 0' is allowed. + * This is used internally only, to allow PBUF_REF for RX. + */ +u8_t +pbuf_header_force(struct pbuf *p, s16_t header_size_increment) +{ + return pbuf_header_impl(p, header_size_increment, 1); +} + /** * Dereference a pbuf chain or queue and deallocate any no-longer-used * pbufs at the head of this chain or queue. diff --git a/src/core/udp.c b/src/core/udp.c index 2d1a9978..b2eb1a14 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -398,7 +398,7 @@ udp_input(struct pbuf *p, struct netif *inp) struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { - pbuf_header(p, hdrs_len); + pbuf_header_force(p, hdrs_len); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); @@ -457,7 +457,7 @@ udp_input(struct pbuf *p, struct netif *inp) #endif /* LWIP_IPV6 */ !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ - pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); + pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN); icmp_port_unreach(ip_current_is_v6(), p); } #endif /* LWIP_ICMP || LWIP_ICMP6 */ diff --git a/src/include/lwip/pbuf.h b/src/include/lwip/pbuf.h index 25a32d7d..6bd6b308 100644 --- a/src/include/lwip/pbuf.h +++ b/src/include/lwip/pbuf.h @@ -42,7 +42,9 @@ extern "C" { /** Currently, the pbuf_custom code is only needed for one specific configuration * of IP_FRAG */ +#ifndef LWIP_SUPPORT_CUSTOM_PBUF #define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) +#endif /* @todo: We need a mechanism to prevent wasting memory in every pbuf (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ @@ -158,6 +160,7 @@ struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, #endif /* LWIP_SUPPORT_CUSTOM_PBUF */ void pbuf_realloc(struct pbuf *p, u16_t size); u8_t pbuf_header(struct pbuf *p, s16_t header_size); +u8_t pbuf_header_force(struct pbuf *p, s16_t header_size); void pbuf_ref(struct pbuf *p); u8_t pbuf_free(struct pbuf *p); u8_t pbuf_clen(struct pbuf *p);