1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #17485 from benpicco/sock_udp_sendv

sys/net/sock: add sock_udp_sendv() API
This commit is contained in:
Kaspar Schleiser 2022-03-01 14:05:21 +01:00 committed by GitHub
commit a17ff53ecf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 244 additions and 32 deletions

View File

@ -573,14 +573,15 @@ int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf)
}
#endif /* defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP) */
ssize_t lwip_sock_send(struct netconn *conn, const void *data, size_t len,
int proto, const struct _sock_tl_ep *remote, int type)
ssize_t lwip_sock_sendv(struct netconn *conn, const iolist_t *snips,
int proto, const struct _sock_tl_ep *remote, int type)
{
ip_addr_t remote_addr;
struct netconn *tmp;
struct netbuf *buf;
struct netbuf *buf = NULL;
size_t payload_len = 0;
int res;
err_t err;
err_t err= ERR_OK;
u16_t remote_port = 0;
#if LWIP_IPV6
@ -598,11 +599,19 @@ ssize_t lwip_sock_send(struct netconn *conn, const void *data, size_t len,
}
buf = netbuf_new();
if ((buf == NULL) || (netbuf_alloc(buf, len) == NULL) ||
(netbuf_take(buf, data, len) != ERR_OK)) {
if (netbuf_alloc(buf, iolist_size(snips)) == NULL) {
netbuf_delete(buf);
return -ENOMEM;
}
for (const iolist_t *snip = snips; snip != NULL; snip = snip->iol_next) {
if (pbuf_take_at(buf->p, snip->iol_base, snip->iol_len, payload_len) != ERR_OK) {
netbuf_delete(buf);
return -ENOMEM;
}
payload_len += snip->iol_len;
}
if ((conn == NULL) && (remote != NULL)) {
if ((res = _create(type, proto, 0, &tmp)) < 0) {
netbuf_delete(buf);
@ -625,13 +634,13 @@ ssize_t lwip_sock_send(struct netconn *conn, const void *data, size_t len,
netbuf_delete(buf);
return -ENOTCONN;
}
res = len; /* set for non-TCP calls */
res = payload_len; /* set for non-TCP calls */
if (remote != NULL) {
err = netconn_sendto(tmp, buf, &remote_addr, remote_port);
}
#if LWIP_TCP
else if (tmp->type & NETCONN_TCP) {
err = netconn_write_partly(tmp, data, len, 0, (size_t *)(&res));
err = netconn_write_partly(tmp, buf->p->payload, buf->p->len, 0, (size_t *)(&res));
}
#endif /* LWIP_TCP */
else {

View File

@ -163,18 +163,17 @@ ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **ctx,
return (ssize_t)buf->ptr->len;
}
ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
ssize_t sock_udp_sendv_aux(sock_udp_t *sock, const iolist_t *snips,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
{
(void)aux;
assert((sock != NULL) || (remote != NULL));
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
if ((remote != NULL) && (remote->port == 0)) {
return -EINVAL;
}
return lwip_sock_send((sock) ? sock->base.conn : NULL, data, len, 0,
(struct _sock_tl_ep *)remote, NETCONN_UDP);
return lwip_sock_sendv((sock) ? sock->base.conn : NULL, snips, 0,
(struct _sock_tl_ep *)remote, NETCONN_UDP);
}
#ifdef SOCK_HAS_ASYNC

View File

@ -56,8 +56,19 @@ int lwip_sock_get_addr(struct netconn *conn, struct _sock_tl_ep *ep, u8_t local)
#if defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP)
int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf);
#endif
ssize_t lwip_sock_send(struct netconn *conn, const void *data, size_t len,
int proto, const struct _sock_tl_ep *remote, int type);
ssize_t lwip_sock_sendv(struct netconn *conn, const iolist_t *snips,
int proto, const struct _sock_tl_ep *remote, int type);
static inline ssize_t lwip_sock_send(struct netconn *conn,
const void *data, size_t len,
int proto, const struct _sock_tl_ep *remote, int type)
{
iolist_t snip = {
.iol_base = (void *)data,
.iol_len = len,
};
return lwip_sock_sendv(conn, &snip, proto, remote, type);
}
/**
* @}
*/

View File

@ -215,12 +215,15 @@ int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local,
return 0;
}
ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
ssize_t sock_udp_sendv_aux(sock_udp_t *sock,
const iolist_t *snips,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
{
(void)aux;
OpenQueueEntry_t *pkt;
open_addr_t dst_addr, src_addr;
size_t payload_len = 0;
uint8_t *payload_dst;
memset(&dst_addr, 0, sizeof(open_addr_t));
memset(&src_addr, 0, sizeof(open_addr_t));
@ -228,7 +231,6 @@ ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
/* asserts for sock_udp_send "pre" */
assert((sock != NULL) || (remote != NULL));
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
/* check remote */
if (remote != NULL) {
@ -302,19 +304,29 @@ ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
pkt->l4_destination_port = dst_port;
pkt->l4_sourcePortORicmpv6Type = src_port;
/* calculate payload size */
payload_len = iolist_size(snips);
/* set payload */
if (packetfunctions_reserveHeader(&pkt, len)) {
if (packetfunctions_reserveHeader(&pkt, payload_len)) {
openqueue_freePacketBuffer(pkt);
return -ENOMEM;
}
memcpy(pkt->payload, data, len);
/* copy payload into packet */
payload_dst = pkt->payload;
while (snips) {
memcpy(payload_dst, snips->iol_base, snips->iol_len);
payload_dst += snips->iol_len;
snips = snips->iol_next;
}
pkt->l4_payload = pkt->payload;
pkt->l4_length = pkt->length;
/* push task to scheduler send */
scheduler_push_task(_sock_transmit_internal, TASKPRIO_UDP);
return len;
return payload_len;
}
void sock_udp_close(sock_udp_t *sock)

View File

@ -137,6 +137,10 @@ ifneq (,$(filter sntp,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter sock_%,$(USEMODULE)))
USEMODULE += iolist
endif
ifneq (,$(filter netdev_ieee802154,$(USEMODULE)))
USEMODULE += ieee802154
USEMODULE += random

View File

@ -103,6 +103,8 @@
#define NET_SOCK_H
#include <stdint.h>
#include <stddef.h>
#include "iolist.h"
#ifdef __cplusplus
extern "C" {

View File

@ -605,6 +605,43 @@ static inline ssize_t sock_udp_recv_buf(sock_udp_t *sock,
return sock_udp_recv_buf_aux(sock, data, buf_ctx, timeout, remote, NULL);
}
/**
* @brief Sends a UDP message to remote end point with non-continous payload
*
* @pre `((sock != NULL || remote != NULL))`
*
* @param[in] sock A UDP sock object. May be `NULL`.
* A sensible local end point should be selected by the
* implementation in that case.
* @param[in] snips List of payload chunks, will be processed in order.
* May be `NULL`.
* @param[in] remote Remote end point for the sent data.
* May be `NULL`, if @p sock has a remote end point.
* sock_udp_ep_t::family may be AF_UNSPEC, if local
* end point of @p sock provides this information.
* sock_udp_ep_t::port may not be 0.
* @param[out] aux Auxiliary data about the transmission.
* May be `NULL`, if it is not required by the application.
*
* @return The number of bytes sent on success.
* @return -EADDRINUSE, if `sock` has no local end-point or was `NULL` and the
* pool of available ephemeral ports is depleted.
* @return -EAFNOSUPPORT, if `remote != NULL` and sock_udp_ep_t::family of
* @p remote is != AF_UNSPEC and not supported.
* @return -EHOSTUNREACH, if @p remote or remote end point of @p sock is not
* reachable.
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote is an invalid address.
* @return -EINVAL, if sock_udp_ep_t::netif of @p remote is not a valid
* interface or contradicts the given local interface (i.e.
* neither the local end point of `sock` nor remote are assigned to
* `SOCK_ADDR_ANY_NETIF` but are nevertheless different.
* @return -EINVAL, if sock_udp_ep_t::port of @p remote is 0.
* @return -ENOMEM, if no memory was available to send @p data.
* @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point.
*/
ssize_t sock_udp_sendv_aux(sock_udp_t *sock, const iolist_t *snips,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux);
/**
* @brief Sends a UDP message to remote end point
*
@ -640,8 +677,18 @@ static inline ssize_t sock_udp_recv_buf(sock_udp_t *sock,
* @return -ENOMEM, if no memory was available to send @p data.
* @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point.
*/
ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux);
static inline ssize_t sock_udp_send_aux(sock_udp_t *sock,
const void *data, size_t len,
const sock_udp_ep_t *remote,
sock_udp_aux_tx_t *aux)
{
const iolist_t snip = {
.iol_base = (void *)data,
.iol_len = len,
};
return sock_udp_sendv_aux(sock, &snip, remote, aux);
}
/**
* @brief Sends a UDP message to remote end point
@ -683,6 +730,45 @@ static inline ssize_t sock_udp_send(sock_udp_t *sock,
return sock_udp_send_aux(sock, data, len, remote, NULL);
}
/**
* @brief Sends a UDP message to remote end point with non-continous payload
*
* @pre `((sock != NULL || remote != NULL))`
*
* @param[in] sock A UDP sock object. May be `NULL`.
* A sensible local end point should be selected by the
* implementation in that case.
* @param[in] snips List of payload chunks, will be processed in order.
* May be `NULL`.
* @param[in] remote Remote end point for the sent data.
* May be `NULL`, if @p sock has a remote end point.
* sock_udp_ep_t::family may be AF_UNSPEC, if local
* end point of @p sock provides this information.
* sock_udp_ep_t::port may not be 0.
*
* @return The number of bytes sent on success.
* @return -EADDRINUSE, if `sock` has no local end-point or was `NULL` and the
* pool of available ephemeral ports is depleted.
* @return -EAFNOSUPPORT, if `remote != NULL` and sock_udp_ep_t::family of
* @p remote is != AF_UNSPEC and not supported.
* @return -EHOSTUNREACH, if @p remote or remote end point of @p sock is not
* reachable.
* @return -EINVAL, if sock_udp_ep_t::addr of @p remote is an invalid address.
* @return -EINVAL, if sock_udp_ep_t::netif of @p remote is not a valid
* interface or contradicts the given local interface (i.e.
* neither the local end point of `sock` nor remote are assigned to
* `SOCK_ADDR_ANY_NETIF` but are nevertheless different.
* @return -EINVAL, if sock_udp_ep_t::port of @p remote is 0.
* @return -ENOMEM, if no memory was available to send @p data.
* @return -ENOTCONN, if `remote == NULL`, but @p sock has no remote end point.
*/
static inline ssize_t sock_udp_sendv(sock_udp_t *sock,
const iolist_t *snips,
const sock_udp_ep_t *remote)
{
return sock_udp_sendv_aux(sock, snips, remote, NULL);
}
#include "sock_types.h"
#ifdef __cplusplus

View File

@ -278,19 +278,20 @@ ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **buf_ctx,
return res;
}
ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
ssize_t sock_udp_sendv_aux(sock_udp_t *sock,
const iolist_t *snips,
const sock_udp_ep_t *remote, sock_udp_aux_tx_t *aux)
{
(void)aux;
int res;
gnrc_pktsnip_t *payload, *pkt;
gnrc_pktsnip_t *pkt, *payload = NULL;
uint16_t src_port = 0, dst_port;
sock_ip_ep_t local;
sock_udp_ep_t remote_cpy;
sock_ip_ep_t *rem;
uint8_t *payload_buf;
assert((sock != NULL) || (remote != NULL));
assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */
if (remote != NULL) {
if (remote->port == 0) {
@ -362,11 +363,21 @@ ssize_t sock_udp_send_aux(sock_udp_t *sock, const void *data, size_t len,
else if (local.family != rem->family) {
return -EINVAL;
}
/* generate payload and header snips */
payload = gnrc_pktbuf_add(NULL, (void *)data, len, GNRC_NETTYPE_UNDEF);
/* allocate snip for payload */
payload = gnrc_pktbuf_add(NULL, NULL, iolist_size(snips), GNRC_NETTYPE_UNDEF);
if (payload == NULL) {
return -ENOMEM;
}
/* copy payload data into payload snip */
payload_buf = payload->data;
while (snips) {
memcpy(payload_buf, snips->iol_base, snips->iol_len);
payload_buf += snips->iol_len;
snips = snips->iol_next;
}
pkt = gnrc_udp_hdr_build(payload, src_port, dst_port);
if (pkt == NULL) {
gnrc_pktbuf_release(payload);

View File

@ -629,6 +629,37 @@ static void test_sock_udp_send__socketed(void)
expect(_check_net());
}
static void test_sock_udp_sendv__socketed(void)
{
static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL };
static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR_REMOTE };
static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR_LOCAL },
.family = AF_INET6,
.netif = _TEST_NETIF,
.port = _TEST_PORT_LOCAL };
static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR_REMOTE },
.family = AF_INET6,
.port = _TEST_PORT_REMOTE };
const iolist_t tail = {
.iol_base = "EFGH",
.iol_len = sizeof("EFGH"),
};
const iolist_t head = {
.iol_next = (void *)&tail,
.iol_base = "ABCD",
.iol_len = sizeof("ABCD") - 1,
};
expect(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP));
expect(sizeof("ABCDEFGH") == sock_udp_sendv(&_sock, &head, NULL));
expect(_check_packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL,
_TEST_PORT_REMOTE, "ABCDEFGH", sizeof("ABCDEFGH"),
_TEST_NETIF, false));
xtimer_usleep(1000); /* let GNRC stack finish */
expect(_check_net());
}
static void test_sock_udp_send__socketed_other_remote(void)
{
static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR_LOCAL };
@ -808,6 +839,7 @@ int main(void)
CALL(test_sock_udp_send__socketed_no_netif());
CALL(test_sock_udp_send__socketed_no_local());
CALL(test_sock_udp_send__socketed());
CALL(test_sock_udp_sendv__socketed());
CALL(test_sock_udp_send__socketed_other_remote());
CALL(test_sock_udp_send__unsocketed_no_local_no_netif());
CALL(test_sock_udp_send__unsocketed_no_netif());

View File

@ -29,6 +29,7 @@
static msg_t _msg_queue[_MSG_QUEUE_SIZE];
static gnrc_netreg_entry_t _udp_handler;
static char _rx_buf[32];
void _net_init(void)
{
@ -130,9 +131,11 @@ bool _check_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
void *data, size_t data_len, uint16_t iface,
bool random_src_port)
{
gnrc_pktsnip_t *pkt, *ipv6, *udp;
gnrc_pktsnip_t *pkt, *ipv6, *udp, *payload;
ipv6_hdr_t *ipv6_hdr;
udp_hdr_t *udp_hdr;
size_t payload_len;
char *payload_buf;
msg_t msg;
msg_receive(&msg);
@ -164,12 +167,22 @@ bool _check_packet(const ipv6_addr_t *src, const ipv6_addr_t *dst,
return _res(pkt, false);
}
udp_hdr = udp->data;
payload = udp->next;
payload_buf = _rx_buf;
while (payload) {
memcpy(payload_buf, payload->data, payload->size);
payload_buf += payload->size;
payload = payload->next;
}
payload_len = payload_buf - _rx_buf;
return _res(pkt, (memcmp(src, &ipv6_hdr->src, sizeof(ipv6_addr_t)) == 0) &&
(memcmp(dst, &ipv6_hdr->dst, sizeof(ipv6_addr_t)) == 0) &&
(ipv6_hdr->nh == PROTNUM_UDP) &&
(random_src_port || (src_port == byteorder_ntohs(udp_hdr->src_port))) &&
(dst_port == byteorder_ntohs(udp_hdr->dst_port)) &&
(udp->next != NULL) &&
(data_len == udp->next->size) &&
(memcmp(data, udp->next->data, data_len) == 0));
(data_len ==payload_len ) &&
(memcmp(data, _rx_buf, data_len) == 0));
}

