4 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 * @desc Datausage module
24 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
33 #include <sys/types.h>
41 #include "module-data.h"
42 #include "nfacct-rule.h"
43 #include "nl-helper.h"
44 #include "resourced.h"
47 #define IPTABLES "/usr/sbin/iptables"
48 #define IPTABLES_CHECK "-C"
53 #define NFACCT_NAME_MOD " -m nfacct --nfacct-name %s"
54 #define REJECT_RULE " -j REJECT"
55 #define ACCEPT_RULE " -j ACCEPT"
57 /* TODO idea to use the same rule both for BLOCK (REJECT) and WARNING (ACCEPT) */
58 #define RULE_APP_OUT "%s -w %s OUTPUT -o %s -m cgroup --cgroup %u %s %s"
59 #define RULE_APP_IN "%s -w %s INPUT -i %s -m cgroup --cgroup %u %s %s"
61 #define RULE_IFACE_OUT "%s -w %s OUTPUT -o %s %s -m cgroup ! --cgroup 0 %s"
62 #define RULE_IFACE_IN "%s -w %s INPUT -i %s %s -m cgroup ! --cgroup 0 %s"
64 #define NFNL_SUBSYS_ACCT 7
66 enum nfnl_acct_flags {
67 NFACCT_F_QUOTA_PKTS = (1 << 0),
68 NFACCT_F_QUOTA_BYTES = (1 << 1),
69 NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */
72 static void prepare_netlink_msg(struct genl *req, int type, int flag)
75 memset(req, 0, sizeof(struct genl));
76 req->n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
77 req->n.nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | type;
78 req->n.nlmsg_flags = NLM_F_REQUEST | flag;
79 req->n.nlmsg_seq = seq;
82 static void add_value_attr(struct genl *req, const void *data, int len, int type)
86 struct nlattr *na = (struct nlattr *)(
87 (char *)req + NLMSG_ALIGN(req->n.nlmsg_len));
90 payload = len + NLA_HDRLEN;
91 na->nla_len = payload;
92 memcpy(NLA_DATA(na), data, len);
93 req->n.nlmsg_len += NLMSG_ALIGN(payload);
97 * following 2 function should be used in combination.
98 * start_nest_attr returns nlattr structure, which should be completed by
100 * before these invocations any number of netlink arguments could be inserted
102 static struct nlattr *start_nest_attr(struct genl *req, uint16_t type)
104 struct nlattr *start = (struct nlattr *)(
105 (char *)req + NLMSG_ALIGN(req->n.nlmsg_len));
107 start->nla_type = NLA_F_NESTED | type;
108 req->n.nlmsg_len += NLMSG_ALIGN(sizeof(struct nlattr));
112 static void end_nest_attr(struct genl *req, struct nlattr *start)
114 start->nla_len = (__u16)(
115 (char *)req + NLMSG_ALIGN(req->n.nlmsg_len) - (char *)start);
118 static void add_string_attr(struct genl *req, const char *str, int type)
120 add_value_attr(req, str, strlen(str) + 1, type);
123 static void add_uint64_attr(struct genl *req, const uint64_t v, int type)
125 add_value_attr(req, &v, sizeof(v), type);
128 /* macros or templare, due uint64 and uint32 is the same functions */
129 static void add_uint32_attr(struct genl *req, const uint32_t v, int type)
131 add_value_attr(req, &v, sizeof(v), type);
134 static resourced_ret_c send_nfacct_request(int sock, struct genl *req)
136 struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
137 int ret = sendto(sock, (char *)(&req->n), req->n.nlmsg_len, 0,
138 (struct sockaddr *)&nladdr, sizeof(nladdr));
139 ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
140 "Failed to send command to get outgoing traffic");
142 return RESOURCED_ERROR_NONE;
145 static resourced_ret_c nfacct_send_new(struct nfacct_rule *counter)
149 prepare_netlink_msg(&req, NFNL_MSG_ACCT_NEW, NLM_F_CREATE | NLM_F_ACK);
150 add_string_attr(&req, counter->name, NFACCT_NAME);
151 _D("counter name %s", counter->name);
153 add_uint64_attr(&req, 0, NFACCT_PKTS);
154 add_uint64_attr(&req, 0, NFACCT_BYTES);
155 if (counter->quota) {
156 _D("quota bytes %"PRId64, counter->quota);
157 add_uint32_attr(&req, htobe32(NFACCT_F_QUOTA_BYTES), NFACCT_FLAGS);
158 add_uint64_attr(&req, htobe64(counter->quota), NFACCT_QUOTA);
161 return send_nfacct_request(counter->carg->sock, &req);
164 static resourced_ret_c nfacct_send_del(struct nfacct_rule *counter)
168 prepare_netlink_msg(&req, NFNL_MSG_ACCT_DEL, NLM_F_ACK);
169 add_string_attr(&req, counter->name, NFACCT_NAME);
170 return send_nfacct_request(counter->carg->sock, &req);
172 #define NFACCT_F_QUOTAS (NFACCT_F_QUOTA_BYTES | NFACCT_F_QUOTA_PKTS)
173 resourced_ret_c nfacct_send_get(struct counter_arg *carg)
177 prepare_netlink_msg(&req, NFNL_MSG_ACCT_GET_CTRZERO,
179 /* due we don't get counter with quota any where else,
180 * here we will request just counters by default */
181 na = start_nest_attr(&req, NFACCT_FILTER);
182 add_uint32_attr(&req, NFACCT_F_QUOTAS,
183 NFACCT_FILTER_ATTR_MASK);
184 add_uint32_attr(&req, 0, NFACCT_FILTER_ATTR_VALUE);
185 end_nest_attr(&req, na);
186 return send_nfacct_request(carg->sock, &req);
189 resourced_ret_c nfacct_send_initiate(struct counter_arg *carg)
192 prepare_netlink_msg(&req, NFNL_MSG_ACCT_GET,
194 return send_nfacct_request(carg->sock, &req);
197 static nfacct_rule_direction convert_to_iotype(int type)
199 return type < NFACCT_COUNTER_LAST_ELEM && type > NFACCT_COUNTER_UNKNOWN ?
200 type : NFACCT_COUNTER_UNKNOWN;
203 static resourced_iface_type convert_to_iftype(int type)
205 return type < RESOURCED_IFACE_LAST_ELEM && type > RESOURCED_IFACE_UNKNOWN ?
206 type : RESOURCED_IFACE_UNKNOWN;
209 bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *cnt)
217 switch (cnt_name[0]) {
219 cnt->intend = NFACCT_COUNTER;
222 cnt->intend = NFACCT_WARN;
225 cnt->intend = NFACCT_BLOCK;
231 io_part = strtok_r(cnt_name, "_", &saveptr);
233 cnt->iotype = convert_to_iotype(atoi(io_part + 1));
237 iftype_part = strtok_r(NULL, "_", &saveptr);
238 if (iftype_part != NULL)
239 cnt->iftype = convert_to_iftype(atoi(iftype_part));
243 classid_part = strtok_r(NULL, "_", &saveptr);
244 if (classid_part != NULL)
245 cnt->classid = atoi(classid_part);
247 cnt->classid = RESOURCED_ALL_APP_CLASSID;
248 return cnt->intend == NFACCT_BLOCK ? true : false;
251 ifname_part = strtok_r(NULL, "\0", &saveptr);
252 if (ifname_part != NULL)
253 STRING_SAVE_COPY(cnt->ifname, ifname_part);
260 static void _process_answer(struct netlink_serialization_params *params)
263 struct rtattr *attr_list[__NFACCT_MAX] = {0};
264 struct counter_arg *carg = params->carg;
265 struct genl *ans = params->ans;;
266 struct nlmsghdr *nlhdr = &ans->n;
267 int len = GENLMSG_PAYLOAD(nlhdr);
268 int ans_len = carg->ans_len;
273 /* parse reply message */
274 na = (struct rtattr *)GENLMSG_DATA(ans);
276 while (NLMSG_OK(nlhdr, ans_len )) {
278 fill_attribute_list(attr_list, NFACCT_MAX,
280 if (!attr_list[NFACCT_NAME] ||
281 !attr_list[NFACCT_BYTES])
283 params->eval_attr(attr_list, carg);
286 nlhdr = NLMSG_NEXT(nlhdr, ans_len);
289 na = (struct rtattr *)GENLMSG_DATA(nlhdr);
292 if (params->post_eval_attr)
293 params->post_eval_attr(carg);
296 netlink_serialization_command *netlink_create_command(
297 struct netlink_serialization_params *params)
299 static netlink_serialization_command command = {0,};
300 command.deserialize_answer = _process_answer;
301 command.params = *params;
305 static unsigned int get_args_number(const char *cmd_buf)
308 unsigned int count = 0;
310 for (str = (char *)cmd_buf; *str != '\0'; ++str) {
317 static void wait_for_rule_cmd(struct nfacct_rule *rule, pid_t pid)
322 pid_t ret_pid = waitpid(pid, &status, 0);
324 strerror_r(errno, buf, sizeof(buf));
325 _D("can't wait for a pid %d %d %s", pid, status, buf);
329 static char* get_cmd_pos(const char *cmd_buf)
331 char *cmd_pos = strstr(cmd_buf, APPEND);
333 cmd_pos = strstr(cmd_buf, INSERT);
338 static bool is_rule_exists(const char *cmd_buf)
342 char *cmd_pos = get_cmd_pos(cmd_buf);
347 buf_len = strlen(cmd_buf) + 1;
348 exec_buf = (char *)malloc(buf_len);
352 strncpy(exec_buf, cmd_buf, buf_len);
353 strncpy(exec_buf + (cmd_pos - cmd_buf), IPTABLES_CHECK,
354 sizeof(IPTABLES_CHECK) - 1);
355 _D("check rule %s", exec_buf);
356 ret = system(exec_buf) == 0;
361 resourced_ret_c exec_iptables_cmd(const char *cmd_buf, pid_t *cmd_pid)
369 const size_t args_number = get_args_number(cmd_buf);
370 char *args[args_number + 2];
374 _D("executing iptables cmd %s in forked process", cmd_buf);
375 ret_value_msg_if(args_number == 0, RESOURCED_ERROR_FAIL, "no arguments");
377 if (is_rule_exists(cmd_buf)) {
378 _D("Rule %s already exists", cmd_buf);
381 args[0] = "iptables";
382 cmd = strtok_r((char *)cmd_buf, " ", &saveptr);
383 for (i = 1; i <= args_number; ++i) {
384 args[i] = strtok_r(NULL, " ", &saveptr);
388 ret = execv(cmd, args);
390 strerror_r(errno, buf, sizeof(buf));
391 _E("Can't execute %s: %s",
398 return RESOURCED_ERROR_NONE;
401 static char *choose_iftype_name(struct nfacct_rule *rule)
403 return strlen(rule->ifname) != 0 ? rule->ifname :
404 get_iftype_name(rule->iftype);
407 static resourced_ret_c exec_iface_cmd(const char *pattern, const char *cmd,
408 const char *nfacct, const char *jump,
409 char *iftype_name, pid_t *pid)
411 char block_buf[MAX_PATH_LENGTH];
414 ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
415 "Invalid network interface name argument");
417 ret = snprintf(block_buf, sizeof(block_buf), pattern, IPTABLES, cmd,
418 iftype_name, nfacct, jump);
419 ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE,
420 "Not enough buffer");
421 return exec_iptables_cmd(block_buf, pid);
424 static resourced_ret_c exec_app_cmd(const char *pattern, const char *cmd,
425 const char *nfacct, const char *jump,
426 const u_int32_t classid, char *iftype_name,
429 char block_buf[MAX_PATH_LENGTH];
431 ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
432 "Invalid network interface name argument");
433 ret = snprintf(block_buf, sizeof(block_buf), pattern, IPTABLES, cmd,
434 iftype_name, classid, nfacct, jump);
435 ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE,
436 "Not enough buffer");
437 return exec_iptables_cmd(block_buf, pid);
440 static char *get_iptables_cmd(const nfacct_rule_action action)
442 if (action == NFACCT_ACTION_APPEND)
444 else if(action == NFACCT_ACTION_DELETE)
446 else if (action == NFACCT_ACTION_INSERT)
452 static char *get_iptables_jump(const nfacct_rule_jump jump)
454 if (jump == NFACCT_JUMP_ACCEPT)
456 else if (jump == NFACCT_JUMP_REJECT)
462 static resourced_ret_c produce_app_rule(struct nfacct_rule *rule,
463 const int send_limit, const int rcv_limit,
464 const nfacct_rule_action action,
465 const nfacct_rule_jump jump,
466 const nfacct_rule_direction iotype)
468 char *set_cmd = get_iptables_cmd(action);
469 char *jump_cmd = get_iptables_jump(jump);
470 char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
471 3*MAX_DEC_SIZE(int) + 4];
472 resourced_ret_c ret = RESOURCED_ERROR_NONE;
476 if (iotype & NFACCT_COUNTER_IN) {
477 rule->quota = rcv_limit;
478 rule->iotype = NFACCT_COUNTER_IN;
479 generate_counter_name(rule);
481 /* to support quated counter we need nfacct,
482 * don't use it in case of just block without a limit
483 * iow, send_limit = 0 and rcv_limit 0 */
484 if (action != NFACCT_ACTION_DELETE) {
485 ret = nfacct_send_new(rule);
486 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
487 "can't set nfacct counter");
490 /* we have a counter, let's key in a rule, drop in case of
491 * send_limit/rcv_limit */
492 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
494 ret = exec_app_cmd(RULE_APP_IN, set_cmd, nfacct_buf,
495 jump_cmd, rule->classid, choose_iftype_name(rule), &pid);
496 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
497 RESOURCED_ERROR_FAIL, "Can't set conditional block for ingress"
498 " traffic, for classid %u, cmd %s, j %s",
499 rule->classid, set_cmd, jump_cmd);
501 /* remove in any case */
502 if (action != NFACCT_ACTION_APPEND) {
503 /* TODO here and everywhere should be not just a del,
504 * here should be get counted value and than
505 * set new counter with that value, but it's minor issue,
506 * due it's not clear when actual counters was stored,
507 * and based on which value settings made such decition */
508 wait_for_rule_cmd(rule, pid);
509 ret = nfacct_send_del(rule);
510 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
511 "can't set nfacct counter");
515 if (iotype & NFACCT_COUNTER_OUT) {
517 rule->iotype = NFACCT_COUNTER_OUT;
518 rule->quota = send_limit;
519 generate_counter_name(rule);
520 if (action != NFACCT_ACTION_DELETE) {
521 ret = nfacct_send_new(rule);
522 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
523 "can't set quota counter");
526 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
527 "can't set counter");
529 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
531 ret = exec_app_cmd(RULE_APP_OUT, set_cmd, nfacct_buf,
532 jump_cmd, rule->classid, choose_iftype_name(rule), &pid);
533 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
534 RESOURCED_ERROR_FAIL, "Can't set conditional block for engress"
535 " traffic, for classid %u, cmd %s, j %s",
536 rule->classid, set_cmd, jump_cmd);
537 if (action != NFACCT_ACTION_APPEND) {
538 wait_for_rule_cmd(rule, pid);
539 ret = nfacct_send_del(rule);
540 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
541 "can't del nfacct counter");
544 return RESOURCED_ERROR_NONE;
547 static resourced_ret_c produce_iface_rule(struct nfacct_rule *rule,
548 const int send_limit, const int rcv_limit,
549 const nfacct_rule_action action,
550 const nfacct_rule_jump jump,
551 const nfacct_rule_direction iotype)
553 char *set_cmd = get_iptables_cmd(action);
554 char *jump_cmd = get_iptables_jump(jump);
555 char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
556 3*MAX_DEC_SIZE(int) + 4];
560 if (iotype & NFACCT_COUNTER_IN) {
562 rule->quota = rcv_limit;
563 rule->iotype = NFACCT_COUNTER_IN;
564 generate_counter_name(rule);
566 if (action != NFACCT_ACTION_DELETE) {
567 ret = nfacct_send_new(rule);
568 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
569 "can't set quota counter");
572 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
573 NFACCT_NAME_MOD, rule->name);
574 ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
575 "Not enought buffer");
577 ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, nfacct_buf,
578 jump_cmd, choose_iftype_name(rule), &pid);
579 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
580 RESOURCED_ERROR_NONE, "Can't set conditional block for ingress"
581 " traffic, for iftype %d, cmd %s, j %s",
582 rule->iftype, set_cmd, jump_cmd);
584 if (action != NFACCT_ACTION_APPEND) {
585 wait_for_rule_cmd(rule, pid);
586 ret = nfacct_send_del(rule);
587 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
588 "can't set quota counter");
592 if (iotype & NFACCT_COUNTER_OUT) {
594 rule->iotype = NFACCT_COUNTER_OUT;
595 rule->quota = send_limit;
596 generate_counter_name(rule);
598 if (action != NFACCT_ACTION_DELETE) {
599 ret = nfacct_send_new(rule);
600 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
601 "can't set quota counter");
604 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
605 NFACCT_NAME_MOD, rule->name);
606 ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
607 "Not enough buffer");
609 ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, nfacct_buf,
610 jump_cmd, choose_iftype_name(rule), &pid);
611 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
612 RESOURCED_ERROR_FAIL, "Can't set conditional block for "
613 " engress traffic, for iftype %d, cmd %s, j %s",
614 rule->iftype, set_cmd, jump_cmd);
616 if (action != NFACCT_ACTION_APPEND) {
617 wait_for_rule_cmd(rule, pid);
618 ret = nfacct_send_del(rule);
619 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
620 "can't set quota counter");
623 return RESOURCED_ERROR_NONE;
626 resourced_ret_c produce_net_rule(struct nfacct_rule *rule,
627 const int send_limit, const int rcv_limit,
628 const nfacct_rule_action action,
629 const nfacct_rule_jump jump,
630 const nfacct_rule_direction iotype)
632 resourced_ret_c ret = RESOURCED_ERROR_NONE;
634 if (action == NFACCT_ACTION_APPEND && rule->intend == NFACCT_WARN
635 && !send_limit && !rcv_limit)
636 return RESOURCED_ERROR_NONE;
638 if (rule->classid != RESOURCED_ALL_APP_CLASSID)
639 ret = produce_app_rule(rule, send_limit,
640 rcv_limit, action, jump,
643 ret = produce_iface_rule(rule, send_limit, rcv_limit,
644 action, jump, iotype);
649 void generate_counter_name(struct nfacct_rule *counter)
651 char warn_symbol = 'c';
652 char *iftype_name = get_iftype_name(counter->iftype);
653 ret_msg_if(iftype_name == NULL, "Can't get interface name!");
654 STRING_SAVE_COPY(counter->ifname, iftype_name);
656 if (counter->intend == NFACCT_WARN)
658 else if (counter->intend == NFACCT_BLOCK)
660 if (counter->classid != RESOURCED_ALL_APP_CLASSID)
661 snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%d_%s",
662 warn_symbol, counter->iotype, counter->iftype,
663 counter->classid, counter->ifname);
665 snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%s",
666 warn_symbol, counter->iotype, counter->iftype,