replace : iotivity -> iotivity-sec
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / caretransmission.c
1 /******************************************************************
2  *
3  * Copyright 2014 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  ******************************************************************/
20
21 // Defining _BSD_SOURCE or _DEFAULT_SOURCE causes header files to expose
22 // definitions that may otherwise be skipped. Skipping can cause implicit
23 // declaration warnings and/or bugs and subtle problems in code execution.
24 // For glibc information on feature test macros,
25 // Refer http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
26 //
27 // This file requires #define use due to random()
28 // For details on compatibility and glibc support,
29 // Refer http://www.gnu.org/software/libc/manual/html_node/BSD-Random.html
30 #define _DEFAULT_SOURCE
31
32 // Defining _POSIX_C_SOURCE macro with 199309L (or greater) as value
33 // causes header files to expose definitions
34 // corresponding to the POSIX.1b, Real-time extensions
35 // (IEEE Std 1003.1b-1993) specification
36 //
37 // For this specific file, see use of clock_gettime,
38 // Refer to http://pubs.opengroup.org/stage7tc1/functions/clock_gettime.html
39 // and to http://man7.org/linux/man-pages/man2/clock_gettime.2.html
40 #ifndef _POSIX_C_SOURCE
41 #define _POSIX_C_SOURCE 200809L
42 #endif
43
44 #ifdef __TIZENRT__
45 #include <tinyara/config.h>
46 #endif
47
48 #include "iotivity_config.h"
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #ifdef TB_LOG
53 #include <inttypes.h>
54 #endif
55
56 #ifndef SINGLE_THREAD
57 #ifdef HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60 #ifdef HAVE_SYS_TIME_H
61 #include <sys/time.h>
62 #endif
63 #ifdef HAVE_SYS_TIMEB_H
64 #include <sys/timeb.h>
65 #endif
66 #ifdef HAVE_TIME_H
67 #include <time.h>
68 #endif
69 #endif
70
71 #if defined(__ANDROID__)
72 #include <linux/time.h>
73 #endif
74
75 #include "caretransmission.h"
76 #include "caremotehandler.h"
77 #include "caprotocolmessage.h"
78 #include "oic_malloc.h"
79 #include "oic_time.h"
80 #include "ocrandom.h"
81 #include "logger.h"
82
83 #define TAG "OIC_CA_RETRANS"
84
85 typedef struct
86 {
87     uint64_t timeStamp;                 /**< last sent time. microseconds */
88 #ifndef SINGLE_THREAD
89     uint64_t timeout;                   /**< timeout value. microseconds */
90 #endif
91     uint8_t triedCount;                 /**< retransmission count */
92     uint16_t messageId;                 /**< coap PDU message id */
93     CADataType_t dataType;              /**< data Type (Request/Response) */
94     CAEndpoint_t *endpoint;             /**< remote endpoint */
95     void *pdu;                          /**< coap PDU */
96     uint32_t size;                      /**< coap PDU size */
97 } CARetransmissionData_t;
98
99 static const uint64_t USECS_PER_SEC = 1000000;
100 static const uint64_t MSECS_PER_SEC = 1000;
101
102 #ifndef SINGLE_THREAD
103 /**
104  * @brief   timeout value is
105  *          between DEFAULT_ACK_TIMEOUT_SEC and
106  *          (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
107  *          DEFAULT_RANDOM_FACTOR       1.5 (CoAP)
108  * @return  microseconds.
109  */
110 static uint64_t CAGetTimeoutValue()
111 {
112     return ((DEFAULT_ACK_TIMEOUT_SEC * MSECS_PER_SEC)
113             + ((MSECS_PER_SEC * OCGetRandomByte()) >> 8)) * MSECS_PER_SEC;
114 }
115
116 CAResult_t CARetransmissionStart(CARetransmission_t *context)
117 {
118     if (NULL == context)
119     {
120         OIC_LOG(ERROR, TAG, "context is empty");
121         return CA_STATUS_INVALID_PARAM;
122     }
123
124     if (NULL == context->threadPool)
125     {
126         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
127         return CA_STATUS_INVALID_PARAM;
128     }
129 #ifndef __TIZENRT__
130     CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
131                                              context, NULL);
132 #else
133     CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
134                                              context, NULL, "IoT_Retransmit",
135                                              CONFIG_IOTIVITY_RETRANSMIT_PTHREAD_STACKSIZE);
136 #endif
137     if (CA_STATUS_OK != res)
138     {
139         OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
140         return res;
141     }
142
143     return res;
144 }
145 #endif
146
147 /**
148  * @brief   check timeout routine
149  * @param   currentTime     [IN]microseconds
150  * @param   retData         [IN]retransmission data
151  * @return  true if the timeout period has elapsed, false otherwise
152  */
153 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
154 {
155 #ifndef SINGLE_THREAD
156     // #1. calculate timeout
157     uint32_t milliTimeoutValue = retData->timeout * 0.001;
158     uint64_t timeout = ((uint64_t) milliTimeoutValue << retData->triedCount) * MSECS_PER_SEC;
159
160     if (currentTime >= retData->timeStamp + timeout)
161     {
162 #ifndef __TIZENRT__
163         OIC_LOG_V(DEBUG, TAG, "%" PRIu64 " microseconds time out!!, tried count(%d)",
164                   timeout, retData->triedCount);
165 #endif
166         return true;
167     }
168 #else
169     // #1. calculate timeout
170     uint64_t timeOut = (2 << retData->triedCount) * USECS_PER_SEC;
171
172     if (currentTime >= retData->timeStamp + timeOut)
173     {
174         OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
175                   (2 << retData->triedCount), retData->triedCount);
176         return true;
177     }
178 #endif
179     return false;
180 }
181
182 static void CACheckRetransmissionList(CARetransmission_t *context)
183 {
184     if (NULL == context)
185     {
186         OIC_LOG(ERROR, TAG, "context is null");
187         return;
188     }
189
190     // mutex lock
191     oc_mutex_lock(context->threadMutex);
192
193     uint32_t i = 0;
194     uint32_t len = u_arraylist_length(context->dataList);
195
196     for (i = 0; i < len; i++)
197     {
198         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
199
200         if (NULL == retData)
201         {
202             continue;
203         }
204
205         uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
206
207         if (CACheckTimeout(currentTime, retData))
208         {
209             // #2. if time's up, send the data.
210             if (NULL != context->dataSendMethod)
211             {
212                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
213                           retData->messageId);
214                 context->dataSendMethod(retData->endpoint, retData->pdu,
215                                         retData->size, retData->dataType);
216             }
217
218             // #3. increase the retransmission count and update timestamp.
219             retData->timeStamp = currentTime;
220             retData->triedCount++;
221         }
222
223         // #4. if tried count is max, remove the retransmission data from list.
224         if (retData->triedCount >= context->config.tryingCount)
225         {
226             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
227             if (NULL == removedData)
228             {
229                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
230                 // mutex unlock
231                 oc_mutex_unlock(context->threadMutex);
232                 return;
233             }
234             OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
235                       "msgid=%d", removedData->messageId);
236
237             // callback for retransmit timeout
238             if (NULL != context->timeoutCallback)
239             {
240                 context->timeoutCallback(removedData->endpoint, removedData->pdu,
241                                          removedData->size);
242             }
243
244             CAFreeEndpoint(removedData->endpoint);
245             OICFree(removedData->pdu);
246
247             OICFree(removedData);
248
249             // modify loop value.
250             len = u_arraylist_length(context->dataList);
251             --i;
252         }
253     }
254
255     // mutex unlock
256     oc_mutex_unlock(context->threadMutex);
257 }
258
259 void CARetransmissionBaseRoutine(void *threadValue)
260 {
261     OIC_LOG(DEBUG, TAG, "retransmission main thread start");
262
263     CARetransmission_t *context = (CARetransmission_t *) threadValue;
264
265     if (NULL == context)
266     {
267         OIC_LOG(ERROR, TAG, "thread data passing error");
268
269         return;
270     }
271
272 #ifdef SINGLE_THREAD
273     if (true == context->isStop)
274     {
275         OIC_LOG(DEBUG, TAG, "thread stopped");
276         return;
277     }
278     CACheckRetransmissionList(context);
279 #else
280
281     while (!context->isStop)
282     {
283         // mutex lock
284         oc_mutex_lock(context->threadMutex);
285
286         if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
287         {
288             // if list is empty, thread will wait
289             OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
290
291             // wait
292             oc_cond_wait(context->threadCond, context->threadMutex);
293
294             OIC_LOG(DEBUG, TAG, "wake up..");
295         }
296         else if (!context->isStop)
297         {
298             // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
299 #ifndef __TIZENRT__
300             OIC_LOG_V(DEBUG, TAG, "wait..(%" PRIu64 ")microseconds",
301                       RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
302 #endif
303
304             // wait
305             uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
306             oc_cond_wait_for(context->threadCond, context->threadMutex, absTime );
307         }
308         else
309         {
310             // we are stopping, so we want to unlock and finish stopping
311         }
312
313         // mutex unlock
314         oc_mutex_unlock(context->threadMutex);
315
316         // check stop flag
317         if (context->isStop)
318         {
319             continue;
320         }
321
322         CACheckRetransmissionList(context);
323     }
324
325     oc_mutex_lock(context->threadMutex);
326     oc_cond_signal(context->threadCond);
327     oc_mutex_unlock(context->threadMutex);
328
329 #endif
330     OIC_LOG(DEBUG, TAG, "retransmission main thread end");
331
332 }
333
334 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
335                                       ca_thread_pool_t handle,
336                                       CADataSendMethod_t retransmissionSendMethod,
337                                       CATimeoutCallback_t timeoutCallback,
338                                       CARetransmissionConfig_t* config)
339 {
340     if (NULL == context)
341     {
342         OIC_LOG(ERROR, TAG, "thread instance is empty");
343         return CA_STATUS_INVALID_PARAM;
344     }
345 #ifndef SINGLE_THREAD
346     if (NULL == handle)
347     {
348         OIC_LOG(ERROR, TAG, "thread pool handle is empty");
349         return CA_STATUS_INVALID_PARAM;
350     }
351 #endif
352     OIC_LOG(DEBUG, TAG, "thread initialize");
353
354     memset(context, 0, sizeof(CARetransmission_t));
355
356     CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
357                                      .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
358
359     if (config)
360     {
361         cfg = *config;
362     }
363
364     // set send thread data
365     context->threadPool = handle;
366     context->threadMutex = oc_mutex_new();
367     context->threadCond = oc_cond_new();
368     context->dataSendMethod = retransmissionSendMethod;
369     context->timeoutCallback = timeoutCallback;
370     context->config = cfg;
371     context->isStop = false;
372     context->dataList = u_arraylist_create();
373
374     return CA_STATUS_OK;
375 }
376
377 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
378                                     const CAEndpoint_t *endpoint,
379                                     CADataType_t dataType,
380                                     const void *pdu, uint32_t size)
381 {
382     if (NULL == context || NULL == endpoint || NULL == pdu)
383     {
384         OIC_LOG(ERROR, TAG, "invalid parameter");
385         return CA_STATUS_INVALID_PARAM;
386     }
387
388     // #0. check support transport type
389     if (!(context->config.supportType & endpoint->adapter))
390     {
391         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
392         return CA_NOT_SUPPORTED;
393     }
394
395     // #1. check PDU method type and get message id.
396     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
397     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
398
399     OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
400
401     if (CA_MSG_CONFIRM != type)
402     {
403         OIC_LOG(DEBUG, TAG, "not supported message type");
404         return CA_NOT_SUPPORTED;
405     }
406
407     // create retransmission data
408     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
409                                           1, sizeof(CARetransmissionData_t));
410
411     if (NULL == retData)
412     {
413         OIC_LOG(ERROR, TAG, "memory error");
414         return CA_MEMORY_ALLOC_FAILED;
415     }
416
417     // copy PDU data
418     void *pduData = (void *) OICMalloc(size);
419     if (NULL == pduData)
420     {
421         OICFree(retData);
422         OIC_LOG(ERROR, TAG, "memory error");
423         return CA_MEMORY_ALLOC_FAILED;
424     }
425     memcpy(pduData, pdu, size);
426
427     // clone remote endpoint
428     CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
429     if (NULL == remoteEndpoint)
430     {
431         OICFree(retData);
432         OICFree(pduData);
433         OIC_LOG(ERROR, TAG, "memory error");
434         return CA_MEMORY_ALLOC_FAILED;
435     }
436
437     // #2. add additional information. (time stamp, retransmission count...)
438     retData->timeStamp = OICGetCurrentTime(TIME_IN_US);
439 #ifndef SINGLE_THREAD
440     retData->timeout = CAGetTimeoutValue();
441 #endif
442     retData->triedCount = 0;
443     retData->messageId = messageId;
444     retData->endpoint = remoteEndpoint;
445     retData->pdu = pduData;
446     retData->size = size;
447     retData->dataType = dataType;
448 #ifndef SINGLE_THREAD
449     // mutex lock
450     oc_mutex_lock(context->threadMutex);
451
452     uint32_t i = 0;
453     uint32_t len = u_arraylist_length(context->dataList);
454
455     // #3. add data into list
456     for (i = 0; i < len; i++)
457     {
458         CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
459
460         if (NULL == currData)
461         {
462             continue;
463         }
464
465         // found index
466         if (NULL != currData->endpoint && currData->messageId == messageId
467             && (currData->endpoint->adapter == endpoint->adapter))
468         {
469             OIC_LOG(ERROR, TAG, "Duplicate message ID");
470
471             // mutex unlock
472             oc_mutex_unlock(context->threadMutex);
473
474             OICFree(retData);
475             OICFree(pduData);
476             OICFree(remoteEndpoint);
477             return CA_STATUS_FAILED;
478         }
479     }
480
481     bool res = u_arraylist_add(context->dataList, (void *) retData);
482     if (!res)
483     {
484         OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
485
486         oc_mutex_unlock(context->threadMutex);
487
488         OICFree(retData);
489         OICFree(pduData);
490         OICFree(remoteEndpoint);
491         return CA_MEMORY_ALLOC_FAILED;
492     }
493
494     // notify the thread
495     oc_cond_signal(context->threadCond);
496
497     // mutex unlock
498     oc_mutex_unlock(context->threadMutex);
499
500 #else
501     bool res = u_arraylist_add(context->dataList, (void *) retData);
502     if (!res)
503     {
504         OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
505
506         oc_mutex_unlock(context->threadMutex);
507
508         OICFree(retData);
509         OICFree(pduData);
510         OICFree(remoteEndpoint);
511         return CA_MEMORY_ALLOC_FAILED;
512     }
513
514     CACheckRetransmissionList(context);
515 #endif
516     return CA_STATUS_OK;
517 }
518
519 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
520                                         const CAEndpoint_t *endpoint, const void *pdu,
521                                         uint32_t size, void **retransmissionPdu)
522 {
523     OIC_LOG(DEBUG, TAG, "IN");
524     if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
525     {
526         OIC_LOG(ERROR, TAG, "invalid parameter");
527         return CA_STATUS_INVALID_PARAM;
528     }
529
530     // #0. check support transport type
531     if (!(context->config.supportType & endpoint->adapter))
532     {
533         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
534         return CA_STATUS_OK;
535     }
536
537     // #1. check PDU method type and get message id.
538     // ACK, RST --> remove the CON data
539     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
540     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
541     CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
542
543     OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
544               type, messageId, code);
545
546     if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
547         || (CA_MSG_RESET == type && CA_EMPTY != code))
548     {
549         return CA_STATUS_OK;
550     }
551
552     // mutex lock
553     oc_mutex_lock(context->threadMutex);
554     uint32_t len = u_arraylist_length(context->dataList);
555
556     // find index
557     uint32_t i;
558     for (i = 0; i < len; i++)
559     {
560         CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
561                 context->dataList, i);
562
563         if (NULL == retData)
564         {
565             continue;
566         }
567
568         // found index
569         if (NULL != retData->endpoint && retData->messageId == messageId
570             && (retData->endpoint->adapter == endpoint->adapter))
571         {
572             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
573             // if retransmission was finish..token will be unavailable.
574             if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
575             {
576                 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
577
578                 if (NULL == retData->pdu)
579                 {
580                     OIC_LOG(ERROR, TAG, "retData->pdu is null");
581                     OICFree(retData);
582                     // mutex unlock
583                     oc_mutex_unlock(context->threadMutex);
584
585                     return CA_STATUS_FAILED;
586                 }
587
588                 // copy PDU data
589                 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
590                 if ((*retransmissionPdu) == NULL)
591                 {
592                     OICFree(retData);
593                     OIC_LOG(ERROR, TAG, "memory error");
594
595                     // mutex unlock
596                     oc_mutex_unlock(context->threadMutex);
597
598                     return CA_MEMORY_ALLOC_FAILED;
599                 }
600                 memcpy((*retransmissionPdu), retData->pdu, retData->size);
601             }
602
603             // #2. remove data from list
604             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
605             if (NULL == removedData)
606             {
607                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
608
609                 // mutex unlock
610                 oc_mutex_unlock(context->threadMutex);
611
612                 return CA_STATUS_FAILED;
613             }
614
615             OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
616
617             CAFreeEndpoint(removedData->endpoint);
618             OICFree(removedData->pdu);
619             OICFree(removedData);
620
621             break;
622         }
623     }
624
625     // mutex unlock
626     oc_mutex_unlock(context->threadMutex);
627
628     OIC_LOG(DEBUG, TAG, "OUT");
629     return CA_STATUS_OK;
630 }
631
632 CAResult_t CARetransmissionClearAdapterData(CARetransmission_t *context, CATransportAdapter_t type)
633 {
634     if (NULL == context)
635     {
636         OIC_LOG(ERROR, TAG, "thread instance is empty..");
637         return CA_STATUS_INVALID_PARAM;
638     }
639
640     OIC_LOG_V(DEBUG, TAG, "clear queue data for Adapter : %d ", type);
641
642     // mutex lock
643     oc_mutex_lock(context->threadMutex);
644
645     uint32_t len = u_arraylist_length(context->dataList);
646
647     // find index
648     uint32_t i;
649     for (i = 0; i < len; i++)
650     {
651         // get data
652         CARetransmissionData_t *removedData = (CARetransmissionData_t *) u_arraylist_get(
653             context->dataList, i);
654
655         // free
656         if (NULL != removedData && NULL != removedData->endpoint &&
657                 removedData->endpoint->adapter == type)
658         {
659             CAFreeEndpoint(removedData->endpoint);
660             OICFree(removedData->pdu);
661             OICFree(removedData);
662         }
663     }
664
665     // mutex unlock
666     oc_mutex_unlock(context->threadMutex);
667
668     return CA_STATUS_OK;
669 }
670
671 CAResult_t CARetransmissionStop(CARetransmission_t *context)
672 {
673     if (NULL == context)
674     {
675         OIC_LOG(ERROR, TAG, "context is empty..");
676         return CA_STATUS_INVALID_PARAM;
677     }
678
679     OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
680
681     // mutex lock
682     oc_mutex_lock(context->threadMutex);
683
684     // set stop flag
685     context->isStop = true;
686
687     // notify the thread
688     oc_cond_signal(context->threadCond);
689
690     oc_cond_wait(context->threadCond, context->threadMutex);
691
692     // mutex unlock
693     oc_mutex_unlock(context->threadMutex);
694
695     return CA_STATUS_OK;
696 }
697
698 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
699 {
700     if (NULL == context)
701     {
702         OIC_LOG(ERROR, TAG, "context is empty..");
703         return CA_STATUS_INVALID_PARAM;
704     }
705
706     OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
707
708     oc_mutex_free(context->threadMutex);
709     context->threadMutex = NULL;
710     oc_cond_free(context->threadCond);
711     u_arraylist_free(&context->dataList);
712
713     return CA_STATUS_OK;
714 }