Set STC_DEBUG_LOG flag
[platform/core/connectivity/stc-manager.git] / src / helper / helper-nfacct-rule.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <errno.h>
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include <arpa/inet.h>
26
27 #include "counter.h"
28 #include "helper-nfacct-rule.h"
29
30 #include "configure_stub.h"
31
32 #define IPTABLES "/usr/sbin/iptables"
33 #define IP6TABLES "/usr/sbin/ip6tables"
34 #define IPTABLES_CHECK "-C"
35 #define APPEND "-A"
36 #define DELETE "-D"
37 #define INSERT "-I"
38
39 #define NFACCT_NAME_MOD " -m nfacct --nfacct-name %s"
40 #define REJECT_RULE " -j REJECT"
41 #define ACCEPT_RULE " -j ACCEPT"
42 #define OUT_RULE "OUTPUT"
43 #define IN_RULE "INPUT"
44 #define FORWARD_RULE "FORWARD"
45
46 /* TODO idea to use the same rule both for BLOCK (REJECT) and WARNING (ACCEPT) */
47 #define RULE_APP_OUT "%s -w %s OUTPUT -o %s -m cgroup --cgroup %u %s %s"
48 #define RULE_APP_IN "%s -w %s INPUT -i %s -m cgroup --cgroup %u %s %s"
49
50 /* iptables -w [I/A/D] [OUTPUT/FORWARD/INPUT] -o/-i iface -m nfacct --nfacct-name name -j ACCEPT/REJECT */
51
52 #define RULE_IFACE_OUT "%s -w %s %s -o %s %s %s"
53 #define RULE_IFACE_IN "%s -w %s %s -i %s %s  %s"
54
55 #define NFNL_SUBSYS_ACCT                7
56 #define BUF_SIZE_FOR_ERR 100
57
58 static void prepare_netlink_msg(struct genl *req, int type, int flag)
59 {
60         int seq = time(NULL);
61         memset(req, 0, sizeof(struct genl));
62         req->n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
63         req->n.nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | type;
64         req->n.nlmsg_flags = NLM_F_REQUEST | flag;
65         req->n.nlmsg_seq = seq;
66 }
67
68 static void add_value_attr(struct genl *req, const void *data, int len,
69                            int type)
70 {
71         int payload;
72         /* get tail */
73         struct nlattr *na = (struct nlattr *)((char *)req +
74                                               NLMSG_ALIGN(req->n.nlmsg_len));
75
76         na->nla_type = type;
77         payload = len + NLA_HDRLEN;
78         na->nla_len = payload;
79         memcpy(NLA_DATA(na), data, len);
80         req->n.nlmsg_len += NLMSG_ALIGN(payload);
81 }
82
83 /*
84  * following 2 function should be used in combination.
85  * start_nest_attr returns nlattr structure, which should be completed by
86  * end_nest_attr,
87  * before these invocations any number of netlink arguments could be inserted
88  * */
89 static struct nlattr *start_nest_attr(struct genl *req, uint16_t type)
90 {
91         struct nlattr *start = (struct nlattr *)((char *)req +
92                                                  NLMSG_ALIGN(req->n.nlmsg_len));
93
94         start->nla_type = NLA_F_NESTED | type;
95         req->n.nlmsg_len += NLMSG_ALIGN(sizeof(struct nlattr));
96         return start;
97 }
98
99 static void end_nest_attr(struct genl *req, struct nlattr *start)
100 {
101         start->nla_len = (__u16)((char *)req +
102                                  NLMSG_ALIGN(req->n.nlmsg_len) - (char *)start);
103 }
104
105 static void add_string_attr(struct genl *req, const char *str, int type)
106 {
107         add_value_attr(req, str, strlen(str) + 1, type);
108 }
109
110 static void add_uint64_attr(struct genl *req, const uint64_t v, int type)
111 {
112         add_value_attr(req, &v, sizeof(v), type);
113 }
114
115 /* macros or templare, due uint64 and uint32 is the same functions */
116 static void add_uint32_attr(struct genl *req, const uint32_t v, int type)
117 {
118         add_value_attr(req, &v, sizeof(v), type);
119 }
120
121 static stc_error_e send_nfacct_request(int sock, struct genl *req)
122 {
123         struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
124         int ret = sendto(sock, (char *)(&req->n), req->n.nlmsg_len, 0,
125                          (struct sockaddr *)&nladdr, sizeof(nladdr));
126         ret_value_msg_if(ret < 0, STC_ERROR_FAIL,
127                          "Failed to send nfacct request, error [%d]", ret);
128
129         return STC_ERROR_NONE;
130 }
131
132 static stc_error_e nfacct_send_new(nfacct_rule_s *counter)
133 {
134         struct genl req;
135
136         prepare_netlink_msg(&req, NFNL_MSG_ACCT_NEW, NLM_F_CREATE | NLM_F_ACK);
137         add_string_attr(&req, counter->name, NFACCT_NAME);
138
139         STC_LOGD("counter name %s", counter->name);
140
141         /* padding */
142         add_uint64_attr(&req, 0, NFACCT_PKTS);
143         add_uint64_attr(&req, 0, NFACCT_BYTES);
144         if (counter->quota) {
145                 STC_LOGD("quota bytes %"PRId64, counter->quota);
146
147                 add_uint32_attr(&req, htobe32(NFACCT_F_QUOTA_BYTES),
148                                 NFACCT_FLAGS);
149                 add_uint64_attr(&req, htobe64(counter->quota), NFACCT_QUOTA);
150         }
151
152         return send_nfacct_request(counter->carg->sock, &req);
153 }
154
155 stc_error_e nfacct_send_del(nfacct_rule_s *counter)
156 {
157         struct genl req;
158
159 #if STC_DEBUG_LOG
160         STC_LOGD("send remove request for %s", counter->name);
161 #endif
162
163         prepare_netlink_msg(&req, NFNL_MSG_ACCT_DEL, NLM_F_ACK);
164         add_string_attr(&req, counter->name, NFACCT_NAME);
165         return send_nfacct_request(counter->carg->sock, &req);
166 }
167 #define NFACCT_F_QUOTAS (NFACCT_F_QUOTA_BYTES | NFACCT_F_QUOTA_PKTS)
168
169 static stc_error_e internal_nfacct_send_get(struct counter_arg *carg,
170                                             enum nfnl_acct_msg_types get_type,
171                                             const char *name,
172                                             int mask, int filter)
173 {
174         struct genl req;
175         struct nlattr *na;
176         int flag = !name ? NLM_F_DUMP : 0;
177         prepare_netlink_msg(&req, get_type,
178                             flag);
179         /* due we don't get counter with quota any where else,
180          * here we will request just counters by default */
181         if (name)
182                 add_string_attr(&req, name, NFACCT_NAME);
183
184         na = start_nest_attr(&req, NFACCT_FILTER);
185         add_uint32_attr(&req, htonl(mask),
186                         NFACCT_FILTER_ATTR_MASK);
187         add_uint32_attr(&req, htonl(filter), NFACCT_FILTER_ATTR_VALUE);
188         end_nest_attr(&req, na);
189         return send_nfacct_request(carg->sock, &req);
190 }
191
192 stc_error_e nfacct_send_get_counters(struct counter_arg *carg, const char *name)
193 {
194         /* get and reset countes value */
195         return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET_CTRZERO, name,
196                                         NFACCT_F_QUOTAS, 0);
197 }
198
199 stc_error_e nfacct_send_get_quotas(struct counter_arg *carg, const char *name)
200 {
201         /* just get counters */
202         return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET, name,
203                                         NFACCT_F_QUOTA_BYTES,
204                                         NFACCT_F_QUOTA_BYTES);
205 }
206
207 stc_error_e nfacct_send_get_all(struct counter_arg *carg)
208 {
209         /* get and reset everything, used when quiting */
210         return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET_CTRZERO, NULL,
211                                         0, 0);
212 }
213
214 stc_error_e nfacct_send_get(nfacct_rule_s *rule)
215 {
216         if (rule->intend == NFACCT_BLOCK || rule->intend == NFACCT_WARN)
217                 return nfacct_send_get_quotas(rule->carg, rule->name);
218         else if (rule->intend == NFACCT_COUNTER)
219                 return nfacct_send_get_counters(rule->carg, rule->name);
220
221         return STC_ERROR_INVALID_PARAMETER;
222 }
223
224 static nfacct_rule_direction convert_to_iotype(int type)
225 {
226         return (type < NFACCT_COUNTER_LAST_ELEM &&
227                 type > NFACCT_COUNTER_UNKNOWN) ? type : NFACCT_COUNTER_UNKNOWN;
228 }
229
230 static stc_iface_type_e convert_to_iftype(int type)
231 {
232         return (type < STC_IFACE_LAST_ELEM &&
233                 type > STC_IFACE_UNKNOWN) ? type : STC_IFACE_UNKNOWN;
234 }
235
236 bool recreate_counter_by_name(char *cnt_name, nfacct_rule_s *cnt)
237 {
238         char *iftype_part;
239         char *classid_part;
240         char *io_part;
241         char *ifname_part;
242         char *save_ptr = NULL;
243         char name[NFACCT_NAME_MAX] = {0}; /* parse buffer to avoid cnt_name modification */
244
245         strncpy(name, cnt_name, sizeof(name) - 1);
246
247         switch (name[0]) {
248         case 'c':
249                 cnt->intend  = NFACCT_COUNTER;
250                 break;
251         case 'w':
252                 cnt->intend  = NFACCT_WARN;
253                 break;
254         case 'r':
255                 cnt->intend  = NFACCT_BLOCK;
256                 break;
257         case 't':
258                 cnt->intend  = NFACCT_TETH_COUNTER;
259                 break;
260         default:
261                 return false;
262         }
263
264         STRING_SAVE_COPY(cnt->name, cnt_name);
265
266         if (cnt->intend == NFACCT_TETH_COUNTER) {
267                 char ifname_buf[MAX_IFACE_LENGTH];
268                 int ifname_len;
269                 stc_iface_type_e iface;
270                 /* tbnep+:seth_w0; means comes by bt go away by mobile interface,
271                  * it's outgoing traffic, due all tethering is mobile databased */
272                 iftype_part = strchr(name, ':');
273                 ret_value_msg_if(iftype_part == NULL,
274                                  false, "Invalid format of the tethering counter %s", name);
275                 ifname_len = iftype_part - name - 1;
276                 strncpy(ifname_buf, name + 1, ifname_len); /* skip first t */
277                 ifname_buf[ifname_len] = '\0';
278                 iface = get_iftype_by_name(ifname_buf);
279                 /* check first part is it datacall */
280                 if (iface == STC_IFACE_DATACALL) {
281                         strncpy(cnt->ifname, ifname_buf, MAX_IFACE_LENGTH);
282                         cnt->iotype = NFACCT_COUNTER_IN;
283                 } else {
284                         /* +1, due : symbol and till the end of cnt_name */
285                         strncpy(ifname_buf, iftype_part + 1, MAX_IFACE_LENGTH);
286                         iface = get_iftype_by_name(ifname_buf);
287                         if (iface == STC_IFACE_DATACALL) {
288                                 cnt->iotype = NFACCT_COUNTER_OUT;
289                                 strncpy(cnt->ifname, ifname_buf, MAX_IFACE_LENGTH);
290                         }
291                 }
292
293                 if (cnt->iotype == NFACCT_COUNTER_UNKNOWN) {
294                         STC_LOGE("can't determine tethering direction %s", name);
295                         return false;
296                 }
297                 cnt->iftype = STC_IFACE_DATACALL;
298                 cnt->classid = STC_TETHERING_APP_CLASSID;
299                 return true;
300         }
301
302         io_part = strtok_r(name, "_", &save_ptr);
303         if (io_part != NULL)
304                 cnt->iotype = convert_to_iotype(atoi(io_part + 1));
305         else
306                 return false;
307
308         iftype_part = strtok_r(NULL, "_", &save_ptr);
309         if (iftype_part != NULL)
310                 cnt->iftype = convert_to_iftype(atoi(iftype_part));
311         else
312                 return false;
313
314         classid_part = strtok_r(NULL, "_", &save_ptr);
315         if (classid_part != NULL)
316                 cnt->classid = atoi(classid_part);
317         else {
318                 cnt->classid = STC_ALL_APP_CLASSID;
319                 return cnt->intend == NFACCT_BLOCK ? true : false;
320         }
321
322         ifname_part = strtok_r(NULL, "\0", &save_ptr);
323         if (ifname_part != NULL)
324                 STRING_SAVE_COPY(cnt->ifname, ifname_part);
325         else
326                 return false;
327
328         return true;
329 }
330
331 static void _process_answer(struct netlink_serialization_params *params)
332 {
333         struct rtattr *na;
334         struct rtattr *attr_list[__NFACCT_MAX] = {0};
335         struct counter_arg *carg = params->carg;
336         struct genl *ans = params->ans;;
337         struct nlmsghdr *nlhdr = &ans->n;
338         int len = GENLMSG_PAYLOAD(nlhdr);
339         int ans_len = carg->ans_len;
340
341         if (len == 0)
342                 return;
343
344         /* parse reply message */
345         na = (struct rtattr *)GENLMSG_DATA(ans);
346
347         while (NLMSG_OK(nlhdr, ans_len)) {
348                 fill_attribute_list(attr_list, NFACCT_MAX,
349                                     na, len);
350                 if (!attr_list[NFACCT_NAME] ||
351                     !attr_list[NFACCT_BYTES])
352                         goto next;
353                 params->eval_attr(attr_list, carg);
354
355 next:
356                 nlhdr = NLMSG_NEXT(nlhdr, ans_len);
357                 if (ans_len < 0)
358                         break;
359                 na = (struct rtattr *)GENLMSG_DATA(nlhdr);
360         }
361
362         if (params->post_eval_attr)
363                 params->post_eval_attr(carg);
364 }
365
366 netlink_serialization_command *
367 netlink_create_command(struct netlink_serialization_params *params)
368 {
369         static netlink_serialization_command command = {0,};
370         command.deserialize_answer = _process_answer;
371         command.params = *params;
372         return &command;
373 }
374
375 static unsigned int get_args_number(const char *cmd_buf)
376 {
377         char *str;
378         unsigned int count = 0;
379
380         for (str = (char *)cmd_buf; *str != '\0'; ++str) {
381                 if (*str == ' ')
382                         ++count;
383         }
384         return count;
385 }
386
387 static void wait_for_rule_cmd(pid_t pid)
388 {
389         int status;
390         pid_t ret_pid;
391
392         if (!pid || pid == -1) {
393                 STC_LOGD("no need to wait");
394                 return;
395         }
396
397         ret_pid = waitpid(pid, &status, 0);
398         if (ret_pid < 0) {
399                 char buf[BUF_SIZE_FOR_ERR] = { 0 };
400                 STC_LOGD("can't wait for a pid %d %d %s", pid, status,
401                          strerror_r(errno, buf, BUF_SIZE_FOR_ERR));
402         }
403 }
404
405 static char* get_cmd_pos(const char *cmd_buf)
406 {
407         char *cmd_pos = strstr(cmd_buf, APPEND);
408         if (!cmd_pos)
409                 cmd_pos = strstr(cmd_buf, INSERT);
410
411         return cmd_pos;
412 }
413
414 static bool is_rule_present(const char *cmd_buf)
415 {
416         bool ret = false;
417         pid_t pid = fork();
418
419         if (pid == 0) {
420                 gchar **args = NULL;
421                 size_t buf_len;
422                 char *exec_buf;
423                 char *cmd_pos = get_cmd_pos(cmd_buf);
424
425                 if (!cmd_pos)
426                         exit(1);
427
428                 buf_len = strlen(cmd_buf) + 1;
429                 exec_buf = (char *)malloc(buf_len);
430                 if (!exec_buf)
431                         exit(1);
432
433                 strncpy(exec_buf, cmd_buf, buf_len);
434                 strncpy(exec_buf + (cmd_pos - cmd_buf), IPTABLES_CHECK,
435                         sizeof(IPTABLES_CHECK) - 1);
436
437 #if STC_DEBUG_LOG
438                 STC_LOGD("check rule %s", exec_buf);
439 #endif
440
441                 args = g_strsplit_set(exec_buf, " ", -1);
442
443                 ret = execv(args[0], args);
444                 if (ret) {
445                         char buf[BUF_SIZE_FOR_ERR] = { 0 };
446                         STC_LOGE("Can't execute %s: %s",
447                                  cmd_buf, strerror_r(errno, buf,
448                                                      BUF_SIZE_FOR_ERR));
449                 }
450
451                 free(exec_buf);
452                 g_strfreev(args);
453                 exit(ret);
454         }
455
456         return ret;
457 }
458
459 stc_error_e exec_iptables_cmd(const char *cmd_buf, pid_t *cmd_pid)
460 {
461         const size_t args_number = get_args_number(cmd_buf);
462         *cmd_pid = 0;
463
464         ret_value_msg_if(args_number == 0, STC_ERROR_FAIL, "no arguments");
465
466         pid_t pid = fork();
467
468         if (pid == 0) {
469                 char *cmd;
470                 unsigned int i;
471                 char *args[args_number + 2];
472                 int ret;
473                 char *save_ptr = NULL;
474
475 #if STC_DEBUG_LOG
476                 STC_LOGD("executing iptables cmd %s in forked process", cmd_buf);
477 #endif
478
479                 if (is_rule_present(cmd_buf)) {
480                         STC_LOGD("Rule %s already present", cmd_buf);
481                         exit(0);
482                 }
483
484                 args[0] = "iptables";
485                 cmd = strtok_r((char *)cmd_buf, " ", &save_ptr);
486                 if (cmd == NULL) {
487                         STC_LOGE("no arguments");
488                         exit(-EINVAL);
489                 }
490
491                 for (i = 1; i <= args_number; ++i)
492                         args[i] = strtok_r(NULL, " ", &save_ptr);
493
494                 args[i] = NULL;
495
496                 ret = execv(cmd, args);
497                 if (ret) {
498                         char buf[BUF_SIZE_FOR_ERR] = { 0 };
499                         STC_LOGE("Can't execute %s: %s",
500                                  cmd_buf, strerror_r(errno, buf,
501                                                      BUF_SIZE_FOR_ERR));
502                 }
503                 exit(ret);
504         }
505
506         *cmd_pid = pid;
507         return STC_ERROR_NONE;
508 }
509
510 stc_error_e exec_ip6tables_cmd(const char *cmd_buf, pid_t *cmd_pid)
511 {
512         const size_t args_number = get_args_number(cmd_buf);
513         *cmd_pid = 0;
514
515         ret_value_msg_if(args_number == 0, STC_ERROR_FAIL, "no arguments");
516
517         pid_t pid = fork();
518
519         if (pid == 0) {
520                 char *cmd;
521                 unsigned int i;
522                 char *args[args_number + 2];
523                 int ret;
524                 char *save_ptr = NULL;
525
526 #if STC_DEBUG_LOG
527                 STC_LOGD("executing ip6tables cmd %s in forked process", cmd_buf);
528 #endif
529
530                 if (is_rule_present(cmd_buf)) {
531                         STC_LOGD("Rule %s already present", cmd_buf);
532                         exit(0);
533                 }
534
535                 args[0] = "ip6tables";
536                 cmd = strtok_r((char *)cmd_buf, " ", &save_ptr);
537                 if (cmd == NULL) {
538                         STC_LOGE("no arguments");
539                         exit(-EINVAL);
540                 }
541
542                 for (i = 1; i <= args_number; ++i)
543                         args[i] = strtok_r(NULL, " ", &save_ptr);
544
545                 args[i] = NULL;
546
547                 ret = execv(cmd, args);
548                 if (ret) {
549                         char buf[BUF_SIZE_FOR_ERR] = { 0 };
550                         STC_LOGE("Can't execute %s: %s",
551                                  cmd_buf, strerror_r(errno, buf,
552                                                      BUF_SIZE_FOR_ERR));
553                 }
554                 exit(ret);
555         }
556
557         *cmd_pid = pid;
558         return STC_ERROR_NONE;
559 }
560
561 static char *choose_iftype_name(nfacct_rule_s *rule)
562 {
563         return strlen(rule->ifname) != 0 ? rule->ifname :
564                 get_iftype_name(rule->iftype);
565 }
566
567 static stc_error_e exec_iface_cmd(const char *pattern, const char *cmd,
568                                   const char *chain, const char *nfacct,
569                                   const char *jump, char *iftype_name,
570                                   pid_t *pid, nfacct_rule_iptype iptype)
571 {
572         char block_buf[MAX_PATH_LENGTH];
573         int ret;
574         const char *iptables_type = IPTABLES;
575
576         ret_value_msg_if(iftype_name == NULL, STC_ERROR_FAIL,
577                          "Invalid network interface name argument");
578
579         if (iptype == NFACCT_TYPE_IPV6)
580                 iptables_type = IP6TABLES;
581
582         ret = snprintf(block_buf, sizeof(block_buf), pattern, iptables_type,
583                        cmd, chain, iftype_name, nfacct, jump);
584         ret_value_msg_if(ret > sizeof(block_buf), STC_ERROR_FAIL,
585                          "Not enough buffer");
586
587         if (iptype == NFACCT_TYPE_IPV6)
588                 exec_ip6tables_cmd(block_buf, pid);
589         else
590                 exec_iptables_cmd(block_buf, pid);
591
592         wait_for_rule_cmd(*pid);
593
594         return STC_ERROR_NONE;
595 }
596
597 static stc_error_e exec_app_cmd(const char *pattern, const char *cmd,
598                                 const char *nfacct, const char *jump,
599                                 const uint32_t classid, char *iftype_name,
600                                 pid_t *pid, nfacct_rule_iptype iptype)
601 {
602         char block_buf[MAX_PATH_LENGTH];
603         int ret;
604         const char *iptables_type = IPTABLES;
605
606         ret_value_msg_if(iftype_name == NULL, STC_ERROR_FAIL,
607                          "Invalid network interface name argument");
608
609         if (iptype == NFACCT_TYPE_IPV6)
610                 iptables_type = IP6TABLES;
611
612         ret = snprintf(block_buf, sizeof(block_buf), pattern, iptables_type,
613                        cmd, iftype_name, classid, nfacct, jump);
614         ret_value_msg_if(ret > sizeof(block_buf), STC_ERROR_FAIL,
615                          "Not enough buffer");
616
617         if (iptype == NFACCT_TYPE_IPV6)
618                 exec_ip6tables_cmd(block_buf, pid);
619         else
620                 exec_iptables_cmd(block_buf, pid);
621
622         wait_for_rule_cmd(*pid);
623
624         return STC_ERROR_NONE;
625 }
626
627 static char *get_iptables_cmd(const nfacct_rule_action action)
628 {
629         if (action == NFACCT_ACTION_APPEND)
630                 return APPEND;
631         else if (action == NFACCT_ACTION_DELETE)
632                 return DELETE;
633         else if (action == NFACCT_ACTION_INSERT)
634                 return INSERT;
635
636         return "";
637 }
638
639 static char *get_iptables_chain(const nfacct_rule_direction iotype)
640 {
641         if (iotype == NFACCT_COUNTER_IN)
642                 return IN_RULE;
643         else if (iotype == NFACCT_COUNTER_OUT)
644                 return OUT_RULE;
645
646         return "";
647 }
648
649 static char *get_iptables_jump(const nfacct_rule_jump jump)
650 {
651         if (jump == NFACCT_JUMP_ACCEPT)
652                 return ACCEPT_RULE;
653         else if (jump == NFACCT_JUMP_REJECT)
654                 return REJECT_RULE;
655
656         return "";
657 }
658
659 static stc_error_e produce_app_rule(nfacct_rule_s *rule)
660 {
661         if (rule == NULL)
662                 return STC_ERROR_INVALID_PARAMETER;
663
664         char *set_cmd = get_iptables_cmd(rule->action);
665         char *jump_cmd = get_iptables_jump(rule->jump);
666         char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
667                 3*MAX_DEC_SIZE(int) + 4];
668         stc_error_e ret = STC_ERROR_NONE;
669         pid_t pid = 0;
670
671         /* income part */
672         if (rule->iotype & NFACCT_COUNTER_IN) {
673                 rule->quota = rule->rcv_limit;
674                 rule->iotype = NFACCT_COUNTER_IN;
675                 generate_counter_name(rule);
676
677                 /* to support quated counter we need nfacct,
678                  *      don't use it in case of just block without a limit
679                  *      iow, send_limit = 0 and rcv_limit 0 */
680                 if (rule->action != NFACCT_ACTION_DELETE) {
681                         ret = nfacct_send_del(rule);
682                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
683                                          "can't del quota counter");
684
685                         ret = nfacct_send_new(rule);
686                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
687                                          "can't set nfacct counter");
688                         keep_counter(rule);
689                 }
690
691                 /* we have a counter, let's key in a rule, drop in case of
692                  *  send_limit/rcv_limit */
693                 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
694                                rule->name);
695                 ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0,
696                                  STC_ERROR_FAIL, "Not enought buffer");
697
698                 ret = exec_app_cmd(RULE_APP_IN, set_cmd, nfacct_buf, jump_cmd,
699                                    rule->classid, choose_iftype_name(rule),
700                                    &pid, rule->iptype);
701                 ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
702                                  "Can't set conditional block for ingress"
703                                  " traffic, for classid %u, cmd %s, j %s",
704                                  rule->classid, set_cmd, jump_cmd);
705
706                 /* remove in any case */
707                 if (rule->action == NFACCT_ACTION_DELETE) {
708                         /* TODO here and everywhere should be not just a del,
709                          *      here should be get counted value and than
710                          *      set new counter with that value, but it's minor issue,
711                          *      due it's not clear when actual counters was stored,
712                          *      and based on which value settings made such decition */
713                         rule->iptables_rule = nfacct_send_del;
714                         set_finalize_flag(rule);
715                         nfacct_send_get(rule);
716                         ret = nfacct_send_del(rule);
717                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
718                                          "can't del quota counter");
719                 }
720         }
721
722         if (rule->iotype & NFACCT_COUNTER_OUT) {
723                 /* outcome part */
724                 rule->iotype = NFACCT_COUNTER_OUT;
725                 rule->quota = rule->send_limit;
726                 generate_counter_name(rule);
727                 if (rule->action != NFACCT_ACTION_DELETE) {
728                         ret = nfacct_send_del(rule);
729                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
730                                          "can't del quota counter");
731
732                         ret = nfacct_send_new(rule);
733                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
734                                          "can't set quota counter");
735                         keep_counter(rule);
736                 }
737
738                 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
739                                rule->name);
740                 ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0,
741                                  STC_ERROR_FAIL, "Not enought buffer");
742
743                 ret = exec_app_cmd(RULE_APP_OUT, set_cmd, nfacct_buf, jump_cmd,
744                                    rule->classid, choose_iftype_name(rule),
745                                    &pid, rule->iptype);
746                 ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
747                                  "Can't set conditional block for engress"
748                                  " traffic, for classid %u, cmd %s, j %s",
749                                  rule->classid, set_cmd, jump_cmd);
750                 if (rule->action == NFACCT_ACTION_DELETE) {
751                         rule->iptables_rule = nfacct_send_del;
752                         /* not effective, it's better to replace
753                          * set_finalize_flag by set_property,
754                          * due keep_counter it necessary only for
755                          * setting iptables_rule */
756                         set_finalize_flag(rule);
757                         nfacct_send_get(rule);
758                         ret = nfacct_send_del(rule);
759                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
760                                          "can't del quota counter");
761                 }
762         }
763         return STC_ERROR_NONE;
764 }
765
766 static stc_error_e produce_iface_rule(nfacct_rule_s *rule)
767 {
768         if (rule == NULL)
769                 return STC_ERROR_INVALID_PARAMETER;
770
771         char *set_cmd = get_iptables_cmd(rule->action);
772         char *jump_cmd = get_iptables_jump(rule->jump);
773         char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
774                 3*MAX_DEC_SIZE(int) + 4];
775         stc_error_e ret;
776         pid_t pid = 0;
777
778         if (rule->iotype & NFACCT_COUNTER_IN) {
779                 /* income part */
780                 rule->iotype = NFACCT_COUNTER_IN;
781                 rule->quota = rule->rcv_limit;
782                 generate_counter_name(rule);
783
784                 if (rule->action != NFACCT_ACTION_DELETE) {
785                         /* send delete comman in case of creation,
786                          * because nfacct doesn't reset value for nfacct quota
787                          * in case of quota existing */
788                         ret = nfacct_send_del(rule);
789                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
790                                          "can't del quota counter");
791
792                         ret = nfacct_send_new(rule);
793                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
794                                          "can't set quota counter");
795                         keep_counter(rule);
796                 }
797
798                 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
799                                NFACCT_NAME_MOD, rule->name);
800                 ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0,
801                                  STC_ERROR_FAIL, "Not enought buffer");
802
803                 ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd,
804                                      get_iptables_chain(rule->iotype),
805                                      nfacct_buf, jump_cmd,
806                                      choose_iftype_name(rule), &pid,
807                                      rule->iptype);
808                 ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
809                                  "Can't set conditional block for ingress"
810                                  " traffic, for iftype %d, cmd %s, j %s",
811                                  rule->iftype, set_cmd, jump_cmd);
812
813                 /* for tethering */
814                 if (rule->intend == NFACCT_WARN ||
815                     rule->intend == NFACCT_BLOCK) {
816                         /* RULE_IFACE_OUT is not a misprint here */
817                         ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd,
818                                              FORWARD_RULE, nfacct_buf, jump_cmd,
819                                              choose_iftype_name(rule), &pid,
820                                              rule->iptype);
821                         ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
822                                          "Can't set forward rule for ingress "
823                                          "traffic, for iftype %d, cmd %s, j %s",
824                                          rule->iftype, set_cmd, jump_cmd);
825                 }
826                 /* tethering */
827
828                 if (rule->action == NFACCT_ACTION_DELETE) {
829                         rule->iptables_rule = nfacct_send_del;
830                         set_finalize_flag(rule);
831                         nfacct_send_get(rule);
832                         ret = nfacct_send_del(rule);
833                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
834                                          "can't del quota counter");
835                 }
836         }
837
838         if (rule->iotype & NFACCT_COUNTER_OUT) {
839                 /* outcome part */
840                 rule->iotype = NFACCT_COUNTER_OUT;
841                 rule->quota = rule->send_limit;
842                 generate_counter_name(rule);
843
844                 if (rule->action != NFACCT_ACTION_DELETE) {
845                         /* send delete comman in case of creation,
846                          * because nfacct doesn't reset value for nfacct quota
847                          * in case of quota existing */
848                         ret = nfacct_send_del(rule);
849                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
850                                          "can't del quota counter");
851
852                         ret = nfacct_send_new(rule);
853                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
854                                          "can't set quota counter");
855                         keep_counter(rule);
856                 }
857
858                 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
859                                NFACCT_NAME_MOD, rule->name);
860                 ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0,
861                                  STC_ERROR_FAIL, "Not enough buffer");
862
863                 ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, OUT_RULE,
864                                      nfacct_buf, jump_cmd,
865                                      choose_iftype_name(rule), &pid,
866                                      rule->iptype);
867                 ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
868                                  "Can't set conditional block for "
869                                  "engress traffic, for iftype %d, cmd %s, j %s",
870                                  rule->iftype, set_cmd, jump_cmd);
871                 /* for tethering  */
872                 if (rule->intend == NFACCT_WARN ||
873                     rule->intend == NFACCT_BLOCK) {
874                         ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd,
875                                              FORWARD_RULE, nfacct_buf, jump_cmd,
876                                              choose_iftype_name(rule), &pid,
877                                              rule->iptype);
878                         ret_value_msg_if(ret != STC_ERROR_NONE, STC_ERROR_FAIL,
879                                          "Can't set forward rule for engress "
880                                          "traffic, for iftype %d, cmd %s, j %s",
881                                          rule->iftype, set_cmd, jump_cmd);
882                 }
883                 /* tethering  */
884
885                 if (rule->action == NFACCT_ACTION_DELETE) {
886                         rule->iptables_rule = nfacct_send_del;
887                         set_finalize_flag(rule);
888                         nfacct_send_get(rule);
889                         ret = nfacct_send_del(rule);
890                         ret_value_msg_if(ret != STC_ERROR_NONE, ret,
891                                          "can't del quota counter");
892                 }
893         }
894         return STC_ERROR_NONE;
895 }
896
897 stc_error_e produce_net_rule(nfacct_rule_s *rule)
898 {
899         stc_error_e ret = STC_ERROR_NONE;
900
901         if (rule == NULL)
902                 return STC_ERROR_INVALID_PARAMETER;
903
904         if (rule->action == NFACCT_ACTION_APPEND &&
905             rule->intend == NFACCT_WARN &&
906             !rule->send_limit && !rule->rcv_limit)
907                 return STC_ERROR_NONE;
908
909         if (rule->classid != STC_ALL_APP_CLASSID &&
910             rule->classid != STC_TETHERING_APP_CLASSID &&
911             rule->classid != STC_TOTAL_DATACALL_CLASSID &&
912             rule->classid != STC_TOTAL_WIFI_CLASSID &&
913             rule->classid != STC_TOTAL_BLUETOOTH_CLASSID &&
914             rule->classid != STC_TOTAL_IPV4_CLASSID &&
915             rule->classid != STC_TOTAL_IPV6_CLASSID)
916                 ret = produce_app_rule(rule);
917         else
918                 ret = produce_iface_rule(rule);
919
920         return ret;
921 }
922
923 void generate_counter_name(nfacct_rule_s *counter)
924 {
925         char warn_symbol = 'c';
926         if (!strlen(counter->ifname)) {
927                 char *iftype_name = get_iftype_name(counter->iftype);
928                 /* trace counter name, maybe name was already generated */
929                 ret_msg_if(iftype_name == NULL,
930                            "Can't get interface name for counter %s, iftype %d)!",
931                            counter->name, counter->iftype);
932                 STRING_SAVE_COPY(counter->ifname, iftype_name);
933         }
934
935         if (counter->intend  == NFACCT_WARN)
936                 warn_symbol = 'w';
937         else if (counter->intend  == NFACCT_BLOCK)
938                 warn_symbol = 'r';
939         snprintf(counter->name, NFACCT_NAME_MAX, "%c%d_%d_%d_%s",
940                  warn_symbol, counter->iotype, counter->iftype,
941                  counter->classid, counter->ifname);
942 }
943