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_delete_chain(struct connman_iptables *table,
351 GList *chain_head, *chain_tail, *list, *next;
352 struct connman_iptables_entry *entry;
354 chain_head = find_chain_head(table, name);
355 if (chain_head == NULL)
358 chain_tail = find_chain_tail(table, name);
359 if (chain_head == NULL)
364 while (list != chain_tail) {
366 next = g_list_next(list);
368 table->num_entries--;
369 table->size -= entry->entry->next_offset;
371 table->entries = g_list_remove(table->entries, list->data);
376 update_offsets(table);
381 static int connman_iptables_add_chain(struct connman_iptables *table,
385 struct ipt_entry *entry_head;
386 struct ipt_entry *entry_return;
387 struct ipt_error_target *error;
388 struct ipt_standard_target *standard;
389 u_int16_t entry_head_size, entry_return_size;
391 last = g_list_last(table->entries);
394 * An empty chain is composed of:
395 * - A head entry, with no match and an error target.
396 * The error target data is the chain name.
397 * - A tail entry, with no match and a standard target.
398 * The standard target verdict is XT_RETURN (return to the
403 entry_head_size = sizeof(struct ipt_entry) +
404 sizeof(struct ipt_error_target);
405 entry_head = g_try_malloc0(entry_head_size);
406 if (entry_head == NULL)
409 memset(entry_head, 0, entry_head_size);
411 entry_head->target_offset = sizeof(struct ipt_entry);
412 entry_head->next_offset = entry_head_size;
414 error = (struct ipt_error_target *) entry_head->elems;
415 strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
416 error->t.u.user.target_size = ALIGN(sizeof(struct ipt_error_target));
417 strcpy(error->error, name);
419 if (connman_add_entry(table, entry_head, last) < 0)
423 entry_return_size = sizeof(struct ipt_entry) +
424 sizeof(struct ipt_standard_target);
425 entry_return = g_try_malloc0(entry_return_size);
426 if (entry_return == NULL)
429 memset(entry_return, 0, entry_return_size);
431 entry_return->target_offset = sizeof(struct ipt_entry);
432 entry_return->next_offset = entry_return_size;
434 standard = (struct ipt_standard_target *) entry_return->elems;
435 standard->target.u.user.target_size =
436 ALIGN(sizeof(struct ipt_standard_target));
437 standard->verdict = XT_RETURN;
439 if (connman_add_entry(table, entry_return, last) < 0)
446 g_free(entry_return);
451 static struct ipt_entry *
452 new_rule(struct connman_iptables *table,
453 char *target_name, struct xtables_target *xt_t,
454 char *match_name, struct xtables_match *xt_m)
456 struct ipt_entry *new_entry;
457 size_t match_size, target_size;
458 int is_builtin = is_builtin_target(target_name);
461 match_size = xt_m->m->u.match_size;
466 target_size = ALIGN(xt_t->t->u.target_size);
468 target_size = ALIGN(sizeof(struct xt_standard_target));
470 new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
472 if (new_entry == NULL)
475 new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
476 new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
479 struct xt_entry_match *entry_match;
481 entry_match = (struct xt_entry_match *)new_entry->elems;
482 memcpy(entry_match, xt_m->m, match_size);
486 struct xt_entry_target *entry_target;
489 struct xt_standard_target *target;
491 target = (struct xt_standard_target *)(xt_t->t);
492 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
493 target->verdict = target_to_verdict(target_name);
496 entry_target = ipt_get_target(new_entry);
497 memcpy(entry_target, xt_t->t, target_size);
499 struct connman_iptables_entry *target_rule;
500 struct xt_standard_target *target;
504 * This is a user defined target, i.e. a chain jump.
505 * We search for the chain head, and the target verdict
506 * is the first rule's offset on this chain.
507 * The offset is from the beginning of the table.
510 chain_head = find_chain_head(table, target_name);
511 if (chain_head == NULL || chain_head->next == NULL) {
516 target_rule = chain_head->next->data;
518 target = (struct xt_standard_target *)ipt_get_target(new_entry);
519 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
520 target->target.u.user.target_size = target_size;
521 target->verdict = target_rule->offset;
528 connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
529 char *target_name, struct xtables_target *xt_t,
530 char *match_name, struct xtables_match *xt_m)
533 struct ipt_entry *new_entry;
535 chain_tail = find_chain_tail(table, chain_name);
536 if (chain_tail == NULL)
539 new_entry = new_rule(table,
542 if (new_entry == NULL)
545 return connman_add_entry(table, new_entry, chain_tail->prev);
548 static struct ipt_replace *
549 connman_iptables_blob(struct connman_iptables *table)
551 struct ipt_replace *r;
553 struct connman_iptables_entry *e;
554 unsigned char *entry_index;
556 r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
560 memset(r, 0, sizeof(*r) + table->size);
562 r->counters = g_try_malloc0(sizeof(struct xt_counters)
563 * table->num_entries);
564 if (r->counters == NULL) {
569 strcpy(r->name, table->info->name);
570 r->num_entries = table->num_entries;
571 r->size = table->size;
573 r->num_counters = table->old_entries;
574 r->valid_hooks = table->info->valid_hooks;
576 memcpy(r->hook_entry, table->info->hook_entry,
577 sizeof(table->info->hook_entry));
578 memcpy(r->underflow, table->info->underflow,
579 sizeof(table->info->underflow));
581 entry_index = (unsigned char *)r->entries;
582 for (list = table->entries; list; list = list->next) {
585 memcpy(entry_index, e->entry, e->entry->next_offset);
586 entry_index += e->entry->next_offset;
592 static void dump_target(struct connman_iptables *table,
593 struct ipt_entry *entry)
596 struct xtables_target *xt_t;
597 struct xt_entry_target *target;
599 target = ipt_get_target(entry);
601 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
602 struct xt_standard_target *t;
604 t = (struct xt_standard_target *)target;
606 switch (t->verdict) {
608 printf("\ttarget RETURN\n");
612 printf("\ttarget ACCEPT\n");
616 printf("\ttarget DROP\n");
620 printf("\ttarget QUEUE\n");
624 printf("\ttarget STOP\n");
628 printf("\tJUMP @%p (0x%x)\n",
629 (char*)table->blob_entries->entrytable +
630 t->verdict, t->verdict);
634 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
635 XTF_LOAD_MUST_SUCCEED);
637 if(xt_t->print != NULL)
638 xt_t->print(NULL, target, 1);
640 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
642 printf("\ttarget %s\n", target->u.user.name);
646 if(xt_t->print != NULL) {
648 xt_t->print(NULL, target, 1);
654 static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
656 struct xtables_match *xt_m;
657 struct xt_entry_match *match;
659 if (entry->elems == (unsigned char *)entry + entry->target_offset)
662 match = (struct xt_entry_match *) entry->elems;
664 if (!strlen(match->u.user.name))
667 xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
671 if(xt_m->print != NULL) {
673 xt_m->print(NULL, match, 1);
680 printf("\tmatch %s\n", match->u.user.name);
684 static int connman_iptables_dump_entry(struct ipt_entry *entry,
685 struct connman_iptables *table)
687 struct xt_entry_target *target;
691 offset = (char *)entry - (char *)table->blob_entries->entrytable;
692 target = ipt_get_target(entry);
693 builtin = is_hook_entry(table, entry);
695 if (entry_to_offset(table, entry) + entry->next_offset ==
696 table->blob_entries->size) {
697 printf("End of CHAIN 0x%x\n", offset);
701 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
702 printf("USER CHAIN (%s) %p match %p target %p size %d\n",
703 target->data, entry, entry->elems,
704 (char *)entry + entry->target_offset,
708 } else if (builtin >= 0) {
709 printf("CHAIN (%s) %p match %p target %p size %d\n",
710 hooknames[builtin], entry, entry->elems,
711 (char *)entry + entry->target_offset,
714 printf("RULE %p match %p target %p size %d\n", entry,
716 (char *)entry + entry->target_offset,
720 dump_match(table, entry);
721 dump_target(table, entry);
726 static void connman_iptables_dump(struct connman_iptables *table)
728 printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
730 table->info->valid_hooks, table->info->num_entries,
733 ENTRY_ITERATE(table->blob_entries->entrytable,
734 table->blob_entries->size,
735 connman_iptables_dump_entry, table);
739 static int connman_iptables_get_entries(struct connman_iptables *table)
741 socklen_t entry_size;
743 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
745 return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
746 table->blob_entries, &entry_size);
749 static int connman_iptables_replace(struct connman_iptables *table,
750 struct ipt_replace *r)
752 return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
753 sizeof(*r) + r->size);
756 static void connman_iptables_cleanup(struct connman_iptables *table)
759 struct connman_iptables_entry *entry;
761 close(table->ipt_sock);
763 for (list = table->entries; list; list = list->next) {
766 g_free(entry->entry);
770 g_free(table->blob_entries);
773 xtables_free_opts(1);
776 static int connman_iptables_commit(struct connman_iptables *table)
778 struct ipt_replace *repl;
780 repl = connman_iptables_blob(table);
782 return connman_iptables_replace(table, repl);
785 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
787 struct ipt_entry *new_entry;
789 new_entry = g_try_malloc0(entry->next_offset);
790 if (new_entry == NULL)
793 memcpy(new_entry, entry, entry->next_offset);
795 return connman_add_entry(table, new_entry, NULL);
798 static struct connman_iptables *connman_iptables_init(const char *table_name)
800 struct connman_iptables *table;
803 table = g_try_new0(struct connman_iptables, 1);
807 table->info = g_try_new0(struct ipt_getinfo, 1);
808 if (table->info == NULL)
811 table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
812 if (table->ipt_sock < 0)
815 s = sizeof(*table->info);
816 strcpy(table->info->name, table_name);
817 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
818 table->info, &s) < 0)
821 table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
823 if (table->blob_entries == NULL)
826 strcpy(table->blob_entries->name, table_name);
827 table->blob_entries->size = table->info->size;
829 if (connman_iptables_get_entries(table) < 0)
832 table->num_entries = 0;
833 table->old_entries = table->info->num_entries;
836 ENTRY_ITERATE(table->blob_entries->entrytable,
837 table->blob_entries->size,
845 connman_iptables_cleanup(table);
851 static struct option connman_iptables_opts[] = {
852 {.name = "append", .has_arg = 1, .val = 'A'},
853 {.name = "list", .has_arg = 2, .val = 'L'},
854 {.name = "new-chain", .has_arg = 1, .val = 'N'},
855 {.name = "delete-chain", .has_arg = 1, .val = 'X'},
856 {.name = "in-interface", .has_arg = 1, .val = 'i'},
857 {.name = "jump", .has_arg = 1, .val = 'j'},
858 {.name = "match", .has_arg = 1, .val = 'm'},
859 {.name = "out-interface", .has_arg = 1, .val = 'o'},
860 {.name = "table", .has_arg = 1, .val = 't'},
864 struct xtables_globals connman_iptables_globals = {
866 .opts = connman_iptables_opts,
867 .orig_opts = connman_iptables_opts,
870 int main(int argc, char *argv[])
872 struct connman_iptables *table;
873 struct xtables_match *xt_m;
874 struct xtables_target *xt_t;
875 char *table_name, *chain, *new_chain, *match_name, *target_name;
879 gboolean dump, invert, delete;
881 xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
886 table_name = chain = new_chain = match_name = target_name = NULL;
892 while ((c = getopt_long(argc, argv,
893 "-A:L::N:X:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
909 delete_chain = optarg;
913 target_name = optarg;
914 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
919 size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
921 xt_t->t = g_try_malloc0(size);
924 xt_t->t->u.target_size = size;
925 strcpy(xt_t->t->u.user.name, target_name);
926 xt_t->t->u.user.revision = xt_t->revision;
927 if (xt_t->init != NULL)
929 connman_iptables_globals.opts =
930 xtables_merge_options(connman_iptables_globals.opts,
932 &xt_t->option_offset);
933 if (connman_iptables_globals.opts == NULL)
944 xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
945 size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
946 xt_m->m = g_try_malloc0(size);
949 xt_m->m->u.match_size = size;
950 strcpy(xt_m->m->u.user.name, xt_m->name);
951 xt_m->m->u.user.revision = xt_m->revision;
952 if (xt_m->init != NULL)
954 if (xt_m != xt_m->next) {
955 connman_iptables_globals.opts =
956 xtables_merge_options(connman_iptables_globals.opts,
958 &xt_m->option_offset);
959 if (connman_iptables_globals.opts == NULL)
973 if (optarg[0] == '!' && optarg[1] == '\0') {
975 printf("Consecutive ! not allowed\n");
982 printf("Invalid option\n");
987 if (xt_t == NULL || xt_t->parse == NULL ||
988 !xt_t->parse(c - xt_t->option_offset, argv, invert,
989 &xt_t->tflags, NULL, &xt_t->t)) {
990 if (xt_m == NULL || xt_m->parse == NULL)
993 xt_m->parse(c - xt_m->option_offset, argv,
994 invert, &xt_m->mflags, NULL, &xt_m->m);
1001 if (table_name == NULL)
1002 table_name = "filter";
1004 table = connman_iptables_init(table_name);
1009 if (delete_chain == NULL)
1012 printf("Delete chain %s\n", delete_chain);
1014 connman_iptables_delete_chain(table, delete_chain);
1020 connman_iptables_dump(table);
1025 if (chain && new_chain)
1029 printf("New chain %s\n", new_chain);
1031 connman_iptables_add_chain(table, new_chain);
1037 if (target_name == NULL)
1040 printf("Adding %s to %s (match %s)\n", target_name, chain, match_name);
1042 connman_iptables_add_rule(table, chain, target_name, xt_t,
1050 connman_iptables_commit(table);
1053 connman_iptables_cleanup(table);