#include "lwip/tcp.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
-#include "lwip/tcpip.h"
+#include "lwip/memp.h"
#include "lwip/pbuf.h"
+#include "lwip/priv/tcpip_priv.h"
#if LWIP_CHECKSUM_ON_COPY
#include "lwip/inet_chksum.h"
#endif
#include <string.h>
-#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \
+/* If the netconn API is not required publicly, then we include the necessary
+ files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
(sin)->sin_len = sizeof(struct sockaddr_in); \
(sin)->sin_family = AF_INET; \
(sin)->sin_port = htons((port)); \
- inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \
+ inet_addr_from_ipaddr(&(sin)->sin_addr, ipaddr); \
memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
-#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \
- inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+ inet_addr_to_ipaddr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
(port) = ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+ (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+ (sin6)->sin6_family = AF_INET6; \
+ (sin6)->sin6_port = htons((port)); \
+ (sin6)->sin6_flowinfo = 0; \
+ inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+ (sin6)->sin6_scope_id = 0; }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+ inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+ (port) = ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port);
+
#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
((namelen) == sizeof(struct sockaddr_in6)))
#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
#define SOCK_ADDR_TYPE_MATCH(name, sock) \
((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
(((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
-#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \
- (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
- (sin6)->sin6_family = AF_INET6; \
- (sin6)->sin6_port = htons((port)); \
- (sin6)->sin6_flowinfo = 0; \
- inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0)
-#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \
- if (isipv6) { \
- IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
- } else { \
- IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \
- } } while(0)
-#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \
- inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \
- (port) = ntohs((sin6)->sin6_port); }while(0)
-#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \
- if (isipv6) { \
- SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+ if (IP_IS_V6(ipaddr)) { \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
} else { \
- SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
} } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
(type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
-#else /* LWIP_IPV6 */
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
-#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \
- IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
-#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \
- SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
#endif /* LWIP_IPV6 */
#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+ if ((sock)->conn == NULL) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+ if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+ if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+ name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+ if (name == NULL) { \
+ sock_set_errno(sock, ENOMEM); \
+ return -1; \
+ } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
+ s32_t loc = (val); \
+ ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
+ ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
+#endif
#define NUM_SOCKETS MEMP_NUM_NETCONN
+/** This is overridable for the rare case where more than 255 threads
+ * select on the same socket...
+ */
+#ifndef SELWAIT_T
+#define SELWAIT_T u8_t
+#endif
+
/** Contains all internal pointers and states used for a socket */
struct lwip_sock {
/** sockets currently are built on netconns, each socket has one netconn */
tested by select */
u16_t sendevent;
/** error happened for this socket, set by event_callback(), tested by select */
- u16_t errevent;
- /** last error that occurred on this socket */
- int err;
+ u16_t errevent;
+ /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */
+ u8_t err;
/** counter of how many threads are waiting for this socket using select */
- int select_waiting;
+ SELWAIT_T select_waiting;
};
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define SELECT_SEM_T sys_sem_t*
+#define SELECT_SEM_PTR(sem) (sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define SELECT_SEM_T sys_sem_t
+#define SELECT_SEM_PTR(sem) (&(sem))
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
/** Description for a task waiting in select */
struct lwip_select_cb {
/** Pointer to the next waiting task */
/** don't signal the same semaphore twice: set to 1 when signalled */
int sem_signalled;
/** semaphore to wake up a task waiting for select */
- sys_sem_t sem;
-};
-
-/** This struct is used to pass data to the set/getsockopt_internal
- * functions running in tcpip_thread context (only a void* is allowed) */
-struct lwip_setgetsockopt_data {
- /** socket struct for which to change options */
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- /** socket index for which to change options */
- int s;
-#endif /* LWIP_DEBUG */
- /** level of the option to process */
- int level;
- /** name of the option to process */
- int optname;
- /** set: value to set the option to
- * get: value of the option is stored here */
- void *optval;
- /** size of *optval */
- socklen_t *optlen;
- /** if an error occures, it is temporarily stored here */
- err_t err;
+ SELECT_SEM_T sem;
};
/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
#if LWIP_IPV6
struct sockaddr_in6 sin6;
#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+#if LWIP_IGMP
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+ a socket is closed */
+struct lwip_socket_multicast_pair {
+ /** the socket (+1 to not require initialization) */
+ int sa;
+ /** the interface address */
+ ip4_addr_t if_addr;
+ /** the group address */
+ ip4_addr_t multi_addr;
};
+struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
/** The global array of available sockets */
static struct lwip_sock sockets[NUM_SOCKETS];
/** The global list of tasks waiting for select */
static struct lwip_select_cb *select_cb_list;
-/** This counter is increased from lwip_select when the list is chagned
+/** This counter is increased from lwip_select when the list is changed
and checked in event_callback to see if it has changed. */
static volatile int select_cb_ctr;
EINVAL, /* ERR_VAL -6 Illegal value. */
EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
EADDRINUSE, /* ERR_USE -8 Address in use. */
- EALREADY, /* ERR_ISCONN -9 Already connected. */
- ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
- ECONNRESET, /* ERR_RST -11 Connection reset. */
- ENOTCONN, /* ERR_CLSD -12 Connection closed. */
- ENOTCONN, /* ERR_CONN -13 Not connected. */
- EIO, /* ERR_ARG -14 Illegal argument. */
- -1, /* ERR_IF -15 Low-level netif error */
+ EALREADY, /* ERR_ALREADY -9 Already connecting. */
+ EISCONN, /* ERR_ISCONN -10 Conn already established.*/
+ ENOTCONN, /* ERR_CONN -11 Not connected. */
+ ECONNABORTED, /* ERR_ABRT -12 Connection aborted. */
+ ECONNRESET, /* ERR_RST -13 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -14 Connection closed. */
+ EIO, /* ERR_ARG -15 Illegal argument. */
+ -1, /* ERR_IF -16 Low-level netif error */
};
-#define ERR_TO_ERRNO_TABLE_SIZE \
- (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+#define ERR_TO_ERRNO_TABLE_SIZE LWIP_ARRAYSIZE(err_to_errno_table)
#define err_to_errno(err) \
((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
err_to_errno_table[-(err)] : EIO)
-#ifdef ERRNO
+#if LWIP_SOCKET_SET_ERRNO
#ifndef set_errno
-#define set_errno(err) errno = (err)
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
#endif
-#else /* ERRNO */
+#else /* LWIP_SOCKET_SET_ERRNO */
#define set_errno(err)
-#endif /* ERRNO */
+#endif /* LWIP_SOCKET_SET_ERRNO */
#define sock_set_errno(sk, e) do { \
- sk->err = (e); \
- set_errno(sk->err); \
+ const int sockerr = (e); \
+ sk->err = (u8_t)sockerr; \
+ set_errno(sockerr); \
} while (0)
-/* Forward delcaration of some functions */
+/* Forward declaration of some functions */
static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
-static void lwip_getsockopt_internal(void *arg);
-static void lwip_setsockopt_internal(void *arg);
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
-/**
- * Initialize this module. This function has to be called before any other
- * functions in this module!
- */
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)
+{
+ if ((sockaddr->sa_family) == AF_INET6) {
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V6;
+ } else {
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V4;
+ }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+ netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
void
-lwip_socket_init(void)
+lwip_socket_thread_cleanup(void)
{
+ netconn_thread_cleanup();
}
/**
{
struct lwip_sock *sock;
+ s -= LWIP_SOCKET_OFFSET;
+
if ((s < 0) || (s >= NUM_SOCKETS)) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET));
set_errno(EBADF);
return NULL;
}
sock = &sockets[s];
if (!sock->conn) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET));
set_errno(EBADF);
return NULL;
}
static struct lwip_sock *
tryget_socket(int s)
{
+ s -= LWIP_SOCKET_OFFSET;
if ((s < 0) || (s >= NUM_SOCKETS)) {
return NULL;
}
sockets[i].errevent = 0;
sockets[i].err = 0;
sockets[i].select_waiting = 0;
- return i;
+ return i + LWIP_SOCKET_OFFSET;
}
SYS_ARCH_UNPROTECT(lev);
}
{
struct lwip_sock *sock, *nsock;
struct netconn *newconn;
- ipX_addr_t naddr;
- u16_t port;
+ ip_addr_t naddr;
+ u16_t port = 0;
int newsock;
err_t err;
SYS_ARCH_DECL_PROTECT(lev);
/* Prevent automatic window updates, we do this on our own! */
netconn_set_noautorecved(newconn, 1);
- /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
- * not be NULL if addr is valid.
- */
- if (addr != NULL) {
- union sockaddr_aligned tempaddr;
- /* get the IP address and port of the remote host */
- err = netconn_peer(newconn, ipX_2_ip(&naddr), &port);
- if (err != ERR_OK) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
- netconn_delete(newconn);
- sock_set_errno(sock, err_to_errno(err));
- return -1;
- }
- LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
-
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port);
- if (*addrlen > tempaddr.sa.sa_len) {
- *addrlen = tempaddr.sa.sa_len;
- }
- MEMCPY(addr, &tempaddr, *addrlen);
- }
-
newsock = alloc_socket(newconn, 1);
if (newsock == -1) {
netconn_delete(newconn);
sock_set_errno(sock, ENFILE);
return -1;
}
- LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
+ LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
- nsock = &sockets[newsock];
+ nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
/* See event_callback: If data comes in right away after an accept, even
* though the server task might not have created a new socket yet.
newconn->socket = newsock;
SYS_ARCH_UNPROTECT(lev);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
if (addr != NULL) {
- LWIP_DEBUGF(SOCKETS_DEBUG, (" addr="));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr);
+ union sockaddr_aligned tempaddr;
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, &naddr, &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+
+ IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+ if (*addrlen > tempaddr.sa.sa_len) {
+ *addrlen = tempaddr.sa.sa_len;
+ }
+ MEMCPY(addr, &tempaddr, *addrlen);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+ } else {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
}
sock_set_errno(sock, 0);
lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
{
struct lwip_sock *sock;
- ipX_addr_t local_addr;
+ ip_addr_t local_addr;
u16_t local_port;
err_t err;
return -1;
}
- /* check size, familiy and alignment of 'name' */
+ /* check size, family and alignment of 'name' */
LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
LWIP_UNUSED_ARG(namelen);
- SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port);
+ SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
- ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
- err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port);
+ err = netconn_bind(sock->conn, &local_addr, local_port);
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
{
struct lwip_sock *sock;
int is_tcp = 0;
+ err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
return -1;
}
- if(sock->conn != NULL) {
+ if (sock->conn != NULL) {
is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
} else {
LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
}
- netconn_delete(sock->conn);
+#if LWIP_IGMP
+ /* drop all possibly joined IGMP memberships */
+ lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+
+ err = netconn_delete(sock->conn);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
free_socket(sock, is_tcp);
set_errno(0);
if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
/* sockaddr does not match socket type (IPv4/IPv6) */
- sock_set_errno(sock, err_to_errno(ERR_VAL));
- return -1;
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
}
- /* check size, familiy and alignment of 'name' */
- LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
- IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
LWIP_UNUSED_ARG(namelen);
if (name->sa_family == AF_UNSPEC) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
err = netconn_disconnect(sock->conn);
} else {
- ipX_addr_t remote_addr;
+ ip_addr_t remote_addr;
u16_t remote_port;
- SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port);
+
+ /* check size, family and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
- ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
- err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port);
+ err = netconn_connect(sock->conn, &remote_addr, remote_port);
}
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return EOPNOTSUPP;
+ return -1;
}
sock_set_errno(sock, err_to_errno(err));
return -1;
buf = sock->lastdata;
} else {
/* If this is non-blocking call, then check first */
- if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+ if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
(sock->rcvevent <= 0)) {
if (off > 0) {
/* update receive window */
if (off > 0) {
/* update receive window */
netconn_recved(sock->conn, (u32_t)off);
+ if (err == ERR_CLSD) {
+ /* closed but already received data, ensure select gets the FIN, too */
+ event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+ }
/* already received data, return that */
sock_set_errno(sock, 0);
return off;
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
len -= copylen;
- if ( (len <= 0) ||
- (p->flags & PBUF_FLAG_PUSH) ||
- (sock->rcvevent <= 0) ||
- ((flags & MSG_PEEK)!=0)) {
+ if ((len <= 0) ||
+ (p->flags & PBUF_FLAG_PUSH) ||
+ (sock->rcvevent <= 0) ||
+ ((flags & MSG_PEEK) != 0)) {
done = 1;
}
} else {
#endif /* !SOCKETS_DEBUG */
{
u16_t port;
- ipX_addr_t tmpaddr;
- ipX_addr_t *fromaddr;
+ ip_addr_t tmpaddr;
+ ip_addr_t *fromaddr;
union sockaddr_aligned saddr;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
fromaddr = &tmpaddr;
- /* @todo: this does not work for IPv6, yet */
- netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0);
+ netconn_getaddr(sock->conn, fromaddr, &port, 0);
} else {
port = netbuf_fromport((struct netbuf *)buf);
- fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf);
+ fromaddr = netbuf_fromaddr((struct netbuf *)buf);
}
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- &saddr, fromaddr, port);
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, fromaddr);
+ IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+ ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
- if (*fromlen > saddr.sa.sa_len) {
- *fromlen = saddr.sa.sa_len;
+#if SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* SOCKETS_DEBUG */
+ {
+ if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
}
- MEMCPY(from, &saddr, *fromlen);
}
}
} else {
netbuf_delete((struct netbuf *)buf);
}
+ buf = NULL;
}
}
} while (!done);
- if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) {
+ if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) &&
+ ((flags & MSG_PEEK) == 0)) {
/* update receive window */
netconn_recved(sock->conn, (u32_t)off);
}
return (err == ERR_OK ? (int)written : -1);
}
+int
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ struct lwip_sock *sock;
+ struct netbuf *chain_buf;
+ u16_t remote_port;
+ int i;
+#if LWIP_TCP
+ u8_t write_flags;
+ size_t written;
+#endif
+ int size = 0;
+ err_t err = ERR_OK;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ LWIP_UNUSED_ARG(msg->msg_control);
+ LWIP_UNUSED_ARG(msg->msg_controllen);
+ LWIP_UNUSED_ARG(msg->msg_flags);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ written = 0;
+ err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
+ if (err == ERR_OK) {
+ size += written;
+ /* check that the entire IO vector was accepected, if not return a partial write */
+ if (written != msg->msg_iov[i].iov_len)
+ break;
+ }
+ /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
+ else if (err == ERR_WOULDBLOCK && size > 0) {
+ err = ERR_OK;
+ /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
+ break;
+ } else {
+ size = -1;
+ break;
+ }
+ }
+ sock_set_errno(sock, err_to_errno(err));
+ return size;
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+
+ LWIP_UNUSED_ARG(flags);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+ IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ /* initialize chain buffer with destination */
+ chain_buf = netbuf_new();
+ if (!chain_buf) {
+ sock_set_errno(sock, err_to_errno(ERR_MEM));
+ return -1;
+ }
+ if (msg->msg_name) {
+ SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port);
+ netbuf_fromport(chain_buf) = remote_port;
+ }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ size += msg->msg_iov[i].iov_len;
+ }
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) {
+ err = ERR_MEM;
+ }
+ else {
+ /* flatten the IO vectors */
+ size_t offset = 0;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+ offset += msg->msg_iov[i].iov_len;
+ }
+#if LWIP_CHECKSUM_ON_COPY
+ {
+ /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+ u16_t chksum = ~inet_chksum_pbuf(chain_buf->p);
+ netbuf_set_chksum(chain_buf, chksum);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ err = ERR_OK;
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* create a chained netbuf from the IO vectors */
+ err = netbuf_ref(chain_buf, msg->msg_iov[0].iov_base, (u16_t)msg->msg_iov[0].iov_len);
+ if (err == ERR_OK) {
+ struct netbuf *tail_buf;
+ size = msg->msg_iov[0].iov_len;
+ for (i = 1; i < msg->msg_iovlen; i++) {
+ tail_buf = netbuf_new();
+ if (!tail_buf) {
+ err = ERR_MEM;
+ break;
+ } else {
+ err = netbuf_ref(tail_buf, msg->msg_iov[i].iov_base, (u16_t)msg->msg_iov[i].iov_len);
+ if (err == ERR_OK) {
+ netbuf_chain(chain_buf, tail_buf);
+ size += msg->msg_iov[i].iov_len;
+ } else {
+ netbuf_delete(tail_buf);
+ break;
+ }
+ }
+ }
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (err == ERR_OK) {
+ /* send the data */
+ err = netconn_send(sock->conn, chain_buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_delete(chain_buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? size : -1);
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
int
lwip_sendto(int s, const void *data, size_t size, int flags,
const struct sockaddr *to, socklen_t tolen)
err_t err;
u16_t short_size;
u16_t remote_port;
-#if !LWIP_TCPIP_CORE_LOCKING
struct netbuf buf;
-#endif
sock = get_socket(s);
if (!sock) {
sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
LWIP_UNUSED_ARG(tolen);
-#if LWIP_TCPIP_CORE_LOCKING
- /* Special speedup for fast UDP/RAW sending: call the raw API directly
- instead of using the netconn functions. */
- {
- struct pbuf* p;
- ipX_addr_t *remote_addr;
- ipX_addr_t remote_addr_tmp;
-
-#if LWIP_NETIF_TX_SINGLE_PBUF
- p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
- if (p != NULL) {
-#if LWIP_CHECKSUM_ON_COPY
- u16_t chksum = 0;
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
- chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
- } else
-#endif /* LWIP_CHECKSUM_ON_COPY */
- MEMCPY(p->payload, data, size);
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
- p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
- if (p != NULL) {
- p->payload = (void*)data;
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-
- if (to != NULL) {
- SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6,
- to, &remote_addr_tmp, remote_port);
- remote_addr = &remote_addr_tmp;
- } else {
- remote_addr = &sock->conn->pcb.raw->remote_ip;
- if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_RAW) {
- remote_port = 0;
- } else {
- remote_port = sock->conn->pcb.udp->remote_port;
- }
- }
-
- LOCK_TCPIP_CORE();
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
- err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr));
- } else {
-#if LWIP_UDP
-#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
- err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
- ipX_2_ip(remote_addr), remote_port, 1, chksum);
-#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
- err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
- ipX_2_ip(remote_addr), remote_port);
-#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
-#else /* LWIP_UDP */
- err = ERR_ARG;
-#endif /* LWIP_UDP */
- }
- UNLOCK_TCPIP_CORE();
-
- pbuf_free(p);
- } else {
- err = ERR_MEM;
- }
- }
-#else /* LWIP_TCPIP_CORE_LOCKING */
/* initialize a buffer */
buf.p = buf.ptr = NULL;
#if LWIP_CHECKSUM_ON_COPY
buf.flags = 0;
#endif /* LWIP_CHECKSUM_ON_COPY */
if (to) {
- SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port);
+ SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
} else {
remote_port = 0;
- ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+ ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
}
netbuf_fromport(&buf) = remote_port;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
s, data, short_size, flags));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, &buf.addr);
+ ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
/* make the buffer point to the data that should be sent */
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
netbuf_set_chksum(&buf, chksum);
- err = ERR_OK;
} else
#endif /* LWIP_CHECKSUM_ON_COPY */
{
- err = netbuf_take(&buf, data, short_size);
+ MEMCPY(buf.p->payload, data, short_size);
}
+ err = ERR_OK;
}
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
err = netbuf_ref(&buf, data, short_size);
/* deallocated the buffer */
netbuf_free(&buf);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
+
sock_set_errno(sock, err_to_errno(err));
return (err == ERR_OK ? short_size : -1);
}
return lwip_send(s, data, size, 0);
}
+int
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+ struct msghdr msg;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+ Blame the opengroup standard for this inconsistency. */
+ msg.msg_iov = (struct iovec *)(size_t)iov;
+ msg.msg_iovlen = iovcnt;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return lwip_sendmsg(s, &msg, 0);
+}
+
/**
* Go through the readset and writeset lists and see which socket of the sockets
* set in the sets has events. On return, readset, writeset and exceptset have
/* Go through each socket in each list to count number of sockets which
currently match */
- for(i = 0; i < maxfdp1; i++) {
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
void* lastdata = NULL;
s16_t rcvevent = 0;
u16_t sendevent = 0;
return nready;
}
-/**
- * Processing exceptset is not yet implemented.
- */
int
lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
struct timeval *timeout)
fd_set lreadset, lwriteset, lexceptset;
u32_t msectimeout;
struct lwip_select_cb select_cb;
- err_t err;
int i;
+ int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
SYS_ARCH_DECL_PROTECT(lev);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
select_cb.writeset = writeset;
select_cb.exceptset = exceptset;
select_cb.sem_signalled = 0;
- err = sys_sem_new(&select_cb.sem, 0);
- if (err != ERR_OK) {
+#if LWIP_NETCONN_SEM_PER_THREAD
+ select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
/* failed to create semaphore */
set_errno(ENOMEM);
return -1;
}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
/* Protect the select_cb_list */
SYS_ARCH_PROTECT(lev);
select_cb_list->prev = &select_cb;
}
select_cb_list = &select_cb;
- /* Increasing this counter tells even_callback that the list has changed. */
+ /* Increasing this counter tells event_callback that the list has changed. */
select_cb_ctr++;
/* Now we can safely unprotect */
SYS_ARCH_UNPROTECT(lev);
/* Increase select_waiting for each socket we are interested in */
- for(i = 0; i < maxfdp1; i++) {
+ maxfdp2 = maxfdp1;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
if ((readset && FD_ISSET(i, readset)) ||
(writeset && FD_ISSET(i, writeset)) ||
(exceptset && FD_ISSET(i, exceptset))) {
- struct lwip_sock *sock = tryget_socket(i);
- LWIP_ASSERT("sock != NULL", sock != NULL);
+ struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev);
- sock->select_waiting++;
- LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ break;
+ }
SYS_ARCH_UNPROTECT(lev);
}
}
- /* Call lwip_selscan again: there could have been events between
- the last scan (whithout us on the list) and putting us on the list! */
- nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
- if (!nready) {
- /* Still none ready, just wait to be woken */
- if (timeout == 0) {
- /* Wait forever */
- msectimeout = 0;
- } else {
- msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
- if (msectimeout == 0) {
- /* Wait 1ms at least (0 means wait forever) */
- msectimeout = 1;
+ if (nready >= 0) {
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (without us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msectimeout == 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ }
}
- }
- waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
+ waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
}
- /* Increase select_waiting for each socket we are interested in */
- for(i = 0; i < maxfdp1; i++) {
+
+ /* Decrease select_waiting for each socket we are interested in */
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
if ((readset && FD_ISSET(i, readset)) ||
(writeset && FD_ISSET(i, writeset)) ||
(exceptset && FD_ISSET(i, exceptset))) {
- struct lwip_sock *sock = tryget_socket(i);
- LWIP_ASSERT("sock != NULL", sock != NULL);
+ struct lwip_sock *sock;
SYS_ARCH_PROTECT(lev);
- sock->select_waiting--;
- LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ /* @todo: what if this is a new socket (reallocated?) in this case,
+ select_waiting-- would be wrong (a global 'sockalloc' counter,
+ stored per socket could help) */
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting > 0) {
+ sock->select_waiting--;
+ }
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ }
SYS_ARCH_UNPROTECT(lev);
}
}
LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
select_cb.prev->next = select_cb.next;
}
- /* Increasing this counter tells even_callback that the list has changed. */
+ /* Increasing this counter tells event_callback that the list has changed. */
select_cb_ctr++;
SYS_ARCH_UNPROTECT(lev);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(select_cb.sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
sys_sem_free(&select_cb.sem);
- if (waitres == SYS_ARCH_TIMEOUT) {
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ if (nready < 0) {
+ /* This happens when a socket got closed while waiting */
+ set_errno(EBADF);
+ return -1;
+ }
+
+ if (waitres == SYS_ARCH_TIMEOUT) {
/* Timeout */
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
/* This is OK as the local fdsets are empty and nready is zero,
/* At this point, SYS_ARCH is still protected! */
again:
for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
if (scb->sem_signalled == 0) {
/* semaphore not signalled yet */
int do_signal = 0;
if (do_signal) {
scb->sem_signalled = 1;
/* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
- lead to the select thread taking itself off the list, invalidagin the semaphore. */
- sys_sem_signal(&scb->sem);
+ lead to the select thread taking itself off the list, invalidating the semaphore. */
+ sys_sem_signal(SELECT_SEM_PTR(scb->sem));
}
}
/* unlock interrupts with each step */
- last_select_cb_ctr = select_cb_ctr;
SYS_ARCH_UNPROTECT(lev);
/* this makes sure interrupt protection time is short */
SYS_ARCH_PROTECT(lev);
if (sock->conn != NULL) {
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return EOPNOTSUPP;
+ return -1;
}
} else {
sock_set_errno(sock, ENOTCONN);
- return ENOTCONN;
+ return -1;
}
if (how == SHUT_RD) {
shut_rx = 1;
} else if (how == SHUT_WR) {
shut_tx = 1;
- } else if(how == SHUT_RDWR) {
+ } else if (how == SHUT_RDWR) {
shut_rx = 1;
shut_tx = 1;
} else {
sock_set_errno(sock, EINVAL);
- return EINVAL;
+ return -1;
}
err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
{
struct lwip_sock *sock;
union sockaddr_aligned saddr;
- ipX_addr_t naddr;
+ ip_addr_t naddr;
u16_t port;
+ err_t err;
sock = get_socket(s);
if (!sock) {
/* get the IP address and port */
/* @todo: this does not work for IPv6, yet */
- netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local);
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- &saddr, &naddr, port);
+ err = netconn_getaddr(sock->conn, &naddr, &port, local);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, &naddr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
if (*namelen > saddr.sa.sa_len) {
int
lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
{
- err_t err = ERR_OK;
+ u8_t err;
struct lwip_sock *sock = get_socket(s);
- struct lwip_setgetsockopt_data data;
+#if !LWIP_TCPIP_CORE_LOCKING
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
if (!sock) {
return -1;
return -1;
}
- /* Do length and type checks for the various options first, to keep it readable. */
- switch (level) {
-
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
-
- case SO_ACCEPTCONN:
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_ERROR:
- case SO_KEEPALIVE:
- /* UNIMPL case SO_CONTIMEO: */
- /* UNIMPL case SO_SNDTIMEO: */
-#if LWIP_SO_RCVTIMEO
- case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
- case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
- /* UNIMPL case SO_OOBINLINE: */
- /* UNIMPL case SO_SNDBUF: */
- /* UNIMPL case SO_RCVLOWAT: */
- /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
-#endif /* SO_REUSE */
- case SO_TYPE:
- /* UNIMPL case SO_USELOOPBACK: */
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-
- case SO_NO_CHECK:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
-#if LWIP_UDP
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP ||
- ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
- /* this flag is only available for UDP, not for UDP lite */
- err = EAFNOSUPPORT;
- }
-#endif /* LWIP_UDP */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- /* UNIMPL case IP_HDRINCL: */
- /* UNIMPL case IP_RCVDSTADDR: */
- /* UNIMPL case IP_RCVIF: */
- case IP_TTL:
- case IP_TOS:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- if (*optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- break;
- case IP_MULTICAST_IF:
- if (*optlen < sizeof(struct in_addr)) {
- err = EINVAL;
- }
- break;
- case IP_MULTICAST_LOOP:
- if (*optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
-#endif /* LWIP_IGMP */
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no TCP socket, ignore any options. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
-
- switch (optname) {
- case TCP_NODELAY:
- case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
- case TCP_KEEPIDLE:
- case TCP_KEEPINTVL:
- case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_TCP */
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no UDP lite socket, ignore any options. */
- if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
- return 0;
- }
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- case UDPLITE_RECV_CSCOV:
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP && LWIP_UDPLITE*/
-/* UNDEFINED LEVEL */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
- s, level, optname));
- err = ENOPROTOOPT;
- } /* switch */
+#else /* LWIP_TCPIP_CORE_LOCKING */
-
- if (err != ERR_OK) {
- sock_set_errno(sock, err);
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
return -1;
}
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+ /* write back optlen and optval */
+ *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+ memcpy(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
- /* Now do the actual option processing */
- data.sock = sock;
-#ifdef LWIP_DEBUG
- data.s = s;
-#endif /* LWIP_DEBUG */
- data.level = level;
- data.optname = optname;
- data.optval = optval;
- data.optlen = optlen;
- data.err = err;
- tcpip_callback(lwip_getsockopt_internal, &data);
- sys_arch_sem_wait(&sock->conn->op_completed, 0);
/* maybe lwip_getsockopt_internal has changed err */
- err = data.err;
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
sock_set_errno(sock, err);
return err ? -1 : 0;
}
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
static void
-lwip_getsockopt_internal(void *arg)
+lwip_getsockopt_callback(void *arg)
{
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- int s;
-#endif /* LWIP_DEBUG */
- int level, optname;
- void *optval;
struct lwip_setgetsockopt_data *data;
-
LWIP_ASSERT("arg != NULL", arg != NULL);
-
data = (struct lwip_setgetsockopt_data*)arg;
- sock = data->sock;
-#ifdef LWIP_DEBUG
- s = data->s;
-#endif /* LWIP_DEBUG */
- level = data->level;
- optname = data->optname;
- optval = data->optval;
+
+ data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+ &data->optlen);
+
+ sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ u8_t err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
switch (level) {
case SOL_SOCKET:
switch (optname) {
- /* The option flags */
+#if LWIP_TCP
case SO_ACCEPTCONN:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+ return ENOPROTOOPT;
+ }
+ if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+ *(int*)optval = 1;
+ } else {
+ *(int*)optval = 0;
+ }
+ break;
+#endif /* LWIP_TCP */
+
+ /* The option flags */
case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
case SO_KEEPALIVE:
- /* UNIMPL case SO_OOBINCLUDE: */
#if SO_REUSE
case SO_REUSEADDR:
- case SO_REUSEPORT:
#endif /* SO_REUSE */
- /*case SO_USELOOPBACK: UNIMPL */
- *(int*)optval = sock->conn->pcb.ip->so_options & optname;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
s, optname, (*(int*)optval?"on":"off")));
break;
case SO_TYPE:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
case NETCONN_RAW:
*(int*)optval = SOCK_RAW;
break;
case SO_ERROR:
- /* only overwrite ERR_OK or tempoary errors */
- if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
+ LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
+ /* only overwrite ERR_OK or temporary errors */
+ if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
sock_set_errno(sock, err_to_errno(sock->conn->last_err));
- }
- *(int *)optval = sock->err;
+ }
+ *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err);
sock->err = 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
s, *(int *)optval));
break;
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
- *(int *)optval = netconn_get_recvtimeout(sock->conn);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
break;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
case SO_RCVBUF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
*(int *)optval = netconn_get_recvbufsize(sock->conn);
break;
#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER:
+ {
+ s16_t conn_linger;
+ struct linger* linger = (struct linger*)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+ conn_linger = sock->conn->linger;
+ if (conn_linger >= 0) {
+ linger->l_onoff = 1;
+ linger->l_linger = (int)conn_linger;
+ } else {
+ linger->l_onoff = 0;
+ linger->l_linger = 0;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
#if LWIP_UDP
case SO_NO_CHECK:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+ /* this flag is only available for UDP, not for UDP lite */
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
*(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
break;
#endif /* LWIP_UDP*/
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
case IPPROTO_IP:
switch (optname) {
case IP_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
*(int*)optval = sock->conn->pcb.ip->ttl;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
s, *(int *)optval));
break;
case IP_TOS:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
*(int*)optval = sock->conn->pcb.ip->tos;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
s, *(int *)optval));
break;
-#if LWIP_IGMP
+#if LWIP_MULTICAST_TX_OPTIONS
case IP_MULTICAST_TTL:
- *(u8_t*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ return ENOPROTOOPT;
+ }
+ *(u8_t*)optval = sock->conn->pcb.udp->mcast_ttl;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
s, *(int *)optval));
break;
case IP_MULTICAST_IF:
- inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ return ENOPROTOOPT;
+ }
+ inet_addr_from_ipaddr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
s, *(u32_t *)optval));
break;
case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
*(u8_t*)optval = 1;
} else {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
s, *(int *)optval));
break;
-#endif /* LWIP_IGMP */
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#if LWIP_TCP
/* Level: IPPROTO_TCP */
case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
switch (optname) {
case TCP_NODELAY:
*(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
break;
case TCP_KEEPALIVE:
*(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
s, *(int *)optval));
break;
#if LWIP_TCP_KEEPALIVE
case TCP_KEEPIDLE:
*(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n",
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
s, *(int *)optval));
break;
case TCP_KEEPINTVL:
*(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
s, *(int *)optval));
break;
case TCP_KEEPCNT:
*(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
s, *(int *)optval));
break;
#endif /* LWIP_TCP_KEEPALIVE */
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ /* @todo: this does not work for datagram sockets, yet */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ return ENOPROTOOPT;
+ }
+ *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+ s, *(int *)optval));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
#if LWIP_UDP && LWIP_UDPLITE
/* Level: IPPROTO_UDPLITE */
case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ return ENOPROTOOPT;
+ }
switch (optname) {
case UDPLITE_SEND_CSCOV:
*(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
s, (*(int*)optval)) );
break;
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+ if (sock->conn->pcb.raw->chksum_reqd == 0) {
+ *(int *)optval = -1;
+ } else {
+ *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
default:
- LWIP_ASSERT("unhandled level", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (level) */
- sys_sem_signal(&sock->conn->op_completed);
+
+ return err;
}
int
lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
{
+ u8_t err = 0;
struct lwip_sock *sock = get_socket(s);
- err_t err = ERR_OK;
- struct lwip_setgetsockopt_data data;
+#if !LWIP_TCPIP_CORE_LOCKING
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
if (!sock) {
return -1;
return -1;
}
- /* Do length and type checks for the various options first, to keep it readable. */
- switch (level) {
-
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
-
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_KEEPALIVE:
- /* UNIMPL case case SO_CONTIMEO: */
- /* UNIMPL case case SO_SNDTIMEO: */
-#if LWIP_SO_RCVTIMEO
- case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
- case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
- /* UNIMPL case SO_OOBINLINE: */
- /* UNIMPL case SO_SNDBUF: */
- /* UNIMPL case SO_RCVLOWAT: */
- /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
-#endif /* SO_REUSE */
- /* UNIMPL case SO_USELOOPBACK: */
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
- case SO_NO_CHECK:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
-#if LWIP_UDP
- if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) ||
- ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
- /* this flag is only available for UDP, not for UDP lite */
- err = EAFNOSUPPORT;
- }
-#endif /* LWIP_UDP */
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- /* UNIMPL case IP_HDRINCL: */
- /* UNIMPL case IP_RCVDSTADDR: */
- /* UNIMPL case IP_RCVIF: */
- case IP_TTL:
- case IP_TOS:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- if (optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_MULTICAST_IF:
- if (optlen < sizeof(struct in_addr)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_MULTICAST_LOOP:
- if (optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_ADD_MEMBERSHIP:
- case IP_DROP_MEMBERSHIP:
- if (optlen < sizeof(struct ip_mreq)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
-#endif /* LWIP_IGMP */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no TCP socket, ignore any options. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
-
- switch (optname) {
- case TCP_NODELAY:
- case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
- case TCP_KEEPIDLE:
- case TCP_KEEPINTVL:
- case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_TCP */
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no UDP lite socket, ignore any options. */
- if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn)))
- return 0;
-
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- case UDPLITE_RECV_CSCOV:
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP && LWIP_UDPLITE */
-/* UNDEFINED LEVEL */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
- s, level, optname));
- err = ENOPROTOOPT;
- } /* switch (level) */
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
+#else /* LWIP_TCPIP_CORE_LOCKING */
- if (err != ERR_OK) {
- sock_set_errno(sock, err);
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
return -1;
}
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+ memcpy(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
-
- /* Now do the actual option processing */
- data.sock = sock;
-#ifdef LWIP_DEBUG
- data.s = s;
-#endif /* LWIP_DEBUG */
- data.level = level;
- data.optname = optname;
- data.optval = (void*)optval;
- data.optlen = &optlen;
- data.err = err;
- tcpip_callback(lwip_setsockopt_internal, &data);
- sys_arch_sem_wait(&sock->conn->op_completed, 0);
- /* maybe lwip_setsockopt_internal has changed err */
- err = data.err;
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
sock_set_errno(sock, err);
return err ? -1 : 0;
}
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
static void
-lwip_setsockopt_internal(void *arg)
+lwip_setsockopt_callback(void *arg)
{
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- int s;
-#endif /* LWIP_DEBUG */
- int level, optname;
- const void *optval;
struct lwip_setgetsockopt_data *data;
-
LWIP_ASSERT("arg != NULL", arg != NULL);
-
data = (struct lwip_setgetsockopt_data*)arg;
- sock = data->sock;
-#ifdef LWIP_DEBUG
- s = data->s;
-#endif /* LWIP_DEBUG */
- level = data->level;
- optname = data->optname;
- optval = data->optval;
+
+ data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+ data->optlen);
+
+ sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ u8_t err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
switch (level) {
case SOL_SOCKET:
switch (optname) {
+ /* SO_ACCEPTCONN is get-only */
+
/* The option flags */
case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
case SO_KEEPALIVE:
- /* UNIMPL case SO_OOBINCLUDE: */
#if SO_REUSE
case SO_REUSEADDR:
- case SO_REUSEPORT:
#endif /* SO_REUSE */
- /* UNIMPL case SO_USELOOPBACK: */
- if (*(int*)optval) {
- sock->conn->pcb.ip->so_options |= optname;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ if (*(const int*)optval) {
+ ip_set_option(sock->conn->pcb.ip, optname);
} else {
- sock->conn->pcb.ip->so_options &= ~optname;
+ ip_reset_option(sock->conn->pcb.ip, optname);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
- s, optname, (*(int*)optval?"on":"off")));
+ s, optname, (*(const int*)optval?"on":"off")));
+ break;
+
+ /* SO_TYPE is get-only */
+ /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
break;
+#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
case SO_RCVTIMEO:
- netconn_set_recvtimeout(sock->conn, *(int*)optval);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
break;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
case SO_RCVBUF:
- netconn_set_recvbufsize(sock->conn, *(int*)optval);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+ netconn_set_recvbufsize(sock->conn, *(const int*)optval);
break;
#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER:
+ {
+ const struct linger* linger = (const struct linger*)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+ if (linger->l_onoff) {
+ int lingersec = linger->l_linger;
+ if (lingersec < 0) {
+ return EINVAL;
+ }
+ if (lingersec > 0xFFFF) {
+ lingersec = 0xFFFF;
+ }
+ sock->conn->linger = (s16_t)lingersec;
+ } else {
+ sock->conn->linger = -1;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
#if LWIP_UDP
case SO_NO_CHECK:
- if (*(int*)optval) {
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+ /* this flag is only available for UDP, not for UDP lite */
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ if (*(const int*)optval) {
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
} else {
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
break;
#endif /* LWIP_UDP */
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
case IPPROTO_IP:
switch (optname) {
case IP_TTL:
- sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
s, sock->conn->pcb.ip->ttl));
break;
case IP_TOS:
- sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
s, sock->conn->pcb.ip->tos));
break;
-#if LWIP_IGMP
+#if LWIP_MULTICAST_TX_OPTIONS
case IP_MULTICAST_TTL:
- sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ sock->conn->pcb.udp->mcast_ttl = (u8_t)(*(const u8_t*)optval);
break;
case IP_MULTICAST_IF:
- inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
+ {
+ ip4_addr_t if_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+ inet_addr_to_ipaddr(&if_addr, (const struct in_addr*)optval);
+ udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+ }
break;
case IP_MULTICAST_LOOP:
- if (*(u8_t*)optval) {
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ if (*(const u8_t*)optval) {
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
} else {
udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
}
break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#if LWIP_IGMP
case IP_ADD_MEMBERSHIP:
case IP_DROP_MEMBERSHIP:
{
/* If this is a TCP or a RAW socket, ignore these options. */
- struct ip_mreq *imr = (struct ip_mreq *)optval;
- ip_addr_t if_addr;
- ip_addr_t multi_addr;
+ /* @todo: assign membership to this socket so that it is dropped when closing the socket */
+ err_t igmp_err;
+ const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+ ip4_addr_t if_addr;
+ ip4_addr_t multi_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
- if(optname == IP_ADD_MEMBERSHIP){
- data->err = igmp_joingroup(&if_addr, &multi_addr);
+ if (optname == IP_ADD_MEMBERSHIP) {
+ if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+ /* cannot track membership (out of memory) */
+ err = ENOMEM;
+ igmp_err = ERR_OK;
+ } else {
+ igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+ }
} else {
- data->err = igmp_leavegroup(&if_addr, &multi_addr);
+ igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+ lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
}
- if(data->err != ERR_OK) {
- data->err = EADDRNOTAVAIL;
+ if (igmp_err != ERR_OK) {
+ err = EADDRNOTAVAIL;
}
}
break;
#endif /* LWIP_IGMP */
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#if LWIP_TCP
/* Level: IPPROTO_TCP */
case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
switch (optname) {
case TCP_NODELAY:
- if (*(int*)optval) {
+ if (*(const int*)optval) {
tcp_nagle_disable(sock->conn->pcb.tcp);
} else {
tcp_nagle_enable(sock->conn->pcb.tcp);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
- s, (*(int *)optval)?"on":"off") );
+ s, (*(const int *)optval)?"on":"off") );
break;
case TCP_KEEPALIVE:
- sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
s, sock->conn->pcb.tcp->keep_idle));
break;
#if LWIP_TCP_KEEPALIVE
case TCP_KEEPIDLE:
- sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval);
+ sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
s, sock->conn->pcb.tcp->keep_idle));
break;
case TCP_KEEPINTVL:
- sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
+ sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
s, sock->conn->pcb.tcp->keep_intvl));
break;
case TCP_KEEPCNT:
- sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
s, sock->conn->pcb.tcp->keep_cnt));
break;
#endif /* LWIP_TCP_KEEPALIVE */
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ /* @todo: this does not work for datagram sockets, yet */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+ if (*(const int*)optval) {
+ sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY;
+ } else {
+ sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+ s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0)));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
#if LWIP_UDP && LWIP_UDPLITE
/* Level: IPPROTO_UDPLITE */
case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ return ENOPROTOOPT;
+ }
switch (optname) {
case UDPLITE_SEND_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
/* don't allow illegal values! */
sock->conn->pcb.udp->chksum_len_tx = 8;
} else {
- sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
- s, (*(int*)optval)) );
+ s, (*(const int*)optval)) );
break;
case UDPLITE_RECV_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8)) || (*(int*)optval > 0xffff)) {
+ if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
/* don't allow illegal values! */
sock->conn->pcb.udp->chksum_len_rx = 8;
} else {
- sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
- s, (*(int*)optval)) );
+ s, (*(const int*)optval)) );
break;
default:
- LWIP_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (optname) */
break;
#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+ if (*(const int *)optval < 0) {
+ sock->conn->pcb.raw->chksum_reqd = 0;
+ } else if (*(const int *)optval & 1) {
+ /* Per RFC3542, odd offsets are not allowed */
+ return EINVAL;
+ } else {
+ sock->conn->pcb.raw->chksum_reqd = 1;
+ sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+ s, sock->conn->pcb.raw->chksum_reqd));
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
default:
- LWIP_ASSERT("unhandled level", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
break;
} /* switch (level) */
- sys_sem_signal(&sock->conn->op_completed);
+
+ return err;
}
int
u8_t val;
#if LWIP_SO_RCVBUF
u16_t buflen = 0;
- s16_t recv_avail;
+ int recv_avail;
#endif /* LWIP_SO_RCVBUF */
if (!sock) {
if (recv_avail < 0) {
recv_avail = 0;
}
- *((u16_t*)argp) = (u16_t)recv_avail;
+ *((int*)argp) = recv_avail;
/* Check if there is data left from the last recv operation. /maq 041215 */
if (sock->lastdata) {
buflen = p->tot_len;
buflen -= sock->lastoffset;
- *((u16_t*)argp) += buflen;
+ *((int*)argp) += buflen;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
#endif /* LWIP_SO_RCVBUF */
#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
- case FIONBIO:
+ case (long)FIONBIO:
val = 0;
if (argp && *(u32_t*)argp) {
val = 1;
struct lwip_sock *sock = get_socket(s);
int ret = -1;
- if (!sock || !sock->conn) {
+ if (!sock) {
return -1;
}
switch (cmd) {
case F_GETFL:
ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ sock_set_errno(sock, 0);
break;
case F_SETFL:
if ((val & ~O_NONBLOCK) == 0) {
/* only O_NONBLOCK, all other bits are zero */
netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
ret = 0;
+ sock_set_errno(sock, 0);
+ } else {
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
}
break;
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
break;
}
return ret;
}
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ /* s+1 is stored in the array to prevent having to initialize the array
+ (default initialization is to 0) */
+ int sa = s + 1;
+ int i;
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sa == 0) {
+ socket_ipv4_multicast_memberships[i].sa = sa;
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ /* s+1 is stored in the array to prevent having to initialize the array
+ (default initialization is to 0) */
+ int sa = s + 1;
+ int i;
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if ((socket_ipv4_multicast_memberships[i].sa == sa) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+ socket_ipv4_multicast_memberships[i].sa = 0;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+ return;
+ }
+ }
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void lwip_socket_drop_registered_memberships(int s)
+{
+ /* s+1 is stored in the array to prevent having to initialize the array
+ (default initialization is to 0) */
+ int sa = s + 1;
+ int i;
+
+ LWIP_ASSERT("socket has no netconn", sockets[s].conn != NULL);
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sa == sa) {
+ ip_addr_t multi_addr, if_addr;
+ ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+ ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+ socket_ipv4_multicast_memberships[i].sa = 0;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+ netconn_join_leave_group(sockets[s].conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+ }
+ }
+}
+#endif /* LWIP_IGMP */
#endif /* LWIP_SOCKET */