4 * Copyright (C) 2007-2012 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>
28 #include <sys/types.h>
29 #include <arpa/inet.h>
32 #include <linux/netfilter_ipv4/ip_tables.h>
36 static const char *hooknames[] = {
37 [NF_IP_PRE_ROUTING] = "PREROUTING",
38 [NF_IP_LOCAL_IN] = "INPUT",
39 [NF_IP_FORWARD] = "FORWARD",
40 [NF_IP_LOCAL_OUT] = "OUTPUT",
41 [NF_IP_POST_ROUTING] = "POSTROUTING",
44 #define LABEL_ACCEPT "ACCEPT"
45 #define LABEL_DROP "DROP"
46 #define LABEL_QUEUE "QUEUE"
47 #define LABEL_RETURN "RETURN"
49 #define XT_OPTION_OFFSET_SCALE 256
51 /* fn returns 0 to continue iteration */
52 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
59 for (__i = 0, __n = 0; __i < (size); \
60 __i += __entry->next_offset, __n++) { \
61 __entry = (void *)(entries) + __i; \
65 __ret = fn(__entry, ## args); \
72 /* fn returns 0 to continue iteration */
73 #define _XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
74 _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
76 #define ENTRY_ITERATE(entries, size, fn, args...) \
77 _XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
79 #define MIN_ALIGN (__alignof__(struct ipt_entry))
81 #define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
84 struct xt_entry_target t;
85 char error[IPT_TABLE_MAXNAMELEN];
88 struct connman_iptables_entry {
92 struct ipt_entry *entry;
95 struct connman_iptables {
98 struct ipt_getinfo *info;
99 struct ipt_get_entries *blob_entries;
101 unsigned int num_entries;
102 unsigned int old_entries;
105 unsigned int underflow[NF_INET_NUMHOOKS];
106 unsigned int hook_entry[NF_INET_NUMHOOKS];
112 static struct ipt_entry *get_entry(struct connman_iptables *table,
115 return (struct ipt_entry *)((char *)table->blob_entries->entrytable +
119 static int is_hook_entry(struct connman_iptables *table,
120 struct ipt_entry *entry)
124 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
125 if ((table->info->valid_hooks & (1 << i))
126 && get_entry(table, table->info->hook_entry[i]) == entry)
133 static unsigned long entry_to_offset(struct connman_iptables *table,
134 struct ipt_entry *entry)
136 return (void *)entry - (void *)table->blob_entries->entrytable;
139 static int target_to_verdict(char *target_name)
141 if (!strcmp(target_name, LABEL_ACCEPT))
142 return -NF_ACCEPT - 1;
144 if (!strcmp(target_name, LABEL_DROP))
147 if (!strcmp(target_name, LABEL_QUEUE))
148 return -NF_QUEUE - 1;
150 if (!strcmp(target_name, LABEL_RETURN))
156 static gboolean is_builtin_target(char *target_name)
158 if (!strcmp(target_name, LABEL_ACCEPT) ||
159 !strcmp(target_name, LABEL_DROP) ||
160 !strcmp(target_name, LABEL_QUEUE) ||
161 !strcmp(target_name, LABEL_RETURN))
167 static gboolean is_jump(struct connman_iptables_entry *e)
169 struct xt_entry_target *target;
171 target = ipt_get_target(e->entry);
173 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
174 struct xt_standard_target *t;
176 t = (struct xt_standard_target *)target;
178 switch (t->verdict) {
194 static gboolean is_chain(struct connman_iptables *table,
195 struct connman_iptables_entry *e)
197 struct ipt_entry *entry;
198 struct xt_entry_target *target;
204 target = ipt_get_target(entry);
205 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
211 static GList *find_chain_head(struct connman_iptables *table,
215 struct connman_iptables_entry *head;
216 struct ipt_entry *entry;
217 struct xt_entry_target *target;
220 for (list = table->entries; list; list = list->next) {
225 builtin = head->builtin;
226 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
229 /* User defined chain */
230 target = ipt_get_target(entry);
231 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
232 !strcmp((char *)target->data, chain_name))
239 static GList *find_chain_tail(struct connman_iptables *table,
242 GList *chain_head, *list;
243 struct connman_iptables_entry *tail;
245 chain_head = find_chain_head(table, chain_name);
246 if (chain_head == NULL)
249 /* Then we look for the next chain */
250 for (list = chain_head->next; list; list = list->next) {
253 if (is_chain(table, tail))
257 /* Nothing found, we return the table end */
258 return g_list_last(table->entries);
261 static void update_offsets(struct connman_iptables *table)
264 struct connman_iptables_entry *entry, *prev_entry;
266 for (list = table->entries; list; list = list->next) {
269 if (list == table->entries) {
276 prev_entry = prev->data;
278 entry->offset = prev_entry->offset +
279 prev_entry->entry->next_offset;
283 static void update_targets_reference(struct connman_iptables *table,
284 struct connman_iptables_entry *entry_before,
285 struct connman_iptables_entry *modified_entry,
286 gboolean is_removing)
288 struct connman_iptables_entry *tmp;
289 struct xt_standard_target *t;
293 offset = modified_entry->entry->next_offset;
295 for (list = table->entries; list; list = list->next) {
301 t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
303 if (is_removing == TRUE) {
304 if (t->verdict >= entry_before->offset)
305 t->verdict -= offset;
307 if (t->verdict > entry_before->offset)
308 t->verdict += offset;
313 static int connman_add_entry(struct connman_iptables *table,
314 struct ipt_entry *entry, GList *before,
317 struct connman_iptables_entry *e, *entry_before;
322 e = g_try_malloc0(sizeof(struct connman_iptables_entry));
327 e->builtin = builtin;
329 table->entries = g_list_insert_before(table->entries, before, e);
330 table->num_entries++;
331 table->size += entry->next_offset;
333 if (before == NULL) {
334 e->offset = table->size - entry->next_offset;
339 entry_before = before->data;
342 * We've just appended/insterted a new entry. All references
343 * should be bumped accordingly.
345 update_targets_reference(table, entry_before, e, FALSE);
347 update_offsets(table);
352 static int remove_table_entry(struct connman_iptables *table,
353 struct connman_iptables_entry *entry)
357 table->num_entries--;
358 table->size -= entry->entry->next_offset;
359 removed = entry->entry->next_offset;
361 g_free(entry->entry);
363 table->entries = g_list_remove(table->entries, entry);
368 static int connman_iptables_flush_chain(struct connman_iptables *table,
371 GList *chain_head, *chain_tail, *list, *next;
372 struct connman_iptables_entry *entry;
373 int builtin, removed = 0;
375 chain_head = find_chain_head(table, name);
376 if (chain_head == NULL)
379 chain_tail = find_chain_tail(table, name);
380 if (chain_tail == NULL)
383 entry = chain_head->data;
384 builtin = entry->builtin;
389 list = chain_head->next;
391 if (list == chain_tail->prev)
394 while (list != chain_tail->prev) {
396 next = g_list_next(list);
398 removed += remove_table_entry(table, entry);
404 struct connman_iptables_entry *e;
408 entry->builtin = builtin;
410 table->underflow[builtin] -= removed;
412 for (list = chain_tail; list; list = list->next) {
415 builtin = e->builtin;
419 table->hook_entry[builtin] -= removed;
420 table->underflow[builtin] -= removed;
424 update_offsets(table);
429 static int connman_iptables_delete_chain(struct connman_iptables *table,
432 GList *chain_head, *chain_tail;
433 struct connman_iptables_entry *entry;
435 chain_head = find_chain_head(table, name);
436 if (chain_head == NULL)
439 entry = chain_head->data;
441 /* We cannot remove builtin chain */
442 if (entry->builtin >= 0)
445 chain_tail = find_chain_tail(table, name);
446 if (chain_tail == NULL)
449 /* Chain must be flushed */
450 if (chain_head->next != chain_tail->prev)
453 remove_table_entry(table, entry);
455 entry = chain_tail->prev->data;
456 remove_table_entry(table, entry);
458 update_offsets(table);
463 static int connman_iptables_add_chain(struct connman_iptables *table,
467 struct ipt_entry *entry_head;
468 struct ipt_entry *entry_return;
469 struct error_target *error;
470 struct ipt_standard_target *standard;
471 u_int16_t entry_head_size, entry_return_size;
473 last = g_list_last(table->entries);
476 * An empty chain is composed of:
477 * - A head entry, with no match and an error target.
478 * The error target data is the chain name.
479 * - A tail entry, with no match and a standard target.
480 * The standard target verdict is XT_RETURN (return to the
485 entry_head_size = sizeof(struct ipt_entry) +
486 sizeof(struct error_target);
487 entry_head = g_try_malloc0(entry_head_size);
488 if (entry_head == NULL)
491 memset(entry_head, 0, entry_head_size);
493 entry_head->target_offset = sizeof(struct ipt_entry);
494 entry_head->next_offset = entry_head_size;
496 error = (struct error_target *) entry_head->elems;
497 strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
498 error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
499 strcpy(error->error, name);
501 if (connman_add_entry(table, entry_head, last, -1) < 0)
505 entry_return_size = sizeof(struct ipt_entry) +
506 sizeof(struct ipt_standard_target);
507 entry_return = g_try_malloc0(entry_return_size);
508 if (entry_return == NULL)
511 memset(entry_return, 0, entry_return_size);
513 entry_return->target_offset = sizeof(struct ipt_entry);
514 entry_return->next_offset = entry_return_size;
516 standard = (struct ipt_standard_target *) entry_return->elems;
517 standard->target.u.user.target_size =
518 ALIGN(sizeof(struct ipt_standard_target));
519 standard->verdict = XT_RETURN;
521 if (connman_add_entry(table, entry_return, last, -1) < 0)
527 g_free(entry_return);
534 static struct ipt_entry *new_rule(struct ipt_ip *ip,
535 char *target_name, struct xtables_target *xt_t,
536 struct xtables_rule_match *xt_rm)
538 struct xtables_rule_match *tmp_xt_rm;
539 struct ipt_entry *new_entry;
540 size_t match_size, target_size;
543 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
544 match_size += tmp_xt_rm->match->m->u.match_size;
547 target_size = ALIGN(xt_t->t->u.target_size);
549 target_size = ALIGN(sizeof(struct xt_standard_target));
551 new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
553 if (new_entry == NULL)
556 memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
558 new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
559 new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
563 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
564 tmp_xt_rm = tmp_xt_rm->next) {
565 memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
566 tmp_xt_rm->match->m->u.match_size);
567 match_size += tmp_xt_rm->match->m->u.match_size;
571 struct xt_entry_target *entry_target;
573 entry_target = ipt_get_target(new_entry);
574 memcpy(entry_target, xt_t->t, target_size);
580 static void update_hooks(struct connman_iptables *table, GList *chain_head,
581 struct ipt_entry *entry)
584 struct connman_iptables_entry *head, *e;
587 if (chain_head == NULL)
590 head = chain_head->data;
592 builtin = head->builtin;
596 table->underflow[builtin] += entry->next_offset;
598 for (list = chain_head->next; list; list = list->next) {
601 builtin = e->builtin;
605 table->hook_entry[builtin] += entry->next_offset;
606 table->underflow[builtin] += entry->next_offset;
610 static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
611 struct ipt_ip *ip, char *chain_name,
612 char *target_name, struct xtables_target *xt_t,
613 int *builtin, struct xtables_rule_match *xt_rm)
615 GList *chain_tail, *chain_head;
616 struct ipt_entry *new_entry;
617 struct connman_iptables_entry *head;
619 chain_head = find_chain_head(table, chain_name);
620 if (chain_head == NULL)
623 chain_tail = find_chain_tail(table, chain_name);
624 if (chain_tail == NULL)
627 new_entry = new_rule(ip, target_name, xt_t, xt_rm);
628 if (new_entry == NULL)
631 update_hooks(table, chain_head, new_entry);
634 * If the chain is builtin, and does not have any rule,
635 * then the one that we're inserting is becoming the head
636 * and thus needs the builtin flag.
638 head = chain_head->data;
639 if (head->builtin < 0)
641 else if (chain_head == chain_tail->prev) {
642 *builtin = head->builtin;
649 static int connman_iptables_append_rule(struct connman_iptables *table,
650 struct ipt_ip *ip, char *chain_name,
651 char *target_name, struct xtables_target *xt_t,
652 struct xtables_rule_match *xt_rm)
655 struct ipt_entry *new_entry;
656 int builtin = -1, ret;
658 chain_tail = find_chain_tail(table, chain_name);
659 if (chain_tail == NULL)
662 new_entry = prepare_rule_inclusion(table, ip, chain_name,
663 target_name, xt_t, &builtin, xt_rm);
664 if (new_entry == NULL)
667 ret = connman_add_entry(table, new_entry, chain_tail->prev, builtin);
674 static int connman_iptables_insert_rule(struct connman_iptables *table,
675 struct ipt_ip *ip, char *chain_name,
676 char *target_name, struct xtables_target *xt_t,
677 struct xtables_rule_match *xt_rm)
680 struct ipt_entry *new_entry;
681 int builtin = -1, ret;
683 chain_head = find_chain_head(table, chain_name);
684 if (chain_head == NULL)
687 new_entry = prepare_rule_inclusion(table, ip, chain_name,
688 target_name, xt_t, &builtin, xt_rm);
689 if (new_entry == NULL)
693 chain_head = chain_head->next;
695 ret = connman_add_entry(table, new_entry, chain_head, builtin);
702 static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
703 struct ipt_entry *i_e2)
705 if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
708 if (i_e1->target_offset != i_e2->target_offset)
711 if (i_e1->next_offset != i_e2->next_offset)
717 static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
718 struct xt_entry_target *xt_e_t2)
720 if (xt_e_t1 == NULL || xt_e_t2 == NULL)
723 if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
724 struct xt_standard_target *xt_s_t1;
725 struct xt_standard_target *xt_s_t2;
727 xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
728 xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
730 if (xt_s_t1->verdict != xt_s_t2->verdict)
733 if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
736 if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
743 static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
744 struct xt_entry_match *xt_e_m2)
746 if (xt_e_m1 == NULL || xt_e_m2 == NULL)
749 if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
752 if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
755 if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
761 static GList *find_existing_rule(struct connman_iptables *table,
762 struct ipt_ip *ip, char *chain_name,
763 char *target_name, struct xtables_target *xt_t,
764 struct xtables_match *xt_m,
765 struct xtables_rule_match *xt_rm)
767 GList *chain_tail, *chain_head, *list;
768 struct xt_entry_target *xt_e_t = NULL;
769 struct xt_entry_match *xt_e_m = NULL;
770 struct connman_iptables_entry *entry;
771 struct ipt_entry *entry_test;
774 chain_head = find_chain_head(table, chain_name);
775 if (chain_head == NULL)
778 chain_tail = find_chain_tail(table, chain_name);
779 if (chain_tail == NULL)
785 entry_test = new_rule(ip, target_name, xt_t, xt_rm);
786 if (entry_test == NULL)
790 xt_e_t = ipt_get_target(entry_test);
792 xt_e_m = (struct xt_entry_match *)entry_test->elems;
794 entry = chain_head->data;
795 builtin = entry->builtin;
800 list = chain_head->next;
802 for (; list != chain_tail->prev; list = list->next) {
803 struct connman_iptables_entry *tmp;
804 struct ipt_entry *tmp_e;
809 if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
813 struct xt_entry_target *tmp_xt_e_t;
815 tmp_xt_e_t = ipt_get_target(tmp_e);
817 if (!is_same_target(tmp_xt_e_t, xt_e_t))
822 struct xt_entry_match *tmp_xt_e_m;
824 tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
826 if (!is_same_match(tmp_xt_e_m, xt_e_m))
835 if (list != chain_tail->prev)
841 static int connman_iptables_delete_rule(struct connman_iptables *table,
842 struct ipt_ip *ip, char *chain_name,
843 char *target_name, struct xtables_target *xt_t,
844 struct xtables_match *xt_m,
845 struct xtables_rule_match *xt_rm)
847 struct connman_iptables_entry *entry;
848 GList *chain_tail, *list;
849 int builtin, removed;
853 chain_tail = find_chain_tail(table, chain_name);
854 if (chain_tail == NULL)
857 list = find_existing_rule(table, ip, chain_name, target_name,
867 builtin = entry->builtin;
869 /* We have deleted a rule,
870 * all references should be bumped accordingly */
871 if (list->next != NULL)
872 update_targets_reference(table, list->next->data,
875 removed += remove_table_entry(table, entry);
881 entry->builtin = builtin;
884 table->underflow[builtin] -= removed;
885 for (list = chain_tail; list; list = list->next) {
888 builtin = entry->builtin;
892 table->hook_entry[builtin] -= removed;
893 table->underflow[builtin] -= removed;
897 update_offsets(table);
902 static int connman_iptables_compare_rule(struct connman_iptables *table,
903 struct ipt_ip *ip, char *chain_name,
904 char *target_name, struct xtables_target *xt_t,
905 struct xtables_match *xt_m,
906 struct xtables_rule_match *xt_rm)
908 struct connman_iptables_entry *entry;
911 found = find_existing_rule(table, ip, chain_name, target_name,
924 static int connman_iptables_change_policy(struct connman_iptables *table,
925 char *chain_name, char *policy)
928 struct connman_iptables_entry *entry;
929 struct xt_entry_target *target;
930 struct xt_standard_target *t;
933 verdict = target_to_verdict(policy);
937 chain_head = find_chain_head(table, chain_name);
938 if (chain_head == NULL)
941 entry = chain_head->data;
942 if (entry->builtin < 0)
945 target = ipt_get_target(entry->entry);
947 t = (struct xt_standard_target *)target;
948 t->verdict = verdict;
953 static struct ipt_replace *connman_iptables_blob(struct connman_iptables *table)
955 struct ipt_replace *r;
957 struct connman_iptables_entry *e;
958 unsigned char *entry_index;
960 r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
964 memset(r, 0, sizeof(*r) + table->size);
966 r->counters = g_try_malloc0(sizeof(struct xt_counters)
967 * table->old_entries);
968 if (r->counters == NULL) {
973 strcpy(r->name, table->info->name);
974 r->num_entries = table->num_entries;
975 r->size = table->size;
977 r->num_counters = table->old_entries;
978 r->valid_hooks = table->info->valid_hooks;
980 memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
981 memcpy(r->underflow, table->underflow, sizeof(table->underflow));
983 entry_index = (unsigned char *)r->entries;
984 for (list = table->entries; list; list = list->next) {
987 memcpy(entry_index, e->entry, e->entry->next_offset);
988 entry_index += e->entry->next_offset;
994 static void dump_target(struct connman_iptables *table,
995 struct ipt_entry *entry)
998 struct xtables_target *xt_t;
999 struct xt_entry_target *target;
1001 target = ipt_get_target(entry);
1003 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
1004 struct xt_standard_target *t;
1006 t = (struct xt_standard_target *)target;
1008 switch (t->verdict) {
1010 printf("\ttarget RETURN\n");
1013 case -NF_ACCEPT - 1:
1014 printf("\ttarget ACCEPT\n");
1018 printf("\ttarget DROP\n");
1022 printf("\ttarget QUEUE\n");
1026 printf("\ttarget STOP\n");
1030 printf("\tJUMP @%p (0x%x)\n",
1031 (char*)table->blob_entries->entrytable +
1032 t->verdict, t->verdict);
1036 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
1037 XTF_LOAD_MUST_SUCCEED);
1039 if(xt_t->print != NULL)
1040 xt_t->print(NULL, target, 1);
1042 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
1044 printf("\ttarget %s\n", target->u.user.name);
1048 if(xt_t->print != NULL) {
1049 printf("\ttarget ");
1050 xt_t->print(NULL, target, 1);
1056 static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
1058 struct xtables_match *xt_m;
1059 struct xt_entry_match *match;
1061 if (entry->elems == (unsigned char *)entry + entry->target_offset)
1064 match = (struct xt_entry_match *) entry->elems;
1066 if (!strlen(match->u.user.name))
1069 xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
1073 if(xt_m->print != NULL) {
1075 xt_m->print(NULL, match, 1);
1082 printf("\tmatch %s\n", match->u.user.name);
1086 static int connman_iptables_dump_entry(struct ipt_entry *entry,
1087 struct connman_iptables *table)
1089 struct xt_entry_target *target;
1090 unsigned int offset;
1093 offset = (char *)entry - (char *)table->blob_entries->entrytable;
1094 target = ipt_get_target(entry);
1095 builtin = is_hook_entry(table, entry);
1097 if (entry_to_offset(table, entry) + entry->next_offset ==
1098 table->blob_entries->size) {
1099 printf("End of CHAIN 0x%x\n", offset);
1103 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
1104 printf("USER CHAIN (%s) %p match %p target %p size %d\n",
1105 target->data, entry, entry->elems,
1106 (char *)entry + entry->target_offset,
1107 entry->next_offset);
1110 } else if (builtin >= 0) {
1111 printf("CHAIN (%s) %p match %p target %p size %d\n",
1112 hooknames[builtin], entry, entry->elems,
1113 (char *)entry + entry->target_offset,
1114 entry->next_offset);
1116 printf("RULE %p match %p target %p size %d\n", entry,
1118 (char *)entry + entry->target_offset,
1119 entry->next_offset);
1122 dump_match(table, entry);
1123 dump_target(table, entry);
1128 static void connman_iptables_dump_hook(struct connman_iptables *table)
1131 printf("hooks: \n");
1132 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1133 if ((table->info->valid_hooks & (1 << i)))
1134 printf("%s entry %p underflow %p (%#x)\n",
1136 table->blob_entries->entrytable +
1137 table->info->hook_entry[i],
1138 table->blob_entries->entrytable +
1139 table->info->underflow[i],
1140 table->info->underflow[i]);
1144 static void connman_iptables_dump(struct connman_iptables *table)
1146 printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
1148 table->info->valid_hooks, table->info->num_entries,
1151 connman_iptables_dump_hook(table);
1153 ENTRY_ITERATE(table->blob_entries->entrytable,
1154 table->blob_entries->size,
1155 connman_iptables_dump_entry, table);
1159 static int connman_iptables_get_entries(struct connman_iptables *table)
1161 socklen_t entry_size;
1163 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
1165 return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
1166 table->blob_entries, &entry_size);
1169 static int connman_iptables_replace(struct connman_iptables *table,
1170 struct ipt_replace *r)
1172 return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
1173 sizeof(*r) + r->size);
1176 static void connman_iptables_cleanup(struct connman_iptables *table)
1179 struct connman_iptables_entry *entry;
1181 close(table->ipt_sock);
1183 for (list = table->entries; list; list = list->next) {
1186 g_free(entry->entry);
1189 g_free(table->info);
1190 g_free(table->blob_entries);
1193 xtables_free_opts(1);
1196 static int connman_iptables_commit(struct connman_iptables *table)
1198 struct ipt_replace *repl;
1200 repl = connman_iptables_blob(table);
1202 return connman_iptables_replace(table, repl);
1205 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
1207 struct ipt_entry *new_entry;
1210 new_entry = g_try_malloc0(entry->next_offset);
1211 if (new_entry == NULL)
1214 memcpy(new_entry, entry, entry->next_offset);
1216 builtin = is_hook_entry(table, entry);
1218 return connman_add_entry(table, new_entry, NULL, builtin);
1221 static struct connman_iptables *connman_iptables_init(const char *table_name)
1223 struct connman_iptables *table = NULL;
1224 char *module = NULL;
1227 if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
1230 module = g_strconcat("iptable_", table_name, NULL);
1234 if (xtables_insmod(module, NULL, TRUE) != 0)
1240 table = g_try_new0(struct connman_iptables, 1);
1244 table->info = g_try_new0(struct ipt_getinfo, 1);
1245 if (table->info == NULL)
1248 table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
1249 if (table->ipt_sock < 0)
1252 s = sizeof(*table->info);
1253 strcpy(table->info->name, table_name);
1254 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
1255 table->info, &s) < 0)
1258 table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
1260 if (table->blob_entries == NULL)
1263 strcpy(table->blob_entries->name, table_name);
1264 table->blob_entries->size = table->info->size;
1266 if (connman_iptables_get_entries(table) < 0)
1269 table->num_entries = 0;
1270 table->old_entries = table->info->num_entries;
1273 memcpy(table->underflow, table->info->underflow,
1274 sizeof(table->info->underflow));
1275 memcpy(table->hook_entry, table->info->hook_entry,
1276 sizeof(table->info->hook_entry));
1278 ENTRY_ITERATE(table->blob_entries->entrytable,
1279 table->blob_entries->size,
1287 connman_iptables_cleanup(table);
1292 static struct option connman_iptables_opts[] = {
1293 {.name = "append", .has_arg = 1, .val = 'A'},
1294 {.name = "compare", .has_arg = 1, .val = 'C'},
1295 {.name = "delete", .has_arg = 1, .val = 'D'},
1296 {.name = "flush-chain", .has_arg = 1, .val = 'F'},
1297 {.name = "insert", .has_arg = 1, .val = 'I'},
1298 {.name = "list", .has_arg = 2, .val = 'L'},
1299 {.name = "new-chain", .has_arg = 1, .val = 'N'},
1300 {.name = "policy", .has_arg = 1, .val = 'P'},
1301 {.name = "delete-chain", .has_arg = 1, .val = 'X'},
1302 {.name = "destination", .has_arg = 1, .val = 'd'},
1303 {.name = "in-interface", .has_arg = 1, .val = 'i'},
1304 {.name = "jump", .has_arg = 1, .val = 'j'},
1305 {.name = "match", .has_arg = 1, .val = 'm'},
1306 {.name = "out-interface", .has_arg = 1, .val = 'o'},
1307 {.name = "source", .has_arg = 1, .val = 's'},
1308 {.name = "table", .has_arg = 1, .val = 't'},
1312 struct xtables_globals connman_iptables_globals = {
1314 .opts = connman_iptables_opts,
1315 .orig_opts = connman_iptables_opts,
1318 static struct xtables_target *prepare_target(struct connman_iptables *table,
1321 struct xtables_target *xt_t = NULL;
1322 gboolean is_builtin, is_user_defined;
1323 GList *chain_head = NULL;
1327 is_user_defined = FALSE;
1329 if (is_builtin_target(target_name))
1332 chain_head = find_chain_head(table, target_name);
1333 if (chain_head != NULL && chain_head->next != NULL)
1334 is_user_defined = TRUE;
1337 if (is_builtin || is_user_defined)
1338 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
1339 XTF_LOAD_MUST_SUCCEED);
1341 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
1346 target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
1348 xt_t->t = g_try_malloc0(target_size);
1349 if (xt_t->t == NULL)
1352 xt_t->t->u.target_size = target_size;
1354 if (is_builtin || is_user_defined) {
1355 struct xt_standard_target *target;
1357 target = (struct xt_standard_target *)(xt_t->t);
1358 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
1360 if (is_builtin == TRUE)
1361 target->verdict = target_to_verdict(target_name);
1362 else if (is_user_defined == TRUE) {
1363 struct connman_iptables_entry *target_rule;
1365 if (chain_head == NULL) {
1370 target_rule = chain_head->next->data;
1371 target->verdict = target_rule->offset;
1374 strcpy(xt_t->t->u.user.name, target_name);
1375 xt_t->t->u.user.revision = xt_t->revision;
1376 if (xt_t->init != NULL)
1377 xt_t->init(xt_t->t);
1380 #if XTABLES_VERSION_CODE > 5
1381 if (xt_t->x6_options != NULL)
1382 connman_iptables_globals.opts =
1383 xtables_options_xfrm(
1384 connman_iptables_globals.orig_opts,
1386 connman_iptables_globals.opts,
1388 &xt_t->option_offset);
1391 connman_iptables_globals.opts =
1392 xtables_merge_options(
1393 #if XTABLES_VERSION_CODE > 5
1394 connman_iptables_globals.orig_opts,
1396 connman_iptables_globals.opts,
1398 &xt_t->option_offset);
1400 if (connman_iptables_globals.opts == NULL) {
1408 static struct xtables_match *prepare_matches(struct connman_iptables *table,
1409 struct xtables_rule_match **xt_rm, char *match_name)
1411 struct xtables_match *xt_m;
1414 if (match_name == NULL)
1417 xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
1418 match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
1420 xt_m->m = g_try_malloc0(match_size);
1421 if (xt_m->m == NULL)
1424 xt_m->m->u.match_size = match_size;
1425 strcpy(xt_m->m->u.user.name, xt_m->name);
1426 xt_m->m->u.user.revision = xt_m->revision;
1428 if (xt_m->init != NULL)
1429 xt_m->init(xt_m->m);
1431 if (xt_m == xt_m->next)
1434 #if XTABLES_VERSION_CODE > 5
1435 if (xt_m->x6_options != NULL)
1436 connman_iptables_globals.opts =
1437 xtables_options_xfrm(
1438 connman_iptables_globals.orig_opts,
1439 connman_iptables_globals.opts,
1441 &xt_m->option_offset);
1444 connman_iptables_globals.opts =
1445 xtables_merge_options(
1446 #if XTABLES_VERSION_CODE > 5
1447 connman_iptables_globals.orig_opts,
1449 connman_iptables_globals.opts,
1451 &xt_m->option_offset);
1453 if (connman_iptables_globals.opts == NULL) {
1462 static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
1465 uint32_t prefixlength;
1469 tokens = g_strsplit(str, "/", 2);
1473 if (!inet_pton(AF_INET, tokens[0], ip)) {
1478 if (tokens[1] != NULL) {
1479 prefixlength = strtol(tokens[1], NULL, 10);
1480 if (prefixlength > 31) {
1485 tmp = ~(0xffffffff >> prefixlength);
1490 mask->s_addr = htonl(tmp);
1491 ip->s_addr = ip->s_addr & mask->s_addr;
1499 int main(int argc, char *argv[])
1501 struct connman_iptables *table;
1502 struct xtables_rule_match *xt_rm, *tmp_xt_rm;
1503 struct xtables_match *xt_m, *xt_m_t;
1504 struct xtables_target *xt_t;
1506 char *table_name, *chain, *new_chain, *match_name, *target_name;
1507 char *delete_chain, *flush_chain, *policy;
1508 int c, in_len, out_len;
1509 gboolean dump, invert, delete, insert, delete_rule, compare_rule;
1511 xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
1517 delete_rule = FALSE;
1518 compare_rule = FALSE;
1519 chain = new_chain = match_name = target_name = NULL;
1520 delete_chain = flush_chain = policy = NULL;
1521 memset(&ip, 0, sizeof(struct ipt_ip));
1527 /* extension's options will generate false-positives errors */
1530 while ((c = getopt_long(argc, argv,
1531 "-A:C:D:F:I:L::N:P:X:d:i:j:m:o:s:t:",
1532 connman_iptables_globals.opts, NULL)) != -1) {
1535 /* It is either -A, -C, -D or -I at once */
1543 /* It is either -A, -C, -D or -I at once */
1548 compare_rule = TRUE;
1552 /* It is either -A, -C, -D or -I at once */
1561 flush_chain = optarg;
1565 /* It is either -A, -C, -D or -I at once */
1584 policy = argv[optind++];
1592 delete_chain = optarg;
1596 if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
1600 ip.invflags |= IPT_INV_DSTIP;
1606 in_len = strlen(optarg);
1608 if (in_len + 1 > IFNAMSIZ)
1611 strcpy(ip.iniface, optarg);
1612 memset(ip.iniface_mask, 0xff, in_len + 1);
1615 ip.invflags |= IPT_INV_VIA_IN;
1620 target_name = optarg;
1621 xt_t = prepare_target(table, target_name);
1628 match_name = optarg;
1629 xt_m = prepare_matches(table, &xt_rm, match_name);
1636 out_len = strlen(optarg);
1638 if (out_len + 1 > IFNAMSIZ)
1641 strcpy(ip.outiface, optarg);
1642 memset(ip.outiface_mask, 0xff, out_len + 1);
1645 ip.invflags |= IPT_INV_VIA_OUT;
1650 if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
1654 ip.invflags |= IPT_INV_SRCIP;
1659 table_name = optarg;
1661 table = connman_iptables_init(table_name);
1668 if (optarg[0] == '!' && optarg[1] == '\0') {
1670 printf("Consecutive ! not allowed\n");
1677 printf("Invalid option\n");
1682 #if XTABLES_VERSION_CODE > 5
1683 if (xt_t != NULL && (xt_t->x6_parse != NULL ||
1684 xt_t->parse != NULL) &&
1685 (c >= (int) xt_t->option_offset &&
1686 c < (int) xt_t->option_offset +
1687 XT_OPTION_OFFSET_SCALE)) {
1688 xtables_option_tpcall(c, argv,
1689 invert, xt_t, NULL);
1694 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1695 tmp_xt_rm = tmp_xt_rm->next) {
1696 xt_m_t = tmp_xt_rm->match;
1698 if (tmp_xt_rm->completed ||
1699 (xt_m_t->x6_parse == NULL &&
1700 xt_m_t->parse == NULL))
1703 if (c < (int) xt_m_t->option_offset ||
1704 c >= (int) xt_m_t->option_offset
1705 + XT_OPTION_OFFSET_SCALE)
1708 xtables_option_mpcall(c, argv,
1709 invert, xt_m_t, NULL);
1714 if (xt_t == NULL || xt_t->parse == NULL ||
1715 !xt_t->parse(c - xt_t->option_offset,
1716 argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
1718 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1719 tmp_xt_rm = tmp_xt_rm->next) {
1720 xt_m_t = tmp_xt_rm->match;
1722 if (tmp_xt_rm->completed ||
1723 xt_m_t->parse == NULL)
1726 if (xt_m->parse(c - xt_m->option_offset,
1727 argv, invert, &xt_m->mflags,
1739 #if XTABLES_VERSION_CODE > 5
1740 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1741 tmp_xt_rm = tmp_xt_rm->next)
1742 xtables_option_mfcall(tmp_xt_rm->match);
1745 xtables_option_tfcall(xt_t);
1747 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1748 tmp_xt_rm = tmp_xt_rm->next)
1749 if (tmp_xt_rm->match->final_check != NULL)
1750 tmp_xt_rm->match->final_check(
1751 tmp_xt_rm->match->mflags);
1753 if (xt_t != NULL && xt_t->final_check != NULL)
1754 xt_t->final_check(xt_t->tflags);
1757 if (table == NULL) {
1758 table_name = "filter";
1760 table = connman_iptables_init(table_name);
1766 if (delete_chain == NULL)
1769 printf("Delete chain %s\n", delete_chain);
1771 connman_iptables_delete_chain(table, delete_chain);
1777 printf("Flush chain %s\n", flush_chain);
1779 connman_iptables_flush_chain(table, flush_chain);
1785 connman_iptables_dump(table);
1790 if (chain && new_chain)
1794 printf("New chain %s\n", new_chain);
1796 connman_iptables_add_chain(table, new_chain);
1802 if (policy != NULL) {
1803 printf("Changing policy of %s to %s\n", chain, policy);
1805 connman_iptables_change_policy(table, chain, policy);
1810 if (compare_rule == TRUE) {
1813 ret = connman_iptables_compare_rule(table, &ip,
1814 chain, target_name, xt_t, xt_m, xt_rm);
1817 printf("Rule exists.\n");
1819 printf("Rule does not exist.\n");
1824 if (delete_rule == TRUE) {
1825 printf("Deleting %s to %s (match %s)\n", target_name,
1828 connman_iptables_delete_rule(table, &ip, chain,
1829 target_name, xt_t, xt_m, xt_rm);
1834 if (insert == TRUE) {
1835 printf("Inserting %s to %s (match %s)\n", target_name,
1838 connman_iptables_insert_rule(table, &ip, chain,
1839 target_name, xt_t, xt_rm);
1841 printf("Appending %s to %s (match %s)\n", target_name,
1844 connman_iptables_append_rule(table, &ip, chain,
1845 target_name, xt_t, xt_rm);
1851 connman_iptables_commit(table);
1854 connman_iptables_cleanup(table);