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_chain(struct connman_iptables *table,
160 struct connman_iptables_entry *e)
163 struct ipt_entry *entry;
164 struct xt_entry_target *target;
167 builtin = is_hook_entry(table, entry);
171 target = ipt_get_target(entry);
172 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
178 static GList *find_chain_tail(struct connman_iptables *table,
181 GList *chain_head, *list;
182 struct connman_iptables_entry *head, *tail;
183 struct ipt_entry *entry;
184 struct xt_entry_target *target;
187 /* First we look for the head */
188 for (list = table->entries; list; list = list->next) {
193 builtin = is_hook_entry(table, entry);
194 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
197 /* User defined chain */
198 target = ipt_get_target(entry);
199 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
200 !strcmp((char *)target->data, chain_name))
209 /* Then we look for the next chain */
210 for (list = chain_head->next; list; list = list->next) {
214 if (is_chain(table, tail))
218 /* Nothing found, we return the table end */
219 return g_list_last(table->entries);
222 static void update_offsets(struct connman_iptables *table)
225 struct connman_iptables_entry *entry, *prev_entry;
227 for (list = table->entries; list; list = list->next) {
230 if (list == table->entries) {
237 prev_entry = prev->data;
239 entry->offset = prev_entry->offset +
240 prev_entry->entry->next_offset;
244 static int connman_add_entry(struct connman_iptables *table,
245 struct ipt_entry *entry, GList *before)
247 struct connman_iptables_entry *e;
252 e = g_try_malloc0(sizeof(struct connman_iptables_entry));
258 table->entries = g_list_insert_before(table->entries, before, e);
259 table->num_entries++;
260 table->size += entry->next_offset;
262 update_offsets(table);
267 static int connman_iptables_add_chain(struct connman_iptables *table,
271 struct ipt_entry *entry_head;
272 struct ipt_entry *entry_return;
273 struct ipt_error_target *error;
274 struct ipt_standard_target *standard;
275 u_int16_t entry_head_size, entry_return_size;
277 last = g_list_last(table->entries);
280 * An empty chain is composed of:
281 * - A head entry, with no match and an error target.
282 * The error target data is the chain name.
283 * - A tail entry, with no match and a standard target.
284 * The standard target verdict is XT_RETURN (return to the
289 entry_head_size = sizeof(struct ipt_entry) +
290 sizeof(struct ipt_error_target);
291 entry_head = g_try_malloc0(entry_head_size);
292 if (entry_head == NULL)
295 memset(entry_head, 0, entry_head_size);
297 entry_head->target_offset = sizeof(struct ipt_entry);
298 entry_head->next_offset = entry_head_size;
300 error = (struct ipt_error_target *) entry_head->elems;
301 strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
302 error->t.u.user.target_size = ALIGN(sizeof(struct ipt_error_target));
303 strcpy(error->error, name);
305 if (connman_add_entry(table, entry_head, last) < 0)
309 entry_return_size = sizeof(struct ipt_entry) +
310 sizeof(struct ipt_standard_target);
311 entry_return = g_try_malloc0(entry_return_size);
312 if (entry_return == NULL)
315 memset(entry_return, 0, entry_return_size);
317 entry_return->target_offset = sizeof(struct ipt_entry);
318 entry_return->next_offset = entry_return_size;
320 standard = (struct ipt_standard_target *) entry_return->elems;
321 standard->target.u.user.target_size =
322 ALIGN(sizeof(struct ipt_standard_target));
323 standard->verdict = XT_RETURN;
325 if (connman_add_entry(table, entry_return, last) < 0)
332 g_free(entry_return);
337 static struct ipt_entry *
338 new_rule(char *target_name, struct xtables_target *xt_t,
339 char *match_name, struct xtables_match *xt_m)
341 struct ipt_entry *new_entry;
342 size_t match_size, target_size;
343 int is_builtin = is_builtin_target(target_name);
346 match_size = xt_m->m->u.match_size;
351 target_size = ALIGN(xt_t->t->u.target_size);
355 new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
357 if (new_entry == NULL)
360 new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
361 new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
364 struct xt_entry_match *entry_match;
366 entry_match = (struct xt_entry_match *)new_entry->elems;
367 memcpy(entry_match, xt_m->m, match_size);
371 struct xt_entry_target *entry_target;
374 struct xt_standard_target *target;
376 target = (struct xt_standard_target *)(xt_t->t);
377 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
378 target->verdict = target_to_verdict(target_name);
381 entry_target = ipt_get_target(new_entry);
382 memcpy(entry_target, xt_t->t, target_size);
389 connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
390 char *target_name, struct xtables_target *xt_t,
391 char *match_name, struct xtables_match *xt_m)
394 struct ipt_entry *new_entry;
396 chain_tail = find_chain_tail(table, chain_name);
397 if (chain_tail == NULL)
400 new_entry = new_rule(target_name, xt_t,
402 if (new_entry == NULL)
405 return connman_add_entry(table, new_entry, chain_tail->prev);
408 static struct ipt_replace *
409 connman_iptables_blob(struct connman_iptables *table)
411 struct ipt_replace *r;
413 struct connman_iptables_entry *e;
414 unsigned char *entry_index;
416 r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
420 memset(r, 0, sizeof(*r) + table->size);
422 r->counters = g_try_malloc0(sizeof(struct xt_counters)
423 * table->num_entries);
424 if (r->counters == NULL) {
429 strcpy(r->name, table->info->name);
430 r->num_entries = table->num_entries;
431 r->size = table->size;
433 r->num_counters = table->old_entries;
434 r->valid_hooks = table->info->valid_hooks;
436 memcpy(r->hook_entry, table->info->hook_entry,
437 sizeof(table->info->hook_entry));
438 memcpy(r->underflow, table->info->underflow,
439 sizeof(table->info->underflow));
441 entry_index = (unsigned char *)r->entries;
442 for (list = table->entries; list; list = list->next) {
445 memcpy(entry_index, e->entry, e->entry->next_offset);
446 entry_index += e->entry->next_offset;
452 static void dump_target(struct connman_iptables *table,
453 struct ipt_entry *entry)
456 struct xtables_target *xt_t;
457 struct xt_entry_target *target;
459 target = ipt_get_target(entry);
461 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
462 struct xt_standard_target *t;
464 t = (struct xt_standard_target *)target;
466 switch (t->verdict) {
468 printf("\ttarget RETURN\n");
472 printf("\ttarget ACCEPT\n");
476 printf("\ttarget DROP\n");
480 printf("\ttarget QUEUE\n");
484 printf("\ttarget STOP\n");
488 printf("\tJUMP @%p (0x%x)\n",
489 (char*)table->blob_entries->entrytable +
490 t->verdict, t->verdict);
494 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
495 XTF_LOAD_MUST_SUCCEED);
497 if(xt_t->print != NULL)
498 xt_t->print(NULL, target, 1);
500 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
502 printf("\ttarget %s\n", target->u.user.name);
506 if(xt_t->print != NULL) {
508 xt_t->print(NULL, target, 1);
514 static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
516 struct xtables_match *xt_m;
517 struct xt_entry_match *match;
519 if (entry->elems == (unsigned char *)entry + entry->target_offset)
522 match = (struct xt_entry_match *) entry->elems;
524 if (!strlen(match->u.user.name))
527 xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
531 if(xt_m->print != NULL) {
533 xt_m->print(NULL, match, 1);
540 printf("\tmatch %s\n", match->u.user.name);
544 static int connman_iptables_dump_entry(struct ipt_entry *entry,
545 struct connman_iptables *table)
547 struct xt_entry_target *target;
551 offset = (char *)entry - (char *)table->blob_entries->entrytable;
552 target = ipt_get_target(entry);
553 builtin = is_hook_entry(table, entry);
555 if (entry_to_offset(table, entry) + entry->next_offset ==
556 table->blob_entries->size) {
557 printf("End of CHAIN 0x%x\n", offset);
561 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
562 printf("USER CHAIN (%s) %p match %p target %p size %d\n",
563 target->data, entry, entry->elems,
564 (char *)entry + entry->target_offset,
568 } else if (builtin >= 0) {
569 printf("CHAIN (%s) %p match %p target %p size %d\n",
570 hooknames[builtin], entry, entry->elems,
571 (char *)entry + entry->target_offset,
574 printf("RULE %p match %p target %p size %d\n", entry,
576 (char *)entry + entry->target_offset,
580 dump_match(table, entry);
581 dump_target(table, entry);
586 static void connman_iptables_dump(struct connman_iptables *table)
588 printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
590 table->info->valid_hooks, table->info->num_entries,
593 ENTRY_ITERATE(table->blob_entries->entrytable,
594 table->blob_entries->size,
595 connman_iptables_dump_entry, table);
599 static int connman_iptables_get_entries(struct connman_iptables *table)
601 socklen_t entry_size;
603 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
605 return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
606 table->blob_entries, &entry_size);
609 static int connman_iptables_replace(struct connman_iptables *table,
610 struct ipt_replace *r)
612 return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
613 sizeof(*r) + r->size);
616 static void connman_iptables_cleanup(struct connman_iptables *table)
618 close(table->ipt_sock);
620 g_free(table->blob_entries);
623 xtables_free_opts(1);
626 static int connman_iptables_commit(struct connman_iptables *table)
628 struct ipt_replace *repl;
630 repl = connman_iptables_blob(table);
632 return connman_iptables_replace(table, repl);
635 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
637 return connman_add_entry(table, entry, NULL);
640 static struct connman_iptables *connman_iptables_init(const char *table_name)
642 struct connman_iptables *table;
645 table = g_try_new0(struct connman_iptables, 1);
649 table->info = g_try_new0(struct ipt_getinfo, 1);
650 if (table->info == NULL)
653 table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
654 if (table->ipt_sock < 0)
657 s = sizeof(*table->info);
658 strcpy(table->info->name, table_name);
659 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
660 table->info, &s) < 0)
663 table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
665 if (table->blob_entries == NULL)
668 strcpy(table->blob_entries->name, table_name);
669 table->blob_entries->size = table->info->size;
671 if (connman_iptables_get_entries(table) < 0)
674 table->num_entries = 0;
675 table->old_entries = table->info->num_entries;
678 ENTRY_ITERATE(table->blob_entries->entrytable,
679 table->blob_entries->size,
687 connman_iptables_cleanup(table);
693 static struct option connman_iptables_opts[] = {
694 {.name = "append", .has_arg = 1, .val = 'A'},
695 {.name = "list", .has_arg = 2, .val = 'L'},
696 {.name = "new-chain", .has_arg = 1, .val = 'N'},
697 {.name = "in-interface", .has_arg = 1, .val = 'i'},
698 {.name = "jump", .has_arg = 1, .val = 'j'},
699 {.name = "match", .has_arg = 1, .val = 'm'},
700 {.name = "out-interface", .has_arg = 1, .val = 'o'},
701 {.name = "table", .has_arg = 1, .val = 't'},
705 struct xtables_globals connman_iptables_globals = {
707 .opts = connman_iptables_opts,
708 .orig_opts = connman_iptables_opts,
711 int main(int argc, char *argv[])
713 struct connman_iptables *table;
714 struct xtables_match *xt_m;
715 struct xtables_target *xt_t;
716 char *table_name, *chain, *new_chain, *match_name, *target_name;
719 gboolean dump, invert;
721 xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
725 table_name = chain = new_chain = match_name = target_name = NULL;
730 while ((c = getopt_long(argc, argv,
731 "-A:L::N:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
746 target_name = optarg;
747 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
752 size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
754 xt_t->t = g_try_malloc0(size);
757 xt_t->t->u.target_size = size;
758 strcpy(xt_t->t->u.user.name, target_name);
759 xt_t->t->u.user.revision = xt_t->revision;
760 if (xt_t->init != NULL)
762 connman_iptables_globals.opts =
763 xtables_merge_options(connman_iptables_globals.opts,
765 &xt_t->option_offset);
766 if (connman_iptables_globals.opts == NULL)
777 xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
778 size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
779 xt_m->m = g_try_malloc0(size);
782 xt_m->m->u.match_size = size;
783 strcpy(xt_m->m->u.user.name, xt_m->name);
784 xt_m->m->u.user.revision = xt_m->revision;
785 if (xt_m->init != NULL)
787 if (xt_m != xt_m->next) {
788 connman_iptables_globals.opts =
789 xtables_merge_options(connman_iptables_globals.opts,
791 &xt_m->option_offset);
792 if (connman_iptables_globals.opts == NULL)
806 if (optarg[0] == '!' && optarg[1] == '\0') {
808 printf("Consecutive ! not allowed\n");
815 printf("Invalid option\n");
820 if (xt_t == NULL || xt_t->parse == NULL ||
821 !xt_t->parse(c - xt_t->option_offset, argv, invert,
822 &xt_t->tflags, NULL, &xt_t->t)) {
823 if (xt_m == NULL || xt_m->parse == NULL)
826 xt_m->parse(c - xt_m->option_offset, argv,
827 invert, &xt_m->mflags, NULL, &xt_m->m);
834 if (table_name == NULL)
835 table_name = "filter";
837 table = connman_iptables_init(table_name);
842 connman_iptables_dump(table);
847 if (chain && new_chain)
851 printf("New chain %s\n", new_chain);
853 connman_iptables_add_chain(table, new_chain);
859 if (target_name == NULL)
862 printf("Adding %s to %s (match %s)\n", target_name, chain, match_name);
864 connman_iptables_add_rule(table, chain, target_name, xt_t,
872 connman_iptables_commit(table);
875 connman_iptables_cleanup(table);