You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
14 KiB

/*
* 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 "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/opt.h"
#if !LWIP_IPV4 && !LWIP_IPV6
#error "lwip_sock needs IPv4 or IPv6 support"
#endif
/**
* @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)
{
if (netif_num > UINT8_MAX) {
return NULL;
}
for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
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)
{
assert(addr != NULL);
assert((netif_num >= 0) && (netif_num <= UINT8_MAX));
for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
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:
/* link-local address is always the 0th */
return (netif_get_ip6_addr_match(netif, ip_2_ip6(addr)) >= 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 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;
memcpy(remote_addr, &remote->addr, sizeof(remote->addr));
#if LWIP_IPV6 && LWIP_IPV4
remote_addr->type = lwip_af_to_ip_addr_type(family);
#endif
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)) {
memcpy(local_addr, &local->addr, sizeof(local->addr));
res = 1;
}
else {
return -EINVAL;
}
}
else if (local != NULL) {
memcpy(local_addr, &local->addr, sizeof(local->addr));
res = 1;
}
#if LWIP_IPV6 && LWIP_IPV4
if (local_addr != NULL) {
local_addr->type = lwip_af_to_ip_addr_type(family);
}
#endif
return res;
}
static int _create(int type, int proto, uint16_t flags, struct netconn **out)
{
if ((*out = netconn_new_with_proto_and_callback(type, proto, NULL)) == NULL) {
return -ENOMEM;
}
#if SO_REUSE
if (flags & SOCK_FLAGS_REUSE_EP) {
ip_set_option((*out)->pcb.ip, SOF_REUSEADDR);
}
#else
(void)flags;
#endif
return 0;
}
#include <stdio.h>
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)) {
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)) {
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)) {
for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) {
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) {
return (int)netif->num + 1;
}
#endif
}
else {
#if LWIP_IPV4
if (netif_ip4_addr(netif)->addr == ip_2_ip4(bind_addr)->addr) {
return (int)netif->num + 1;
}
#endif
}
}
}
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 LWIP_IPV6 && LWIP_IPV4
ep->family = (addr.type == IPADDR_TYPE_V6) ? AF_INET6 : AF_INET;
#elif LWIP_IPV6
ep->family = (conn->type & NETCONN_TYPE_IPV6) ? AF_INET6 : AF_INET;
#elif LWIP_IPV4
ep->family = AF_INET;
#endif
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;
}
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) && !cib_avail(&conn->recvmbox.mbox.cib)) {
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
return res;
}
ssize_t lwip_sock_send(struct netconn **conn, const void *data, size_t len,
int proto, const struct _sock_tl_ep *remote, int type)
{
ip_addr_t remote_addr;
struct netconn *tmp;
struct netbuf *buf;
int res;
err_t err;
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)) {
return -EINVAL;
}
}
buf = netbuf_new();
if ((buf == NULL) || (netbuf_alloc(buf, len) == NULL) ||
(netbuf_take(buf, data, len) != ERR_OK)) {
netbuf_delete(buf);
return -ENOMEM;
}
if (((conn == NULL) || (*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) &&
(remote->netif != lwip_sock_bind_addr_to_netif(&addr)))) {
return -EINVAL;
}
tmp = *conn;
}
else {
netbuf_delete(buf);
return -ENOTCONN;
}
if (remote != NULL) {
err = netconn_sendto(tmp, buf, &remote_addr, remote_port);
}
else {
err = netconn_send(tmp, buf);
}
switch (err) {
case ERR_OK:
res = len;
if (conn != NULL) {
*conn = tmp;
}
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);
return res;
}
/** @} */