View File

@ -1288,6 +1288,38 @@ static void test_sock_udp_send6__socketed(void)
expect(_check_net());
}
static void test_sock_udp_sendv6__socketed(void)
{
static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
static const ipv6_addr_t dst_addr = { .u8 = _TEST_ADDR6_REMOTE };
static const sock_udp_ep_t local = { .addr = { .ipv6 = _TEST_ADDR6_LOCAL },
.family = AF_INET6,
.netif = _TEST_NETIF,
.port = _TEST_PORT_LOCAL };
static const sock_udp_ep_t remote = { .addr = { .ipv6 = _TEST_ADDR6_REMOTE },
.family = AF_INET6,
.port = _TEST_PORT_REMOTE };
const iolist_t tail = {
.iol_base = "EFGH",
.iol_len = sizeof("EFGH"),
};
const iolist_t head = {
.iol_next = (void *)&tail,
.iol_base = "ABCD",
.iol_len = sizeof("ABCD") - 1,
};
expect(0 == sock_udp_create(&_sock, &local, &remote, SOCK_FLAGS_REUSE_EP));
expect(sizeof("ABCDEFGH") == sock_udp_sendv(&_sock, &head, NULL));
expect(_check_6packet(&src_addr, &dst_addr, _TEST_PORT_LOCAL,
_TEST_PORT_REMOTE, "ABCDEFGH", sizeof("ABCDEFGH"),
_TEST_NETIF, false));
xtimer_usleep(1000); /* let GNRC stack finish */
expect(_check_net());
}
static void test_sock_udp_send6__socketed_other_remote(void)
{
static const ipv6_addr_t src_addr = { .u8 = _TEST_ADDR6_LOCAL };
@ -1531,6 +1563,7 @@ int main(void)
CALL(test_sock_udp_send6__socketed_no_netif());
CALL(test_sock_udp_send6__socketed_no_local());
CALL(test_sock_udp_send6__socketed());
CALL(test_sock_udp_sendv6__socketed());
CALL(test_sock_udp_send6__socketed_other_remote());
CALL(test_sock_udp_send6__unsocketed_no_local_no_netif());
CALL(test_sock_udp_send6__unsocketed_no_netif());