From: Senthil Kumar G S Date: Mon, 14 Oct 2019 06:46:30 +0000 (+0530) Subject: New APIs to configure TCP Socket Keep-Alive. X-Git-Tag: submit/tizen/20191031.021857~4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c0c400a22770c08afd3c9eda73a2e42459f15e53;p=platform%2Fupstream%2Fiotivity.git New APIs to configure TCP Socket Keep-Alive. Added new APIs to enable TCP keep alive, set and unset configuration parameters. https://github.sec.samsung.net/RS7-IOTIVITY/IoTivity/pull/587/commits/731061249edabbf05b9e8cb4a35f7dfe41009183 (cherry-picked from 731061249edabbf05b9e8cb4a35f7dfe41009183) Change-Id: Id63c1796832dcea842e0b059c977a38d307e27a5 Signed-off-by: Senthil Kumar G S Signed-off-by: Sudipto --- diff --git a/resource/c_common/octhread/include/octhread.h b/resource/c_common/octhread/include/octhread.h index 3caffa080..54ba69d58 100755 --- a/resource/c_common/octhread/include/octhread.h +++ b/resource/c_common/octhread/include/octhread.h @@ -141,6 +141,16 @@ OCThreadResult_t oc_thread_cancel(oc_thread t); */ oc_mutex oc_mutex_new(void); +/** + * Creates new mutex that supports recursion. Use oc_mutex_free to free the created mutex. + * + * @note The use of recursive mutex in IoTivity is discouraged. Please use it sporadically. + * + * @return Reference to newly created mutex, otherwise NULL. + * + */ +oc_mutex oc_mutex_new_recursive(void); + /** * Lock the mutex. * diff --git a/resource/c_common/octhread/src/noop/octhread.c b/resource/c_common/octhread/src/noop/octhread.c index 39cfff987..c8aafbe80 100755 --- a/resource/c_common/octhread/src/noop/octhread.c +++ b/resource/c_common/octhread/src/noop/octhread.c @@ -79,6 +79,11 @@ oc_mutex oc_mutex_new(void) return (oc_mutex)&g_mutexInfo; } +oc_mutex oc_mutex_new_recursive(void) +{ + return oc_mutex_new(); +} + bool oc_mutex_free(oc_mutex mutex) { return true; diff --git a/resource/csdk/connectivity/api/cautilinterface.h b/resource/csdk/connectivity/api/cautilinterface.h index 563c781a6..af1f3b0d7 100644 --- a/resource/csdk/connectivity/api/cautilinterface.h +++ b/resource/csdk/connectivity/api/cautilinterface.h @@ -451,6 +451,37 @@ CAResult_t CAUtilStopGattServer(); #if defined(__TIZEN__) CAResult_t CAGetTCPIPHeader(CATransportAdapter_t adapter, int flag, TCPHeaderInfo* info); + +/** + * Enables keep-alive on the given endpoint & configures it with user defined values. + * + * @param[in] endpoint Endpoint on keep-alive needs to be enabled. + * @param[in] time The number of seconds a connection needs to be idle + * before TCP begins sending out keep-alive probes. + * @param[in] cnt The maximum number of TCP keep-alive probes to send before + * giving up and killing the connection if no response is obtained from the other end + * @param[in] intvl The number of seconds between TCP keep-alive probes. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CASetTCPKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl); + +/** + * Disables keep-alive on the given endpoint. + * + * @param[in] endpoint Endpoint on which keep-alive needs to be disabled. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CAUnSetTCPKeepAlive(const CAEndpoint_t *endpoint); + +/** + * Get the last error code (errno) to identify the reason + * when disconnection happens during keep-alive. + * + * @return - Direct 'errno' value from kernel + */ +int CAGetTCPLastErrorCode(); #endif #ifdef __cplusplus diff --git a/resource/csdk/connectivity/inc/catcpadapter.h b/resource/csdk/connectivity/inc/catcpadapter.h index 2c9bb43f6..252e80b9e 100644 --- a/resource/csdk/connectivity/inc/catcpadapter.h +++ b/resource/csdk/connectivity/inc/catcpadapter.h @@ -214,6 +214,38 @@ void CATerminateTCP(); * @return ::CA_STATUS_OK or Appropriate error code. */ CAResult_t CAGetTCPIPHeaderInfo(CATransportFlags_t flag, TCPHeaderInfo* tcpHeaderInfo); + +/** + * Enables keep-alive on the given endpoint & configures it with user defined values. + * + * @param[in] endpoint Endpoint on keep-alive needs to be enabled. + * @param[in] time The number of seconds a connection needs to be idle + * before TCP begins sending out keep-alive probes. + * @param[in] cnt The maximum number of TCP keep-alive probes to send before + * giving up and killing the connection if no response is obtained from the other end + * @param[in] intvl The number of seconds between TCP keep-alive probes. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CATCPSetKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl); + +/** + * Disables keep-alive on the given endpoint. + * + * @param[in] endpoint Endpoint on which keep-alive needs to be disabled. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CATCPUnSetKeepAlive(const CAEndpoint_t *endpoint); + +/** + * Get the last error code (errno) to identify the reason + * when disconnection happens during keep-alive. + * + * @return - Direct 'errno' value from kernel + */ +int CATCPGetLastErrorCode(); + #endif #ifdef TCP_ADAPTER diff --git a/resource/csdk/connectivity/inc/catcpinterface.h b/resource/csdk/connectivity/inc/catcpinterface.h index d61736565..24d69d56a 100644 --- a/resource/csdk/connectivity/inc/catcpinterface.h +++ b/resource/csdk/connectivity/inc/catcpinterface.h @@ -270,6 +270,37 @@ CAResult_t CAConstructCoAP(CATCPSessionInfo_t *svritem, unsigned char **data, */ void CACleanData(CATCPSessionInfo_t *svritem); +/** + * Enables keep-alive on the given endpoint & configures it with user defined values. + * + * @param[in] endpoint Endpoint on keep-alive needs to be enabled. + * @param[in] time The number of seconds a connection needs to be idle + * before TCP begins sending out keep-alive probes. + * @param[in] cnt The maximum number of TCP keep-alive probes to send before + * giving up and killing the connection if no response is obtained from the other end + * @param[in] intvl The number of seconds between TCP keep-alive probes. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CASetKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl); + +/** + * Disables keep-alive on the given endpoint. + * + * @param[in] endpoint Endpoint on which keep-alive needs to be disabled. + * + * @return ::CA_STATUS_OK or Appropriate error code. + */ +CAResult_t CAUnSetKeepAlive(const CAEndpoint_t *endpoint); + +/** + * Get the last error code (errno) to identify the reason + * when disconnection happens during keep-alive. + * + * @return - Direct 'errno' value from kernel + */ +int CAGetLastErrorCode(); + /** * Create a mutex object. * diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c index 3dd9a7a26..4c5b995c5 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c @@ -975,6 +975,22 @@ CAResult_t CAGetTCPIPHeaderInfo(CATransportFlags_t flag, TCPHeaderInfo* tcpHeade return res; } + +CAResult_t CATCPSetKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl) +{ + return CASetKeepAlive(endpoint, time, cnt, intvl); +} + +CAResult_t CATCPUnSetKeepAlive(const CAEndpoint_t *endpoint) +{ + return CAUnSetKeepAlive(endpoint); +} + +int CATCPGetLastErrorCode() +{ + return CAGetLastErrorCode(); +} + #endif #ifdef SINGLE_THREAD diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c index d4bd440cc..6a2e24c94 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,12 @@ static CATCPErrorHandleCallback g_tcpErrorHandler = NULL; */ static CATCPConnectionHandleCallback g_connectionCallback = NULL; +/** + * Error code to hold the errno(Timeout, Destination unreachable, etc) when send/recv fails. + * Main purpose is to hold the reason for connection drop when keep-alive is in use. + */ +static int g_lastErrorCode; + static CASocketFd_t CACreateAcceptSocket(int family, CASocket_t *sock); static void CAAcceptConnection(CATransportFlags_t flag, CASocket_t *sock); static void CAFindReadyMessage(); @@ -180,7 +187,7 @@ CAResult_t CATCPCreateMutex() { if (!g_mutexObjectList) { - g_mutexObjectList = oc_mutex_new(); + g_mutexObjectList = oc_mutex_new_recursive(); if (!g_mutexObjectList) { OIC_LOG(ERROR, TAG, "Failed to create mutex!"); @@ -720,6 +727,8 @@ static void CAReceiveMessage(int fd) len = recv(fd, svritem->tlsdata + svritem->tlsLen, nbRead, 0); if (len < 0) { + g_lastErrorCode = errno; + OIC_LOG_V(DEBUG, TAG, "Set g_lastErrorCode to %s", strerror(g_lastErrorCode)); OIC_LOG_V(ERROR, TAG, "recv failed %s", strerror(errno)); res = CA_RECEIVE_FAILED; } @@ -772,6 +781,8 @@ static void CAReceiveMessage(int fd) len = recv(fd, svritem->tlsdata, TLS_DATA_MAX_SIZE, 0); if (len < 0) { + g_lastErrorCode = errno; + OIC_LOG_V(DEBUG, TAG, "Set g_lastErrorCode to %s", strerror(g_lastErrorCode)); OIC_LOG_V(ERROR, TAG, "recv failed %s", strerror(errno)); res = CA_RECEIVE_FAILED; } @@ -1392,6 +1403,8 @@ static ssize_t sendData(const CAEndpoint_t *endpoint, const void *data, #endif if (-1 == len) { + g_lastErrorCode = errno; + OIC_LOG_V(DEBUG, TAG, "Set g_lastErrorCode to %s", strerror(g_lastErrorCode)); if (EWOULDBLOCK != errno && EAGAIN != errno) { OIC_LOG_V(ERROR, TAG, "unicast ipv4tcp sendTo failed: %s", strerror(errno)); @@ -1776,3 +1789,120 @@ void CATCPSetErrorHandler(CATCPErrorHandleCallback errorHandleCallback) { g_tcpErrorHandler = errorHandleCallback; } + +CAResult_t CASetKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl) +{ + OIC_LOG(INFO, TAG, "IN - CATCPSetKeepAlive"); + + VERIFY_NON_NULL(endpoint, TAG, "endpoint is NULL"); + + size_t index = 0; + bool revertKeepAliveSetting = false; + CAResult_t result = CA_STATUS_OK; + oc_mutex_lock(g_mutexObjectList); + CATCPSessionInfo_t *svritem = CAGetTCPSessionInfoFromEndpoint(endpoint, &index); + if (!svritem) + { + // There is no connection with the given endpoint, so the parameter is considered to be invalid. + result = CA_STATUS_INVALID_PARAM; + goto exit; + } + + if (svritem->fd < 0 || svritem->state != CONNECTED) + { + result = CA_DESTINATION_DISCONNECTED; + goto exit; + } + + int option = 1; + if (-1 == setsockopt(svritem->fd, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof(option))) + { + OIC_LOG_V(ERROR, TAG, "setsockopt SO_KEEPALIVE failed on socket(%d): %s", + svritem->fd, strerror(errno)); + result = CA_SOCKET_OPERATION_FAILED; + goto exit; + } + + if (-1 == setsockopt(svritem->fd, SOL_TCP, TCP_KEEPIDLE, &time, sizeof(time))) + { + OIC_LOG_V(ERROR, TAG, "setsockopt TCP_KEEPIDLE failed on socket(%d): %s", + svritem->fd, strerror(errno)); + revertKeepAliveSetting = true; + result = CA_SOCKET_OPERATION_FAILED; + goto exit; + } + + if (-1 == setsockopt(svritem->fd, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + { + OIC_LOG_V(ERROR, TAG, "setsockopt TCP_KEEPCNT failed on socket(%d): %s", + svritem->fd, strerror(errno)); + revertKeepAliveSetting = true; + result = CA_SOCKET_OPERATION_FAILED; + goto exit; + } + + if (-1 == setsockopt(svritem->fd, SOL_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) + { + OIC_LOG_V(ERROR, TAG, "setsockopt TCP_KEEPINTVL failed on socket(%d): %s", + svritem->fd, strerror(errno)); + revertKeepAliveSetting = true; + result = CA_SOCKET_OPERATION_FAILED; + } + +exit: + if (revertKeepAliveSetting) + { + // Unset SO_KEEPALIVE + option = 0; + if (-1 == setsockopt(svritem->fd, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof(option))) + { + OIC_LOG_V(ERROR, TAG, "unset SO_KEEPALIVE failed on socket(%d): %s", + svritem->fd, strerror(errno)); + } + } + oc_mutex_unlock(g_mutexObjectList); + OIC_LOG(INFO, TAG, "OUT - CATCPSetKeepAlive"); + return result; +} + +CAResult_t CAUnSetKeepAlive(const CAEndpoint_t *endpoint) +{ + OIC_LOG(INFO, TAG, "IN - CATCPUnSetKeepAlive"); + + VERIFY_NON_NULL(endpoint, TAG, "endpoint is NULL"); + + size_t index = 0; + CAResult_t result = CA_STATUS_OK; + oc_mutex_lock(g_mutexObjectList); + CATCPSessionInfo_t *svritem = CAGetTCPSessionInfoFromEndpoint(endpoint, &index); + if (!svritem) + { + // There is no connection with the given endpoint, so the parameter is considered to be invalid. + result = CA_STATUS_INVALID_PARAM; + goto exit; + } + + if (svritem->fd < 0 || svritem->state != CONNECTED) + { + result = CA_DESTINATION_DISCONNECTED; + goto exit; + } + + int option = 0; + if (-1 == setsockopt(svritem->fd, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof(option))) + { + OIC_LOG_V(ERROR, TAG, "setsockopt SO_KEEPALIVE failed on socket(%d): %s", + svritem->fd, strerror(errno)); + result = CA_SOCKET_OPERATION_FAILED; + } + +exit: + oc_mutex_unlock(g_mutexObjectList); + OIC_LOG(INFO, TAG, "OUT - CATCPUnSetKeepAlive"); + return result; +} + +int CAGetLastErrorCode() +{ + return g_lastErrorCode; +} diff --git a/resource/csdk/connectivity/util/src/cautilinterface.c b/resource/csdk/connectivity/util/src/cautilinterface.c index 0a03b117e..c8a11bbcd 100644 --- a/resource/csdk/connectivity/util/src/cautilinterface.c +++ b/resource/csdk/connectivity/util/src/cautilinterface.c @@ -550,4 +550,23 @@ CAResult_t CAGetTCPIPHeader(CATransportAdapter_t adapter, int flag, TCPHeaderInf #endif return res; } + +CAResult_t CASetTCPKeepAlive(const CAEndpoint_t *endpoint, int time, int cnt, int intvl) +{ + OIC_LOG(DEBUG, TAG, "CASetTCPKeepAlive"); + return CATCPSetKeepAlive(endpoint, time, cnt, intvl); +} + +CAResult_t CAUnSetTCPKeepAlive(const CAEndpoint_t *endpoint) +{ + OIC_LOG(DEBUG, TAG, "CAUnSetTCPKeepAlive"); + return CATCPUnSetKeepAlive(endpoint); +} + +int CAGetTCPLastErrorCode() +{ + OIC_LOG(DEBUG, TAG, "CAGetTCPLastErrorCode"); + return CATCPGetLastErrorCode(); +} + #endif