1 /* ****************************************************************
3 * Copyright 2015 Samsung Electronics All Rights Reserved.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 ******************************************************************/
21 #include "oickeepalive.h"
25 #include "oic_malloc.h"
26 #include "oic_string.h"
29 #include "uarraylist.h"
30 #include "ocstackinternal.h"
31 #include "ocpayloadcbor.h"
32 #include "ocpayload.h"
33 #include "ocresourcehandler.h"
37 * Logging tag for module name.
39 #define TAG "OIC_RI_KEEPALIVE"
41 static const uint64_t USECS_PER_SEC = 1000000;
43 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 #define VERIFY_SUCCESS(op, successCode) { if ((op) != (successCode)) \
47 {OIC_LOG_V(FATAL, TAG, "%s failed!!", #op); goto exit;} }
49 #define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OIC_LOG((logLevel), \
50 TAG, #arg " is NULL"); return (retVal); } }
52 #define VERIFY_NON_NULL_NR(arg, logLevel) { if (!(arg)) { OIC_LOG((logLevel), \
53 TAG, #arg " is NULL"); return; } }
55 #define VERIFY_NON_NULL_V(arg) { if (!arg) {OIC_LOG_V(FATAL, TAG, "%s is NULL", #arg);\
59 * The KeepAlive table entries are removed
60 * if it can't receive response message within 60 seconds.
62 #define KEEPALIVE_RESPONSE_TIMEOUT_SEC 60
65 * The Min time interval value. (2 minutes)
66 * start from 2 minutes and increases in multiples of 2 up to a maximum of 64minutes.
68 #define KEEPALIVE_MIN_INTERVAL 2
71 * The Max time interval value. (64 minutes)
73 #define KEEPALIVE_MAX_INTERVAL 64
76 * KeepAlive key to parser Payload Table.
78 static const char INTERVAL[] = "in";
81 * To check if KeepAlive is initialized.
83 static bool g_isKeepAliveInitialized = false;
86 * Pointer to handle of the newly created KeepAlive resource.
88 static OCResourceHandle g_keepAliveHandle = NULL;
91 * KeepAlive table which holds connection interval.
93 static u_arraylist_t *g_keepAliveConnectionTable = NULL;
96 * KeepAlive table entries.
100 OCMode mode; /**< host Mode of Operation. */
101 CAEndpoint_t remoteAddr; /**< destination Address. */
102 uint32_t interval; /**< time interval for KeepAlive. in seconds.*/
103 bool sentPingMsg; /**< if oic client already sent ping message. */
104 uint64_t timeStamp; /**< last sent or received ping message. in microseconds. */
108 * Send disconnect message to remove connection.
110 static OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry);
113 * Send ping message to remote endpoint.
115 static OCStackResult SendPingMessage(KeepAliveEntry_t *entry);
118 * Ping Message callback registered with RI for KeepAlive Request.
120 static OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
121 OCClientResponse * clientResponse);
124 * This function creates KeepAlive resource.
125 * @return ::OC_STACK_OK or Appropriate error code.
127 static OCStackResult CreateKeepAliveResource();
130 * This function deletes KeepAlive resource.
131 * @return ::OC_STACK_OK or Appropriate error code.
133 static OCStackResult DeleteKeepAliveResource();
136 * API to handle the GET request received for a KeepAlive resource.
137 * @param[in] endPoint RemoteEndpoint which sent the packet.
138 * @param[in] requestInfo Received coap packet.
139 * @return ::OC_STACK_OK or Appropriate error code.
141 static OCStackResult HandleKeepAliveGETRequest(const CAEndpoint_t* endPoint,
142 const CARequestInfo_t* requestInfo);
145 * API to handle the PUT request received for a KeepAlive resource.
146 * @param[in] endPoint RemoteEndpoint which sent the packet.
147 * @param[in] requestInfo Received coap packet.
148 * @return ::OC_STACK_OK or Appropriate error code.
150 static OCStackResult HandleKeepAlivePUTRequest(const CAEndpoint_t* endPoint,
151 const CARequestInfo_t* requestInfo);
154 * API to handle the Response payload.
155 * @param[in] endpoint RemoteEndpoint which sent the packet.
156 * @param[in] responseCode Received reseponse code.
157 * @return ::OC_STACK_OK or Appropriate error code.
159 static OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
160 OCStackResult responseCode);
163 * Gets keepalive entry.
164 * @param[in] endpoint Remote Endpoint information (like ipaddress,
165 * port, reference uri and transport type) to
166 * which the ping message has to be sent.
167 * @param[out] index index of array list.
168 * @return KeepAlive entry to send ping message.
170 static KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index);
173 * Add keepalive entry.
174 * @param[in] endpoint Remote Endpoint information (like ipaddress,
175 * port, reference uri and transport type).
176 * @param[in] mode Whether it is OIC Server or OIC Client.
177 * @return The KeepAlive entry added in KeepAlive Table.
179 static KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode);
182 * Remove keepalive entry.
183 * @param[in] endpoint Remote Endpoint information (like ipaddress,
184 * port, reference uri and transport type).
185 * @return The KeepAlive entry removed in KeepAlive Table.
187 static OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint);
189 OCStackResult InitializeKeepAlive()
191 OIC_LOG(DEBUG, TAG, "InitializeKeepAlive IN");
192 if (g_isKeepAliveInitialized)
194 OIC_LOG(DEBUG, TAG, "KeepAlive already initialized");
198 // Create the KeepAlive Resource[/oic/ping].
199 OCStackResult result = CreateKeepAliveResource();
200 if (OC_STACK_OK != result)
202 OIC_LOG_V(ERROR, TAG, "CreateKeepAliveResource failed[%d]", result);
206 if (!g_keepAliveConnectionTable)
208 g_keepAliveConnectionTable = u_arraylist_create();
209 if (NULL == g_keepAliveConnectionTable)
211 OIC_LOG(ERROR, TAG, "Creating KeepAlive Table failed");
212 TerminateKeepAlive();
213 return OC_STACK_ERROR;
217 g_isKeepAliveInitialized = true;
219 OIC_LOG(DEBUG, TAG, "InitializeKeepAlive OUT");
223 OCStackResult TerminateKeepAlive()
225 OIC_LOG(DEBUG, TAG, "TerminateKeepAlive IN");
226 if (!g_isKeepAliveInitialized)
228 OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
229 return OC_STACK_ERROR;
232 // Delete the KeepAlive Resource[/oic/ping].
233 OCStackResult result = DeleteKeepAliveResource();
234 if (OC_STACK_OK != result)
236 OIC_LOG_V(ERROR, TAG, "DeleteKeepAliveResource failed[%d]", result);
240 if (NULL != g_keepAliveConnectionTable)
242 u_arraylist_destroy(g_keepAliveConnectionTable);
243 g_keepAliveConnectionTable = NULL;
246 g_isKeepAliveInitialized = false;
248 OIC_LOG(DEBUG, TAG, "TerminateKeepAlive OUT");
252 OCStackResult CreateKeepAliveResource()
254 OIC_LOG(DEBUG, TAG, "InitKeepAliveResource IN");
256 // Create a KeepAlive resource
257 OCStackResult result = OCCreateResource(&g_keepAliveHandle,
258 KEEPALIVE_RESOURCE_TYPE_NAME,
259 KEEPALIVE_RESOURCE_INTF_NAME,
260 KEEPALIVE_RESOURCE_URI,
265 if (OC_STACK_OK != result)
267 OIC_LOG_V(ERROR, TAG, "Create resource for KeepAlive failed[%d]", result);
270 OIC_LOG(DEBUG, TAG, "InitKeepAliveResource OUT");
274 OCStackResult DeleteKeepAliveResource()
276 OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource IN");
278 // Create a KeepAlive resource
279 OCStackResult result = OCDeleteResource(g_keepAliveHandle);
281 if (OC_STACK_OK != result)
283 OIC_LOG_V(ERROR, TAG, "Delete resource for KeepAlive failed[%d]", result);
286 OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource OUT");
290 OCStackResult HandleKeepAliveRequest(const CAEndpoint_t* endPoint,
291 const CARequestInfo_t* requestInfo)
293 VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
294 VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM);
296 OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest IN");
298 OCStackResult result = OC_STACK_OK;
299 if (CA_PUT == requestInfo->method)
301 result = HandleKeepAlivePUTRequest(endPoint, requestInfo);
303 else if (CA_GET == requestInfo->method)
305 result = HandleKeepAliveGETRequest(endPoint, requestInfo);
308 OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest OUT");
312 OCStackResult HandleKeepAliveGETRequest(const CAEndpoint_t* endPoint,
313 const CARequestInfo_t* requestInfo)
315 VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
316 VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM);
318 OIC_LOG_V(DEBUG, TAG, "Find Ping resource [%s]", requestInfo->info.resourceUri);
320 CAResponseResult_t result = CA_VALID;
321 OCResource *resourcePtr = FindResourceByUri(requestInfo->info.resourceUri);
324 // Resource URL not specified
325 OIC_LOG_V(DEBUG, TAG, "There is no Ping resource [%s]", requestInfo->info.resourceUri);
326 result = CA_NOT_FOUND;
329 SendDirectStackResponse(endPoint, requestInfo->info.messageId, result, requestInfo->info.type,
330 requestInfo->info.numOptions, requestInfo->info.options,
331 requestInfo->info.token, requestInfo->info.tokenLength,
332 requestInfo->info.resourceUri);
337 OCStackResult HandleKeepAlivePUTRequest(const CAEndpoint_t* endPoint,
338 const CARequestInfo_t* requestInfo)
340 VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
341 VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM);
343 // Get entry from KeepAlive table.
345 KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index);
348 OIC_LOG(ERROR, TAG, "Received the first keepalive message from client");
349 entry = AddKeepAliveEntry(endPoint, OC_SERVER);
352 OIC_LOG(ERROR, TAG, "Failed to add new keepalive entry");
353 return OC_STACK_ERROR;
357 OCPayload *ocPayload = NULL;
358 OCParsePayload(&ocPayload, PAYLOAD_TYPE_REPRESENTATION,
359 requestInfo->info.payload, requestInfo->info.payloadSize);
360 OCRepPayload *repPayload = (OCRepPayload *)ocPayload;
362 uint32_t interval = 0;
363 OCRepPayloadGetPropInt(repPayload, INTERVAL, &interval);
364 entry->interval = interval;
365 OIC_LOG_V(DEBUG, TAG, "Received interval is [%d]", entry->interval);
366 entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
368 // Send response message.
369 SendDirectStackResponse(endPoint, requestInfo->info.messageId, CA_VALID, requestInfo->info.type,
370 requestInfo->info.numOptions, requestInfo->info.options,
371 requestInfo->info.token, requestInfo->info.tokenLength,
372 requestInfo->info.resourceUri);
377 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
378 OCStackResult responseCode)
380 VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
382 OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse IN");
384 // Get entry from KeepAlive table.
386 KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index);
389 OIC_LOG(ERROR, TAG, "There is no connection info in KeepAlive table");
391 if (OC_STACK_NO_RESOURCE == responseCode)
393 OIC_LOG(ERROR, TAG, "Server doesn't have a ping resource");
394 return OC_STACK_ERROR;
396 else if (OC_STACK_OK == responseCode)
398 entry = AddKeepAliveEntry(endPoint, OC_CLIENT);
401 OIC_LOG(ERROR, TAG, "Failed to add new KeepAlive entry");
402 return OC_STACK_ERROR;
405 // Send first ping message
406 return SendPingMessage(entry);
410 // Set sentPingMsg values with false.
411 entry->sentPingMsg = false;
413 OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse OUT");
417 void ProcessKeepAlive()
419 if (!g_isKeepAliveInitialized)
421 OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
425 uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
427 for (uint32_t i = 0; i < len; i++)
429 KeepAliveEntry_t *entry = u_arraylist_get(g_keepAliveConnectionTable, i);
435 uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
436 if (OC_CLIENT == entry->mode)
438 if (entry->sentPingMsg)
441 * If an OIC Client does not receive the response within 1 minutes,
442 * terminate the connection.
443 * In this case the timeStamp means last time sent ping message.
445 if ((KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) <= currentTime - entry->timeStamp)
447 OIC_LOG(DEBUG, TAG, "Client does not receive the response within 1 minutes.");
449 // Send message to disconnect session.
450 SendDisconnectMessage(entry);
455 if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
456 <= currentTime - entry->timeStamp)
458 // Increase interval value.
459 if (KEEPALIVE_MAX_INTERVAL > entry->interval)
461 entry->interval = entry->interval << 1;
464 OCStackResult result = SendPingMessage(entry);
465 if (OC_STACK_OK != result)
467 OIC_LOG(ERROR, TAG, "Failed to send ping request");
473 else if (OC_SERVER == entry->mode)
476 * If an OIC Server does not receive a PUT request to ping resource
477 * within the specified interval time, terminate the connection.
478 * In this case the timeStamp means last time received ping message.
480 if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
481 <= currentTime - entry->timeStamp)
483 OIC_LOG(DEBUG, TAG, "Server does not receive a PUT request.");
484 SendDisconnectMessage(entry);
490 OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry)
492 VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
495 * Send empty message to disconnect a connection.
496 * If CA get the empty message from RI, CA will disconnect a connection.
498 CARequestInfo_t requestInfo = { .method = CA_PUT };
499 return CASendRequest(&entry->remoteAddr, &requestInfo);
502 OCStackResult SendPingMessage(KeepAliveEntry_t *entry)
504 VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
506 // Send ping message.
507 OCCallbackData pingData = { .cb = PingRequestCallback };
508 OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
509 CopyEndpointToDevAddr(&(entry->remoteAddr), &devAddr);
511 OCRepPayload *payload = OCRepPayloadCreate();
514 OIC_LOG(ERROR, TAG, "Failed to allocate Payload");
515 return OC_STACK_ERROR;
517 payload->base.type = PAYLOAD_TYPE_REPRESENTATION;
518 OCRepPayloadSetPropInt(payload, INTERVAL, entry->interval);
520 OCDoResource(NULL, OC_REST_PUT, KEEPALIVE_RESOURCE_URI, &devAddr,
521 (OCPayload *) payload, CT_ADAPTER_TCP, OC_LOW_QOS, &pingData, NULL, 0);
523 // Update timeStamp with time sent ping message for next ping message.
524 entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
525 entry->sentPingMsg = true;
527 OIC_LOG_V(DEBUG, TAG, "Client sent ping message, interval [%d]", entry->interval);
532 OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
533 OCClientResponse *clientResponse)
535 OIC_LOG(DEBUG, TAG, "PingRequestCallback IN");
538 if (NULL == clientResponse)
540 OIC_LOG(ERROR, TAG, "clientResponse is NULL");
541 return OC_STACK_KEEP_TRANSACTION;
544 CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP };
545 CopyDevAddrToEndpoint(&(clientResponse->devAddr), &endpoint);
547 HandleKeepAliveResponse(&endpoint, clientResponse->result);
549 OIC_LOG(DEBUG, TAG, "PingRequestCallback OUT");
550 return OC_STACK_KEEP_TRANSACTION;
553 KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index)
555 if (!g_keepAliveConnectionTable)
557 OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
561 uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
563 for (uint32_t i = 0; i < len; i++)
565 KeepAliveEntry_t *entry = u_arraylist_get(g_keepAliveConnectionTable, i);
571 if (!strncmp(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr))
572 && (entry->remoteAddr.port == endpoint->port))
574 OIC_LOG(DEBUG, TAG, "Connection Info found in KeepAlive table");
583 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode)
587 OIC_LOG(ERROR, TAG, "endpoint is NULL");
591 if (!g_keepAliveConnectionTable)
593 OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
597 KeepAliveEntry_t *entry = (KeepAliveEntry_t *) OICCalloc(1, sizeof(KeepAliveEntry_t));
600 OIC_LOG(ERROR, TAG, "Failed to Calloc KeepAlive Entry");
605 entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
606 entry->interval = KEEPALIVE_MIN_INTERVAL;
607 entry->remoteAddr.adapter = endpoint->adapter;
608 entry->remoteAddr.flags = endpoint->flags;
609 entry->remoteAddr.interface = endpoint->interface;
610 entry->remoteAddr.port = endpoint->port;
611 strncpy(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr));
613 bool result = u_arraylist_add(g_keepAliveConnectionTable, (void *)entry);
616 OIC_LOG(ERROR, TAG, "Adding node to head failed");
624 OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint)
626 VERIFY_NON_NULL(endpoint, FATAL, OC_STACK_INVALID_PARAM);
629 KeepAliveEntry_t *entry = GetEntryFromEndpoint(endpoint, &index);
632 OIC_LOG(ERROR, TAG, "There is no entry in keepalive table.");
633 return OC_STACK_ERROR;
636 KeepAliveEntry_t *removedEntry = u_arraylist_remove(g_keepAliveConnectionTable, index);
637 if (NULL == removedEntry)
639 OIC_LOG(ERROR, TAG, "Removed Entry is NULL");
640 return OC_STACK_ERROR;
643 OIC_LOG_V(DEBUG, TAG, "Remove Connection Info from KeepAlive table, "
644 "remote addr=%s port:%d", removedEntry->remoteAddr.addr,
645 removedEntry->remoteAddr.port);
647 OICFree(removedEntry);
652 void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint)
654 VERIFY_NON_NULL_NR(endpoint, FATAL);
656 OIC_LOG(DEBUG, TAG, "Received the connected device information from CA");
658 // Send discover message to find ping resource
659 OCCallbackData pingData = { .cb = PingRequestCallback };
660 OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
661 CopyEndpointToDevAddr(endpoint, &devAddr);
662 return OCDoResource(NULL, OC_REST_DISCOVER, KEEPALIVE_RESOURCE_URI, &devAddr, NULL,
663 OC_ADAPTER_TCP, OC_HIGH_QOS, &pingData, NULL, 0);
666 void HandleKeepAliveDisconnCB(const CAEndpoint_t *endpoint)
668 VERIFY_NON_NULL_NR(endpoint, FATAL);
670 OIC_LOG(DEBUG, TAG, "Received the disconnected device information from CA");
672 OCStackResult result = RemoveKeepAliveEntry(endpoint);
673 if(result != OC_STACK_OK)
675 OIC_LOG(ERROR, TAG, "Failed to remove entry");