tizen 2.3 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         FILE *uevent;
170         resourced_iface_type ret = RESOURCED_IFACE_UNKNOWN;
171
172         snprintf(buffer, UNIX_PATH_MAX, UEVENT_FMT, iface);
173         uevent = fopen(buffer, "r");
174
175         if (!uevent)
176                 return ret;
177
178         while (!feof(uevent)) {
179                 if (fgets(buffer, UNIX_PATH_MAX, uevent) == NULL)
180                         break;
181                 key_buffer = strtok(buffer, UEVENT_DELIM);
182                 value_buffer = strtok(NULL, UEVENT_DELIM);
183                 if (strcmp(key_buffer, DEVTYPE_KEY) != 0)
184                         continue;
185                 ret = convert_iftype(value_buffer);
186                 break;
187         }
188
189         fclose(uevent);
190
191         /* work around, in case of missing DEVTYPE field */
192         if (ret == RESOURCED_IFACE_UNKNOWN)
193                 ret = get_predefined_iftype(iface);
194
195         return ret;
196 }
197
198 static void reset_tree(GTree *new, GTree **old,
199         pthread_rwlock_t *guard)
200 {
201         GTree *release = *old;
202
203         pthread_rwlock_wrlock(guard);
204         *old = new;
205         pthread_rwlock_unlock(guard);
206         if (release)
207                 free_iftypes_tree(release);
208 }
209
210 bool is_address_exists(const char *name)
211 {
212 #ifdef SIOCDIFADDR
213         struct ifreq ifr;
214         static int fd;
215         if (!fd)
216                 fd = socket(AF_INET, SOCK_DGRAM, 0);
217
218         memset(&ifr, 0, sizeof(struct ifreq));
219         strcpy(ifr.ifr_name, name);
220         return ioctl(fd, SIOCGIFADDR, &ifr) == 0;
221 #endif /* SIOCDIFADDR */
222         return true;
223 }
224
225 static int fill_ifaces_relation(struct parse_result *result,
226                                 void UNUSED *user_data)
227 {
228         struct iface_relation *relation;
229         if (strcmp(result->section, IFACES_TYPE_SECTION))
230                 return RESOURCED_ERROR_NONE;
231
232         relation = (struct iface_relation *)malloc(sizeof(struct iface_relation));
233
234         ret_value_msg_if(relation == NULL, RESOURCED_ERROR_NONE,
235                 "Failed to allocated memory!");
236
237         relation->iftype = convert_iftype(result->name);
238         STRING_SAVE_COPY(relation->ifname, result->value);
239
240         ifnames_relations = g_slist_prepend(ifnames_relations, relation);
241         return RESOURCED_ERROR_NONE;
242 }
243
244 int init_iftype(void)
245 {
246         int i, ret;
247         resourced_iface_type iftype;
248         struct if_nameindex *ids = if_nameindex();
249         GTree *iftypes_next = create_iface_tree();
250         GTree *ifnames_next = create_iface_tree();
251
252         if (ids == NULL) {
253                 _E("Failed to initialize iftype table! errno: %d, %s",
254                         errno, strerror(errno));
255                 return RESOURCED_ERROR_FAIL;
256         }
257
258         if (!ifnames_relations) {
259                 ret = config_parse(NET_INTERFACE_NAMES_FILE,
260                                    fill_ifaces_relation, NULL);
261                 if (ret != 0)
262                         _D("Can't parse config file %s",
263                            NET_INTERFACE_NAMES_FILE);
264         }
265
266         iface_stat_allowance();
267
268         for (i = 0; ids[i].if_index != 0; ++i) {
269                 if (!is_address_exists(ids[i].if_name))
270                         continue;
271                 iftype = read_iftype(ids[i].if_name);
272                 put_iftype_to_tree(iftypes_next, ids[i].if_index, iftype);
273                 /*  we know here iftype/ids[i].if_name, lets populate
274                  *      ifnames_tree */
275                 put_ifname_to_tree(ifnames_next, ids[i].if_name, iftype);
276                 _D("ifname %s, ifype %d", ids[i].if_name, iftype);
277         }
278
279         /* Do not forget to free the memory */
280         if_freenameindex(ids);
281
282         reset_tree(iftypes_next, &iftypes, &iftypes_guard);
283         reset_tree(ifnames_next, &ifnames, &ifnames_guard);
284         return RESOURCED_ERROR_NONE;
285 }
286
287 void finalize_iftypes(void)
288 {
289         reset_tree(NULL, &iftypes, &iftypes_guard);
290         reset_tree(NULL, &ifnames, &ifnames_guard);
291         g_slist_free_full(ifnames_relations, free);
292 }
293
294 resourced_iface_type convert_iftype(const char *buffer)
295 {
296         if (!buffer) {
297                 _E("Malloc of answer_get_stat failed\n");
298                 return RESOURCED_IFACE_UNKNOWN;
299         }
300
301         if (strcmp(buffer, DATACALL_VALUE) == 0)
302                 return RESOURCED_IFACE_DATACALL;
303
304         if (strcmp(buffer, WIFI_VALUE) == 0)
305                 return RESOURCED_IFACE_WIFI;
306
307         if (strcmp(buffer, BLUETOOTH_VALUE) == 0)
308                 return RESOURCED_IFACE_BLUETOOTH;
309
310         if (strcmp(buffer, WIRED_VALUE) == 0)
311                 return RESOURCED_IFACE_WIRED;
312         if (strcmp(buffer, ALL_NET_IFACE_VALUE) == 0)
313                 return RESOURCED_IFACE_ALL;
314         return RESOURCED_IFACE_UNKNOWN;
315 }
316
317 int is_allowed_ifindex(int ifindex)
318 {
319         return iface_stat[get_iftype(ifindex)];
320 }
321
322 resourced_iface_type get_iftype(int ifindex)
323 {
324         return get_iftype_from_tree(iftypes, ifindex);
325 }
326
327 static gboolean print_ifname(gpointer key, gpointer value, gpointer data)
328 {
329         _D("ifname %s", (char *)value);
330         return FALSE;
331 }
332
333 static char *get_ifname_from_tree(GTree *ifnames_tree, int iftype)
334 {
335         char *ret = NULL;
336
337         ret_value_msg_if(!ifnames_tree, NULL, "Please provide valid argument!");
338
339         pthread_rwlock_rdlock(&ifnames_guard);
340         ret = (char *)g_tree_lookup(ifnames_tree, (gpointer)iftype);
341         pthread_rwlock_unlock(&ifnames_guard);
342         if (ret == NULL)
343                 g_tree_foreach(ifnames_tree, print_ifname, NULL);
344
345         return ret;
346 }
347
348 char *get_iftype_name(resourced_iface_type iftype)
349 {
350         return get_ifname_from_tree(ifnames, iftype);
351 }
352
353 static gboolean search_loopback(gpointer key,
354                   gpointer value,
355                   gpointer data)
356 {
357         int *res = (int *)data;
358         if (!value)
359                 return FALSE;
360         *res = *(int *)value == RESOURCED_IFACE_UNKNOWN ? TRUE : FALSE;
361         return *res;
362 }
363
364 static bool is_only_loopback(GTree *iftypes_tree)
365 {
366         int nodes = g_tree_nnodes(iftypes_tree);
367         int res = 0;
368
369         if (nodes > 1)
370                 return false;
371
372         g_tree_foreach(iftypes_tree, search_loopback, &res);
373         return res;
374 }
375
376 void for_each_ifindex(ifindex_iterator iter, void(*empty_func)(void *),
377         void *data)
378 {
379         pthread_rwlock_rdlock(&iftypes_guard);
380         if (!is_only_loopback(iftypes))
381                 g_tree_foreach(iftypes, (GTraverseFunc)iter, data);
382         else if (empty_func)
383                 empty_func(data);
384
385         pthread_rwlock_unlock(&iftypes_guard);
386 }
387
388 void set_wifi_allowance(const resourced_option_state wifi_option)
389 {
390         iface_stat[RESOURCED_IFACE_WIFI] = wifi_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
391 }
392
393 void set_datacall_allowance(const resourced_option_state datacall_option)
394 {
395         iface_stat[RESOURCED_IFACE_DATACALL] = datacall_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
396 }