mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
735 lines
19 KiB
C
735 lines
19 KiB
C
/*
|
|
* Copyright (C) 2016 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
*
|
|
* @file
|
|
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include "lwip/sock_internal.h"
|
|
|
|
#include "net/af.h"
|
|
#include "net/ipv4/addr.h"
|
|
#include "net/ipv6/addr.h"
|
|
#include "net/sock.h"
|
|
#include "timex.h"
|
|
|
|
#include "lwip/err.h"
|
|
#include "lwip/ip.h"
|
|
#include "lwip/netif.h"
|
|
#include "lwip/tcp.h"
|
|
#include "lwip/netif.h"
|
|
#include "lwip/opt.h"
|
|
|
|
#include "sock_types.h"
|
|
|
|
#if !LWIP_IPV4 && !LWIP_IPV6
|
|
#error "lwip_sock needs IPv4 or IPv6 support"
|
|
#endif
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/**
|
|
* @brief Checks if an address family is *not* supported by the lwIP
|
|
* implementation
|
|
*
|
|
* @param[in] af An address family
|
|
*
|
|
* @return true, if @p af is *not* supported.
|
|
* @return false, if @p af is supported.
|
|
*/
|
|
static inline bool _af_not_supported(int af)
|
|
{
|
|
switch (af) {
|
|
#if LWIP_IPV4
|
|
case AF_INET:
|
|
return false;
|
|
#endif
|
|
#if LWIP_IPV6
|
|
case AF_INET6:
|
|
return false;
|
|
#endif
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#if LWIP_IPV4 && LWIP_IPV6
|
|
static inline u8_t lwip_af_to_ip_addr_type(int af)
|
|
{
|
|
switch (af) {
|
|
case AF_INET:
|
|
return IPADDR_TYPE_V4;
|
|
case AF_INET6:
|
|
case AF_UNSPEC: /* in case of any address */
|
|
return IPADDR_TYPE_V6;
|
|
default:
|
|
return 0xff;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static bool _ep_isany(const struct _sock_tl_ep *ep)
|
|
{
|
|
uint8_t *ep_addr;
|
|
|
|
if (ep == NULL) {
|
|
return true;
|
|
}
|
|
|
|
ep_addr = (uint8_t *)&ep->addr;
|
|
for (unsigned i = 0; i < sizeof(ep->addr); i++) {
|
|
#if LWIP_IPV4
|
|
/* stop checking IPv6 for IPv4 addresses */
|
|
if ((ep->family == AF_INET) && i >= sizeof(ep->addr.ipv4)) {
|
|
break;
|
|
}
|
|
#endif
|
|
if (ep_addr[i] != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static const ip_addr_t *_netif_to_bind_addr(int family, uint16_t netif_num)
|
|
{
|
|
struct netif *netif;
|
|
if (netif_num > UINT8_MAX) {
|
|
return NULL;
|
|
}
|
|
|
|
/* cppcheck-suppress uninitvar ; assigned by macro */
|
|
NETIF_FOREACH(netif) {
|
|
if (netif->num == (netif_num - 1)) {
|
|
switch (family) {
|
|
#if LWIP_IPV4
|
|
case AF_INET:
|
|
return &netif->ip_addr;
|
|
#endif
|
|
#if LWIP_IPV6
|
|
case AF_INET6:
|
|
/* link-local address is always the 0th */
|
|
return &netif->ip6_addr[0];
|
|
#endif
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool _addr_on_netif(int family, int netif_num, const ip_addr_t *addr)
|
|
{
|
|
struct netif *netif;
|
|
assert(addr != NULL);
|
|
assert((netif_num >= 0) && (netif_num <= UINT8_MAX));
|
|
/* cppcheck-suppress uninitvar ; assigned by macro */
|
|
NETIF_FOREACH(netif) {
|
|
if (netif->num == (netif_num - 1)) {
|
|
switch (family) {
|
|
#if LWIP_IPV4
|
|
case AF_INET:
|
|
return ip_2_ip4(&netif->ip_addr)->addr == ip_2_ip4(addr)->addr;
|
|
#endif
|
|
#if LWIP_IPV6
|
|
case AF_INET6: {
|
|
LOCK_TCPIP_CORE();
|
|
/* link-local address is always the 0th */
|
|
s8_t match = netif_get_ip6_addr_match(netif, ip_2_ip6(addr));
|
|
UNLOCK_TCPIP_CORE();
|
|
return match >= 0;
|
|
}
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#ifdef MODULE_LWIP_SOCK_IP
|
|
#define _set_port(p, ep, type) \
|
|
if (!((type) & NETCONN_RAW)) { \
|
|
p = (ep)->port; \
|
|
}
|
|
#else
|
|
#define _set_port(p, ep, type) \
|
|
p = (ep)->port;
|
|
#endif
|
|
|
|
static void _convert_ip_addr(ip_addr_t *lwip_addr, int family,
|
|
const void *sock_addr, size_t sock_addr_size)
|
|
{
|
|
memcpy(lwip_addr, sock_addr, sock_addr_size);
|
|
#if LWIP_IPV6 && LWIP_IPV4
|
|
if (family == AF_INET6) {
|
|
ip6_addr_clear_zone(&lwip_addr->u_addr.ip6);
|
|
}
|
|
|
|
lwip_addr->type = lwip_af_to_ip_addr_type(family);
|
|
#elif LWIP_IPV6
|
|
(void)family;
|
|
ip6_addr_clear_zone(lwip_addr);
|
|
#else
|
|
(void)family;
|
|
#endif
|
|
}
|
|
|
|
static int _sock_ep_to_netconn_pars(const struct _sock_tl_ep *local,
|
|
const struct _sock_tl_ep *remote,
|
|
ip_addr_t *local_addr, u16_t *local_port,
|
|
ip_addr_t *remote_addr, u16_t *remote_port,
|
|
int *type)
|
|
{
|
|
bool res = 0;
|
|
int family = AF_UNSPEC;
|
|
uint16_t netif = SOCK_ADDR_ANY_NETIF;
|
|
|
|
if ((local != NULL) && (remote != NULL) &&
|
|
(remote->netif != SOCK_ADDR_ANY_NETIF) &&
|
|
(local->netif != SOCK_ADDR_ANY_NETIF) &&
|
|
(remote->netif != local->netif)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if LWIP_IPV6
|
|
*type &= ~NETCONN_TYPE_IPV6;
|
|
#else
|
|
(void)type; /* is read but not set => compiler complains */
|
|
#endif
|
|
if (local != NULL) {
|
|
if (_af_not_supported(local->family)) {
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
|
|
if (local->netif != SOCK_ADDR_ANY_NETIF) {
|
|
netif = local->netif;
|
|
}
|
|
|
|
family = local->family;
|
|
_set_port(*local_port, local, *type);
|
|
}
|
|
if (remote != NULL) {
|
|
if (_af_not_supported(remote->family) ||
|
|
((local != NULL) && (local->family != remote->family))) {
|
|
return -EAFNOSUPPORT;
|
|
}
|
|
|
|
if ((remote->netif != SOCK_ADDR_ANY_NETIF) &&
|
|
(local != NULL) && (local->netif != SOCK_ADDR_ANY_NETIF)) {
|
|
netif = remote->netif;
|
|
}
|
|
|
|
family = remote->family;
|
|
_convert_ip_addr(remote_addr, family, &remote->addr,
|
|
sizeof(remote->addr));
|
|
|
|
if (ip_addr_isany(remote_addr)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
_set_port(*remote_port, remote, *type);
|
|
}
|
|
|
|
#if LWIP_IPV6
|
|
if (family == AF_INET6) {
|
|
*type |= NETCONN_TYPE_IPV6;
|
|
}
|
|
#endif
|
|
|
|
if (netif != SOCK_ADDR_ANY_NETIF) {
|
|
if (_ep_isany(local)) {
|
|
const ip_addr_t *tmp = _netif_to_bind_addr(family, netif);
|
|
if (tmp != NULL) {
|
|
memcpy(local_addr, tmp, sizeof(ip_addr_t));
|
|
res = 1;
|
|
}
|
|
else {
|
|
/* netif was not a valid interface */
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
/* case (local == NULL) is included in _ep_isany() */
|
|
/* cast to ip_addr_t alright, since type field is never used */
|
|
else if (_addr_on_netif(family, netif, (ip_addr_t *)&local->addr)) {
|
|
_convert_ip_addr(local_addr, family, &local->addr,
|
|
sizeof(local->addr));
|
|
res = 1;
|
|
}
|
|
else {
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
else if (local != NULL) {
|
|
_convert_ip_addr(local_addr, family, &local->addr, sizeof(local->addr));
|
|
res = 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void _netconn_cb(struct netconn *conn, enum netconn_evt evt,
|
|
u16_t len)
|
|
{
|
|
#if IS_ACTIVE(SOCK_HAS_ASYNC)
|
|
lwip_sock_base_t *sock = netconn_get_callback_arg(conn);
|
|
|
|
if (sock && conn->pcb.raw &&
|
|
/* lwIP's TCP implementation initializes callback_arg.socket with -1
|
|
* when not provided */
|
|
(conn->callback_arg.socket != -1)) {
|
|
sock_async_flags_t flags = 0;
|
|
|
|
(void)len;
|
|
switch (evt) {
|
|
case NETCONN_EVT_RCVPLUS:
|
|
if (LWIP_TCP && (conn->type & NETCONN_TCP)) {
|
|
#if LWIP_TCP /* additional guard needed due to dependent member access */
|
|
switch (conn->pcb.tcp->state) {
|
|
case CLOSED:
|
|
case CLOSE_WAIT:
|
|
case CLOSING:
|
|
flags |= SOCK_ASYNC_CONN_FIN;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (mbox_avail(&conn->acceptmbox.mbox)) {
|
|
flags |= SOCK_ASYNC_CONN_RECV;
|
|
}
|
|
|
|
if (mbox_avail(&conn->recvmbox.mbox)) {
|
|
flags |= SOCK_ASYNC_MSG_RECV;
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
flags |= SOCK_ASYNC_MSG_RECV;
|
|
}
|
|
break;
|
|
case NETCONN_EVT_SENDPLUS:
|
|
flags |= SOCK_ASYNC_MSG_SENT;
|
|
break;
|
|
case NETCONN_EVT_ERROR:
|
|
if (LWIP_TCP && (conn->type & NETCONN_TCP)) {
|
|
/* try to report this */
|
|
flags |= SOCK_ASYNC_CONN_FIN;
|
|
}
|
|
break;
|
|
case NETCONN_EVT_RCVMINUS:
|
|
case NETCONN_EVT_SENDMINUS:
|
|
break;
|
|
default:
|
|
LWIP_ASSERT("unknown event", 0);
|
|
break;
|
|
}
|
|
|
|
if (flags && sock->async_cb.gen) {
|
|
sock->async_cb.gen(sock, flags, sock->async_cb_arg);
|
|
}
|
|
}
|
|
#else
|
|
(void)conn;
|
|
(void)evt;
|
|
(void)len;
|
|
#endif
|
|
}
|
|
|
|
static int _create(int type, int proto, uint16_t flags, struct netconn **out)
|
|
{
|
|
if ((*out = netconn_new_with_proto_and_callback(
|
|
type, proto,
|
|
IS_ACTIVE(SOCK_HAS_ASYNC) ? _netconn_cb : NULL)) == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
netconn_set_callback_arg(*out, NULL);
|
|
#if SO_REUSE
|
|
if (flags & SOCK_FLAGS_REUSE_EP) {
|
|
ip_set_option((*out)->pcb.ip, SOF_REUSEADDR);
|
|
}
|
|
#else
|
|
(void)flags;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int lwip_sock_create(struct netconn **conn, const struct _sock_tl_ep *local,
|
|
const struct _sock_tl_ep *remote, int proto,
|
|
uint16_t flags, int type)
|
|
{
|
|
assert(conn != NULL);
|
|
#if LWIP_IPV6
|
|
assert(!(type & NETCONN_TYPE_IPV6));
|
|
#endif
|
|
|
|
ip_addr_t local_addr, remote_addr;
|
|
u16_t local_port = 0, remote_port = 0; /* 0 is used by lwIP to
|
|
* automatically generate
|
|
* port */
|
|
/* convert parameters */
|
|
int bind = _sock_ep_to_netconn_pars(local, remote, &local_addr, &local_port,
|
|
&remote_addr, &remote_port, &type);
|
|
|
|
/* error occurred during parameter conversion */
|
|
if (bind < 0) {
|
|
return bind;
|
|
}
|
|
|
|
if ((remote != NULL) && ip_addr_isany_val(remote_addr)) {
|
|
return -EINVAL;
|
|
}
|
|
/* if local or remote parameters are given */
|
|
else if ((local != NULL) || (remote != NULL)) {
|
|
int res = 0;
|
|
|
|
if ((res = _create(type, proto, flags, conn)) < 0) {
|
|
return res;
|
|
}
|
|
|
|
/* if parameters (local->netif, remote->netif, local->addr or
|
|
* local->port) demand binding */
|
|
if (bind) {
|
|
switch (netconn_bind(*conn, &local_addr, local_port)) {
|
|
#if LWIP_TCP
|
|
case ERR_BUF:
|
|
res = -ENOMEM;
|
|
break;
|
|
#endif
|
|
case ERR_USE:
|
|
res = -EADDRINUSE;
|
|
break;
|
|
case ERR_VAL:
|
|
res = -EINVAL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (res < 0) {
|
|
netconn_delete(*conn);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
if (remote != NULL) {
|
|
switch (netconn_connect(*conn, &remote_addr, remote_port)) {
|
|
#if LWIP_TCP
|
|
case ERR_BUF:
|
|
res = -ENOMEM;
|
|
break;
|
|
case ERR_INPROGRESS:
|
|
res = -EINPROGRESS;
|
|
break;
|
|
case ERR_ISCONN:
|
|
res = -EISCONN;
|
|
break;
|
|
case ERR_IF:
|
|
case ERR_RTE:
|
|
res = -ENETUNREACH;
|
|
break;
|
|
case ERR_ABRT:
|
|
res = -ETIMEDOUT;
|
|
break;
|
|
#endif
|
|
case ERR_USE:
|
|
res = -EADDRINUSE;
|
|
break;
|
|
case ERR_VAL:
|
|
res = -EINVAL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (res < 0) {
|
|
netconn_delete(*conn);
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint16_t lwip_sock_bind_addr_to_netif(const ip_addr_t *bind_addr)
|
|
{
|
|
assert(bind_addr != NULL);
|
|
|
|
if (!ip_addr_isany(bind_addr)) {
|
|
struct netif *netif;
|
|
LOCK_TCPIP_CORE();
|
|
/* cppcheck-suppress uninitvar ; assigned by macro */
|
|
NETIF_FOREACH(netif) {
|
|
if (IP_IS_V6(bind_addr)) { /* XXX crappy API yields crappy code */
|
|
#if LWIP_IPV6
|
|
if (netif_get_ip6_addr_match(netif, ip_2_ip6(bind_addr)) >= 0) {
|
|
UNLOCK_TCPIP_CORE();
|
|
return (int)netif->num + 1;
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
#if LWIP_IPV4
|
|
if (netif_ip4_addr(netif)->addr == ip_2_ip4(bind_addr)->addr) {
|
|
UNLOCK_TCPIP_CORE();
|
|
return (int)netif->num + 1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
UNLOCK_TCPIP_CORE();
|
|
}
|
|
|
|
return SOCK_ADDR_ANY_NETIF;
|
|
}
|
|
|
|
int lwip_sock_get_addr(struct netconn *conn, struct _sock_tl_ep *ep, u8_t local)
|
|
{
|
|
ip_addr_t addr;
|
|
int res;
|
|
#ifdef MODULE_LWIP_SOCK_IP
|
|
u16_t port = UINT16_MAX;
|
|
u16_t *port_ptr = &port;
|
|
/* addr needs to be NULL because netconn_getaddr returns error on connected
|
|
* conns as "as connecting is only a helper for upper layers [sic]" */
|
|
memset(&addr, 0, sizeof(addr));
|
|
#else
|
|
u16_t *port_ptr = &ep->port;
|
|
#endif
|
|
|
|
assert(ep != NULL);
|
|
if (conn == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
#ifdef MODULE_LWIP_SOCK_IP
|
|
if (!(conn->type & NETCONN_RAW)) {
|
|
port_ptr = &ep->port;
|
|
}
|
|
#endif
|
|
if ((res = netconn_getaddr(conn, &addr, port_ptr, local)) != ERR_OK
|
|
#ifdef MODULE_LWIP_SOCK_IP
|
|
/* XXX lwIP's API is very inconsistent here so we need to check if addr
|
|
* was changed */
|
|
&& !local && ip_addr_isany_val(addr)
|
|
#endif
|
|
) {
|
|
return res;
|
|
}
|
|
|
|
if (NETCONNTYPE_ISIPV6(conn->type)) {
|
|
ep->family = AF_INET6;
|
|
}
|
|
else if (IS_ACTIVE(LWIP_IPV4)) {
|
|
ep->family = AF_INET;
|
|
}
|
|
else {
|
|
ep->family = AF_UNSPEC;
|
|
}
|
|
|
|
if (local) {
|
|
ep->netif = lwip_sock_bind_addr_to_netif(&addr);
|
|
}
|
|
else {
|
|
ep->netif = SOCK_ADDR_ANY_NETIF;
|
|
}
|
|
|
|
memcpy(&ep->addr, &addr, sizeof(ep->addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP)
|
|
int lwip_sock_recv(struct netconn *conn, uint32_t timeout, struct netbuf **buf)
|
|
{
|
|
int res;
|
|
|
|
if (conn == NULL) {
|
|
return -EADDRNOTAVAIL;
|
|
}
|
|
|
|
#if LWIP_SO_RCVTIMEO
|
|
if ((timeout != 0) && (timeout != SOCK_NO_TIMEOUT)) {
|
|
netconn_set_recvtimeout(conn, timeout / US_PER_MS);
|
|
}
|
|
else
|
|
#endif
|
|
if ((timeout == 0) && !mbox_avail(&conn->recvmbox.mbox)) {
|
|
return -EAGAIN;
|
|
}
|
|
|
|
switch (netconn_recv(conn, buf)) {
|
|
case ERR_OK:
|
|
res = 0;
|
|
break;
|
|
#if LWIP_SO_RCVTIMEO
|
|
case ERR_TIMEOUT:
|
|
res = -ETIMEDOUT;
|
|
break;
|
|
#endif
|
|
case ERR_MEM:
|
|
res = -ENOMEM;
|
|
break;
|
|
default:
|
|
res = -EPROTO;
|
|
break;
|
|
}
|
|
|
|
/* unset flags */
|
|
#if LWIP_SO_RCVTIMEO
|
|
netconn_set_recvtimeout(conn, 0);
|
|
#endif
|
|
#if IS_ACTIVE(SOCK_HAS_ASYNC)
|
|
lwip_sock_base_t *sock = netconn_get_callback_arg(conn);
|
|
|
|
if (sock && sock->async_cb.gen && mbox_avail(&conn->recvmbox.mbox)) {
|
|
sock->async_cb.gen(sock, SOCK_ASYNC_MSG_RECV, sock->async_cb_arg);
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
#endif /* defined(MODULE_LWIP_SOCK_UDP) || defined(MODULE_LWIP_SOCK_IP) */
|
|
|
|
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 = NULL;
|
|
size_t payload_len = 0;
|
|
int res;
|
|
err_t err= ERR_OK;
|
|
u16_t remote_port = 0;
|
|
|
|
#if LWIP_IPV6
|
|
assert(!(type & NETCONN_TYPE_IPV6));
|
|
#endif
|
|
if (remote != NULL) {
|
|
if ((res = _sock_ep_to_netconn_pars(NULL, remote, NULL, NULL,
|
|
&remote_addr, &remote_port,
|
|
&type)) < 0) {
|
|
return res;
|
|
}
|
|
|
|
if (ip_addr_isany_val(remote_addr)) {
|
|
DEBUG("[lwip_sock_sendv] remote_addr is all zero\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
buf = netbuf_new();
|
|
|
|
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);
|
|
return res;
|
|
}
|
|
}
|
|
else if (conn != NULL) {
|
|
ip_addr_t addr;
|
|
u16_t port;
|
|
|
|
if ((remote != NULL)
|
|
&& (remote->netif != SOCK_ADDR_ANY_NETIF)
|
|
&& (netconn_getaddr(conn, &addr, &port, 1) == 0)) {
|
|
if (IS_ACTIVE(ENABLE_DEBUG)) {
|
|
DEBUG("[lwip_sock_sendv] sending from: \"");
|
|
/* lwip's ip_addr_debug triggers a -Waddress warning when
|
|
* compiled with the ESP toolchain. In absence of a upstream
|
|
* solution, we just disable the warning here. */
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Waddress"
|
|
ip_addr_debug_print(LWIP_DBG_ON, &addr);
|
|
#pragma GCC diagnostic pop
|
|
DEBUG("\"\n");
|
|
}
|
|
uint16_t netif = lwip_sock_bind_addr_to_netif(&addr);
|
|
if ((remote->netif != netif)
|
|
&& (netif != SOCK_ADDR_ANY_NETIF)) {
|
|
DEBUG("[lwip_sock_sendv] lwip_sock_bind_addr_to_netif() "
|
|
"returned %u, but expected %u\n",
|
|
(unsigned)netif, (unsigned)remote->netif);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
tmp = conn;
|
|
}
|
|
else {
|
|
netbuf_delete(buf);
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
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, buf->p->payload, buf->p->len, 0, (size_t *)(&res));
|
|
}
|
|
#endif /* LWIP_TCP */
|
|
else {
|
|
err = netconn_send(tmp, buf);
|
|
}
|
|
|
|
switch (err) {
|
|
case ERR_OK:
|
|
break;
|
|
case ERR_BUF:
|
|
case ERR_MEM:
|
|
res = -ENOMEM;
|
|
break;
|
|
case ERR_RTE:
|
|
case ERR_IF:
|
|
res = -EHOSTUNREACH;
|
|
break;
|
|
case ERR_VAL:
|
|
default:
|
|
res = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
netbuf_delete(buf);
|
|
|
|
if (conn == NULL) {
|
|
netconn_delete(tmp);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/** @} */
|