New APIs to configure TCP Socket Keep-Alive. 97/216597/3
authorSenthil Kumar G S <senthil.gs@samsung.com>
Mon, 14 Oct 2019 06:46:30 +0000 (12:16 +0530)
committerPyun DoHyun <dh79.pyun@samsung.com>
Wed, 30 Oct 2019 00:55:35 +0000 (00:55 +0000)
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 <senthil.gs@samsung.com>
Signed-off-by: Sudipto <sudipto.bal@samsung.com>
resource/c_common/octhread/include/octhread.h
resource/c_common/octhread/src/noop/octhread.c
resource/csdk/connectivity/api/cautilinterface.h
resource/csdk/connectivity/inc/catcpadapter.h
resource/csdk/connectivity/inc/catcpinterface.h
resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c
resource/csdk/connectivity/src/tcp_adapter/catcpserver.c
resource/csdk/connectivity/util/src/cautilinterface.c

index 3caffa0..54ba69d 100755 (executable)
@@ -142,6 +142,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.
  *
  * @param  mutex  The mutex to be locked.
index 39cfff9..c8aafbe 100755 (executable)
@@ -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;
index 563c781..af1f3b0 100644 (file)
@@ -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
index 2c9bb43..252e80b 100644 (file)
@@ -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
index d617365..24d69d5 100644 (file)
@@ -271,6 +271,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.
  *
  * @return  ::CA_STATUS_OK or Appropriate error code.
index 3dd9a7a..4c5b995 100644 (file)
@@ -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
index d4bd440..6a2e24c 100644 (file)
@@ -32,6 +32,7 @@
 #include <fcntl.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <net/if.h>
 #include <errno.h>
 
@@ -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;
+}
index 0a03b11..c8a11bb 100644 (file)
@@ -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