tizen 2.3.1 release
[kernel/api/system-resource.git] / src / network / datausage-common.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  * @file datausage.c
21  *
22  * @desc Datausage module
23  *
24  */
25
26 #include "appid-helper.h"
27 #include "config.h"
28 #include "const.h"
29 #include "counter-process.h"
30 #include "counter.h"
31 #include "cgroup.h"
32 #include "datausage-restriction.h"
33 #include "generic-netlink.h"
34 #include "net-cls-cgroup.h"
35 #include "nl-helper.h"
36 #include "notifier.h"
37 #include "notification.h" /* for sending datausage dbus notification */
38 #include "daemon-options.h"
39 #include "datausage-common.h"
40 #include "datausage-quota.h"
41 #include "datausage-vconf-callbacks.h"
42 #include "iface-cb.h"
43 #include "macro.h"
44 #include "module-data.h"
45 #include "module.h"
46 #include "nfacct-rule.h"
47 #include "protocol-info.h"
48 #include "resourced.h"
49 #include "restriction-handler.h"
50 #include "roaming.h"
51 #include "storage.h"
52 #include "trace.h"
53
54 #include <linux/rtnetlink.h>
55
56 #ifdef CONFIG_DATAUSAGE_NFACCT
57
58
59 struct make_rule_context {
60         struct counter_arg *carg;
61         struct nfacct_rule *counter;
62 };
63
64 struct nfacct_key {
65         u_int32_t classid;
66         resourced_iface_type iftype;
67         nfacct_rule_direction iotype;
68         char ifname[MAX_NAME_LENGTH];
69 };
70
71 enum nfacct_state {
72         NFACCT_STATE_ACTIVE,    /* kernel counter is applied */
73         NFACCT_STATE_DEACTIVATED, /* kernel counter was removed, but this counter
74                 is still active, and it will be required for network interface,
75                 when it will be activated */
76 };
77
78 struct nfacct_value {
79         pid_t pid;
80         enum nfacct_state state;
81 };
82
83 static nfacct_rule_jump get_jump_by_intend(struct nfacct_rule *counter)
84 {
85         if (counter->intend == NFACCT_WARN)
86                 return NFACCT_JUMP_ACCEPT;
87         else if (counter->intend == NFACCT_BLOCK)
88                 return NFACCT_JUMP_REJECT;
89
90         return NFACCT_JUMP_UNKNOWN;
91 }
92
93 static resourced_ret_c add_iptables_in(struct nfacct_rule *counter)
94 {
95         return produce_net_rule(counter, 0, 0,
96                 NFACCT_ACTION_INSERT, get_jump_by_intend(counter),
97                 NFACCT_COUNTER_IN);
98 }
99
100 static resourced_ret_c add_iptables_out(struct nfacct_rule *counter)
101 {
102         return produce_net_rule(counter, 0, 0,
103                 NFACCT_ACTION_INSERT, get_jump_by_intend(counter),
104                 NFACCT_COUNTER_OUT);
105 }
106
107 static resourced_ret_c del_iptables_in(struct nfacct_rule *counter)
108 {
109         return produce_net_rule(counter, 0, 0,
110                 NFACCT_ACTION_DELETE, get_jump_by_intend(counter),
111                 NFACCT_COUNTER_IN);
112 }
113
114 static resourced_ret_c del_iptables_out(struct nfacct_rule *counter)
115 {
116         return produce_net_rule(counter, 0, 0,
117                 NFACCT_ACTION_DELETE, get_jump_by_intend(counter),
118                 NFACCT_COUNTER_OUT);
119 }
120
121 #endif /* CONFIG_DATAUSAGE_NFACCT */
122
123 static void resourced_roaming_cb_init(void)
124 {
125         regist_roaming_cb(get_roaming_restriction_cb());
126 }
127
128 static int app_launch_cb(void *data)
129 {
130         struct proc_status *p_data = (struct proc_status*)data;
131         int ret;
132         ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL,
133                 "Please provide valid argument!");
134         ret = join_net_cls(p_data->appid, p_data->pid);
135         if (ret != RESOURCED_ERROR_NONE)
136                 _D("Failed to start network counting.");
137         return ret;
138 }
139
140 #ifdef CONFIG_DATAUSAGE_NFACCT
141
142 static int remove_each_counter(
143         gpointer key,
144         gpointer value,
145         gpointer data)
146 {
147         struct nfacct_rule *counter = (struct nfacct_rule *)data;
148         resourced_iface_type iftype = *(resourced_iface_type *)value;
149         struct nfacct_key nf_key;
150
151         if (iftype == RESOURCED_IFACE_UNKNOWN)
152                 return FALSE;
153
154         nf_key.classid = counter->classid;
155         nf_key.iotype = counter->iotype;
156         counter->iftype = nf_key.iftype = iftype;
157
158         generate_counter_name(counter);
159         counter->iptables_rule(counter);
160
161         /*  remove from local tree  */
162 #ifdef DEBUG_ENABLED
163         {
164                 gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key);
165                 if (t)
166                         _I("Element exists, remove it!");
167                 else
168                         _D("Element doesn't exist!");
169         }
170 #endif
171
172         g_tree_remove(counter->carg->nf_cntrs, &nf_key);
173 #ifdef DEBUG_ENABLED
174         {
175                 gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key);
176                 if (t)
177                         _E("Element wasn't removed!");
178         }
179 #endif
180
181         return FALSE;
182 }
183
184 static void remove_nfacct_counters_for_all_iface(u_int32_t classid, struct counter_arg *carg)
185 {
186         struct nfacct_rule counter = {
187                 .classid = classid,
188                 .iotype = NFACCT_COUNTER_IN,
189                 .iptables_rule = del_iptables_in,
190                 .carg = carg,
191                 /* .name until we don't have iftype,
192                 *       we couldn't get name */
193         };
194
195         /* TODO rework for_each_ifindex to avoid cast,
196          * right now cast is necessary due for_each_ifindex directy pass
197          * given function into g_tree_foreach */
198         /* remove for ingress counter */
199         for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter);
200         /* remove for engress counter */
201         counter.iotype = NFACCT_COUNTER_OUT;
202         counter.iptables_rule = del_iptables_out;
203         for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter);
204 }
205
206 struct match_nftree_context
207 {
208         u_int32_t classid;
209         pid_t pid;
210 };
211
212 static gboolean match_pid(gpointer key,
213         gpointer value,
214         gpointer data)
215 {
216         struct match_nftree_context *ctx = (struct match_nftree_context *)data;
217         struct nfacct_value *nf_value = (struct nfacct_value *)value;
218         struct nfacct_key *nf_key = (struct nfacct_key *)key;
219         if (nf_value->pid == ctx->pid) {
220                 ctx->classid = nf_key->classid;
221                 return TRUE;
222         }
223         return FALSE;
224 }
225
226 static u_int32_t get_classid_by_pid(struct counter_arg *carg, const pid_t pid)
227 {
228         struct match_nftree_context ctx = {
229                 .pid = pid,
230                 .classid = RESOURCED_UNKNOWN_CLASSID,
231         };
232         g_tree_foreach(carg->nf_cntrs, match_pid, &ctx);
233         return ctx.classid;
234 }
235
236 static int app_terminate_cb(void *data)
237 {
238         struct proc_status *p_data = (struct proc_status*)data;
239         struct shared_modules_data *m_data;
240         struct counter_arg *carg;
241         u_int32_t classid;
242         ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL,
243                 "Please provide valid argument!");
244
245         m_data = get_shared_modules_data();
246         ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
247                 "Can't get module data!");
248
249         carg = m_data->carg;
250         ret_value_msg_if(carg == NULL, RESOURCED_ERROR_FAIL,
251                 "Cant' get counter arg!");
252         classid = get_classid_by_pid(carg, p_data->pid);
253         ret_value_msg_if(classid == RESOURCED_UNKNOWN_CLASSID,
254                 RESOURCED_ERROR_FAIL, "No classid to terminate!");
255
256         remove_nfacct_counters_for_all_iface(classid, carg);
257         return RESOURCED_ERROR_NONE;
258 }
259
260 static gboolean populate_classid_tree(gpointer key,
261         gpointer value,
262         gpointer data)
263 {
264         GTree *classid_tree = (GTree *)data;
265         struct nfacct_key *nf_key = (struct nfacct_key *)key;
266         struct nfacct_value *nf_value = (struct nfacct_value *)value;
267
268         if (nf_value->state == NFACCT_STATE_ACTIVE)
269                 g_tree_insert(classid_tree, (const gpointer)nf_key->classid, NULL);
270         return FALSE;
271 }
272
273 static gboolean remove_each_counter_by_classid(gpointer key,
274         gpointer value,
275         gpointer data)
276 {
277         u_int32_t classid = (u_int32_t)key;
278         struct counter_arg *carg = (struct counter_arg *)data;
279         remove_nfacct_counters_for_all_iface(classid, carg);
280         return FALSE;
281 }
282
283 static gint pointer_compare(gconstpointer a, gconstpointer b)
284 {
285         return a - b;
286 }
287
288 static int add_one_tizen_os_counter(
289         gpointer key,
290         gpointer value,
291         gpointer data)
292 {
293         struct counter_arg *carg = (struct counter_arg *)data;
294         struct nfacct_rule counter = {.name = {0}, .ifname = {0}, 0};
295         resourced_iface_type iftype = *(resourced_iface_type *)value;
296
297         if (iftype <= RESOURCED_IFACE_UNKNOWN ||
298                 iftype >= RESOURCED_IFACE_LAST_ELEM)
299                 return FALSE;
300
301         counter.iotype = NFACCT_COUNTER_IN;
302         counter.iftype = iftype;
303         counter.carg = carg;
304         generate_counter_name(&counter);
305         add_iptables_in(&counter);
306         counter.iotype = NFACCT_COUNTER_OUT;
307         generate_counter_name(&counter);
308         add_iptables_out(&counter);
309         return FALSE;
310 }
311
312 static void add_tizen_os_counters(struct counter_arg *carg) {
313
314         for_each_ifindex((ifindex_iterator)add_one_tizen_os_counter, NULL, carg);
315 }
316
317 static void reload_all_nf_counters(struct counter_arg *carg)
318 {
319         add_tizen_os_counters(carg);
320         /* it can be done by following ways:
321          * 1. just by reading existing net_cls cgroups, looks not robust because
322          *      in this case we are getting network interface type from runtime, and
323          *      it could be changed since the resourced was stopped. And it doesn't
324          *      reflect counter state
325          * 2. By reading from iptables rules. We don't have C code for retriving
326          *      it from kernel unless to use iptables cmd output, but it's not
327          *       robust and not performance effective
328          * 3. Just by obtaining nfacct counters. We could do it without command
329          *      line tool. It reflects current counter state, but not,
330          *       iptables rules
331          */
332         carg->initiate = 1;
333         nfacct_send_initiate(carg);
334 }
335
336 static void remove_whole_nf_counters(struct counter_arg *carg)
337 {
338         GTree *classid_tree = g_tree_new(pointer_compare);; /* tree instead of array for avoiding
339         duplication, manual sort and binary search in case of array */
340         ret_msg_if(carg == NULL,
341                 "Cant' get counter arg!");
342
343         /* fill classid list, due we couldn't iterate on tree and
344          * remove elements from it */
345         g_tree_foreach(carg->nf_cntrs, populate_classid_tree, classid_tree);
346         g_tree_foreach(classid_tree, remove_each_counter_by_classid, carg);
347
348         g_tree_destroy(carg->nf_cntrs);
349         g_tree_destroy(classid_tree);
350 }
351
352 /* notification section */
353 /*
354  * TODO use following constant from kernel header
355  * nfacct/include/linux/netfilter/nfnetlink.h
356  * */
357 #ifndef NFNLGRP_ACCT_QUOTA
358 #define NFNLGRP_ACCT_QUOTA 8
359 #endif
360 #ifndef SOL_NETLINK
361 #define SOL_NETLINK     270
362 #endif
363
364 static inline char *get_public_appid(const uint32_t classid)
365 {
366         char *appid;
367
368         /* following value for ALL is suitable for using in statistics
369            what's why it's not in get_app_id_by_classid */
370         if (classid == RESOURCED_ALL_APP_CLASSID)
371                 return RESOURCED_ALL_APP;
372
373         appid = get_app_id_by_classid(classid, true);
374         return !appid ? UNKNOWN_APP : appid;
375 }
376
377 static void init_nfacct(u_int32_t classid, pid_t pid,
378         nfacct_rule_direction ctype, struct counter_arg *carg,
379         struct nfacct_rule *counter)
380 {
381         counter->iotype = ctype;
382         counter->classid = classid;
383         counter->carg = carg;
384         counter->pid = pid;
385         counter->intend = NFACCT_COUNTER;
386         counter->quota = 0;
387         if (ctype == NFACCT_COUNTER_IN)
388                 counter->iptables_rule = add_iptables_in;
389         else if (ctype == NFACCT_COUNTER_OUT)
390                 counter->iptables_rule = add_iptables_out;
391 }
392
393 static resourced_ret_c del_counter(struct nfacct_rule *counter)
394 {
395         return produce_net_rule(counter, 0, 0,
396                 NFACCT_ACTION_DELETE, get_jump_by_intend(counter),
397                 counter->iotype);
398 }
399
400 static int fill_restriction(struct rtattr *attr_list[__NFACCT_MAX],
401                 void *user_data)
402 {
403         struct counter_arg *carg = (struct counter_arg *)user_data;
404         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
405         char *cnt_name = (char *)RTA_DATA(
406                                 attr_list[NFACCT_NAME]);
407         char *app_id = 0;
408         int ret = 0;
409         resourced_restriction_info rst_info = {0};
410
411         init_nfacct(0, 0, 0, carg, &counter);
412         strncpy(counter.name, cnt_name, sizeof(counter.name)-1);
413         recreate_counter_by_name(cnt_name, &counter);
414
415         app_id = get_public_appid(counter.classid);
416         ret = get_restriction_info(app_id, counter.iftype, &rst_info);
417         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
418                 "Failed to get restriction info!");
419
420         if (counter.intend == NFACCT_BLOCK) {
421                 if (counter.iotype == NFACCT_COUNTER_IN) {
422                         struct nfacct_rule out_counter = counter;
423
424                         /* remove old ones, which were with notification */
425                         counter.iotype = NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT;
426                         ret = del_counter(&counter);
427                         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
428                                 "Can't delete restriction%s", counter.name);
429
430                         out_counter.iotype = NFACCT_COUNTER_OUT;
431                         generate_counter_name(&out_counter);
432                         ret = add_iptables_out(&out_counter);
433                         /* TODO need to think how to release it and what about
434                          * not yet fired rule */
435                         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
436                                 "Can't create auxilary counter %s", out_counter.name);
437                 }
438
439                 if (rst_info.quota_id != NONE_QUOTA_ID)
440                         send_restriction_notification(app_id);
441                 update_restriction_db(app_id, counter.iftype, 0, 0,
442                                       RESOURCED_RESTRICTION_ACTIVATED,
443                 rst_info.quota_id, rst_info.roaming);
444
445         } else if (counter.intend == NFACCT_WARN) {
446                 if (rst_info.quota_id != NONE_QUOTA_ID)
447                         send_restriction_warn_notification(app_id);
448                 /* remove both warnings */
449                 counter.iotype = NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT;
450                 ret = del_counter(&counter);
451                 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
452                         "Can't delete warning %s", counter.name);
453         } else
454                 _E("Unkown restriction notification type");
455
456         return 0;
457 }
458
459 static Eina_Bool noti_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
460 {
461         struct counter_arg *carg = (struct counter_arg *)user_data;
462         struct genl ans;
463         struct netlink_serialization_params ser_param = {0};
464         netlink_serialization_command *netlink_command = NULL;
465         int ret;
466
467         _D("nfacct notification");
468         ret = read_netlink(carg->noti_fd, &ans, sizeof(struct genl));
469         if (ret == 0)
470                 goto out;
471         carg->ans_len = ret;
472         ser_param.carg = carg;
473         ser_param.ans = &ans;
474         ser_param.eval_attr = fill_restriction;
475         netlink_command = netlink_create_command(&ser_param);
476
477         if (!netlink_command)
478                 goto out;
479
480         netlink_command->deserialize_answer(&(netlink_command->params));
481
482 out:
483         return ECORE_CALLBACK_RENEW;
484 }
485
486 static void init_notifier(struct counter_arg *carg)
487 {
488         int ret = 0;
489         int option = NFNLGRP_ACCT_QUOTA;
490         struct sockaddr_nl addr;
491         socklen_t addr_len = sizeof(struct sockaddr_nl);
492
493         carg->noti_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
494         ret_msg_if(carg->noti_fd < 0, "Can't create socket");
495
496         /* bind */
497         memset(&addr, 0, sizeof(struct sockaddr_nl));
498         addr.nl_family = AF_NETLINK;
499         addr.nl_groups = 0;
500         addr.nl_pid = 0;
501
502         ret = bind(carg->noti_fd, (struct sockaddr *) &addr, addr_len);
503         ret_msg_if(ret < 0, "Can't bind notification socket");
504
505         ret = getsockname(carg->noti_fd, (struct sockaddr *)&addr, &addr_len);
506         ret_msg_if(ret < 0, "Can't get sockname!");
507
508         ret_msg_if(addr_len != sizeof(struct sockaddr_nl) ||
509                 addr.nl_family != AF_NETLINK,
510                 "getsockname bad argumetn");
511
512         /* see sock opt */
513
514         ret = setsockopt(carg->noti_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
515                 &option, sizeof(int));
516         ret_msg_if(carg->noti_fd < 0, "Can't set sock opt");
517
518         /* register handler */
519         carg->noti_fd_handler = ecore_main_fd_handler_add(
520                 carg->noti_fd, ECORE_FD_READ, noti_func_cb,
521                 carg, NULL, NULL);
522         ret_msg_if(carg->noti_fd_handler == NULL,
523                          "Failed to add noti callbacks\n");
524 }
525
526 static void fini_notifier(struct counter_arg *carg)
527 {
528         shutdown(carg->noti_fd, SHUT_RDWR);
529         ecore_main_fd_handler_del(carg->noti_fd_handler);
530         close(carg->noti_fd);
531 }
532
533 /* end notification section */
534 #else
535 static int app_terminate_cb(void *data)
536 {
537         return 0;
538 }
539
540 iface_callback *create_counter_callback(void)
541 {
542         return NULL;
543 }
544
545 #endif /* CONFIG_DATAUSAGE_NFACCT */
546
547 static int resourced_datausage_init(void *data)
548 {
549         struct modules_arg *marg = (struct modules_arg *)data;
550         struct shared_modules_data *m_data = get_shared_modules_data();
551         int ret_code;
552
553         load_daemon_opts(marg->opts);
554         _D("Initialize network counter function\n");
555         ret_value_msg_if(marg == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
556                          "Invalid modules argument\n");
557         ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
558                          "Invalid shared modules data\n");
559         /* register notifier cb */
560         register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
561         register_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb);
562         register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb);
563         register_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb);
564         m_data->carg = init_counter_arg(marg->opts);
565         ret_code = resourced_iface_init();
566         ret_value_msg_if(ret_code < 0, ret_code, "resourced_iface_init failed");
567         resourced_roaming_cb_init();
568         ret_code = resourced_init_counter_func(m_data->carg);
569         ret_value_msg_if(ret_code < 0, ret_code, "Error init counter func\n");
570         resourced_add_vconf_datausage_cb(m_data->carg);
571         init_hw_net_protocol_type();
572         reactivate_restrictions();
573
574 #ifdef CONFIG_DATAUSAGE_NFACCT
575         reload_all_nf_counters(m_data->carg);
576
577         /* let's make a notification socket */
578         init_notifier(m_data->carg);
579 #endif
580         return RESOURCED_ERROR_NONE;
581 }
582
583 static int resourced_datausage_finalize(void *data)
584 {
585         struct shared_modules_data *m_data = get_shared_modules_data();
586
587         _D("Finalize network counter function\n");
588         resourced_remove_vconf_datausage_cb();
589         ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
590                          "Invalid shared modules data\n");
591
592 #ifdef CONFIG_DATAUSAGE_NFACCT
593         remove_whole_nf_counters(m_data->carg);
594         fini_notifier(m_data->carg);
595 #endif
596         resourced_finalize_counter_func(m_data->carg);
597         finalize_carg(m_data->carg);
598         finalize_storage_stm();
599         finalize_hw_net_protocol_type();
600         unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
601         unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb);
602         unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb);
603         unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb);
604         resourced_iface_finalize();
605         finalize_iftypes();
606
607         return RESOURCED_ERROR_NONE;
608 }
609
610 #ifdef CONFIG_DATAUSAGE_NFACCT
611
612 static int compare_nfcntr(gconstpointer a, gconstpointer b,
613                      gpointer UNUSED user_data)
614 {
615         struct nfacct_key *key_a = (struct nfacct_key *)a;
616         struct nfacct_key *key_b = (struct nfacct_key *)b;
617         int ret = key_a->classid - key_b->classid;
618
619         if (ret)
620                 return ret;
621         ret = key_a->iftype - key_b->iftype;
622         if (ret)
623                 return ret;
624         ret = key_a->iotype - key_b->iotype;
625         if (ret)
626                 return ret;
627         return strcmp(key_a->ifname, key_b->ifname);
628 }
629
630 GTree *create_nfacct_tree(void)
631 {
632         return g_tree_new_full(compare_nfcntr, NULL, NULL, free);
633 }
634
635 static struct nfacct_value *lookup_counter(struct nfacct_rule *counter)
636 {
637         struct nfacct_key key = {
638                 .classid = counter->classid,
639                 .iftype = counter->iftype,
640                 .iotype = counter->iotype
641         };
642         STRING_SAVE_COPY(key.ifname, counter->ifname);
643
644         return (struct nfacct_value *)g_tree_lookup(counter->carg->nf_cntrs,
645                 &key);
646 }
647
648 /* Called only in case of successful kernle operation */
649 void keep_counter(struct nfacct_rule *counter)
650 {
651         struct nfacct_key *key = NULL;
652         struct nfacct_value *value = NULL;
653
654         key = (struct nfacct_key *)malloc(sizeof(
655                 struct nfacct_key));
656         ret_msg_if(key == NULL,
657                 "Can allocate memory for nfacct_key!");
658
659         value = (struct nfacct_value *)malloc(sizeof(
660                 struct nfacct_value));
661
662         if (value == NULL) {
663                 free(key);
664                 _D("Can allocate memory for nfacct_key!");
665                 return;
666         }
667
668         key->classid = counter->classid;
669         key->iftype = counter->iftype;
670         key->iotype = counter->iotype;
671         STRING_SAVE_COPY(key->ifname, counter->ifname);
672
673         value->pid =  counter->pid;
674         value->state = NFACCT_STATE_ACTIVE;
675
676         g_tree_insert(counter->carg->nf_cntrs, key, value);
677 }
678
679 static int create_each_iptable_rule(gpointer key, gpointer value, void *data)
680 {
681         struct make_rule_context *ctx = (struct make_rule_context *)data;
682         resourced_ret_c ret;
683         resourced_iface_type iftype = *(resourced_iface_type *)value;
684         struct nfacct_value *counter = NULL;
685
686         if (iftype <= RESOURCED_IFACE_UNKNOWN ||
687                 iftype >= RESOURCED_IFACE_LAST_ELEM) {
688                 _D("Unsupported network interface type %d",
689                         iftype);
690                 return RESOURCED_ERROR_NONE;
691         }
692
693         ctx->counter->iftype = iftype;
694         generate_counter_name(ctx->counter);
695         counter = lookup_counter(ctx->counter);
696         if (counter != NULL) {
697                 _D("Counter already exists!");
698                 return RESOURCED_ERROR_NONE;
699         }
700         ret = ctx->counter->iptables_rule(ctx->counter);
701         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL,
702                 "Can't add iptables ingress rule");
703
704         keep_counter(ctx->counter);
705         return RESOURCED_ERROR_NONE;
706 }
707
708 static void populate_incomplete_counter(void *data)
709 {
710         struct make_rule_context *ctx = (struct make_rule_context *)data;
711         struct nfacct_value *counter;
712         generate_counter_name(ctx->counter);
713
714         counter = lookup_counter(ctx->counter);
715         if (counter != NULL) {
716                 _D("Counter already exists!");
717                 return;
718         }
719         keep_counter(ctx->counter);
720 }
721
722 static resourced_ret_c create_iptables_rule(const char *app_id, const pid_t pid)
723 {
724         struct shared_modules_data *m_data = get_shared_modules_data();
725         struct counter_arg *carg = m_data->carg;
726         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
727         struct make_rule_context ctx;
728         uint32_t classid = get_classid_by_app_id(app_id, false);
729
730         ctx.carg = carg;
731         ctx.counter = &counter;
732         init_nfacct(classid, pid, NFACCT_COUNTER_IN, carg, &counter);
733
734         for_each_ifindex((ifindex_iterator)create_each_iptable_rule,
735                 populate_incomplete_counter, &ctx);
736
737         counter.iotype = NFACCT_COUNTER_OUT;
738         counter.iptables_rule = add_iptables_out;
739         for_each_ifindex((ifindex_iterator)create_each_iptable_rule,
740                 populate_incomplete_counter, &ctx);
741
742         return RESOURCED_ERROR_NONE;
743 }
744
745 /* iface reset section */
746 struct iftype_context {
747         resourced_iface_type iftype;
748         struct counter_arg *carg;
749 };
750
751 static bool is_incomplete_counter(struct nfacct_key *nfacct_key, struct nfacct_value *nfacct_value)
752 {
753         return nfacct_key->iftype == RESOURCED_IFACE_UNKNOWN &&
754                 nfacct_value->state == NFACCT_STATE_ACTIVE;
755                         /* special incomplete status unnecessary */
756 }
757
758 static gboolean activate_each_counter_by_iftype(gpointer key,
759         gpointer value,
760         gpointer data)
761 {
762         struct nfacct_key *nfacct_key = (struct nfacct_key *)key;
763         struct nfacct_value *nfacct_value = (struct nfacct_value *)value;
764         struct iftype_context *ctx = (struct iftype_context *)data;
765         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
766         struct nfacct_value *found_counter;
767         int ret = RESOURCED_ERROR_NONE;
768
769         /* ugly check, due in case of RMNET -> WLAN switch,
770          *              WLAN activated before then RMNET is deactivated */
771
772         /*
773          * skip activating in case of
774          * 1. new interface is the same as was before
775          * 2. and counter is still active and new interface is Wifi
776          *      such problem was with WiFi only
777          * 3. and state is not deactivated, it's mean we wil skip in case of active
778          *   incomplete counter
779          * */
780         if (!(ctx->iftype != nfacct_key->iftype &&
781             nfacct_value->state == NFACCT_STATE_ACTIVE &&
782             ctx->iftype == RESOURCED_IFACE_WIFI) &&
783             nfacct_value->state != NFACCT_STATE_DEACTIVATED &&
784             !is_incomplete_counter(nfacct_key, nfacct_value))
785                 /* it means ctx->iftype was activated, but we still have
786                  *      active counter for another interface, assume
787                  *      WLAN is preffered, so lets deactivate it */
788                 return FALSE; /* continue iteration */
789
790
791         counter.classid = nfacct_key->classid;
792         counter.iotype = nfacct_key->iotype;
793         counter.iftype = ctx->iftype;
794         counter.carg = ctx->carg;
795
796         generate_counter_name(&counter);
797
798         found_counter = lookup_counter(&counter);
799         ret_value_msg_if(found_counter != NULL &&
800                 found_counter->state == NFACCT_STATE_ACTIVE, FALSE,
801                 "Counter already exists and active!");
802
803         if (counter.iotype == NFACCT_COUNTER_IN)
804                 ret = add_iptables_in(&counter);
805         else if (counter.iotype == NFACCT_COUNTER_OUT)
806                 ret = add_iptables_out(&counter);
807         else {
808                 _E("Unknown counter direction: %s", counter.name);
809                 return FALSE;
810         }
811
812         if (ret != RESOURCED_ERROR_NONE)
813                 return FALSE;
814
815         if (found_counter != NULL && found_counter->state ==
816                 NFACCT_STATE_DEACTIVATED)
817                 found_counter->state = NFACCT_STATE_ACTIVE;
818         else
819                 keep_counter(&counter);
820
821         return FALSE;
822 }
823
824 static void handle_on_iface_up(const int ifindex)
825 {
826         /* NEW IFACE LET's add COUNTER if it DOESN"T exists */
827         resourced_iface_type iftype;
828         struct shared_modules_data *m_data;
829         struct iftype_context ctx;
830         m_data = get_shared_modules_data();
831         ret_msg_if(m_data == NULL,
832                 "Can't get module data!");
833         iftype = get_iftype(ifindex);
834
835         ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
836                 "Can't get iftype for remove counter");
837
838         ctx.iftype = iftype;
839         ctx.carg = m_data->carg;
840         g_tree_foreach(ctx.carg->nf_cntrs, activate_each_counter_by_iftype, &ctx);
841         add_tizen_os_counters(m_data->carg);
842 }
843
844 struct del_counter_context
845 {
846         struct nfacct_value *nfacct_value;
847         struct nfacct_key *nfacct_key;
848         struct counter_arg *carg;
849 };
850
851 static Eina_Bool del_counter_delayed(void *data)
852 {
853         int ret;
854         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
855         struct del_counter_context *del_ctx = (struct del_counter_context *)data;
856         struct nfacct_value *nfacct_value = del_ctx->nfacct_value;
857         struct nfacct_key *nfacct_key = del_ctx->nfacct_key;
858
859         counter.classid = nfacct_key->classid;
860         counter.iotype = nfacct_key->iotype;
861         counter.iftype = nfacct_key->iftype;
862         counter.carg = del_ctx->carg;
863         STRING_SAVE_COPY(counter.ifname, nfacct_key->ifname);
864
865         generate_counter_name(&counter);
866
867         ret = del_counter(&counter);
868
869         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ECORE_CALLBACK_CANCEL,
870                 "Can't delete counter %s",
871                 counter.name);
872
873         nfacct_value->state = NFACCT_STATE_DEACTIVATED;
874
875         return ECORE_CALLBACK_CANCEL;
876 }
877
878 static gboolean deactivate_each_counter_by_iftype(gpointer key,
879         gpointer value,
880         gpointer data)
881 {
882         struct nfacct_key *nfacct_key = (struct nfacct_key *)key;
883         struct nfacct_value *nfacct_value = (struct nfacct_value *)value;
884         struct iftype_context *ctx = (struct iftype_context *)data;
885         struct del_counter_context *del_ctx = NULL;
886
887         /* deactivate counters only for ctx->iftype interface */
888         if (ctx->iftype != nfacct_key->iftype)
889                 return FALSE; /* continue iteration */
890
891         del_ctx = (struct del_counter_context *)malloc(
892                 sizeof(struct del_counter_context));
893         ret_value_msg_if(del_ctx == NULL, FALSE,
894                 "Can't allocate del_counter_context");
895         del_ctx->nfacct_key = nfacct_key;
896         del_ctx->nfacct_value = nfacct_value;
897         del_ctx->carg = ctx->carg;
898         ecore_timer_add(0, del_counter_delayed, del_ctx);
899
900         return FALSE;
901 }
902
903 static void handle_on_iface_down(const int ifindex)
904 {
905         /* iface is gone, lets remove counter */
906         resourced_iface_type iftype;
907         struct shared_modules_data *m_data;
908         struct iftype_context ctx;
909         m_data = get_shared_modules_data();
910         ret_msg_if(m_data == NULL,
911                 "Can't get module data!");
912         iftype = get_iftype(ifindex);
913
914         ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
915                 "Can't get iftype for remove counter");
916
917         ctx.iftype = iftype;
918         ctx.carg = m_data->carg;
919         g_tree_foreach(ctx.carg->nf_cntrs, deactivate_each_counter_by_iftype, &ctx);
920 }
921
922 iface_callback *create_counter_callback(void)
923 {
924         iface_callback *ret_arg = (iface_callback *)
925                 malloc(sizeof(iface_callback));
926
927         if (!ret_arg) {
928                 _E("Malloc of iface_callback failed\n");
929                 return NULL;
930         }
931         ret_arg->handle_iface_up = handle_on_iface_up;
932         ret_arg->handle_iface_down = handle_on_iface_down;
933
934         return ret_arg;
935 }
936
937 /* end iface reset section */
938
939 #endif /*DATAUSAGE_TYPE*/
940
941 resourced_ret_c join_net_cls(const char *app_id, const pid_t pid)
942 {
943         resourced_ret_c ret;
944         char pkgname[MAX_PATH_LENGTH];
945         extract_pkgname(app_id, pkgname, sizeof(pkgname));
946         ret = make_net_cls_cgroup_with_pid(pid, pkgname);
947         ret_value_if(ret != RESOURCED_ERROR_NONE, ret);
948         ret = update_classids();
949         ret_value_if(ret != RESOURCED_ERROR_NONE, ret);
950 #ifdef CONFIG_DATAUSAGE_NFACCT
951         /* Create iptable rule */
952         ret = create_iptables_rule(app_id, pid);
953         ret_value_if(ret != RESOURCED_ERROR_NONE, ret);
954 #endif /* CONFIG_DATAUSAGE_NFACCT */
955         return RESOURCED_ERROR_NONE;
956 }
957
958 static const struct module_ops datausage_modules_ops = {
959         .priority = MODULE_PRIORITY_NORMAL,
960         .name = "datausage",
961         .init = resourced_datausage_init,
962         .exit = resourced_datausage_finalize,
963 };
964
965 MODULE_REGISTER(&datausage_modules_ops)