/** * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "iot_defines.h" #include "iot_errors.h" #include "sdk_config.h" #include "socket_common.h" #include "socket_trace.h" #include "transport_if.h" #include "portdb.h" #include "app_timer.h" #include "mem_manager.h" #include "nrf_fifo.h" #include "mbuf.h" #include "app_util.h" #include "nrf_platform_port.h" #include "app_util_platform.h" #include "lwip/opt.h" #include "lwip/init.h" #include "lwip/tcp.h" #include "lwip/timers.h" /**@brief Verify that IPv6 address length is correct. */ #define VERIFY_ADDRESS_LEN(len) \ do { \ if ((len) != sizeof(sockaddr_in6_t)) { \ return NRF_ERROR_NULL | IOT_SOCKET_ERR_BASE; \ } \ } while (0) #define LWIP_SYS_TIMER_INTERVAL APP_TIMER_TICKS(100) /**< Interval for timer used as trigger to send. */ #define SOCKET_MAX_PENDING_PACKETS 32 /**< Maximum number of pending received packets. */ #define SOCKET_MAX_PENDING_CONNECTIONS 10 /**< Maximum number of simultaneous connections. */ /**@brief TCP state */ typedef enum { TCP_STATE_IDLE, TCP_STATE_REQUEST_CONNECTION, TCP_STATE_CONNECTED, TCP_STATE_DATA_TX_IN_PROGRESS, TCP_STATE_TCP_SEND_PENDING, TCP_STATE_DISCONNECTED } tcp_state_t; /**@brief LwIP handle structure. */ typedef struct { struct tcp_pcb * p_pcb; /**< A pointer to LwIP TCP protocol control block. */ socket_t * p_socket; /**< A pointer to corresponding socket. */ nrf_fifo_t conn_queue; /**< Connections queue. */ mbuf_t mbuf; /**< Memory management queue. */ volatile tcp_state_t tcp_state; /**< TCP state. */ } lwip_handle_t; static lwip_handle_t lwip_handles[SOCKET_MAX_SOCKET_COUNT]; /**< LwIP handle array. */ /**@brief Timer for LwIP. */ APP_TIMER_DEF(m_lwip_timer_id); static err_t lwip_recv_callback(void * p_arg, struct tcp_pcb * p_pcb, struct pbuf * p_pbuf, err_t err); static uint32_t lwip_buf_read(void * p_ctx, uint32_t read_offset, uint8_t * p_destbuf, uint32_t destbuf_len) { struct pbuf * p_pbuf = (struct pbuf *)p_ctx; uint32_t copy_len = MIN(destbuf_len, (p_pbuf->len - read_offset)); memcpy(p_destbuf, (void *)(((uint8_t *)p_pbuf->payload) + read_offset), copy_len); return copy_len; } static uint32_t lwip_buf_len(void * p_ctx) { struct pbuf * p_pbuf = (struct pbuf *)p_ctx; return p_pbuf->len; } static void lwip_buf_free(void * p_ctx) { nrf_free(((struct pbuf *)p_ctx)->payload); (void) pbuf_free((struct pbuf *)p_ctx); } static void lwip_timer_callback(void * p_ctx) { (void) p_ctx; sys_check_timeouts(); } void nrf_driver_interface_up(iot_interface_t const * p_interface) { UNUSED_PARAMETER(p_interface); transport_interface_up(); } void nrf_driver_interface_down(iot_interface_t const * p_interface) { UNUSED_PARAMETER(p_interface); transport_interface_down(); } void transport_handler_init(void) { lwip_init(); uint32_t err_code = nrf_driver_init(); APP_ERROR_CHECK(err_code); for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) { lwip_handles[i].p_pcb = NULL; lwip_handles[i].p_socket = NULL; lwip_handles[i].tcp_state = TCP_STATE_IDLE; } err_code = app_timer_create(&m_lwip_timer_id, APP_TIMER_MODE_REPEATED, lwip_timer_callback); APP_ERROR_CHECK(err_code); err_code = app_timer_start(m_lwip_timer_id, LWIP_SYS_TIMER_INTERVAL, NULL); APP_ERROR_CHECK(err_code); SOCKET_TRACE("Initialized LWIP transport handler\r\n"); } static void lwip_error_handler(void * p_arg, err_t err) { (void) p_arg; SOCKET_TRACE("Error occured: %d\r\n", (int)err); if(err == ERR_ABRT) { portdb_reset(); } } static err_t lwip_poll_handler(void * p_arg, struct tcp_pcb * p_pcb) { (void) p_arg; (void) p_pcb; return ERR_OK; } static void lwip_drop_connection(void * p_ctx) { struct tcp_pcb * p_pcb = (struct tcp_pcb *)p_ctx; tcp_abort(p_pcb); } static lwip_handle_t * lwip_handle_allocate(socket_t * p_socket, struct tcp_pcb * p_pcb) { lwip_handle_t * p_handle = NULL; for (uint32_t i = 0; i < SOCKET_MAX_SOCKET_COUNT; i++) { if (lwip_handles[i].p_pcb == NULL) { p_handle = &lwip_handles[i]; (void)mbuf_init(&p_handle->mbuf, lwip_buf_read, lwip_buf_len, lwip_buf_free, SOCKET_MAX_PENDING_PACKETS); (void) nrf_fifo_init(&p_handle->conn_queue, SOCKET_MAX_PENDING_CONNECTIONS, socket_wait, lwip_drop_connection); p_handle->p_socket = p_socket; p_handle->tcp_state = TCP_STATE_IDLE; if (p_pcb == NULL) { p_handle->p_pcb = tcp_new(); } else { p_handle->p_pcb = p_pcb; } tcp_arg(p_handle->p_pcb, p_handle); tcp_setprio(p_handle->p_pcb, TCP_PRIO_MIN); tcp_recv(p_handle->p_pcb, lwip_recv_callback); tcp_err(p_handle->p_pcb, lwip_error_handler); tcp_poll(p_handle->p_pcb, lwip_poll_handler, 0); break; } } SOCKET_TRACE("Allocated LWIP socket handle\r\n"); return p_handle; } static void lwip_handle_free(lwip_handle_t * p_handle) { nrf_fifo_deinit(&p_handle->conn_queue); mbuf_deinit(&p_handle->mbuf); p_handle->p_pcb = NULL; p_handle->p_socket = NULL; p_handle->tcp_state = TCP_STATE_IDLE; SOCKET_TRACE("Released LWIP socket handle\r\n"); } static uint32_t lwip_transport_open(socket_t * p_socket) { lwip_handle_t * p_handle = NULL; uint32_t err_code = NRF_SUCCESS; switch (p_socket->so_params.so_type) { case SOCK_STREAM: p_handle = lwip_handle_allocate(p_socket, NULL); if (p_handle == NULL) { err_code = NRF_ERROR_NULL; } else { p_socket->so_ctx = p_handle; } break; default: err_code = NRF_ERROR_NULL; break; } return err_code; } uint32_t lwip_error_convert(err_t lwip_err) { uint32_t err_code = NRF_ERROR_NULL; switch (lwip_err) { case ERR_OK: err_code = NRF_SUCCESS; break; case ERR_MEM: err_code = SOCKET_NO_MEM; break; case ERR_TIMEOUT: err_code = SOCKET_TIMEOUT; break; case ERR_RTE: err_code = SOCKET_NO_ROUTE; break; default: err_code = NRF_ERROR_NULL; break; } return err_code; } static uint32_t lwip_transport_close(socket_t * p_socket) { lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; portdb_free((uint16_t)p_handle->p_pcb->local_port); err_t err_code = tcp_close(p_handle->p_pcb); if (err_code == ERR_OK) { lwip_handle_free(p_handle); } return lwip_error_convert(err_code); } static err_t lwip_connect_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) { lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; // TODO: Error check SOCKET_TRACE("New connection\r\n"); p_handle->tcp_state = TCP_STATE_CONNECTED; return ERR_OK; } static err_t lwip_recv_callback(void * p_arg, struct tcp_pcb * p_pcb, struct pbuf * p_pbuf, err_t err) { lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; socket_t * p_socket = p_handle->p_socket; if (err == ERR_OK) { uint8_t * p_payload_cp = nrf_malloc(p_pbuf->len); struct pbuf * p_pbuf_cp = pbuf_alloc(PBUF_RAW, p_pbuf->len, PBUF_REF); memcpy(p_payload_cp, p_pbuf->payload, p_pbuf->len); p_pbuf_cp->payload = p_payload_cp; p_pbuf_cp->len = p_pbuf->len; p_pbuf_cp->tot_len = p_pbuf->tot_len; uint32_t err_code = mbuf_write(&p_handle->mbuf, p_pbuf_cp); if (err_code == NRF_SUCCESS) { p_socket->so_read_evt++; } else { err = ERR_MEM; } } return err; } static uint32_t lwip_wait_for_state(lwip_handle_t * p_handle, tcp_state_t state) { uint32_t err_code = NRF_SUCCESS; while (err_code == NRF_SUCCESS && p_handle->tcp_state != state && p_handle->tcp_state != TCP_STATE_DISCONNECTED) { err_code = socket_wait(); } if (err_code == NRF_SUCCESS && p_handle->tcp_state != state) { err_code = NRF_ERROR_NULL; } return err_code; } static void lwipaddr_to_sockaddr(const ip6_addr_t * p_lwip_addr, sockaddr_in6_t * p_addr_in6) { struct in6_addr * addr = &p_addr_in6->sin6_addr; addr->s6_addr[0] = p_lwip_addr->addr[0] & 0xFF; addr->s6_addr[1] = (p_lwip_addr->addr[0] >> 8) & 0xFF; addr->s6_addr[2] = (p_lwip_addr->addr[0] >> 16) & 0xFF; addr->s6_addr[3] = (p_lwip_addr->addr[0] >> 24) & 0xFF; addr->s6_addr[4] = p_lwip_addr->addr[1] & 0xFF; addr->s6_addr[5] = (p_lwip_addr->addr[1] >> 8) & 0xFF; addr->s6_addr[6] = (p_lwip_addr->addr[1] >> 16) & 0xFF; addr->s6_addr[7] = (p_lwip_addr->addr[1] >> 24) & 0xFF; addr->s6_addr[8] = p_lwip_addr->addr[2] & 0xFF; addr->s6_addr[9] = (p_lwip_addr->addr[2] >> 8) & 0xFF; addr->s6_addr[10] = (p_lwip_addr->addr[2] >> 16) & 0xFF; addr->s6_addr[11] = (p_lwip_addr->addr[2] >> 24) & 0xFF; addr->s6_addr[12] = p_lwip_addr->addr[3] & 0xFF; addr->s6_addr[13] = (p_lwip_addr->addr[3] >> 8) & 0xFF; addr->s6_addr[14] = (p_lwip_addr->addr[3] >> 16) & 0xFF; addr->s6_addr[15] = (p_lwip_addr->addr[3] >> 24) & 0xFF; } static void sockaddr_to_lwipaddr(const sockaddr_in6_t * p_addr_in6, ip6_addr_t * p_lwip_addr) { const struct in6_addr * addr = &p_addr_in6->sin6_addr; IP6_ADDR_PART(p_lwip_addr, 0, addr->s6_addr[0], addr->s6_addr[1], addr->s6_addr[2], addr->s6_addr[3]); IP6_ADDR_PART(p_lwip_addr, 1, addr->s6_addr[4], addr->s6_addr[5], addr->s6_addr[6], addr->s6_addr[7]); IP6_ADDR_PART(p_lwip_addr, 2, addr->s6_addr[8], addr->s6_addr[9], addr->s6_addr[10], addr->s6_addr[11]); IP6_ADDR_PART(p_lwip_addr, 3, addr->s6_addr[12], addr->s6_addr[13], addr->s6_addr[14], addr->s6_addr[15]); } static uint32_t lwip_transport_bind(socket_t * p_socket, const void * p_addr, socklen_t addr_len) { VERIFY_ADDRESS_LEN(addr_len); const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; uint16_t port = HTONS(p_addr_in6->sin6_port); uint32_t err_code = portdb_register(port); if (err_code == NRF_SUCCESS) { ip6_addr_t lwip_addr; sockaddr_to_lwipaddr(p_addr_in6, &lwip_addr); err_t err = tcp_bind(p_handle->p_pcb, &lwip_addr, port); err_code = lwip_error_convert(err); } return err_code; } static uint32_t lwip_transport_connect(socket_t * p_socket, const void * p_addr, socklen_t addr_len) { VERIFY_ADDRESS_LEN(addr_len); bool is_blocking = ((p_socket->so_flags & O_NONBLOCK) == 0); lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; const sockaddr_in6_t * p_addr_in6 = (const sockaddr_in6_t *)p_addr; uint16_t port = 0; uint32_t err_code = portdb_alloc(&port); SOCKET_TRACE("Binding to port %d\r\n", (int)port); if (err_code == NRF_SUCCESS) { ip6_addr_t any_addr; ip6_addr_set_any(&any_addr); err_t err = tcp_bind (p_handle->p_pcb, &any_addr, port); SOCKET_TRACE("Err %d from bind\r\n", (int)err); err_code = lwip_error_convert(err); } if (err_code == NRF_SUCCESS) { ip6_addr_t remote_addr; sockaddr_to_lwipaddr(p_addr_in6, &remote_addr); p_handle->tcp_state = TCP_STATE_REQUEST_CONNECTION; err_t err = tcp_connect(p_handle->p_pcb, &remote_addr, HTONS(p_addr_in6->sin6_port), lwip_connect_callback); SOCKET_TRACE("Err %d from connect\r\n", (int)err); err_code = lwip_error_convert(err); if (err_code == NRF_SUCCESS && is_blocking) { err_code = lwip_wait_for_state(p_handle, TCP_STATE_CONNECTED); } } if (err_code != NRF_SUCCESS) { SOCKET_TRACE("Error %d when connecting to socket\r\n", (int)err_code); portdb_free(port); } else { SOCKET_TRACE("Successfully connected to remote host!\r\n"); } return err_code; } static err_t lwip_send_complete(void * p_arg, struct tcp_pcb * p_pcb, u16_t len) { lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; if (p_handle->tcp_state == TCP_STATE_TCP_SEND_PENDING) { p_handle->tcp_state = TCP_STATE_DATA_TX_IN_PROGRESS; } return ERR_OK; } static inline uint32_t lwip_check_connected(socket_t * p_socket) { return (p_socket->so_state == STATE_CONNECTED || p_socket->so_params.so_type == SOCK_DGRAM) ? NRF_SUCCESS : SOCKET_NOT_CONNECTED; } static uint32_t lwip_transport_send(socket_t * p_socket, const void * p_buf, uint32_t buf_len, int flags, const void * p_destaddr, socklen_t destaddr_len) { (void) p_destaddr; (void) destaddr_len; lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; if ((p_socket->so_flags & O_NONBLOCK) != 0 && (flags & MSG_WAITALL) == 0) { flags |= MSG_DONTWAIT; } uint32_t err_code = lwip_check_connected(p_socket); if (err_code == NRF_SUCCESS) { uint32_t len = tcp_sndbuf(p_handle->p_pcb); if (len >= buf_len) { tcp_sent(p_handle->p_pcb, lwip_send_complete); p_handle->tcp_state = TCP_STATE_TCP_SEND_PENDING; err_t err = tcp_write(p_handle->p_pcb, p_buf, buf_len, 1); err_code = lwip_error_convert(err); if (err_code == NRF_SUCCESS && (flags & MSG_DONTWAIT) == 0) { err_code = lwip_wait_for_state(p_handle, TCP_STATE_DATA_TX_IN_PROGRESS); } } else { err_code = SOCKET_NO_MEM; } } return err_code; } static uint32_t lwip_transport_recv(socket_t * p_socket, void * p_buf, uint32_t * p_buf_size, int flags, void * p_srcaddr, socklen_t * p_srcaddr_len) { lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; if ((p_socket->so_flags & O_NONBLOCK) != 0 && (flags & MSG_WAITALL) == 0) { flags |= MSG_DONTWAIT; } uint32_t err_code = lwip_check_connected(p_socket); if (err_code == NRF_SUCCESS) { if (mbuf_empty(&p_handle->mbuf) == true) { if ((flags & MSG_DONTWAIT) != 0) { err_code = SOCKET_WOULD_BLOCK; } else { while ((mbuf_empty(&p_handle->mbuf) == true) && (err_code == NRF_SUCCESS)) { err_code = socket_wait(); } } } } if (err_code == NRF_SUCCESS) { *p_buf_size = mbuf_read(&p_handle->mbuf, p_buf, *p_buf_size); tcp_recved(p_handle->p_pcb, *p_buf_size); p_socket->so_read_evt = 0; } return err_code; } static err_t lwip_accept_callback(void * p_arg, struct tcp_pcb * p_pcb, err_t err) { lwip_handle_t * p_handle = (lwip_handle_t *)p_arg; socket_t * p_socket = p_handle->p_socket; if (err == ERR_OK) { (void) nrf_fifo_enq(&p_handle->conn_queue, p_pcb, true); // TODO: Error check p_socket->so_read_evt++; } return err; } static uint32_t lwip_transport_listen(socket_t * p_socket, int backlog) { lwip_handle_t * p_handle = (lwip_handle_t *)p_socket->so_ctx; uint32_t err_code = NRF_SUCCESS; struct tcp_pcb * p_pcb = tcp_listen_with_backlog(p_handle->p_pcb, backlog); if (p_pcb == NULL) { err_code = SOCKET_ADDRESS_IN_USE; } else { p_handle->p_pcb = p_pcb; tcp_accept(p_handle->p_pcb, lwip_accept_callback); } return err_code; } static uint32_t lwip_transport_accept(socket_t * p_socket, socket_t * p_client, void * p_cliaddr, socklen_t * p_cliaddr_len) { VERIFY_ADDRESS_LEN(*p_cliaddr_len); struct tcp_pcb * p_client_pcb = NULL; lwip_handle_t * p_lwip_handle = (lwip_handle_t *)p_socket->so_ctx; uint32_t err_code = nrf_fifo_deq(&p_lwip_handle->conn_queue, (void **)&p_client_pcb, (p_socket->so_flags & O_NONBLOCK) == 0); if (err_code == NRF_SUCCESS) { lwip_handle_t * p_lwip_client = lwip_handle_allocate(p_client, p_client_pcb); sockaddr_in6_t * p_sockaddr = (sockaddr_in6_t *)p_cliaddr; p_client->so_ctx = p_lwip_client; lwipaddr_to_sockaddr(&p_client_pcb->remote_ip, p_sockaddr); p_sockaddr->sin6_port = p_client_pcb->remote_port; p_sockaddr->sin6_len = *p_cliaddr_len; p_sockaddr->sin6_family = AF_INET6; p_sockaddr->sin6_flowinfo = 0; p_sockaddr->sin6_scope_id = 0; /*lint -save -e548 */ tcp_accepted(p_lwip_handle->p_pcb); /*lint -restore */ } return err_code; } /** * @brief Transport for LwIP socket. */ socket_transport_t transport_impl = { .open = lwip_transport_open, .bind = lwip_transport_bind, .connect = lwip_transport_connect, .send = lwip_transport_send, .recv = lwip_transport_recv, .listen = lwip_transport_listen, .accept = lwip_transport_accept, .close = lwip_transport_close };