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)
216 switch (cnt_name[0]) {
218 cnt->intend = NFACCT_COUNTER;
221 cnt->intend = NFACCT_WARN;
224 cnt->intend = NFACCT_BLOCK;
230 io_part = strtok(cnt_name, "_");
232 cnt->iotype = convert_to_iotype(atoi(io_part + 1));
236 iftype_part = strtok(NULL, "_");
237 if (iftype_part != NULL)
238 cnt->iftype = convert_to_iftype(atoi(iftype_part));
242 classid_part = strtok(NULL, "_");
243 if (classid_part != NULL)
244 cnt->classid = atoi(classid_part);
246 cnt->classid = RESOURCED_ALL_APP_CLASSID;
247 return cnt->intend == NFACCT_BLOCK ? true : false;
250 ifname_part = strtok(NULL, "\0");
251 if (ifname_part != NULL)
252 STRING_SAVE_COPY(cnt->ifname, ifname_part);
259 static void _process_answer(struct netlink_serialization_params *params)
262 struct rtattr *attr_list[__NFACCT_MAX] = {0};
263 struct counter_arg *carg = params->carg;
264 struct genl *ans = params->ans;;
265 struct nlmsghdr *nlhdr = &ans->n;
266 int len = GENLMSG_PAYLOAD(nlhdr);
267 int ans_len = carg->ans_len;
272 /* parse reply message */
273 na = (struct rtattr *)GENLMSG_DATA(ans);
275 while (NLMSG_OK(nlhdr, ans_len )) {
277 fill_attribute_list(attr_list, NFACCT_MAX,
279 if (!attr_list[NFACCT_NAME] ||
280 !attr_list[NFACCT_BYTES])
282 params->eval_attr(attr_list, carg);
285 nlhdr = NLMSG_NEXT(nlhdr, ans_len);
288 na = (struct rtattr *)GENLMSG_DATA(nlhdr);
291 if (params->post_eval_attr)
292 params->post_eval_attr(carg);
295 netlink_serialization_command *netlink_create_command(
296 struct netlink_serialization_params *params)
298 static netlink_serialization_command command = {0,};
299 command.deserialize_answer = _process_answer;
300 command.params = *params;
304 static unsigned int get_args_number(const char *cmd_buf)
307 unsigned int count = 0;
309 for (str = (char *)cmd_buf; *str != '\0'; ++str) {
316 static void wait_for_rule_cmd(struct nfacct_rule *rule, pid_t pid)
319 pid_t ret_pid = waitpid(pid, &status, 0);
321 _D("can't wait for a pid %d %d %s", pid, status, strerror(errno));
324 static char* get_cmd_pos(const char *cmd_buf)
326 char *cmd_pos = strstr(cmd_buf, APPEND);
328 cmd_pos = strstr(cmd_buf, INSERT);
333 static bool is_rule_exists(const char *cmd_buf)
337 char *cmd_pos = get_cmd_pos(cmd_buf);
342 buf_len = strlen(cmd_buf) + 1;
343 exec_buf = (char *)malloc(buf_len);
347 strncpy(exec_buf, cmd_buf, buf_len);
348 strncpy(exec_buf + (cmd_pos - cmd_buf), IPTABLES_CHECK,
349 sizeof(IPTABLES_CHECK) - 1);
350 _D("check rule %s", exec_buf);
351 ret = system(exec_buf) == 0;
356 resourced_ret_c exec_iptables_cmd(const char *cmd_buf, pid_t *cmd_pid)
363 const size_t args_number = get_args_number(cmd_buf);
364 char *args[args_number + 2];
367 _D("executing iptables cmd %s in forked process", cmd_buf);
368 ret_value_msg_if(args_number == 0, RESOURCED_ERROR_FAIL, "no arguments");
370 if (is_rule_exists(cmd_buf)) {
371 _D("Rule %s already exists", cmd_buf);
374 args[0] = "iptables";
375 cmd = strtok((char *)cmd_buf, " ");
376 for (i = 1; i <= args_number; ++i) {
377 args[i] = strtok(NULL, " ");
381 ret = execv(cmd, args);
383 _E("Can't execute %s: %s",
384 cmd_buf, strerror(errno));
389 return RESOURCED_ERROR_NONE;
392 static char *choose_iftype_name(struct nfacct_rule *rule)
394 return strlen(rule->ifname) != 0 ? rule->ifname :
395 get_iftype_name(rule->iftype);
398 static resourced_ret_c exec_iface_cmd(const char *pattern, const char *cmd,
399 const char *nfacct, const char *jump,
400 char *iftype_name, pid_t *pid)
402 char block_buf[MAX_PATH_LENGTH];
405 ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
406 "Invalid network interface name argument");
408 ret = sprintf(block_buf, pattern, IPTABLES, cmd,
409 iftype_name, nfacct, jump);
410 ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE,
411 "Not enough buffer");
412 return exec_iptables_cmd(block_buf, pid);
415 static resourced_ret_c exec_app_cmd(const char *pattern, const char *cmd,
416 const char *nfacct, const char *jump,
417 const u_int32_t classid, char *iftype_name,
420 char block_buf[MAX_PATH_LENGTH];
422 ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
423 "Invalid network interface name argument");
424 ret = sprintf(block_buf, pattern, IPTABLES, cmd,
425 iftype_name, classid, nfacct, jump);
426 ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE,
427 "Not enough buffer");
428 return exec_iptables_cmd(block_buf, pid);
431 static char *get_iptables_cmd(const nfacct_rule_action action)
433 if (action == NFACCT_ACTION_APPEND)
435 else if(action == NFACCT_ACTION_DELETE)
437 else if (action == NFACCT_ACTION_INSERT)
443 static char *get_iptables_jump(const nfacct_rule_jump jump)
445 if (jump == NFACCT_JUMP_ACCEPT)
447 else if (jump == NFACCT_JUMP_REJECT)
453 static resourced_ret_c produce_app_rule(struct nfacct_rule *rule,
454 const int send_limit, const int rcv_limit,
455 const nfacct_rule_action action,
456 const nfacct_rule_jump jump,
457 const nfacct_rule_direction iotype)
459 char *set_cmd = get_iptables_cmd(action);
460 char *jump_cmd = get_iptables_jump(jump);
461 char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
462 3*MAX_DEC_SIZE(int) + 4];
463 resourced_ret_c ret = RESOURCED_ERROR_NONE;
467 if (iotype & NFACCT_COUNTER_IN) {
468 rule->quota = rcv_limit;
469 rule->iotype = NFACCT_COUNTER_IN;
470 generate_counter_name(rule);
472 /* to support quated counter we need nfacct,
473 * don't use it in case of just block without a limit
474 * iow, send_limit = 0 and rcv_limit 0 */
475 if (action != NFACCT_ACTION_DELETE) {
476 ret = nfacct_send_new(rule);
477 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
478 "can't set nfacct counter");
481 /* we have a counter, let's key in a rule, drop in case of
482 * send_limit/rcv_limit */
483 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
485 ret = exec_app_cmd(RULE_APP_IN, set_cmd, nfacct_buf,
486 jump_cmd, rule->classid, choose_iftype_name(rule), &pid);
487 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
488 RESOURCED_ERROR_FAIL, "Can't set conditional block for ingress"
489 " traffic, for classid %u, cmd %s, j %s",
490 rule->classid, set_cmd, jump_cmd);
492 /* remove in any case */
493 if (action != NFACCT_ACTION_APPEND) {
494 /* TODO here and everywhere should be not just a del,
495 * here should be get counted value and than
496 * set new counter with that value, but it's minor issue,
497 * due it's not clear when actual counters was stored,
498 * and based on which value settings made such decition */
499 wait_for_rule_cmd(rule, pid);
500 ret = nfacct_send_del(rule);
501 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
502 "can't set nfacct counter");
506 if (iotype & NFACCT_COUNTER_OUT) {
508 rule->iotype = NFACCT_COUNTER_OUT;
509 rule->quota = send_limit;
510 generate_counter_name(rule);
511 if (action != NFACCT_ACTION_DELETE) {
512 ret = nfacct_send_new(rule);
513 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
514 "can't set quota counter");
517 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
518 "can't set counter");
520 ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
522 ret = exec_app_cmd(RULE_APP_OUT, set_cmd, nfacct_buf,
523 jump_cmd, rule->classid, choose_iftype_name(rule), &pid);
524 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
525 RESOURCED_ERROR_FAIL, "Can't set conditional block for engress"
526 " traffic, for classid %u, cmd %s, j %s",
527 rule->classid, set_cmd, jump_cmd);
528 if (action != NFACCT_ACTION_APPEND) {
529 wait_for_rule_cmd(rule, pid);
530 ret = nfacct_send_del(rule);
531 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
532 "can't del nfacct counter");
535 return RESOURCED_ERROR_NONE;
538 static resourced_ret_c produce_iface_rule(struct nfacct_rule *rule,
539 const int send_limit, const int rcv_limit,
540 const nfacct_rule_action action,
541 const nfacct_rule_jump jump,
542 const nfacct_rule_direction iotype)
544 char *set_cmd = get_iptables_cmd(action);
545 char *jump_cmd = get_iptables_jump(jump);
546 char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
547 3*MAX_DEC_SIZE(int) + 4];
551 if (iotype & NFACCT_COUNTER_IN) {
553 rule->quota = rcv_limit;
554 rule->iotype = NFACCT_COUNTER_IN;
555 generate_counter_name(rule);
557 if (action != NFACCT_ACTION_DELETE) {
558 ret = nfacct_send_new(rule);
559 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
560 "can't set quota counter");
563 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
564 NFACCT_NAME_MOD, rule->name);
565 ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
566 "Not enought buffer");
568 ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, nfacct_buf,
569 jump_cmd, choose_iftype_name(rule), &pid);
570 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
571 RESOURCED_ERROR_NONE, "Can't set conditional block for ingress"
572 " traffic, for iftype %d, cmd %s, j %s",
573 rule->iftype, set_cmd, jump_cmd);
575 if (action != NFACCT_ACTION_APPEND) {
576 wait_for_rule_cmd(rule, pid);
577 ret = nfacct_send_del(rule);
578 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
579 "can't set quota counter");
583 if (iotype & NFACCT_COUNTER_OUT) {
585 rule->iotype = NFACCT_COUNTER_OUT;
586 rule->quota = send_limit;
587 generate_counter_name(rule);
589 if (action != NFACCT_ACTION_DELETE) {
590 ret = nfacct_send_new(rule);
591 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
592 "can't set quota counter");
595 ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
596 NFACCT_NAME_MOD, rule->name);
597 ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
598 "Not enough buffer");
600 ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, nfacct_buf,
601 jump_cmd, choose_iftype_name(rule), &pid);
602 ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
603 RESOURCED_ERROR_FAIL, "Can't set conditional block for "
604 " engress traffic, for iftype %d, cmd %s, j %s",
605 rule->iftype, set_cmd, jump_cmd);
607 if (action != NFACCT_ACTION_APPEND) {
608 wait_for_rule_cmd(rule, pid);
609 ret = nfacct_send_del(rule);
610 ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
611 "can't set quota counter");
614 return RESOURCED_ERROR_NONE;
617 resourced_ret_c produce_net_rule(struct nfacct_rule *rule,
618 const int send_limit, const int rcv_limit,
619 const nfacct_rule_action action,
620 const nfacct_rule_jump jump,
621 const nfacct_rule_direction iotype)
623 resourced_ret_c ret = RESOURCED_ERROR_NONE;
625 if (action == NFACCT_ACTION_APPEND && rule->intend == NFACCT_WARN
626 && !send_limit && !rcv_limit)
627 return RESOURCED_ERROR_NONE;
629 if (rule->classid != RESOURCED_ALL_APP_CLASSID)
630 ret = produce_app_rule(rule, send_limit,
631 rcv_limit, action, jump,
634 ret = produce_iface_rule(rule, send_limit, rcv_limit,
635 action, jump, iotype);
640 void generate_counter_name(struct nfacct_rule *counter)
642 char warn_symbol = 'c';
643 char *iftype_name = get_iftype_name(counter->iftype);
644 ret_msg_if(iftype_name == NULL, "Can't get interface name!");
645 STRING_SAVE_COPY(counter->ifname, iftype_name);
647 if (counter->intend == NFACCT_WARN)
649 else if (counter->intend == NFACCT_BLOCK)
651 if (counter->classid != RESOURCED_ALL_APP_CLASSID)
652 snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%d_%s",
653 warn_symbol, counter->iotype, counter->iftype,
654 counter->classid, counter->ifname);
656 snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%s",
657 warn_symbol, counter->iotype, counter->iftype,