tizen 2.3.1 release
[kernel/api/system-resource.git] / src / network / iface-cb.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2000 - 2013 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  * @file iface-cb.c
22  *
23  * @desc Network interface callbacks entity
24  *
25  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
26  *
27  */
28
29 #include "macro.h"
30 #include "datausage-common.h"
31 #include "restriction-handler.h"
32 #include "settings.h"
33 #include "storage.h"
34 #include "trace.h"
35
36 #include <Ecore.h>
37 #include <linux/genetlink.h>
38 #include <linux/netlink.h>
39 #include <linux/rtnetlink.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <sys/socket.h> /*for netlink.h*/
43 #include <sys/types.h>
44 #include <unistd.h>
45
46 #define ADDR_EVENT_BUF_LEN 4096
47 static int iface_fd;
48
49 static iface_callbacks *ifcallbacks;
50 static Ecore_Fd_Handler *iface_ecore_fd_handler;
51
52 static iface_callbacks *create_iface_callback(void)
53 {
54         iface_callbacks *callbacks = NULL;
55         gpointer callback_data = create_restriction_callback();
56
57         if (callback_data)
58                 callbacks = g_list_prepend(callbacks, callback_data);
59         callback_data = create_iface_storage_callback();
60         if (callback_data)
61                 callbacks = g_list_prepend(callbacks, callback_data);
62         callback_data = create_counter_callback();
63         if (callback_data)
64                 callbacks = g_list_prepend(callbacks, callback_data);
65
66         return callbacks;
67 }
68
69 static void _iface_up_iter(gpointer data, gpointer user_data)
70 {
71         iface_callback *arg = (iface_callback *)data;
72         uint32_t ifindex = *(uint32_t *) (user_data);
73         if (arg && arg->handle_iface_up)
74                 arg->handle_iface_up(ifindex);
75 }
76
77 static void _iface_down_iter(gpointer data, gpointer user_data)
78 {
79         iface_callback *arg = (iface_callback *)data;
80         uint32_t ifindex = *(uint32_t *)(user_data);
81         if (arg && arg->handle_iface_down)
82                 arg->handle_iface_down(ifindex);
83 }
84
85 static void process_nlh(int len, const struct nlmsghdr *nlh,
86                                 iface_callbacks *arg)
87 {
88         if (!arg) {
89                 _D("Please provide valid argument!");
90                 return;
91         }
92
93         for (; (NLMSG_OK(nlh, len)) &&
94                 (nlh->nlmsg_type != NLMSG_DONE);
95                 nlh = NLMSG_NEXT(nlh, len)) {
96                 if (nlh->nlmsg_type != RTM_NEWADDR
97                         && nlh->nlmsg_type != RTM_DELADDR)
98                         continue;
99
100                 struct ifaddrmsg *ifa =
101                         (struct ifaddrmsg *) NLMSG_DATA(nlh);
102                 struct rtattr *rth = IFA_RTA(ifa);
103                 int rtl = IFA_PAYLOAD(nlh);
104
105                 for (; rtl && RTA_OK(rth, rtl);
106                         rth = RTA_NEXT(rth, rtl)) {
107                         if (rth->rta_type != IFA_LOCAL)
108                                 continue;
109
110                         if (nlh->nlmsg_type == RTM_NEWADDR) {
111                                 init_iftype();
112                                 return g_list_foreach(arg, _iface_up_iter,
113                                         &(ifa->ifa_index));
114
115                         } else if (nlh->nlmsg_type == RTM_DELADDR) {
116                                 g_list_foreach(arg, _iface_down_iter,
117                                         &(ifa->ifa_index));
118                                 /* network delete hooks require old information,
119                                  * for example for get_iftype by ifindex */
120                                 init_iftype();
121                                 return;
122                         }
123                 }
124         }
125 }
126
127 static Eina_Bool iface_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
128 {
129         char buff[ADDR_EVENT_BUF_LEN];
130         struct nlmsghdr *nlh;
131         iface_callbacks *localiarg = (iface_callbacks *)user_data;
132         int fd;
133         int len;
134
135         if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
136                 _E("ecore_main_fd_handler_active_get error , return\n");
137                 return ECORE_CALLBACK_RENEW;
138         }
139
140         fd = ecore_main_fd_handler_fd_get(fd_handler);
141         if (fd < 0) {
142                 _E("ecore_main_fd_handler_fd_get error");
143                 return ECORE_CALLBACK_RENEW;
144         }
145
146         len = read(fd, buff, ADDR_EVENT_BUF_LEN);
147         if (len < 0)  {
148                 _E("socket read error");
149                 return ECORE_CALLBACK_RENEW;
150         }
151
152         nlh = (struct nlmsghdr *)buff;
153         process_nlh(len, nlh, localiarg);
154
155         return ECORE_CALLBACK_RENEW;
156 }
157
158 static int iface_func_init()
159 {
160         struct sockaddr_nl addr = {0};
161         int sock, error = 0, on;
162
163         sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
164         if (sock == -1) {
165                 _E("Error creating NETLINK_ROUTE socket");
166                 error = errno;
167                 goto handle_error;
168         }
169
170         addr.nl_family = AF_NETLINK;
171         addr.nl_groups = RTMGRP_IPV4_IFADDR;
172 /* Enable address reuse */
173         on = 1;
174         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
175                 _E("Error setsockopt");
176                 error = errno;
177                 goto release_socket;
178         }
179
180         if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
181                 _E("Error bind socket");
182                 error = errno;
183                 goto release_socket;
184         }
185
186         _D("Socket created successfully\n");
187
188         return sock;
189
190 release_socket:
191         close(sock);
192 handle_error:
193         return -error;
194 }
195
196 static void apply_iface_options(void)
197 {
198         resourced_options options = { 0 };
199
200         load_options(&options);
201         set_wifi_allowance(options.wifi);
202         set_datacall_allowance(options.datacall);
203 }
204
205 int resourced_iface_init(void)
206 {
207         ifcallbacks = create_iface_callback();
208         _D("Initialize network interface callbacks\n");
209         ret_value_msg_if(ifcallbacks == NULL, RESOURCED_ERROR_FAIL,
210                          "Error create network interface callbacks");
211         iface_fd = iface_func_init();
212         ret_value_msg_if(iface_fd < 0, RESOURCED_ERROR_FAIL,
213                          "Can not listen network interface changes %d",
214                          iface_fd);
215         iface_ecore_fd_handler = ecore_main_fd_handler_add(
216                 iface_fd, ECORE_FD_READ, iface_func_cb,
217                 (void *)ifcallbacks, NULL, NULL);
218         ret_value_msg_if(iface_ecore_fd_handler == NULL, RESOURCED_ERROR_FAIL,
219                          "Failed to add iface callbacks\n");
220         apply_iface_options();
221         return RESOURCED_ERROR_NONE;
222 }
223
224 void resourced_iface_finalize(void)
225 {
226         _D("Finalize network interface callbacks\n");
227         ecore_main_fd_handler_del(iface_ecore_fd_handler);
228         shutdown(iface_fd, 2);
229         close(iface_fd);
230         finalize_iftypes();
231         g_list_free_full(ifcallbacks, free);
232 }