tizen 2.3.1 release
[kernel/api/system-resource.git] / src / network / iface.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 /*
21  *
22  * @file iface.c
23  *
24  * @desc Utility for working with network interfaces
25  */
26
27
28 #include <errno.h>
29 #include <glib.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <stdbool.h>
33 #include <linux/un.h>
34 #include <net/if.h>
35 #include <pthread.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "config.h"
41 #include "config-parser.h"
42 #include "const.h"
43 #include "iface.h"
44 #include "macro.h"
45 #include "trace.h"
46
47 #define NET_INTERFACE_NAMES_FILE "/etc/resourced/network.conf"
48 #define IFACES_TYPE_SECTION "IFACES_TYPE"
49
50 static int iface_stat[RESOURCED_IFACE_LAST_ELEM - 1];
51 static GTree *iftypes; /* holds int key and value of type resourced_iface_type */
52 static GTree *ifnames; /* for keeping ifype - interface name association */
53
54 static pthread_rwlock_t iftypes_guard = PTHREAD_RWLOCK_INITIALIZER;
55 static pthread_rwlock_t ifnames_guard = PTHREAD_RWLOCK_INITIALIZER;
56
57
58 static const char *UEVENT_FMT = "/sys/class/net/%s/uevent";
59 static const char *DEVTYPE_KEY = "DEVTYPE";
60 static const char *WIRED_VALUE = "gadget";
61 static const char *WIFI_VALUE = "wlan";
62 static const char *BLUETOOTH_VALUE = "bluetooth";
63 static const char *DATACALL_VALUE = "datacall";
64 static const char *ALL_NET_IFACE_VALUE = "all";
65 static const char *UEVENT_DELIM = "=\n";
66
67 static GSList *ifnames_relations;
68
69 struct iface_relation {
70         resourced_iface_type iftype;
71         char ifname[MAX_NAME_LENGTH];
72 };
73
74 static gint compare_int(gconstpointer a, gconstpointer b,
75         gpointer UNUSED userdata)
76 {
77         if (a == b)
78                 return 0;
79         else if (a > b)
80                 return 1;
81         return -1;
82 }
83
84 static GTree *create_iface_tree(void)
85 {
86         return g_tree_new_full(compare_int,
87                 NULL, NULL, free);
88 }
89
90 static void put_iftype_to_tree(GTree *iftypes_tree, int ifindex, int iftype)
91 {
92         gpointer new_value;
93
94         ret_msg_if(!iftypes_tree, "Please provide valid argument!");
95         new_value = (gpointer)malloc(sizeof(int));
96         if (!new_value) {
97                 _E("Malloc of put_iftype_to_tree failed\n");
98                 return;
99         }
100         *(int *)new_value = iftype;
101         g_tree_replace(iftypes_tree, (gpointer)ifindex, new_value);
102 }
103
104 static void put_ifname_to_tree(GTree *ifnames_tree, char *ifname, int iftype)
105 {
106         int name_len = strlen(ifname) + 1;
107         gpointer new_value = (gpointer)malloc(name_len);
108         if (!new_value) {
109                 _E("Malloc of put_ifname_to_tree failed\n");
110                 return;
111         }
112         strncpy(new_value, ifname, name_len);
113
114         if (!ifnames_tree) {
115                 free(new_value);
116                 _E("Please provide valid argument!");
117                 return;
118         }
119         g_tree_replace(ifnames_tree, (gpointer)iftype, new_value);
120 }
121
122 static resourced_iface_type get_iftype_from_tree(GTree *iftypes_tree, int ifindex)
123 {
124         resourced_iface_type ret = RESOURCED_IFACE_UNKNOWN;
125         gpointer table_value;
126
127         ret_value_msg_if(!iftypes_tree, ret, "Please provide valid argument!");
128
129         pthread_rwlock_rdlock(&iftypes_guard);
130         table_value = g_tree_lookup(iftypes_tree, (gpointer)ifindex);
131         pthread_rwlock_unlock(&iftypes_guard);
132         if (table_value != NULL)
133                 ret = *(int *)table_value;
134
135         return ret;
136 }
137
138 static void free_iftypes_tree(GTree *iftypes_tree)
139 {
140         g_tree_destroy(iftypes_tree);
141 }
142
143 static void iface_stat_allowance(void)
144 {
145         size_t i;
146         for (i = 0; i < ARRAY_SIZE(iface_stat); ++i)
147                 iface_stat[i] = 1;
148 }
149
150 static resourced_iface_type get_predefined_iftype(const char *ifname)
151 {
152         struct iface_relation *relation;
153         GSList *iter;
154         gslist_for_each_item(iter, ifnames_relations) {
155                 relation = (struct iface_relation *)iter->data;
156                         if (strstr(ifname, relation->ifname))
157                                 return relation->iftype;
158         }
159         _D("Even in predefined interface name list, interface types wasn't "
160            " find for %s", ifname);
161         return RESOURCED_IFACE_UNKNOWN;
162 }
163
164 static resourced_iface_type read_iftype(const char *iface)
165 {
166         char buffer[UNIX_PATH_MAX];
167         char *key_buffer;
168         char *value_buffer;
169         char *saveptr;
170         FILE *uevent;
171         resourced_iface_type ret = RESOURCED_IFACE_UNKNOWN;
172
173         snprintf(buffer, UNIX_PATH_MAX, UEVENT_FMT, iface);
174         uevent = fopen(buffer, "r");
175
176         if (!uevent)
177                 return ret;
178
179         while (!feof(uevent)) {
180                 if (fgets(buffer, UNIX_PATH_MAX, uevent) == NULL)
181                         break;
182                 key_buffer = strtok_r(buffer, UEVENT_DELIM, &saveptr);
183                 value_buffer = strtok_r(NULL, UEVENT_DELIM, &saveptr);
184                 if (strcmp(key_buffer, DEVTYPE_KEY) != 0)
185                         continue;
186                 ret = convert_iftype(value_buffer);
187                 break;
188         }
189
190         fclose(uevent);
191
192         /* work around, in case of missing DEVTYPE field */
193         if (ret == RESOURCED_IFACE_UNKNOWN)
194                 ret = get_predefined_iftype(iface);
195
196         return ret;
197 }
198
199 static void reset_tree(GTree *new, GTree **old,
200         pthread_rwlock_t *guard)
201 {
202         GTree *release = *old;
203
204         pthread_rwlock_wrlock(guard);
205         *old = new;
206         pthread_rwlock_unlock(guard);
207         if (release)
208                 free_iftypes_tree(release);
209 }
210
211 bool is_address_exists(const char *name)
212 {
213 #ifdef SIOCDIFADDR
214         struct ifreq ifr;
215         static int fd;
216         if (!fd)
217                 fd = socket(AF_INET, SOCK_DGRAM, 0);
218
219         memset(&ifr, 0, sizeof(struct ifreq));
220         strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)-1);
221         return ioctl(fd, SIOCGIFADDR, &ifr) == 0;
222 #endif /* SIOCDIFADDR */
223         return true;
224 }
225
226 static int fill_ifaces_relation(struct parse_result *result,
227                                 void UNUSED *user_data)
228 {
229         struct iface_relation *relation;
230         if (strcmp(result->section, IFACES_TYPE_SECTION))
231                 return RESOURCED_ERROR_NONE;
232
233         relation = (struct iface_relation *)malloc(sizeof(struct iface_relation));
234
235         ret_value_msg_if(relation == NULL, RESOURCED_ERROR_NONE,
236                 "Failed to allocated memory!");
237
238         relation->iftype = convert_iftype(result->name);
239         STRING_SAVE_COPY(relation->ifname, result->value);
240
241         ifnames_relations = g_slist_prepend(ifnames_relations, relation);
242         return RESOURCED_ERROR_NONE;
243 }
244
245 int init_iftype(void)
246 {
247         int i, ret;
248         char buf[256];
249         resourced_iface_type iftype;
250         struct if_nameindex *ids = if_nameindex();
251         GTree *iftypes_next = create_iface_tree();
252         GTree *ifnames_next = create_iface_tree();
253
254         if (ids == NULL) {
255                 strerror_r(errno, buf, sizeof(buf));
256                 _E("Failed to initialize iftype table! errno: %d, %s",
257                         errno, buf);
258                 return RESOURCED_ERROR_FAIL;
259         }
260
261         if (!ifnames_relations) {
262                 ret = config_parse(NET_INTERFACE_NAMES_FILE,
263                                    fill_ifaces_relation, NULL);
264                 if (ret != 0)
265                         _D("Can't parse config file %s",
266                            NET_INTERFACE_NAMES_FILE);
267         }
268
269         iface_stat_allowance();
270
271         for (i = 0; ids[i].if_index != 0; ++i) {
272                 if (!is_address_exists(ids[i].if_name))
273                         continue;
274                 iftype = read_iftype(ids[i].if_name);
275                 put_iftype_to_tree(iftypes_next, ids[i].if_index, iftype);
276                 /*  we know here iftype/ids[i].if_name, lets populate
277                  *      ifnames_tree */
278                 put_ifname_to_tree(ifnames_next, ids[i].if_name, iftype);
279                 _D("ifname %s, ifype %d", ids[i].if_name, iftype);
280         }
281
282         /* Do not forget to free the memory */
283         if_freenameindex(ids);
284
285         reset_tree(iftypes_next, &iftypes, &iftypes_guard);
286         reset_tree(ifnames_next, &ifnames, &ifnames_guard);
287         return RESOURCED_ERROR_NONE;
288 }
289
290 void finalize_iftypes(void)
291 {
292         reset_tree(NULL, &iftypes, &iftypes_guard);
293         reset_tree(NULL, &ifnames, &ifnames_guard);
294         g_slist_free_full(ifnames_relations, free);
295 }
296
297 resourced_iface_type convert_iftype(const char *buffer)
298 {
299         if (!buffer) {
300                 _E("Malloc of answer_get_stat failed\n");
301                 return RESOURCED_IFACE_UNKNOWN;
302         }
303
304         if (strcmp(buffer, DATACALL_VALUE) == 0)
305                 return RESOURCED_IFACE_DATACALL;
306
307         if (strcmp(buffer, WIFI_VALUE) == 0)
308                 return RESOURCED_IFACE_WIFI;
309
310         if (strcmp(buffer, BLUETOOTH_VALUE) == 0)
311                 return RESOURCED_IFACE_BLUETOOTH;
312
313         if (strcmp(buffer, WIRED_VALUE) == 0)
314                 return RESOURCED_IFACE_WIRED;
315         if (strcmp(buffer, ALL_NET_IFACE_VALUE) == 0)
316                 return RESOURCED_IFACE_ALL;
317         return RESOURCED_IFACE_UNKNOWN;
318 }
319
320 int is_allowed_ifindex(int ifindex)
321 {
322         return iface_stat[get_iftype(ifindex)];
323 }
324
325 resourced_iface_type get_iftype(int ifindex)
326 {
327         return get_iftype_from_tree(iftypes, ifindex);
328 }
329
330 static gboolean print_ifname(gpointer key, gpointer value, gpointer data)
331 {
332         _D("ifname %s", (char *)value);
333         return FALSE;
334 }
335
336 static char *get_ifname_from_tree(GTree *ifnames_tree, int iftype)
337 {
338         char *ret = NULL;
339
340         ret_value_msg_if(!ifnames_tree, NULL, "Please provide valid argument!");
341
342         pthread_rwlock_rdlock(&ifnames_guard);
343         ret = (char *)g_tree_lookup(ifnames_tree, (gpointer)iftype);
344         pthread_rwlock_unlock(&ifnames_guard);
345         if (ret == NULL)
346                 g_tree_foreach(ifnames_tree, print_ifname, NULL);
347
348         return ret;
349 }
350
351 char *get_iftype_name(resourced_iface_type iftype)
352 {
353         return get_ifname_from_tree(ifnames, iftype);
354 }
355
356 static gboolean search_loopback(gpointer key,
357                   gpointer value,
358                   gpointer data)
359 {
360         int *res = (int *)data;
361         if (!value)
362                 return FALSE;
363         *res = *(int *)value == RESOURCED_IFACE_UNKNOWN ? TRUE : FALSE;
364         return *res;
365 }
366
367 static bool is_only_loopback(GTree *iftypes_tree)
368 {
369         int nodes = g_tree_nnodes(iftypes_tree);
370         int res = 0;
371
372         if (nodes > 1)
373                 return false;
374
375         g_tree_foreach(iftypes_tree, search_loopback, &res);
376         return res;
377 }
378
379 void for_each_ifindex(ifindex_iterator iter, void(*empty_func)(void *),
380         void *data)
381 {
382         pthread_rwlock_rdlock(&iftypes_guard);
383         if (!is_only_loopback(iftypes))
384                 g_tree_foreach(iftypes, (GTraverseFunc)iter, data);
385         else if (empty_func)
386                 empty_func(data);
387
388         pthread_rwlock_unlock(&iftypes_guard);
389 }
390
391 void set_wifi_allowance(const resourced_option_state wifi_option)
392 {
393         iface_stat[RESOURCED_IFACE_WIFI] = wifi_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
394 }
395
396 void set_datacall_allowance(const resourced_option_state datacall_option)
397 {
398         iface_stat[RESOURCED_IFACE_DATACALL] = datacall_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
399 }