*
*/
+#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
};
struct connman_iptables_entry {
- int builtin;
- int std_target;
- int jump_offset;
+ int offset;
struct ipt_entry *entry;
};
return FALSE;
}
+static gboolean is_jump(struct connman_iptables_entry *e)
+{
+ struct xt_entry_target *target;
+
+ target = ipt_get_target(e->entry);
+
+ if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
+ struct xt_standard_target *t;
+
+ t = (struct xt_standard_target *)target;
+
+ switch (t->verdict) {
+ case XT_RETURN:
+ case -NF_ACCEPT - 1:
+ case -NF_DROP - 1:
+ case -NF_QUEUE - 1:
+ case -NF_STOP - 1:
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+ return false;
+}
+
static gboolean is_chain(struct connman_iptables *table,
struct connman_iptables_entry *e)
{
return FALSE;
}
+static GList *find_chain_head(struct connman_iptables *table,
+ char *chain_name)
+{
+ GList *list;
+ struct connman_iptables_entry *head;
+ struct ipt_entry *entry;
+ struct xt_entry_target *target;
+ int builtin;
+
+ for (list = table->entries; list; list = list->next) {
+ head = list->data;
+ entry = head->entry;
+
+ /* Buit-in chain */
+ builtin = is_hook_entry(table, entry);
+ if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
+ break;
+
+ /* User defined chain */
+ target = ipt_get_target(entry);
+ if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
+ !strcmp((char *)target->data, chain_name))
+ break;
+ }
+
+ return list;
+}
+
static GList *find_chain_tail(struct connman_iptables *table,
char *chain_name)
{
return g_list_last(table->entries);
}
+static void update_offsets(struct connman_iptables *table)
+{
+ GList *list, *prev;
+ struct connman_iptables_entry *entry, *prev_entry;
+
+ for (list = table->entries; list; list = list->next) {
+ entry = list->data;
+
+ if (list == table->entries) {
+ entry->offset = 0;
+
+ continue;
+ }
+
+ prev = list->prev;
+ prev_entry = prev->data;
+
+ entry->offset = prev_entry->offset +
+ prev_entry->entry->next_offset;
+ }
+}
+
static int connman_add_entry(struct connman_iptables *table,
struct ipt_entry *entry, GList *before)
{
- struct connman_iptables_entry *e;
+ GList *list;
+ struct connman_iptables_entry *e, *tmp, *entry_before;
+ struct xt_standard_target *t;
if (table == NULL)
return -1;
table->num_entries++;
table->size += entry->next_offset;
+ if (before == NULL) {
+ e->offset = table->size - entry->next_offset;
+
+ return 0;
+ }
+
+ entry_before = before->data;
+
+ /*
+ * We've just insterted a new entry. All references before it
+ * should be bumped accordingly.
+ */
+ for (list = table->entries; list != before; list = list->next) {
+ tmp = list->data;
+
+ if (!is_jump(tmp))
+ continue;
+
+ t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
+
+ if (t->verdict >= entry_before->offset)
+ t->verdict += entry->next_offset;
+ }
+
+ update_offsets(table);
+
+ return 0;
+}
+
+static int connman_iptables_delete_chain(struct connman_iptables *table,
+ char *name)
+{
+ GList *chain_head, *chain_tail, *list, *next;
+ struct connman_iptables_entry *entry;
+
+ chain_head = find_chain_head(table, name);
+ if (chain_head == NULL)
+ return -EINVAL;
+
+ chain_tail = find_chain_tail(table, name);
+ if (chain_head == NULL)
+ return -EINVAL;
+
+ list = chain_head;
+
+ while (list != chain_tail) {
+ entry = list->data;
+ next = g_list_next(list);
+
+ table->num_entries--;
+ table->size -= entry->entry->next_offset;
+
+ table->entries = g_list_remove(table->entries, list->data);
+
+ list = next;
+ }
+
+ update_offsets(table);
+
return 0;
}
}
static struct ipt_entry *
-new_builtin_rule(char *target_name,
- char *match_name, int match_argc, char **match_argv)
+new_rule(struct connman_iptables *table,
+ char *target_name, struct xtables_target *xt_t,
+ char *match_name, struct xtables_match *xt_m)
{
struct ipt_entry *new_entry;
size_t match_size, target_size;
- struct xtables_match *xt_m;
- struct xt_standard_target *target;
-
- xt_m = NULL;
- match_size = 0;
-
- if (match_name) {
- xt_m = xtables_find_match(match_name, XTF_TRY_LOAD, NULL);
- if (xt_m == NULL)
- return NULL;
+ int is_builtin = is_builtin_target(target_name);
- match_size = ALIGN(sizeof(struct xt_entry_match)) + xt_m->size;
- }
+ if (xt_m)
+ match_size = xt_m->m->u.match_size;
+ else
+ match_size = 0;
- target_size = ALIGN(sizeof(struct xt_standard_target));
+ if (xt_t)
+ target_size = ALIGN(xt_t->t->u.target_size);
+ else
+ target_size = ALIGN(sizeof(struct xt_standard_target));
new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
match_size);
new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
match_size;
-
if (xt_m) {
struct xt_entry_match *entry_match;
entry_match = (struct xt_entry_match *)new_entry->elems;
- entry_match->u.match_size = match_size;
- strcpy(entry_match->u.user.name, xt_m->name);
- entry_match->u.user.revision = xt_m->revision;
- if (xt_m->init != NULL)
- xt_m->init(entry_match);
+ memcpy(entry_match, xt_m->m, match_size);
}
- target = (struct xt_standard_target *)(new_entry->elems + match_size);
- strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
- target->target.u.user.target_size =
- ALIGN(sizeof(struct ipt_standard_target));
- target->verdict = target_to_verdict(target_name);
+ if (xt_t) {
+ struct xt_entry_target *entry_target;
- return new_entry;
-}
+ if (is_builtin) {
+ struct xt_standard_target *target;
-static struct ipt_entry *
-new_custom_rule(char *target_name, int target_argc, char **target_argv,
- char *match_name, int match_argc, char **match_argv)
-{
- return NULL;
-}
+ target = (struct xt_standard_target *)(xt_t->t);
+ strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+ target->verdict = target_to_verdict(target_name);
+ }
-static struct ipt_entry *
-new_rule(char *target_name, int target_argc, char **target_argv,
- char *match_name, int match_argc, char **match_argv)
-{
- struct ipt_entry *new_entry;
+ entry_target = ipt_get_target(new_entry);
+ memcpy(entry_target, xt_t->t, target_size);
+ } else {
+ struct connman_iptables_entry *target_rule;
+ struct xt_standard_target *target;
+ GList *chain_head;
+
+ /*
+ * This is a user defined target, i.e. a chain jump.
+ * We search for the chain head, and the target verdict
+ * is the first rule's offset on this chain.
+ * The offset is from the beginning of the table.
+ */
+
+ chain_head = find_chain_head(table, target_name);
+ if (chain_head == NULL || chain_head->next == NULL) {
+ g_free(new_entry);
+ return NULL;
+ }
- if (is_builtin_target(target_name))
- new_entry = new_builtin_rule(target_name,
- match_name, match_argc, match_argv);
- else
- new_entry = new_custom_rule(target_name,
- target_argc, target_argv,
- match_name, match_argc, match_argv);
+ target_rule = chain_head->next->data;
+
+ target = (struct xt_standard_target *)ipt_get_target(new_entry);
+ strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
+ target->target.u.user.target_size = target_size;
+ target->verdict = target_rule->offset;
+ }
return new_entry;
}
static int
connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
- char *target_name, int target_argc, char **target_argv,
- char *match_name, int match_argc, char **match_argv)
+ char *target_name, struct xtables_target *xt_t,
+ char *match_name, struct xtables_match *xt_m)
{
GList *chain_tail;
struct ipt_entry *new_entry;
if (chain_tail == NULL)
return -EINVAL;
- printf("Chains found\n");
-
- new_entry = new_rule(target_name, target_argc, target_argv,
- match_name, match_argc, match_argv);
+ new_entry = new_rule(table,
+ target_name, xt_t,
+ match_name, xt_m);
if (new_entry == NULL)
return -EINVAL;
if(xt_t->print != NULL)
xt_t->print(NULL, target, 1);
} else {
- printf("\ttarget %s\n", target->u.user.name);
-
xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
- if (xt_t == NULL)
+ if (xt_t == NULL) {
+ printf("\ttarget %s\n", target->u.user.name);
return;
+ }
- if(xt_t->print != NULL)
+ if(xt_t->print != NULL) {
+ printf("\ttarget ");
xt_t->print(NULL, target, 1);
+ printf("\n");
+ }
}
}
struct xtables_match *xt_m;
struct xt_entry_match *match;
+ if (entry->elems == (unsigned char *)entry + entry->target_offset)
+ return;
+
match = (struct xt_entry_match *) entry->elems;
if (!strlen(match->u.user.name))
static void connman_iptables_cleanup(struct connman_iptables *table)
{
+ GList *list;
+ struct connman_iptables_entry *entry;
+
close(table->ipt_sock);
+
+ for (list = table->entries; list; list = list->next) {
+ entry = list->data;
+
+ g_free(entry->entry);
+ }
+
g_free(table->info);
g_free(table->blob_entries);
g_free(table);
+
+ xtables_free_opts(1);
+}
+
+static int connman_iptables_commit(struct connman_iptables *table)
+{
+ struct ipt_replace *repl;
+
+ repl = connman_iptables_blob(table);
+
+ return connman_iptables_replace(table, repl);
}
static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
{
- return connman_add_entry(table, entry, NULL);
+ struct ipt_entry *new_entry;
+
+ new_entry = g_try_malloc0(entry->next_offset);
+ if (new_entry == NULL)
+ return -ENOMEM;
+
+ memcpy(new_entry, entry, entry->next_offset);
+
+ return connman_add_entry(table, new_entry, NULL);
}
static struct connman_iptables *connman_iptables_init(const char *table_name)
return NULL;
}
+
+static struct option connman_iptables_opts[] = {
+ {.name = "append", .has_arg = 1, .val = 'A'},
+ {.name = "list", .has_arg = 2, .val = 'L'},
+ {.name = "new-chain", .has_arg = 1, .val = 'N'},
+ {.name = "delete-chain", .has_arg = 1, .val = 'X'},
+ {.name = "in-interface", .has_arg = 1, .val = 'i'},
+ {.name = "jump", .has_arg = 1, .val = 'j'},
+ {.name = "match", .has_arg = 1, .val = 'm'},
+ {.name = "out-interface", .has_arg = 1, .val = 'o'},
+ {.name = "table", .has_arg = 1, .val = 't'},
+ {NULL},
+};
+
+struct xtables_globals connman_iptables_globals = {
+ .option_offset = 0,
+ .opts = connman_iptables_opts,
+ .orig_opts = connman_iptables_opts,
+};
+
int main(int argc, char *argv[])
{
- struct ipt_replace *repl;
struct connman_iptables *table;
+ struct xtables_match *xt_m;
+ struct xtables_target *xt_t;
+ char *table_name, *chain, *new_chain, *match_name, *target_name;
+ char *delete_chain;
+ int c;
+ size_t size;
+ gboolean dump, invert, delete;
+
+ xtables_init_all(&connman_iptables_globals, NFPROTO_IPV4);
+
+ dump = FALSE;
+ invert = FALSE;
+ delete = FALSE;
+ table_name = chain = new_chain = match_name = target_name = NULL;
+ delete_chain = NULL;
+ table = NULL;
+ xt_m = NULL;
+ xt_t = NULL;
- xtables_init();
- xtables_set_nfproto(NFPROTO_IPV4);
+ while ((c = getopt_long(argc, argv,
+ "-A:L::N:X:j:i:m:o:t:", connman_iptables_globals.opts, NULL)) != -1) {
+ switch (c) {
+ case 'A':
+ chain = optarg;
+ break;
+
+ case 'L':
+ dump = true;
+ break;
+
+ case 'N':
+ new_chain = optarg;
+ break;
+
+ case 'X':
+ delete = true;
+ delete_chain = optarg;
+ break;
+
+ case 'j':
+ target_name = optarg;
+ xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
+
+ if (xt_t == NULL)
+ break;
+
+ size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
+
+ xt_t->t = g_try_malloc0(size);
+ if (xt_t->t == NULL)
+ goto out;
+ xt_t->t->u.target_size = size;
+ strcpy(xt_t->t->u.user.name, target_name);
+ xt_t->t->u.user.revision = xt_t->revision;
+ if (xt_t->init != NULL)
+ xt_t->init(xt_t->t);
+ connman_iptables_globals.opts =
+ xtables_merge_options(connman_iptables_globals.opts,
+ xt_t->extra_opts,
+ &xt_t->option_offset);
+ if (connman_iptables_globals.opts == NULL)
+ goto out;
+
+ break;
+
+ case 'i':
+ break;
+
+ case 'm':
+ match_name = optarg;
+
+ xt_m = xtables_find_match(optarg, XTF_LOAD_MUST_SUCCEED, NULL);
+ size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
+ xt_m->m = g_try_malloc0(size);
+ if (xt_m == NULL)
+ goto out;
+ xt_m->m->u.match_size = size;
+ strcpy(xt_m->m->u.user.name, xt_m->name);
+ xt_m->m->u.user.revision = xt_m->revision;
+ if (xt_m->init != NULL)
+ xt_m->init(xt_m->m);
+ if (xt_m != xt_m->next) {
+ connman_iptables_globals.opts =
+ xtables_merge_options(connman_iptables_globals.opts,
+ xt_m->extra_opts,
+ &xt_m->option_offset);
+ if (connman_iptables_globals.opts == NULL)
+ goto out;
+ }
+
+ break;
+
+ case 'o':
+ break;
+
+ case 't':
+ table_name = optarg;
+ break;
+
+ case 1:
+ if (optarg[0] == '!' && optarg[1] == '\0') {
+ if (invert)
+ printf("Consecutive ! not allowed\n");
+
+ invert = TRUE;
+ optarg[0] = '\0';
+ continue;
+ }
+
+ printf("Invalid option\n");
+
+ return -1;
+
+ default:
+ if (xt_t == NULL || xt_t->parse == NULL ||
+ !xt_t->parse(c - xt_t->option_offset, argv, invert,
+ &xt_t->tflags, NULL, &xt_t->t)) {
+ if (xt_m == NULL || xt_m->parse == NULL)
+ break;
- table = connman_iptables_init("filter");
+ xt_m->parse(c - xt_m->option_offset, argv,
+ invert, &xt_m->mflags, NULL, &xt_m->m);
+ }
+
+ break;
+ }
+ }
+
+ if (table_name == NULL)
+ table_name = "filter";
+
+ table = connman_iptables_init(table_name);
if (table == NULL)
return -1;
- connman_iptables_dump(table);
+ if (delete) {
+ if (delete_chain == NULL)
+ goto out;
- if (argv[1]) {
- connman_iptables_add_chain(table, argv[1]);
+ printf("Delete chain %s\n", delete_chain);
- connman_iptables_add_rule(table, argv[1],
- "ACCEPT", 0, NULL,
- NULL, 0, NULL);
+ connman_iptables_delete_chain(table, delete_chain);
- repl = connman_iptables_blob(table);
+ goto commit;
+ }
- connman_iptables_replace(table, repl);
+ if (dump) {
+ connman_iptables_dump(table);
+
+ return 0;
}
+ if (chain && new_chain)
+ return -1;
+
+ if (new_chain) {
+ printf("New chain %s\n", new_chain);
+
+ connman_iptables_add_chain(table, new_chain);
+
+ goto commit;
+ }
+
+ if (chain) {
+ if (target_name == NULL)
+ return -1;
+
+ printf("Adding %s to %s (match %s)\n", target_name, chain, match_name);
+
+ connman_iptables_add_rule(table, chain, target_name, xt_t,
+ match_name, xt_m);
+
+ goto commit;
+ }
+
+commit:
+
+ connman_iptables_commit(table);
+
+out:
connman_iptables_cleanup(table);
+ if (xt_t)
+ g_free(xt_t->t);
+
+ if (xt_m)
+ g_free(xt_m->m);
+
return 0;
}