[IOT-1361]Change "CAFindInterfaceChange()" to support IPv4/6
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / ip_adapter / android / caipnwmonitor.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 #include "caipinterface.h"
22
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netdb.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <unistd.h>
29
30 #include <arpa/inet.h>
31 #include <linux/if.h>
32 #include <coap/utlist.h>
33
34 #include "caadapterutils.h"
35 #include "caipnwmonitor.h"
36 #include "logger.h"
37 #include "oic_malloc.h"
38 #include "oic_string.h"
39 #include "org_iotivity_ca_CaIpInterface.h"
40
41 #define TAG "OIC_CA_IP_MONITOR"
42 #define NETLINK_MESSAGE_LENGTH  (4096)
43
44 /**
45  * Used to storing adapter changes callback interface.
46  */
47 static struct CAIPCBData_t *g_adapterCallbackList = NULL;
48
49 /**
50  * Create new interface item to add in activated interface list.
51  * @param[in]  index    Network interface index number.
52  * @param[in]  name     Network interface name.
53  * @param[in]  family   Network interface family type.
54  * @param[in]  addr     New interface address.
55  * @param[in]  flags    The active flag word of a device.
56  * @return  CAInterface_t objects.
57  */
58 static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
59                                          const char *addr, int flags);
60
61 /**
62  * Add created new interface item activated interface list.
63  * @param[in]  iflist   Network interface array list.
64  * @param[in]  index    Network interface index number.
65  * @param[in]  name     Network interface name.
66  * @param[in]  family   Network interface family type.
67  * @param[in]  addr     New interface address.
68  * @param[in]  flags    The active flag word of a device.
69  * @return  ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h).
70  */
71 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
72                                      const char *name, int family, const char *addr, int flags);
73
74 /**
75  * Initialize JNI interface.
76  * @return  ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h).
77  */
78 CAResult_t CAIPJniInit();
79
80 /**
81  * Destroy JNI interface.
82  * @return  ::CA_STATUS_OK or ERROR CODES (::CAResult_t error codes in cacommon.h).
83  */
84 static CAResult_t CAIPDestroyJniInterface();
85
86 #define MAX_INTERFACE_INFO_LENGTH 1024 // allows 32 interfaces from SIOCGIFCONF
87
88 CAResult_t CAIPStartNetworkMonitor(CAIPAdapterStateChangeCallback callback,
89                                    CATransportAdapter_t adapter)
90 {
91     CAResult_t res = CAIPJniInit();
92     if (CA_STATUS_OK != res)
93     {
94         OIC_LOG(ERROR, TAG, "failed to initialize ip jni interface");
95         return res;
96     }
97
98     return CAIPSetNetworkMonitorCallback(callback, adapter);
99 }
100
101 CAResult_t CAIPStopNetworkMonitor(CATransportAdapter_t adapter)
102 {
103     CAIPUnSetNetworkMonitorCallback(adapter);
104
105     // if there is no callback to pass the changed status, stop monitoring.
106     if (!g_adapterCallbackList)
107     {
108         return CAIPDestroyJniInterface();
109     }
110
111     return CA_STATUS_OK;
112 }
113
114 int CAGetPollingInterval(int interval)
115 {
116     return interval;
117 }
118
119 static void CAIPPassNetworkChangesToAdapter(CANetworkStatus_t status)
120 {
121     CAIPCBData_t *cbitem = NULL;
122     LL_FOREACH(g_adapterCallbackList, cbitem)
123     {
124         if (cbitem && cbitem->adapter)
125         {
126             cbitem->callback(cbitem->adapter, status);
127         }
128     }
129 }
130
131 CAResult_t CAIPSetNetworkMonitorCallback(CAIPAdapterStateChangeCallback callback,
132                                          CATransportAdapter_t adapter)
133 {
134     if (!callback)
135     {
136         OIC_LOG(ERROR, TAG, "callback is null");
137         return CA_STATUS_INVALID_PARAM;
138     }
139
140     CAIPCBData_t *cbitem = NULL;
141     LL_FOREACH(g_adapterCallbackList, cbitem)
142     {
143         if (cbitem && adapter == cbitem->adapter && callback == cbitem->callback)
144         {
145             OIC_LOG(DEBUG, TAG, "this callback is already added");
146             return CA_STATUS_OK;
147         }
148     }
149
150     cbitem = (CAIPCBData_t *)OICCalloc(1, sizeof(*cbitem));
151     if (!cbitem)
152     {
153         OIC_LOG(ERROR, TAG, "Malloc failed");
154         return CA_STATUS_FAILED;
155     }
156
157     cbitem->adapter = adapter;
158     cbitem->callback = callback;
159     LL_APPEND(g_adapterCallbackList, cbitem);
160
161     return CA_STATUS_OK;
162 }
163
164 CAResult_t CAIPUnSetNetworkMonitorCallback(CATransportAdapter_t adapter)
165 {
166     CAIPCBData_t *cbitem = NULL;
167     CAIPCBData_t *tmpCbitem = NULL;
168     LL_FOREACH_SAFE(g_adapterCallbackList, cbitem, tmpCbitem)
169     {
170         if (cbitem && adapter == cbitem->adapter)
171         {
172             OIC_LOG(DEBUG, TAG, "remove specific callback");
173             LL_DELETE(g_adapterCallbackList, cbitem);
174             OICFree(cbitem);
175             return CA_STATUS_OK;
176         }
177     }
178     return CA_STATUS_OK;
179 }
180
181 u_arraylist_t *CAFindInterfaceChange()
182 {
183     // release netlink event
184     char *bufPtr = (char *)OICCalloc(NETLINK_MESSAGE_LENGTH, sizeof (char));
185     if (!bufPtr)
186     {
187         OIC_LOG(ERROR, TAG, "Malloc failed");
188         return NULL;
189     }
190     recv(caglobals.ip.netlinkFd, bufPtr, NETLINK_MESSAGE_LENGTH, 0);
191     OICFree(bufPtr);
192     bufPtr = NULL;
193
194     char buf[MAX_INTERFACE_INFO_LENGTH] = { 0 };
195     struct ifconf ifc  = { .ifc_len = MAX_INTERFACE_INFO_LENGTH, .ifc_buf = buf };
196
197     int s = caglobals.ip.u6.fd != -1 ? caglobals.ip.u6.fd : caglobals.ip.u4.fd;
198     if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
199     {
200         OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
201         return NULL;
202     }
203
204     u_arraylist_t *iflist = NULL;
205     CAInterface_t *foundNewInterface = NULL;
206
207     struct ifreq* ifr = ifc.ifc_req;
208     size_t interfaces = ifc.ifc_len / sizeof (ifc.ifc_req[0]);
209     size_t ifreqsize = ifc.ifc_len;
210
211     CAIfItem_t *previous = (CAIfItem_t *)OICMalloc(ifreqsize);
212     if (!previous)
213     {
214         OIC_LOG(ERROR, TAG, "OICMalloc failed");
215         return NULL;
216     }
217
218     memcpy(previous, caglobals.ip.nm.ifItems, ifreqsize);
219     size_t numprevious = caglobals.ip.nm.numIfItems;
220
221     if (ifreqsize > caglobals.ip.nm.sizeIfItems)
222     {
223
224         CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
225         if (!items)
226         {
227             OIC_LOG(ERROR, TAG, "OICRealloc failed");
228             OICFree(previous);
229             return NULL;
230         }
231         caglobals.ip.nm.ifItems = items;
232         caglobals.ip.nm.sizeIfItems = ifreqsize;
233     }
234
235     caglobals.ip.nm.numIfItems = 0;
236     for (size_t i = 0; i < interfaces; i++)
237     {
238         struct ifreq* item = &ifr[i];
239         char *name = item->ifr_name;
240
241         if (ioctl(s, SIOCGIFFLAGS, item) < 0)
242         {
243             OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
244             continue;
245         }
246         int16_t flags = item->ifr_flags;
247         if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
248         {
249             continue;
250         }
251         if (ioctl(s, SIOCGIFINDEX, item) < 0)
252         {
253             OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
254             continue;
255         }
256
257         int ifIndex = item->ifr_ifindex;
258         caglobals.ip.nm.ifItems[i].ifIndex = ifIndex;  // refill interface list
259         caglobals.ip.nm.numIfItems++;
260
261         if (foundNewInterface)
262         {
263             continue;   // continue updating interface list
264         }
265
266         // see if this interface didn't previously exist
267         bool found = false;
268         for (size_t j = 0; j < numprevious; j++)
269         {
270             if (ifIndex == previous[j].ifIndex)
271             {
272                 found = true;
273                 break;
274             }
275         }
276         if (found)
277         {
278             OIC_LOG_V(INFO, TAG, "Interface found: %s", name);
279             continue;
280         }
281
282         // Get address of network interface.
283         char addr[MAX_ADDR_STR_SIZE_CA] = { 0 };
284         struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
285         inet_ntop(AF_INET, (void *)&(sa->sin_addr), addr, sizeof(addr));
286
287         foundNewInterface = CANewInterfaceItem(ifIndex, name, AF_INET, addr, flags);
288     }
289
290     OICFree(previous);
291     // below code is temporary impl for consistency with caipserver.
292     // TODO: whole code which using ioctl will be removed and changed with internal getifaddrs impl.
293     if (foundNewInterface)
294     {
295         iflist = u_arraylist_create();
296
297         if (!iflist)
298         {
299             OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
300             goto exit;
301         }
302
303         CAResult_t result = CAAddInterfaceItem(iflist,
304                                                foundNewInterface->index,
305                                                foundNewInterface->name,
306                                                foundNewInterface->family,
307                                                foundNewInterface->addr,
308                                                foundNewInterface->flags);
309         if (CA_STATUS_OK != result)
310         {
311             goto exit;
312         }
313
314         // release foundNewInterface
315         OICFree(foundNewInterface);
316         foundNewInterface = NULL;
317     }
318     return iflist;
319 exit:
320     OICFree(foundNewInterface);
321     foundNewInterface = NULL;
322     u_arraylist_destroy(iflist);
323     return NULL;
324 }
325
326 u_arraylist_t *CAIPGetInterfaceInformation(int desiredIndex)
327 {
328     u_arraylist_t *iflist = u_arraylist_create();
329     if (!iflist)
330     {
331         OIC_LOG_V(ERROR, TAG, "Failed to create iflist: %s", strerror(errno));
332         return NULL;
333     }
334
335     char buf[MAX_INTERFACE_INFO_LENGTH] = { 0 };
336     struct ifconf ifc = { .ifc_len = MAX_INTERFACE_INFO_LENGTH, .ifc_buf = buf };
337
338     int s = caglobals.ip.u6.fd != -1 ? caglobals.ip.u6.fd : caglobals.ip.u4.fd;
339     if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
340     {
341         OIC_LOG_V(ERROR, TAG, "SIOCGIFCONF failed: %s", strerror(errno));
342         u_arraylist_destroy(iflist);
343         return NULL;
344     }
345
346     struct ifreq* ifr = ifc.ifc_req;
347     size_t interfaces = ifc.ifc_len / sizeof (ifc.ifc_req[0]);
348     size_t ifreqsize = ifc.ifc_len;
349
350     if (ifreqsize > caglobals.ip.nm.sizeIfItems)
351     {
352         CAIfItem_t *items = (CAIfItem_t *)OICRealloc(caglobals.ip.nm.ifItems, ifreqsize);
353         if (!items)
354         {
355             OIC_LOG(ERROR, TAG, "OICRealloc failed");
356             goto exit;
357         }
358         caglobals.ip.nm.ifItems = items;
359         caglobals.ip.nm.sizeIfItems = ifreqsize;
360     }
361
362     caglobals.ip.nm.numIfItems = 0;
363     for (size_t i = 0; i < interfaces; i++)
364     {
365         struct ifreq* item = &ifr[i];
366         char *name = item->ifr_name;
367
368         if (ioctl(s, SIOCGIFFLAGS, item) < 0)
369         {
370             OIC_LOG_V(ERROR, TAG, "SIOCGIFFLAGS failed: %s", strerror(errno));
371             continue;
372         }
373         int16_t flags = item->ifr_flags;
374         if ((flags & IFF_LOOPBACK) || !(flags & IFF_RUNNING))
375         {
376             continue;
377         }
378         if (ioctl(s, SIOCGIFINDEX, item) < 0)
379         {
380             OIC_LOG_V(ERROR, TAG, "SIOCGIFINDEX failed: %s", strerror(errno));
381             continue;
382         }
383
384         int ifindex = item->ifr_ifindex;
385         caglobals.ip.nm.ifItems[i].ifIndex = ifindex;
386         caglobals.ip.nm.numIfItems++;
387
388         if (desiredIndex && (ifindex != desiredIndex))
389         {
390             continue;
391         }
392
393         // Get address of network interface.
394         char addr[MAX_ADDR_STR_SIZE_CA] = { 0 };
395         struct sockaddr_in *sa = (struct sockaddr_in *)&item->ifr_addr;
396         inet_ntop(AF_INET, (void *)&(sa->sin_addr), addr, sizeof(addr));
397
398         // Add IPv4 interface
399         CAResult_t result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET, addr, flags);
400         if (CA_STATUS_OK != result)
401         {
402             goto exit;
403         }
404
405         // Add IPv6 interface
406         result = CAAddInterfaceItem(iflist, ifindex, name, AF_INET6, addr, flags);
407         if (CA_STATUS_OK != result)
408         {
409             goto exit;
410         }
411     }
412     return iflist;
413
414 exit:
415     u_arraylist_destroy(iflist);
416     return NULL;
417 }
418
419 static CAResult_t CAAddInterfaceItem(u_arraylist_t *iflist, int index,
420                                      const char *name, int family, const char *addr, int flags)
421 {
422     CAInterface_t *ifitem = CANewInterfaceItem(index, name, family, addr, flags);
423     if (!ifitem)
424     {
425         return CA_STATUS_FAILED;
426     }
427     bool result = u_arraylist_add(iflist, ifitem);
428     if (!result)
429     {
430         OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
431         OICFree(ifitem);
432         return CA_STATUS_FAILED;
433     }
434
435     return CA_STATUS_OK;
436 }
437
438 static CAInterface_t *CANewInterfaceItem(int index, const char *name, int family,
439                                          const char *addr, int flags)
440 {
441     CAInterface_t *ifitem = (CAInterface_t *)OICCalloc(1, sizeof (CAInterface_t));
442     if (!ifitem)
443     {
444         OIC_LOG(ERROR, TAG, "Malloc failed");
445         return NULL;
446     }
447
448     OICStrcpy(ifitem->name, sizeof (ifitem->name), name);
449     ifitem->index = index;
450     ifitem->family = family;
451     OICStrcpy(ifitem->addr, sizeof (ifitem->addr), addr);
452     ifitem->flags = flags;
453
454     return ifitem;
455 }
456
457 CAResult_t CAIPJniInit()
458 {
459     OIC_LOG(DEBUG, TAG, "CAIPJniInit_IN");
460
461     JavaVM *jvm = CANativeJNIGetJavaVM();
462     if (!jvm)
463     {
464         OIC_LOG(ERROR, TAG, "Could not get JavaVM pointer");
465         return CA_STATUS_FAILED;
466     }
467
468     jobject context = CANativeJNIGetContext();
469     if (!context)
470     {
471         OIC_LOG(ERROR, TAG, "unable to get application context");
472         return CA_STATUS_FAILED;
473     }
474
475     JNIEnv* env = NULL;
476     if ((*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
477     {
478         OIC_LOG(ERROR, TAG, "Could not get JNIEnv pointer");
479         return CA_STATUS_FAILED;
480     }
481
482     jmethodID mid_getApplicationContext = CAGetJNIMethodID(env, "android/content/Context",
483                                                            "getApplicationContext",
484                                                            "()Landroid/content/Context;");
485
486     if (!mid_getApplicationContext)
487     {
488         OIC_LOG(ERROR, TAG, "Could not get getApplicationContext method");
489         return CA_STATUS_FAILED;
490     }
491
492     jobject jApplicationContext = (*env)->CallObjectMethod(env, context,
493                                                            mid_getApplicationContext);
494     if (!jApplicationContext)
495     {
496         OIC_LOG(ERROR, TAG, "Could not get application context");
497         return CA_STATUS_FAILED;
498     }
499
500     jclass cls_CaIpInterface = (*env)->FindClass(env, "org/iotivity/ca/CaIpInterface");
501     if (!cls_CaIpInterface)
502     {
503         OIC_LOG(ERROR, TAG, "Could not get CaIpInterface class");
504         return CA_STATUS_FAILED;
505     }
506
507     jmethodID mid_CaIpInterface_ctor = (*env)->GetMethodID(env, cls_CaIpInterface, "<init>",
508                                                                    "(Landroid/content/Context;)V");
509     if (!mid_CaIpInterface_ctor)
510     {
511         OIC_LOG(ERROR, TAG, "Could not get CaIpInterface constructor method");
512         return CA_STATUS_FAILED;
513     }
514
515     (*env)->NewObject(env, cls_CaIpInterface, mid_CaIpInterface_ctor, jApplicationContext);
516     OIC_LOG(DEBUG, TAG, "Create CaIpInterface instance, success");
517
518     OIC_LOG(DEBUG, TAG, "CAIPJniInit_OUT");
519     return CA_STATUS_OK;
520 }
521
522 static CAResult_t CAIPDestroyJniInterface()
523 {
524     OIC_LOG(DEBUG, TAG, "CAIPDestroyJniInterface");
525
526     JavaVM *jvm = CANativeJNIGetJavaVM();
527     if (!jvm)
528     {
529         OIC_LOG(ERROR, TAG, "Could not get JavaVM pointer");
530         return CA_STATUS_FAILED;
531     }
532
533     bool isAttached = false;
534     JNIEnv* env = NULL;
535     jint res = (*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION_1_6);
536     if (JNI_OK != res)
537     {
538         OIC_LOG(INFO, TAG, "Could not get JNIEnv pointer");
539         res = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
540
541         if (JNI_OK != res)
542         {
543             OIC_LOG(ERROR, TAG, "AttachCurrentThread has failed");
544             return CA_STATUS_FAILED;
545         }
546         isAttached = true;
547     }
548
549     jclass jni_IpInterface = (*env)->FindClass(env, "org/iotivity/ca/CaIpInterface");
550     if (!jni_IpInterface)
551     {
552         OIC_LOG(ERROR, TAG, "Could not get CaIpInterface class");
553         goto error_exit;
554     }
555
556     jmethodID jni_InterfaceDestroyMethod = (*env)->GetStaticMethodID(env, jni_IpInterface,
557                                                                      "destroyIpInterface",
558                                                                      "()V");
559     if (!jni_InterfaceDestroyMethod)
560     {
561         OIC_LOG(ERROR, TAG, "Could not get CaIpInterface destroy method");
562         goto error_exit;
563     }
564
565     (*env)->CallStaticVoidMethod(env, jni_IpInterface, jni_InterfaceDestroyMethod);
566
567     if ((*env)->ExceptionCheck(env))
568     {
569         OIC_LOG(ERROR, TAG, "destroyIpInterface has failed");
570         (*env)->ExceptionDescribe(env);
571         (*env)->ExceptionClear(env);
572         goto error_exit;
573     }
574
575     OIC_LOG(DEBUG, TAG, "Destroy instance for CaIpInterface");
576
577     if (isAttached)
578     {
579         (*jvm)->DetachCurrentThread(jvm);
580     }
581
582     return CA_STATUS_OK;
583
584 error_exit:
585
586     if (isAttached)
587     {
588         (*jvm)->DetachCurrentThread(jvm);
589     }
590
591     return CA_STATUS_FAILED;
592 }
593
594 JNIEXPORT void JNICALL
595 Java_org_iotivity_ca_CaIpInterface_caIpStateEnabled(JNIEnv *env, jclass class)
596 {
597     (void)env;
598     (void)class;
599
600     OIC_LOG(DEBUG, TAG, "Wifi is in Activated State");
601     CAIPPassNetworkChangesToAdapter(CA_INTERFACE_UP);
602 }
603
604 JNIEXPORT void JNICALL
605 Java_org_iotivity_ca_CaIpInterface_caIpStateDisabled(JNIEnv *env, jclass class)
606 {
607     (void)env;
608     (void)class;
609
610     OIC_LOG(DEBUG, TAG, "Wifi is in Deactivated State");
611     CAIPPassNetworkChangesToAdapter(CA_INTERFACE_DOWN);
612 }