4 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include <sys/errno.h>
27 #include <sys/socket.h>
30 #include <linux/netfilter_ipv4/ip_tables.h>
34 static const char *hooknames[] = {
35 [NF_IP_PRE_ROUTING] = "PREROUTING",
36 [NF_IP_LOCAL_IN] = "INPUT",
37 [NF_IP_FORWARD] = "FORWARD",
38 [NF_IP_LOCAL_OUT] = "OUTPUT",
39 [NF_IP_POST_ROUTING] = "POSTROUTING",
42 #define LABEL_ACCEPT "ACCEPT"
43 #define LABEL_DROP "DROP"
44 #define LABEL_QUEUE "QUEUE"
45 #define LABEL_RETURN "RETURN"
47 /* fn returns 0 to continue iteration */
48 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
55 for (__i = 0, __n = 0; __i < (size); \
56 __i += __entry->next_offset, __n++) { \
57 __entry = (void *)(entries) + __i; \
61 __ret = fn(__entry, ## args); \
68 /* fn returns 0 to continue iteration */
69 #define _XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
70 _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
72 #define ENTRY_ITERATE(entries, size, fn, args...) \
73 _XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
75 #define MIN_ALIGN (__alignof__(struct ipt_entry))
77 #define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
79 struct ipt_error_target {
80 struct xt_entry_target t;
81 char error[IPT_TABLE_MAXNAMELEN];
84 struct connman_iptables_entry {
87 struct ipt_entry *entry;
90 struct connman_iptables {
93 struct ipt_getinfo *info;
94 struct ipt_get_entries *blob_entries;
96 unsigned int num_entries;
97 unsigned int old_entries;
104 static struct ipt_entry *get_entry(struct connman_iptables *table,
107 return (struct ipt_entry *)((char *)table->blob_entries->entrytable +
111 static int is_hook_entry(struct connman_iptables *table,
112 struct ipt_entry *entry)
116 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
117 if ((table->info->valid_hooks & (1 << i))
118 && get_entry(table, table->info->hook_entry[i]) == entry)
125 static unsigned long entry_to_offset(struct connman_iptables *table,
126 struct ipt_entry *entry)
128 return (void *)entry - (void *)table->blob_entries->entrytable;
131 static int target_to_verdict(char *target_name)
133 if (!strcmp(target_name, LABEL_ACCEPT))
134 return -NF_ACCEPT - 1;
136 if (!strcmp(target_name, LABEL_DROP))
139 if (!strcmp(target_name, LABEL_QUEUE))
140 return -NF_QUEUE - 1;
142 if (!strcmp(target_name, LABEL_RETURN))
148 static gboolean is_builtin_target(char *target_name)
150 if (!strcmp(target_name, LABEL_ACCEPT) ||
151 !strcmp(target_name, LABEL_DROP) ||
152 !strcmp(target_name, LABEL_QUEUE) ||
153 !strcmp(target_name, LABEL_RETURN))
159 static gboolean is_jump(struct connman_iptables_entry *e)
161 struct xt_entry_target *target;
163 target = ipt_get_target(e->entry);
165 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
166 struct xt_standard_target *t;
168 t = (struct xt_standard_target *)target;
170 switch (t->verdict) {
186 static gboolean is_chain(struct connman_iptables *table,
187 struct connman_iptables_entry *e)
190 struct ipt_entry *entry;
191 struct xt_entry_target *target;
194 builtin = is_hook_entry(table, entry);
198 target = ipt_get_target(entry);
199 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
205 static GList *find_chain_head(struct connman_iptables *table,
209 struct connman_iptables_entry *head;
210 struct ipt_entry *entry;
211 struct xt_entry_target *target;
214 for (list = table->entries; list; list = list->next) {
219 builtin = is_hook_entry(table, entry);
220 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
223 /* User defined chain */
224 target = ipt_get_target(entry);
225 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
226 !strcmp((char *)target->data, chain_name))
233 static GList *find_chain_tail(struct connman_iptables *table,
236 GList *chain_head, *list;
237 struct connman_iptables_entry *head, *tail;
238 struct ipt_entry *entry;
239 struct xt_entry_target *target;
242 /* First we look for the head */
243 for (list = table->entries; list; list = list->next) {
248 builtin = is_hook_entry(table, entry);
249 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
252 /* User defined chain */
253 target = ipt_get_target(entry);
254 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
255 !strcmp((char *)target->data, chain_name))
264 /* Then we look for the next chain */
265 for (list = chain_head->next; list; list = list->next) {
269 if (is_chain(table, tail))
273 /* Nothing found, we return the table end */
274 return g_list_last(table->entries);
277 static void update_offsets(struct connman_iptables *table)
280 struct connman_iptables_entry *entry, *prev_entry;
282 for (list = table->entries; list; list = list->next) {
285 if (list == table->entries) {
292 prev_entry = prev->data;
294 entry->offset = prev_entry->offset +
295 prev_entry->entry->next_offset;
299 static int connman_add_entry(struct connman_iptables *table,
300 struct ipt_entry *entry, GList *before)
303 struct connman_iptables_entry *e, *tmp, *entry_before;
304 struct xt_standard_target *t;
309 e = g_try_malloc0(sizeof(struct connman_iptables_entry));
315 table->entries = g_list_insert_before(table->entries, before, e);
316 table->num_entries++;
317 table->size += entry->next_offset;
319 if (before == NULL) {
320 e->offset = table->size - entry->next_offset;
325 entry_before = before->data;
328 * We've just insterted a new entry. All references before it
329 * should be bumped accordingly.
331 for (list = table->entries; list != before; list = list->next) {
337 t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
339 if (t->verdict >= entry_before->offset)
340 t->verdict += entry->next_offset;
343 update_offsets(table);
348 static int connman_iptables_add_chain(struct connman_iptables *table,
352 struct ipt_entry *entry_head;
353 struct ipt_entry *entry_return;
354 struct ipt_error_target *error;
355 struct ipt_standard_target *standard;
356 u_int16_t entry_head_size, entry_return_size;
358 last = g_list_last(table->entries);
361 * An empty chain is composed of:
362 * - A head entry, with no match and an error target.
363 * The error target data is the chain name.
364 * - A tail entry, with no match and a standard target.
365 * The standard target verdict is XT_RETURN (return to the
370 entry_head_size = sizeof(struct ipt_entry) +
371 sizeof(struct ipt_error_target);
372 entry_head = g_try_malloc0(entry_head_size);
373 if (entry_head == NULL)
376 memset(entry_head, 0, entry_head_size);
378 entry_head->target_offset = sizeof(struct ipt_entry);
379 entry_head->next_offset = entry_head_size;
381 error = (struct ipt_error_target *) entry_head->elems;
382 strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
383 error->t.u.user.target_size = ALIGN(sizeof(struct ipt_error_target));
384 strcpy(error->error, name);
386 if (connman_add_entry(table, entry_head, last) < 0)
390 entry_return_size = sizeof(struct ipt_entry) +
391 sizeof(struct ipt_standard_target);
392 entry_return = g_try_malloc0(entry_return_size);
393 if (entry_return == NULL)
396 memset(entry_return, 0, entry_return_size);
398 entry_return->target_offset = sizeof(struct ipt_entry);
399 entry_return->next_offset = entry_return_size;
401 standard = (struct ipt_standard_target *) entry_return->elems;
402 standard->target.u.user.target_size =
403 ALIGN(sizeof(struct ipt_standard_target));
404 standard->verdict = XT_RETURN;
406 if (connman_add_entry(table, entry_return, last) < 0)
413 g_free(entry_return);
418 static struct ipt_entry *
419 new_rule(struct connman_iptables *table,
420 char *target_name, struct xtables_target *xt_t,
421 char *match_name, struct xtables_match *xt_m)
423 struct ipt_entry *new_entry;
424 size_t match_size, target_size;
425 int is_builtin = is_builtin_target(target_name);
428 match_size = xt_m->m->u.match_size;
433 target_size = ALIGN(xt_t->t->u.target_size);
435 target_size = ALIGN(sizeof(struct xt_standard_target));
437 new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
439 if (new_entry == NULL)
442 new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
443 new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
446 struct xt_entry_match *entry_match;
448 entry_match = (struct xt_entry_match *)new_entry->elems;
449 memcpy(entry_match, xt_m->m, match_size);
453 struct xt_entry_target *entry_target;
456 struct xt_standard_target *target;
458 target = (struct xt_standard_target *)(xt_t->t);
459 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
460 target->verdict = target_to_verdict(target_name);
463 entry_target = ipt_get_target(new_entry);
464 memcpy(entry_target, xt_t->t, target_size);
466 struct connman_iptables_entry *target_rule;
467 struct xt_standard_target *target;
471 * This is a user defined target, i.e. a chain jump.
472 * We search for the chain head, and the target verdict
473 * is the first rule's offset on this chain.
474 * The offset is from the beginning of the table.
477 chain_head = find_chain_head(table, target_name);
478 if (chain_head == NULL || chain_head->next == NULL) {
483 target_rule = chain_head->next->data;
485 target = (struct xt_standard_target *)ipt_get_target(new_entry);
486 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
487 target->target.u.user.target_size = target_size;
488 target->verdict = target_rule->offset;
495 connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
496 char *target_name, struct xtables_target *xt_t,
497 char *match_name, struct xtables_match *xt_m)
500 struct ipt_entry *new_entry;
502 chain_tail = find_chain_tail(table, chain_name);
503 if (chain_tail == NULL)
506 new_entry = new_rule(table,
509 if (new_entry == NULL)
512 return connman_add_entry(table, new_entry, chain_tail->prev);
515 static struct ipt_replace *
516 connman_iptables_blob(struct connman_iptables *table)
518 struct ipt_replace *r;
520 struct connman_iptables_entry *e;
521 unsigned char *entry_index;
523 r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
527 memset(r, 0, sizeof(*r) + table->size);
529 r->counters = g_try_malloc0(sizeof(struct xt_counters)
530 * table->num_entries);
531 if (r->counters == NULL) {
536 strcpy(r->name, table->info->name);
537 r->num_entries = table->num_entries;
538 r->size = table->size;
540 r->num_counters = table->old_entries;
541 r->valid_hooks = table->info->valid_hooks;
543 memcpy(r->hook_entry, table->info->hook_entry,
544 sizeof(table->info->hook_entry));
545 memcpy(r->underflow, table->info->underflow,
546 sizeof(table->info->underflow));
548 entry_index = (unsigned char *)r->entries;
549 for (list = table->entries; list; list = list->next) {
552 memcpy(entry_index, e->entry, e->entry->next_offset);
553 entry_index += e->entry->next_offset;
559 static void dump_target(struct connman_iptables *table,
560 struct ipt_entry *entry)
563 struct xtables_target *xt_t;
564 struct xt_entry_target *target;
566 target = ipt_get_target(entry);
568 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
569 struct xt_standard_target *t;
571 t = (struct xt_standard_target *)target;
573 switch (t->verdict) {
575 printf("\ttarget RETURN\n");
579 printf("\ttarget ACCEPT\n");
583 printf("\ttarget DROP\n");
587 printf("\ttarget QUEUE\n");
591 printf("\ttarget STOP\n");
595 printf("\tJUMP @%p (0x%x)\n",
596 (char*)table->blob_entries->entrytable +
597 t->verdict, t->verdict);
601 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
602 XTF_LOAD_MUST_SUCCEED);
604 if(xt_t->print != NULL)
605 xt_t->print(NULL, target, 1);
607 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
609 printf("\ttarget %s\n", target->u.user.name);
613 if(xt_t->print != NULL) {
615 xt_t->print(NULL, target, 1);
621 static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
623 struct xtables_match *xt_m;
624 struct xt_entry_match *match;
626 if (entry->elems == (unsigned char *)entry + entry->target_offset)
629 match = (struct xt_entry_match *) entry->elems;
631 if (!strlen(match->u.user.name))
634 xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
638 if(xt_m->print != NULL) {
640 xt_m->print(NULL, match, 1);
647 printf("\tmatch %s\n", match->u.user.name);
651 static int connman_iptables_dump_entry(struct ipt_entry *entry,
652 struct connman_iptables *table)
654 struct xt_entry_target *target;
658 offset = (char *)entry - (char *)table->blob_entries->entrytable;
659 target = ipt_get_target(entry);
660 builtin = is_hook_entry(table, entry);
662 if (entry_to_offset(table, entry) + entry->next_offset ==
663 table->blob_entries->size) {
664 printf("End of CHAIN 0x%x\n", offset);
668 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
669 printf("USER CHAIN (%s) %p match %p target %p size %d\n",
670 target->data, entry, entry->elems,
671 (char *)entry + entry->target_offset,
675 } else if (builtin >= 0) {
676 printf("CHAIN (%s) %p match %p target %p size %d\n",
677 hooknames[builtin], entry, entry->elems,
678 (char *)entry + entry->target_offset,
681 printf("RULE %p match %p target %p size %d\n", entry,
683 (char *)entry + entry->target_offset,
687 dump_match(table, entry);
688 dump_target(table, entry);
693 static void connman_iptables_dump(struct connman_iptables *table)
695 printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
697 table->info->valid_hooks, table->info->num_entries,
700 ENTRY_ITERATE(table->blob_entries->entrytable,
701 table->blob_entries->size,
702 connman_iptables_dump_entry, table);
706 static int connman_iptables_get_entries(struct connman_iptables *table)
708 socklen_t entry_size;
710 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
712 return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
713 table->blob_entries, &entry_size);
716 static int connman_iptables_replace(struct connman_iptables *table,
717 struct ipt_replace *r)
719 return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
720 sizeof(*r) + r->size);
723 static void connman_iptables_cleanup(struct connman_iptables *table)
725 close(table->ipt_sock);
727 g_free(table->blob_entries);
730 xtables_free_opts(1);
733 static int connman_iptables_commit(struct connman_iptables *table)
735 struct ipt_replace *repl;
737 repl = connman_iptables_blob(table);
739 return connman_iptables_replace(table, repl);
742 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
744 return connman_add_entry(table, entry, NULL);
747 static struct connman_iptables *connman_iptables_init(const char *table_name)
749 struct connman_iptables *table;
752 table = g_try_new0(struct connman_iptables, 1);
756 table->info = g_try_new0(struct ipt_getinfo, 1);
757 if (table->info == NULL)
760 table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
761 if (table->ipt_sock < 0)
764 s = sizeof(*table->info);
765 strcpy(table->info->name, table_name);
766 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
767 table->info, &s) < 0)
770 table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
772 if (table->blob_entries == NULL)
775 strcpy(table->blob_entries->name, table_name);
776 table->blob_entries->size = table->info->size;
778 if (connman_iptables_get_entries(table) < 0)
781 table->num_entries = 0;
782 table->old_entries = table->info->num_entries;
785 ENTRY_ITERATE(table->blob_entries->entrytable,
786 table->blob_entries->size,
794 connman_iptables_cleanup(table);
800 static struct option connman_iptables_opts[] = {
801 {.name = "append", .has_arg = 1, .val = 'A'},
802 {.name = "list", .has_arg = 2, .val = 'L'},
803 {.name = "new-chain", .has_arg = 1, .val = 'N'},
804 {.name = "in-interface", .has_arg = 1, .val = 'i'},
805 {.name = "jump", .has_arg = 1, .val = 'j'},
806 {.name = "match", .has_arg = 1, .val = 'm'},
807 {.name = "out-interface", .has_arg = 1, .val = 'o'},
808 {.name = "table", .has_arg = 1, .val = 't'},
812 struct xtables_globals connman_iptables_globals = {
814 .opts = connman_iptables_opts,
815 .orig_opts = connman_iptables_opts,
818 int main(int argc, char *argv[])
820 struct connman_iptables *table;
821 struct xtables_match *xt_m;
822 struct xtables_target *xt_t;
823 char *table_name, *chain, *new_chain, *match_name, *target_name;
826 gboolean dump, invert;
828 xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
832 table_name = chain = new_chain = match_name = target_name = NULL;
837 while ((c = getopt_long(argc, argv,
838 "-A:L::N:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
853 target_name = optarg;
854 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
859 size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
861 xt_t->t = g_try_malloc0(size);
864 xt_t->t->u.target_size = size;
865 strcpy(xt_t->t->u.user.name, target_name);
866 xt_t->t->u.user.revision = xt_t->revision;
867 if (xt_t->init != NULL)
869 connman_iptables_globals.opts =
870 xtables_merge_options(connman_iptables_globals.opts,
872 &xt_t->option_offset);
873 if (connman_iptables_globals.opts == NULL)
884 xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
885 size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
886 xt_m->m = g_try_malloc0(size);
889 xt_m->m->u.match_size = size;
890 strcpy(xt_m->m->u.user.name, xt_m->name);
891 xt_m->m->u.user.revision = xt_m->revision;
892 if (xt_m->init != NULL)
894 if (xt_m != xt_m->next) {
895 connman_iptables_globals.opts =
896 xtables_merge_options(connman_iptables_globals.opts,
898 &xt_m->option_offset);
899 if (connman_iptables_globals.opts == NULL)
913 if (optarg[0] == '!' && optarg[1] == '\0') {
915 printf("Consecutive ! not allowed\n");
922 printf("Invalid option\n");
927 if (xt_t == NULL || xt_t->parse == NULL ||
928 !xt_t->parse(c - xt_t->option_offset, argv, invert,
929 &xt_t->tflags, NULL, &xt_t->t)) {
930 if (xt_m == NULL || xt_m->parse == NULL)
933 xt_m->parse(c - xt_m->option_offset, argv,
934 invert, &xt_m->mflags, NULL, &xt_m->m);
941 if (table_name == NULL)
942 table_name = "filter";
944 table = connman_iptables_init(table_name);
949 connman_iptables_dump(table);
954 if (chain && new_chain)
958 printf("New chain %s\n", new_chain);
960 connman_iptables_add_chain(table, new_chain);
966 if (target_name == NULL)
969 printf("Adding %s to %s (match %s)\n", target_name, chain, match_name);
971 connman_iptables_add_rule(table, chain, target_name, xt_t,
979 connman_iptables_commit(table);
982 connman_iptables_cleanup(table);