diff options
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.c | 670 |
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; +} |