diff --git a/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c index 6a47853dfd..47d8531b12 100644 --- a/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c +++ b/pkg/lwip/contrib/sock/ip/lwip_sock_ip.c @@ -106,20 +106,15 @@ static uint16_t _ip6_addr_to_netif(const ip6_addr_p_t *_addr) } #endif -static int _parse_iphdr(const struct netbuf *buf, void *data, size_t max_len, +static int _parse_iphdr(struct netbuf *buf, void **data, void **ctx, sock_ip_ep_t *remote) { - uint8_t *data_ptr = buf->p->payload; - size_t data_len = buf->p->len; + uint8_t *data_ptr = buf->ptr->payload; + size_t data_len = buf->ptr->len; - assert(buf->p->next == NULL); /* TODO this might not be generally the case - * check later with larger payloads */ switch (data_ptr[0] >> 4) { #if LWIP_IPV4 case 4: - if ((data_len - sizeof(struct ip_hdr)) > max_len) { - return -ENOBUFS; - } if (remote != NULL) { struct ip_hdr *iphdr = (struct ip_hdr *)data_ptr; @@ -134,9 +129,6 @@ static int _parse_iphdr(const struct netbuf *buf, void *data, size_t max_len, #endif #if LWIP_IPV6 case 6: - if ((data_len - sizeof(struct ip6_hdr)) > max_len) { - return -ENOBUFS; - } if (remote != NULL) { struct ip6_hdr *iphdr = (struct ip6_hdr *)data_ptr; @@ -150,24 +142,63 @@ static int _parse_iphdr(const struct netbuf *buf, void *data, size_t max_len, break; #endif default: + netbuf_delete(buf); return -EPROTO; } - memcpy(data, data_ptr, data_len); + *data = data_ptr; + *ctx = buf; return (ssize_t)data_len; } ssize_t sock_ip_recv(sock_ip_t *sock, void *data, size_t max_len, uint32_t timeout, sock_ip_ep_t *remote) +{ + void *pkt = NULL; + struct netbuf *ctx = NULL; + uint8_t *ptr = data; + ssize_t res, ret = 0; + bool nobufs = false; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + while ((res = sock_ip_recv_buf(sock, &pkt, (void **)&ctx, timeout, + remote)) > 0) { + if (ctx->p->tot_len > (ssize_t)max_len) { + nobufs = true; + /* progress context to last element */ + while (netbuf_next(ctx) == 0) {} + continue; + } + memcpy(ptr, pkt, res); + ptr += res; + ret += res; + } + return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); +} + +ssize_t sock_ip_recv_buf(sock_ip_t *sock, void **data, void **ctx, + uint32_t timeout, sock_ip_ep_t *remote) { struct netbuf *buf; int res; - assert((sock != NULL) && (data != NULL) && (max_len > 0)); + assert((sock != NULL) && (data != NULL) && (ctx != NULL)); + buf = *ctx; + if (buf != NULL) { + if (netbuf_next(buf) == -1) { + *data = NULL; + netbuf_delete(buf); + *ctx = NULL; + return 0; + } + else { + *data = buf->ptr->payload; + return buf->ptr->len; + } + } if ((res = lwip_sock_recv(sock->base.conn, timeout, &buf)) < 0) { return res; } - res = _parse_iphdr(buf, data, max_len, remote); - netbuf_delete(buf); + res = _parse_iphdr(buf, data, ctx, remote); return res; } diff --git a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c index d64508e0d3..69ece44175 100644 --- a/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c +++ b/pkg/lwip/contrib/sock/udp/lwip_sock_udp.c @@ -71,19 +71,52 @@ int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep) ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, uint32_t timeout, sock_udp_ep_t *remote) { - uint8_t *data_ptr = data; + void *pkt = NULL; + void *ctx = NULL; + uint8_t *ptr = data; + ssize_t res, ret = 0; + bool nobufs = false; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + while ((res = sock_udp_recv_buf(sock, &pkt, &ctx, timeout, + remote)) > 0) { + struct netbuf *buf = ctx; + if (buf->p->tot_len > (ssize_t)max_len) { + nobufs = true; + /* progress context to last element */ + while (netbuf_next(ctx) == 0) {} + continue; + } + memcpy(ptr, pkt, res); + ptr += res; + ret += res; + } + return (nobufs) ? -ENOBUFS : ((res < 0) ? res : ret); +} + +ssize_t sock_udp_recv_buf(sock_udp_t *sock, void **data, void **ctx, + uint32_t timeout, sock_udp_ep_t *remote) +{ struct netbuf *buf; int res; - assert((sock != NULL) && (data != NULL) && (max_len > 0)); + assert((sock != NULL) && (data != NULL) && (ctx != NULL)); + buf = *ctx; + if (buf != NULL) { + if (netbuf_next(buf) == -1) { + *data = NULL; + netbuf_delete(buf); + *ctx = NULL; + return 0; + } + else { + *data = buf->ptr->payload; + return buf->ptr->len; + } + } if ((res = lwip_sock_recv(sock->base.conn, timeout, &buf)) < 0) { return res; } - res = buf->p->tot_len; - if ((unsigned)res > max_len) { - netbuf_delete(buf); - return -ENOBUFS; - } if (remote != NULL) { /* convert remote */ size_t addr_len; @@ -113,13 +146,9 @@ ssize_t sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, memcpy(&remote->addr, &buf->addr, addr_len); remote->port = buf->port; } - /* copy data */ - for (struct pbuf *q = buf->p; q != NULL; q = q->next) { - memcpy(data_ptr, q->payload, q->len); - data_ptr += q->len; - } - netbuf_delete(buf); - return (ssize_t)res; + *data = buf->ptr->payload; + *ctx = buf; + return (ssize_t)buf->ptr->len; } ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, diff --git a/tests/lwip_sock_ip/main.c b/tests/lwip_sock_ip/main.c index 2175e61a13..5df5eff04d 100644 --- a/tests/lwip_sock_ip/main.c +++ b/tests/lwip_sock_ip/main.c @@ -303,6 +303,27 @@ static void test_sock_ip_recv4__non_blocking(void) expect(_check_net()); } +static void test_sock_ip_recv_buf4__success(void) +{ + static const sock_ip_ep_t local = { .family = AF_INET }; + static const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = _TEST_ADDR4_REMOTE }, + .family = AF_INET }; + void *data = NULL, *ctx = NULL; + + expect(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + expect(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv_buf(&_sock, &data, &ctx, + SOCK_NO_TIMEOUT, NULL)); + assert(data != NULL); + assert(ctx != NULL); + assert(0 == sock_ip_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, NULL)); + assert(data == NULL); + assert(ctx == NULL); + assert(_check_net()); +} + static void test_sock_ip_send4__EAFNOSUPPORT(void) { static const sock_ip_ep_t remote = { .addr = { .ipv4_u32 = _TEST_ADDR4_REMOTE }, @@ -823,6 +844,29 @@ static void test_sock_ip_recv6__non_blocking(void) expect(_check_net()); } +static void test_sock_ip_recv_buf6__success(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL }; + static const sock_ip_ep_t local = { .family = AF_INET6 }; + static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6 }; + void *data = NULL, *ctx = NULL; + + assert(0 == sock_ip_create(&_sock, &local, &remote, _TEST_PROTO, + SOCK_FLAGS_REUSE_EP)); + assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PROTO, "ABCD", + sizeof("ABCD"), _TEST_NETIF)); + assert(sizeof("ABCD") == sock_ip_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, + NULL)); + assert(data != NULL); + assert(ctx != NULL); + assert(0 == sock_ip_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, NULL)); + assert(data == NULL); + assert(ctx == NULL); + assert(_check_net()); +} + static void test_sock_ip_send6__EAFNOSUPPORT(void) { static const sock_ip_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, @@ -1119,6 +1163,7 @@ int main(void) CALL(test_sock_ip_recv4__unsocketed_with_remote()); CALL(test_sock_ip_recv4__with_timeout()); CALL(test_sock_ip_recv4__non_blocking()); + CALL(test_sock_ip_recv_buf4__success()); _prepare_send_checks(); CALL(test_sock_ip_send4__EAFNOSUPPORT()); CALL(test_sock_ip_send4__EINVAL_addr()); @@ -1163,6 +1208,7 @@ int main(void) CALL(test_sock_ip_recv6__unsocketed_with_remote()); CALL(test_sock_ip_recv6__with_timeout()); CALL(test_sock_ip_recv6__non_blocking()); + CALL(test_sock_ip_recv_buf6__success()); _prepare_send_checks(); CALL(test_sock_ip_send6__EAFNOSUPPORT()); CALL(test_sock_ip_send6__EINVAL_addr()); diff --git a/tests/lwip_sock_udp/main.c b/tests/lwip_sock_udp/main.c index 8e9d277428..e5564f1a48 100644 --- a/tests/lwip_sock_udp/main.c +++ b/tests/lwip_sock_udp/main.c @@ -393,6 +393,29 @@ static void test_sock_udp_recv4__non_blocking(void) expect(_check_net()); } +static void test_sock_udp_recv_buf4__success(void) +{ + static const sock_udp_ep_t local = { .family = AF_INET, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv4_u32 = _TEST_ADDR4_REMOTE }, + .family = AF_INET, + .port = _TEST_PORT_REMOTE }; + void *data = NULL, *ctx = NULL; + + expect(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + expect(_inject_4packet(_TEST_ADDR4_REMOTE, _TEST_ADDR4_LOCAL, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv_buf(&_sock, &data, &ctx, + SOCK_NO_TIMEOUT, NULL)); + assert(data != NULL); + assert(ctx != NULL); + assert(0 == sock_udp_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, NULL)); + assert(data == NULL); + assert(ctx == NULL); + assert(_check_net()); +} + static void test_sock_udp_send4__EAFNOSUPPORT(void) { static const sock_udp_ep_t remote = { .addr = { .ipv4_u32 = _TEST_ADDR4_REMOTE }, @@ -1033,6 +1056,31 @@ static void test_sock_udp_recv6__non_blocking(void) expect(_check_net()); } +static void test_sock_udp_recv_buf6__success(void) +{ + static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_REMOTE }; + static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_LOCAL }; + static const sock_udp_ep_t local = { .family = AF_INET6, + .port = _TEST_PORT_LOCAL }; + static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, + .family = AF_INET6, + .port = _TEST_PORT_REMOTE }; + void *data = NULL, *ctx = NULL; + + assert(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP)); + assert(_inject_6packet(&src_addr, &dst_addr, _TEST_PORT_REMOTE, + _TEST_PORT_LOCAL, "ABCD", sizeof("ABCD"), + _TEST_NETIF)); + assert(sizeof("ABCD") == sock_udp_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, + NULL)); + assert(data != NULL); + assert(ctx != NULL); + assert(0 == sock_udp_recv_buf(&_sock, &data, &ctx, SOCK_NO_TIMEOUT, NULL)); + assert(data == NULL); + assert(ctx == NULL); + assert(_check_net()); +} + static void test_sock_udp_send6__EAFNOSUPPORT(void) { static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE }, @@ -1360,6 +1408,7 @@ int main(void) CALL(test_sock_udp_recv4__unsocketed_with_remote()); CALL(test_sock_udp_recv4__with_timeout()); CALL(test_sock_udp_recv4__non_blocking()); + CALL(test_sock_udp_recv_buf4__success()); _prepare_send_checks(); CALL(test_sock_udp_send4__EAFNOSUPPORT()); CALL(test_sock_udp_send4__EINVAL_addr()); @@ -1407,6 +1456,7 @@ int main(void) CALL(test_sock_udp_recv6__unsocketed_with_remote()); CALL(test_sock_udp_recv6__with_timeout()); CALL(test_sock_udp_recv6__non_blocking()); + CALL(test_sock_udp_recv_buf6__success()); _prepare_send_checks(); CALL(test_sock_udp_send6__EAFNOSUPPORT()); CALL(test_sock_udp_send6__EINVAL_addr());