summaryrefslogtreecommitdiffstats
path: root/stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c')
-rw-r--r--stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c670
1 files changed, 670 insertions, 0 deletions
diff --git a/stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c b/stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c
new file mode 100644
index 0000000..18588a1
--- /dev/null
+++ b/stacks/lwip_stack/lwip_src/socket/stackx_prot_com.c
@@ -0,0 +1,670 @@
+/*
+*
+* Copyright (c) 2018 Huawei Technologies Co.,Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at:
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "stackx_msg_handler.h"
+#include "stackx_prot_com.h"
+#include "common_pal_bitwide_adjust.h"
+#include "stackx_err.h"
+#include "nstack_securec.h"
+#include "nsfw_rti.h"
+//#include "stackx_custom.h"
+
+#define FAST_SLEEP_TIME 10000
+#define FAST_RETRY_COUNT 100
+#define MAX_WAIT_TIMEOUT 0x7FFFFFFF
+
+/*****************************************************************************
+* Prototype : sbr_getsockopt_sol_socket
+* Description : get sol socket
+* Input : sbr_socket_t * sk
+* int optname
+* void * optval
+* socklen_t optlen
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int
+sbr_getsockopt_sol_socket(sbr_socket_t * sk, int optname, void *optval,
+ socklen_t optlen)
+{
+ int err = 0;
+
+ switch (optname)
+ {
+ case SO_ERROR:
+ {
+ if (optlen < sizeof(int))
+ {
+ return EINVAL;
+ }
+
+ /* only overwrite ERR_OK or tempoary errors */
+ err = sbr_get_sk_errno(sk);
+
+ if ((0 == err) || (EINPROGRESS == err))
+ {
+ err =
+ sbr_spl_err_to_errno(ss_get_last_errno
+ (sbr_get_conn(sk)));
+ sbr_set_sk_errno(sk, err);
+ }
+ else
+ {
+ sbr_set_sk_errno(sk, 0);
+ }
+
+ *(int *) optval = sbr_get_sk_errno(sk);
+
+ return 0;
+ }
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ case SO_REUSEADDR:
+ if (optlen < sizeof(int))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ case SO_RCVTIMEO:
+ case SO_SNDTIMEO:
+ if (optlen < sizeof(struct timeval))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ case SO_LINGER:
+ if (optlen < sizeof(struct linger))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ default:
+ err = ENOPROTOOPT;
+ break;
+ }
+
+ return err;
+}
+
+/*****************************************************************************
+* Prototype : sbr_getsockopt_ipproto_ip
+* Description : get ipproto ip
+* Input : int optname
+* void * optval
+* socklen_t optlen
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int sbr_getsockopt_ipproto_ip(int optname, void *optval, socklen_t optlen)
+{
+ int err = 0;
+
+ switch (optname)
+ {
+ case IP_TOS:
+ if (optlen < sizeof(u8))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ default:
+ err = ENOPROTOOPT;
+ break;
+ }
+
+ return err;
+}
+
+/*****************************************************************************
+* Prototype : sbr_pick_timeout
+* Description : pick time
+* Input : const void * optval
+* socklen_t optlen
+* i32* timeout
+* Output : None
+* Return Value : static inline int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+static inline int
+sbr_pick_timeout(const void *optval, socklen_t optlen, i32 * timeout)
+{
+ if (optlen < sizeof(struct timeval))
+ {
+ return EINVAL;
+ }
+
+ struct timeval *time_val = (struct timeval *) optval;
+ if ((time_val->tv_usec < 0) || (time_val->tv_usec > USEC_TO_SEC))
+ {
+ return EDOM;
+ }
+ else
+ {
+ if (time_val->tv_sec < 0)
+ {
+ *timeout = 0;
+ }
+ else
+ {
+ *timeout = MAX_WAIT_TIMEOUT;
+ if ((time_val->tv_sec != 0) || (time_val->tv_usec != 0))
+ {
+ if (time_val->tv_sec < ((MAX_WAIT_TIMEOUT / 1000) - 1))
+ {
+ *timeout =
+ time_val->tv_sec * 1000 + time_val->tv_usec / 1000;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Prototype : sbr_setsockopt_sol_socket
+* Description : set sol socket
+* Input : sbr_socket_t * sk
+* int optname
+* const void * optval
+* socklen_t optlen
+* netconn_type_t type
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int
+sbr_setsockopt_sol_socket(sbr_socket_t * sk, int optname, const void *optval,
+ socklen_t optlen, spl_netconn_type_t type)
+{
+ int err = 0;
+
+ switch (optname)
+ {
+ case SO_REUSEADDR:
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+ case SO_RCVBUF:
+ case SO_SNDBUF:
+ if (optlen < sizeof(int))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ case SO_RCVTIMEO:
+ err =
+ sbr_pick_timeout(optval, optlen,
+ &sbr_get_fd_share(sk)->recv_timeout);
+ break;
+ case SO_SNDTIMEO:
+ err =
+ sbr_pick_timeout(optval, optlen,
+ &sbr_get_fd_share(sk)->send_timeout);
+ break;
+ case SO_LINGER:
+ if (optlen < sizeof(struct linger))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ default:
+ err = ENOPROTOOPT;
+ break;
+ }
+
+ return err;
+}
+
+/*****************************************************************************
+* Prototype : sbr_setsockopt_ipproto_ip
+* Description : set ipproto ip
+* Input : int optname
+* const void * optval
+* socklen_t optlen
+* netconn_type_t type
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int
+sbr_setsockopt_ipproto_ip(int optname, const void *optval, socklen_t optlen,
+ spl_netconn_type_t type)
+{
+ int err = 0;
+
+ switch (optname)
+ {
+ case IP_TOS:
+ if (optlen < sizeof(u8))
+ {
+ err = EINVAL;
+ }
+
+ break;
+ case IP_MULTICAST_TTL:
+ if (optlen < sizeof(u8))
+ {
+ err = EINVAL;
+ break;
+ }
+
+ if (type != SPL_NETCONN_UDP)
+ {
+ err = EAFNOSUPPORT;
+ break;
+ }
+
+ break;
+ case IP_MULTICAST_IF:
+ if (optlen < sizeof(struct in_addr))
+ {
+ err = EINVAL;
+ break;
+ }
+
+ if (type != SPL_NETCONN_UDP)
+ {
+ err = EAFNOSUPPORT;
+ break;
+ }
+
+ break;
+ case IP_MULTICAST_LOOP:
+ if (optlen < sizeof(u8))
+ {
+ err = EINVAL;
+ break;
+ }
+
+ if (type != SPL_NETCONN_UDP)
+ {
+ err = EAFNOSUPPORT;
+ break;
+ }
+
+ break;
+ default:
+ err = ENOPROTOOPT;
+ break;
+ }
+
+ return err;
+}
+
+/*****************************************************************************
+* Prototype : sbr_dequeue_buf
+* Description : dequeue buf
+* Input : sbr_socket_t * sk
+* void **buf
+* i32 timeout
+* u8 use_l4_ring
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int sbr_dequeue_buf(sbr_socket_t * sk, void **buf, i32 timeout)
+{
+ mring_handle ring = ss_get_recv_ring(sbr_get_conn(sk));
+
+ struct timespec start, end;
+ long timediff;
+ long timediff_sec;
+ long timeout_sec = (long) (timeout / 1000);
+ unsigned int retry_count = 0;
+
+ if (timeout > 0)
+ {
+ if (unlikely(0 != clock_gettime(CLOCK_MONOTONIC, &start)))
+ {
+ NSSBR_LOGERR("Failed to get time, errno = %d", errno);
+ }
+ }
+
+ if (!ss_recv_ring_valid(sbr_get_conn(sk)))
+ {
+ NSSBR_LOGDBG("ring is invalid]fd=%d", sk->fd);
+ sbr_set_sk_io_errno(sk, ENOTCONN);
+ return -1;
+ }
+
+ int dequeue_ret = 0;
+ pid_t pid = get_sys_pid();
+
+ while (1)
+ {
+ if (ss_is_shut_rd(sbr_get_conn(sk)))
+ {
+ NSSBR_LOGDBG("is shut rd]fd=%d", sk->fd);
+ sbr_set_sk_io_errno(sk, EINVAL);
+ return -1;
+ }
+
+ dequeue_ret = nsfw_mem_ring_dequeue(ring, buf);
+ if (1 == dequeue_ret)
+ {
+ pbuf_set_recycle_flg((struct spl_pbuf *) *buf, pid); /*release buf hold by app on abnormal exit */
+ return 0;
+ }
+ else if (0 == dequeue_ret)
+ {
+ /*If the peer reset connect, try to receive data only once */
+ if (ss_can_not_recv(sbr_get_conn(sk)))
+ {
+ NS_LOG_CTRL(LOG_CTRL_RECV_QUEUE_FULL, LOGSBR, "NSSBR",
+ NSLOG_WAR, "try to fetch one more time]fd=%d",
+ sk->fd);
+ /**
+ * l4_ring will not be processed here as can_not_recv flag is
+ * set by TCP only.
+ */
+ if (1 == nsfw_mem_ring_dequeue(ring, buf))
+ {
+ pbuf_set_recycle_flg((struct spl_pbuf *) *buf, pid);
+ return 0;
+ }
+
+ sbr_set_sk_io_errno(sk, ENOTCONN);
+ return -1;
+ }
+
+ int err = ss_get_last_errno(sbr_get_conn(sk));
+ if (SPL_ERR_IS_FATAL(err) || err == ERR_TIMEOUT) /* have to handle ERR_TIMEOUT here, when TCP keepalive timeout. */
+ {
+ NS_LOG_CTRL(LOG_CTRL_RECV_QUEUE_FULL, LOGSBR, "NSSBR",
+ NSLOG_ERR, "connection fatal error!err=%d", err);
+
+ /* l4_ring need to be handled in the future */
+ if (1 == nsfw_mem_ring_dequeue(ring, buf))
+ {
+ pbuf_set_recycle_flg((struct spl_pbuf *) *buf, pid);
+ return 0;
+ }
+
+ sbr_set_sk_io_errno(sk, sbr_spl_err_to_errno(err));
+ return -1;
+ }
+
+ if (0 > timeout)
+ {
+ sbr_set_sk_io_errno(sk, EWOULDBLOCK);
+ return -1;
+ }
+
+ if (retry_count < FAST_RETRY_COUNT)
+ {
+ sys_sleep_ns(0, FAST_SLEEP_TIME);
+ retry_count++;
+ }
+ else
+ {
+ sys_sleep_ns(0, sbr_get_fd_share(sk)->block_polling_time);
+ }
+
+ if (timeout > 0)
+ {
+ if (unlikely(0 != clock_gettime(CLOCK_MONOTONIC, &end)))
+ {
+ NSSBR_LOGERR("Failed to get time, errno = %d", errno);
+ }
+ timediff_sec = end.tv_sec - start.tv_sec;
+ if (timediff_sec >= timeout_sec)
+ {
+ timediff = end.tv_nsec > start.tv_nsec ?
+ (timediff_sec * 1000) + (end.tv_nsec -
+ start.tv_nsec) /
+ USEC_TO_SEC : (timediff_sec * 1000) -
+ ((start.tv_nsec - end.tv_nsec) / USEC_TO_SEC);
+ if (timediff > timeout)
+ {
+ NSSBR_LOGDBG("recv timeout]fd=%d", sk->fd);
+ sbr_set_sk_io_errno(sk, EWOULDBLOCK);
+ return -1;
+ }
+ }
+ }
+ }
+ else
+ {
+ NSSBR_LOGERR("dequeue failed]fd=%d", sk->fd);
+ sbr_set_sk_io_errno(sk, EINVAL);
+ return -1;
+ }
+ }
+}
+
+int sbr_com_peak(sbr_socket_t * sk)
+{
+ NSSBR_LOGERR("not implement]fd=%d", sk->fd);
+ return -1;
+}
+
+/*****************************************************************************
+* Prototype : sbr_com_try_lock_recv
+* Description : try lock recv
+* Input : sbr_socket_t * sk
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int sbr_com_try_lock_recv(sbr_socket_t * sk)
+{
+#ifdef SBR_USE_LOCK
+ return common_spinlock_try_lock_with_pid(&sbr_get_fd_share(sk)->recv_lock,
+ get_sys_pid());
+#else
+ return 1;
+#endif
+}
+
+/*****************************************************************************
+* Prototype : sbr_com_lock_common
+* Description : lock common
+* Input : sbr_socket_t * sk
+* Output : None
+* Return Value : void
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+void sbr_com_lock_common(sbr_socket_t * sk)
+{
+#ifdef SBR_USE_LOCK
+ while (!common_spinlock_try_lock_with_pid
+ (&sbr_get_fd_share(sk)->common_lock, get_sys_pid()))
+ {
+ sys_sleep_ns(0, 0);
+ }
+#endif
+
+}
+
+void sbr_com_fork_parent(sbr_socket_t * sk, pid_t p)
+{
+ i32 ref = ss_inc_fork_ref(sbr_get_conn(sk));
+ NSSBR_LOGINF
+ ("inc fork ref] fd=%d, p=%d, ref=%d, conn=%p, private_data=%p",
+ sk->fd, p, ref, sbr_get_conn(sk), sbr_get_conn(sk)->private_data);
+}
+
+void sbr_com_fork_child(sbr_socket_t * sk, pid_t p, pid_t c)
+{
+ if (ss_add_pid(sbr_get_conn(sk), c) != 0)
+ {
+ NSSBR_LOGERR
+ ("add pid failed] fd=%d, p=%d, c=%d, ref=%d, conn=%p, private_data=%p",
+ sk->fd, p, c, ss_get_fork_ref(sbr_get_conn(sk)),
+ sbr_get_conn(sk), sbr_get_conn(sk)->private_data);
+ }
+ else
+ {
+ NSSBR_LOGINF
+ ("add pid ok] fd=%d, p=%d, c=%d, ref=%d, conn=%p, private_data=%p",
+ sk->fd, p, c, ss_get_fork_ref(sbr_get_conn(sk)),
+ sbr_get_conn(sk), sbr_get_conn(sk)->private_data);
+ }
+}
+
+/*****************************************************************************
+* Prototype : sbr_com_unlock_common
+* Description : unlock common
+* Input : sbr_socket_t * sk
+* Output : None
+* Return Value : void
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+void sbr_com_unlock_common(sbr_socket_t * sk)
+{
+#ifdef SBR_USE_LOCK
+ common_spinlock_unlock(&sbr_get_fd_share(sk)->common_lock);
+#endif
+}
+
+/*****************************************************************************
+* Prototype : sbr_com_free_recv_buf
+* Description : free recv buf,can't free buf in app
+* Input : sbr_socket_t * sk
+* struct spl_pbuf *p
+* Output : None
+* Return Value : void
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+void sbr_com_free_recv_buf(sbr_socket_t * sk, struct spl_pbuf *p)
+{
+ struct spl_pbuf *p_orig = p;
+ if (p)
+ {
+ p->freeNext = NULL;
+ p = (struct spl_pbuf *) ADDR_LTOSH(p);
+
+ if (sbr_get_fd_share(sk)->recoder.totalLen > 0)
+ {
+ ((struct spl_pbuf *)
+ ADDR_SHTOL(sbr_get_fd_share(sk)->recoder.tail))->freeNext = p;
+ sbr_get_fd_share(sk)->recoder.tail = p;
+ }
+ else
+ {
+ sbr_get_fd_share(sk)->recoder.head = p;
+ sbr_get_fd_share(sk)->recoder.tail = p;
+ }
+
+ sbr_get_fd_share(sk)->recoder.totalLen++;
+ }
+
+ /* send MSG only if it's a big packet or number of packets larger than 32 */
+ if ((p_orig && p_orig->tot_len > MAX_RECV_FREE_LEN) ||
+ (sbr_get_fd_share(sk)->recoder.totalLen >= MAX_RECV_FREE_BUF))
+ {
+ sbr_handle_free_recv_buf(sk);
+ }
+
+}
+
+/*****************************************************************************
+* Prototype : sbr_get_sockaddr_and_len
+* Description : get addr and len
+* Input : u16 port
+* spl_ip_addr_t * ipaddr
+* struct sockaddr * addr
+* socklen_t * addrlen
+* Output : None
+* Return Value : int
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+int
+sbr_get_sockaddr_and_len(u16 port, spl_ip_addr_t * ipaddr,
+ struct sockaddr *addr, socklen_t * addrlen)
+{
+ int ret;
+ struct sockaddr_in sin;
+
+ ret = memset_s(&sin, sizeof(sin), 0, sizeof(sin));
+ if (0 != ret)
+ {
+ NSSBR_LOGERR("MEMSET_S failed]ret=%d.", ret);
+ return -1;
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ inet_addr_from_ipaddr(&sin.sin_addr, ipaddr);
+ if (*addrlen > sizeof(struct sockaddr))
+ {
+ *addrlen = sizeof(struct sockaddr);
+ }
+
+ if (*addrlen > 0)
+ {
+ ret = memcpy_s(addr, sizeof(struct sockaddr), &sin, *addrlen);
+ if (0 != ret)
+ {
+ NSSBR_LOGERR("MEMCPY_S failed]ret=%d", ret);
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Prototype : sbr_com_set_app_info
+* Description : set app info to netconn
+* Input : sbr_socket_t * sk
+* void* appinfo
+* Output : None
+* Return Value : void
+* Calls :
+* Called By :
+*
+*****************************************************************************/
+void sbr_com_set_app_info(sbr_socket_t * sk, void *appinfo)
+{
+ return;
+}