X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fiptables.c;h=531e93373c810f07ce26650eb6bccce6300d0930;hb=18ec478652f819b8837b136ed117d8b882b2b3f6;hp=16c370133a6f3335ecf8a49cd468b5362e46e35e;hpb=4f8091d45431d2dcdab2d3c911a53ec6afc97503;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/iptables.c b/src/iptables.c index 16c3701..531e933 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -538,15 +538,15 @@ static int iptables_delete_chain(struct connman_iptables *table, char *name) static struct ipt_entry *new_rule(struct ipt_ip *ip, char *target_name, struct xtables_target *xt_t, - char *match_name, struct xtables_match *xt_m) + struct xtables_rule_match *xt_rm) { + struct xtables_rule_match *tmp_xt_rm; struct ipt_entry *new_entry; size_t match_size, target_size; - if (xt_m) - match_size = xt_m->m->u.match_size; - else - match_size = 0; + match_size = 0; + for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next) + match_size += tmp_xt_rm->match->m->u.match_size; if (xt_t) target_size = ALIGN(xt_t->t->u.target_size); @@ -563,11 +563,13 @@ static struct ipt_entry *new_rule(struct ipt_ip *ip, 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; - memcpy(entry_match, xt_m->m, match_size); + match_size = 0; + for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; + tmp_xt_rm = tmp_xt_rm->next) { + memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m, + tmp_xt_rm->match->m->u.match_size); + match_size += tmp_xt_rm->match->m->u.match_size; } if (xt_t) { @@ -613,8 +615,7 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head, static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, struct ipt_ip *ip, char *chain_name, char *target_name, struct xtables_target *xt_t, - char *match_name, struct xtables_match *xt_m, - int *builtin) + int *builtin, struct xtables_rule_match *xt_rm) { GList *chain_tail, *chain_head; struct ipt_entry *new_entry; @@ -628,7 +629,7 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, if (chain_tail == NULL) return NULL; - new_entry = new_rule(ip, target_name, xt_t, match_name, xt_m); + new_entry = new_rule(ip, target_name, xt_t, xt_rm); if (new_entry == NULL) return NULL; @@ -653,7 +654,7 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, static int iptables_append_rule(struct connman_iptables *table, struct ipt_ip *ip, char *chain_name, char *target_name, struct xtables_target *xt_t, - char *match_name, struct xtables_match *xt_m) + struct xtables_rule_match *xt_rm) { GList *chain_tail; struct ipt_entry *new_entry; @@ -666,7 +667,7 @@ static int iptables_append_rule(struct connman_iptables *table, return -EINVAL; new_entry = prepare_rule_inclusion(table, ip, chain_name, - target_name, xt_t, match_name, xt_m, &builtin); + target_name, xt_t, &builtin, xt_rm); if (new_entry == NULL) return -EINVAL; @@ -680,7 +681,7 @@ static int iptables_append_rule(struct connman_iptables *table, static int iptables_insert_rule(struct connman_iptables *table, struct ipt_ip *ip, char *chain_name, char *target_name, struct xtables_target *xt_t, - char *match_name, struct xtables_match *xt_m) + struct xtables_rule_match *xt_rm) { struct ipt_entry *new_entry; int builtin = -1, ret; @@ -691,11 +692,14 @@ static int iptables_insert_rule(struct connman_iptables *table, return -EINVAL; new_entry = prepare_rule_inclusion(table, ip, chain_name, - target_name, xt_t, match_name, xt_m, &builtin); + target_name, xt_t, &builtin, xt_rm); if (new_entry == NULL) return -EINVAL; - ret = iptables_add_entry(table, new_entry, chain_head->next, builtin); + if (builtin == -1) + chain_head = chain_head->next; + + ret = iptables_add_entry(table, new_entry, chain_head, builtin); if (ret < 0) g_free(new_entry); @@ -761,34 +765,33 @@ static gboolean is_same_match(struct xt_entry_match *xt_e_m1, return TRUE; } -static int iptables_delete_rule(struct connman_iptables *table, +static GList *find_existing_rule(struct connman_iptables *table, struct ipt_ip *ip, char *chain_name, char *target_name, struct xtables_target *xt_t, - char *match_name, struct xtables_match *xt_m) + struct xtables_match *xt_m, + struct xtables_rule_match *xt_rm) { GList *chain_tail, *chain_head, *list; struct xt_entry_target *xt_e_t = NULL; struct xt_entry_match *xt_e_m = NULL; struct connman_iptables_entry *entry; struct ipt_entry *entry_test; - int builtin, removed; - - removed = 0; + int builtin; chain_head = find_chain_head(table, chain_name); if (chain_head == NULL) - return -EINVAL; + return NULL; chain_tail = find_chain_tail(table, chain_name); if (chain_tail == NULL) - return -EINVAL; + return NULL; if (!xt_t && !xt_m) - return -EINVAL; + return NULL; - entry_test = new_rule(ip, target_name, xt_t, match_name, xt_m); + entry_test = new_rule(ip, target_name, xt_t, xt_rm); if (entry_test == NULL) - return -EINVAL; + return NULL; if (xt_t != NULL) xt_e_t = ipt_get_target(entry_test); @@ -803,7 +806,7 @@ static int iptables_delete_rule(struct connman_iptables *table, else list = chain_head->next; - for (entry = NULL; list != chain_tail->prev; list = list->next) { + for (; list != chain_tail->prev; list = list->next) { struct connman_iptables_entry *tmp; struct ipt_entry *tmp_e; @@ -831,14 +834,43 @@ static int iptables_delete_rule(struct connman_iptables *table, continue; } - entry = tmp; break; } - if (entry == NULL) { - g_free(entry_test); + g_free(entry_test); + + if (list != chain_tail->prev) + return list; + + return NULL; +} + +static int iptables_delete_rule(struct connman_iptables *table, + struct ipt_ip *ip, char *chain_name, + char *target_name, struct xtables_target *xt_t, + struct xtables_match *xt_m, + struct xtables_rule_match *xt_rm) +{ + struct connman_iptables_entry *entry; + GList *chain_tail, *list; + int builtin, removed; + + removed = 0; + + chain_tail = find_chain_tail(table, chain_name); + if (chain_tail == NULL) return -EINVAL; - } + + list = find_existing_rule(table, ip, chain_name, target_name, + xt_t, xt_m, xt_rm); + if (list == NULL) + return -EINVAL; + + entry = list->data; + if (entry == NULL) + return -EINVAL; + + builtin = entry->builtin; /* We have deleted a rule, * all references should be bumped accordingly */ @@ -873,6 +905,57 @@ static int iptables_delete_rule(struct connman_iptables *table, return 0; } +static int iptables_compare_rule(struct connman_iptables *table, + struct ipt_ip *ip, char *chain_name, + char *target_name, struct xtables_target *xt_t, + struct xtables_match *xt_m, + struct xtables_rule_match *xt_rm) +{ + struct connman_iptables_entry *entry; + GList *found; + + found = find_existing_rule(table, ip, chain_name, target_name, + xt_t, xt_m, xt_rm); + if (found == NULL) + return -EINVAL; + + entry = found->data; + if (entry == NULL) + return -EINVAL; + + return 0; +} + + +static int iptables_change_policy(struct connman_iptables *table, + char *chain_name, char *policy) +{ + GList *chain_head; + struct connman_iptables_entry *entry; + struct xt_entry_target *target; + struct xt_standard_target *t; + int verdict; + + verdict = target_to_verdict(policy); + if (verdict == 0) + return -EINVAL; + + chain_head = find_chain_head(table, chain_name); + if (chain_head == NULL) + return -EINVAL; + + entry = chain_head->data; + if (entry->builtin < 0) + return -EINVAL; + + target = ipt_get_target(entry->entry); + + t = (struct xt_standard_target *)target; + t->verdict = verdict; + + return 0; +} + static struct ipt_replace *iptables_blob(struct connman_iptables *table) { struct ipt_replace *r; @@ -1121,6 +1204,9 @@ static void table_cleanup(struct connman_iptables *table) GList *list; struct connman_iptables_entry *entry; + if (table == NULL) + return; + close(table->ipt_sock); for (list = table->entries; list; list = list->next) { @@ -1145,17 +1231,18 @@ static struct connman_iptables *iptables_init(char *table_name) DBG("%s", table_name); if (xtables_insmod("ip_tables", NULL, TRUE) != 0) - goto err; + return NULL; module = g_strconcat("iptable_", table_name, NULL); if (module == NULL) - goto err; + return NULL; - if (xtables_insmod(module, NULL, TRUE) != 0) - goto err; + if (xtables_insmod(module, NULL, TRUE) != 0) { + g_free(module); + return NULL; + } g_free(module); - module = NULL; table = g_hash_table_lookup(table_hash, table_name); if (table != NULL) @@ -1208,8 +1295,6 @@ static struct connman_iptables *iptables_init(char *table_name) return table; err: - g_free(module); - table_cleanup(table); return NULL; @@ -1217,11 +1302,13 @@ err: static struct option iptables_opts[] = { {.name = "append", .has_arg = 1, .val = 'A'}, + {.name = "compare", .has_arg = 1, .val = 'C'}, {.name = "delete", .has_arg = 1, .val = 'D'}, {.name = "flush-chain", .has_arg = 1, .val = 'F'}, {.name = "insert", .has_arg = 1, .val = 'I'}, {.name = "list", .has_arg = 2, .val = 'L'}, {.name = "new-chain", .has_arg = 1, .val = 'N'}, + {.name = "policy", .has_arg = 1, .val = 'P'}, {.name = "delete-chain", .has_arg = 1, .val = 'X'}, {.name = "destination", .has_arg = 1, .val = 'd'}, {.name = "in-interface", .has_arg = 1, .val = 'i'}, @@ -1301,16 +1388,16 @@ static struct xtables_target *prepare_target(struct connman_iptables *table, xt_t->init(xt_t->t); } +#if XTABLES_VERSION_CODE > 5 if (xt_t->x6_options != NULL) iptables_globals.opts = xtables_options_xfrm( -#if XTABLES_VERSION_CODE > 5 iptables_globals.orig_opts, -#endif iptables_globals.opts, xt_t->x6_options, &xt_t->option_offset); else +#endif iptables_globals.opts = xtables_merge_options( #if XTABLES_VERSION_CODE > 5 @@ -1354,16 +1441,16 @@ static struct xtables_match *prepare_matches(struct connman_iptables *table, if (xt_m == xt_m->next) goto done; +#if XTABLES_VERSION_CODE > 5 if (xt_m->x6_options != NULL) iptables_globals.opts = xtables_options_xfrm( -#if XTABLES_VERSION_CODE > 5 iptables_globals.orig_opts, -#endif iptables_globals.opts, xt_m->x6_options, &xt_m->option_offset); else +#endif iptables_globals.opts = xtables_merge_options( #if XTABLES_VERSION_CODE > 5 @@ -1382,6 +1469,43 @@ done: return xt_m; } +static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask) +{ + char **tokens; + uint32_t prefixlength; + uint32_t tmp; + int err; + + tokens = g_strsplit(str, "/", 2); + if (tokens == NULL) + return -1; + + if (!inet_pton(AF_INET, tokens[0], ip)) { + err = -1; + goto out; + } + + if (tokens[1] != NULL) { + prefixlength = strtol(tokens[1], NULL, 10); + if (prefixlength > 31) { + err = -1; + goto out; + } + + tmp = ~(0xffffffff >> prefixlength); + } else { + tmp = 0xffffffff; + } + + mask->s_addr = htonl(tmp); + ip->s_addr = ip->s_addr & mask->s_addr; + err = 0; +out: + g_strfreev(tokens); + + return err; +} + static int iptables_command(int argc, char *argv[]) { struct connman_iptables *table; @@ -1390,10 +1514,9 @@ static int iptables_command(int argc, char *argv[]) struct xtables_target *xt_t; struct ipt_ip ip; char *table_name, *chain, *new_chain, *match_name, *target_name; - char *flush_chain, *delete_chain; + char *flush_chain, *delete_chain, *policy; int c, ret, in_len, out_len; - gboolean dump, invert, insert, delete; - struct in_addr src, dst; + gboolean dump, invert, insert, delete, compare; if (argc == 0) return -EINVAL; @@ -1402,8 +1525,9 @@ static int iptables_command(int argc, char *argv[]) invert = FALSE; insert = FALSE; delete = FALSE; - table_name = chain = new_chain = match_name = target_name = NULL; - flush_chain = delete_chain = NULL; + compare = FALSE; + chain = new_chain = match_name = target_name = NULL; + flush_chain = delete_chain = policy = NULL; memset(&ip, 0, sizeof(struct ipt_ip)); table = NULL; xt_rm = NULL; @@ -1411,21 +1535,34 @@ static int iptables_command(int argc, char *argv[]) xt_t = NULL; ret = 0; + /* extension's options will generate false-positives errors */ + opterr = 0; + optind = 0; - while ((c = getopt_long(argc, argv, "-A:F:I:L::N:X:d:j:i:m:o:s:t:", + while ((c = getopt_long(argc, argv, + "-A:C:D:F:I:L::N:P:X:d:j:i:m:o:s:t:", iptables_globals.opts, NULL)) != -1) { switch (c) { case 'A': - /* It is either -A, -D or -I at once */ + /* It is either -A, -C, -D or -I at once */ if (chain) goto out; chain = optarg; break; + case 'C': + /* It is either -A, -C, -D or -I at once */ + if (chain) + goto out; + + chain = optarg; + compare = TRUE; + break; + case 'D': - /* It is either -A, -D or -I at once */ + /* It is either -A, -C, -D or -I at once */ if (chain) goto out; @@ -1438,7 +1575,7 @@ static int iptables_command(int argc, char *argv[]) break; case 'I': - /* It is either -A, -D or -I at once */ + /* It is either -A, -C, -D or -I at once */ if (chain) goto out; @@ -1454,17 +1591,23 @@ static int iptables_command(int argc, char *argv[]) new_chain = optarg; break; + case 'P': + chain = optarg; + if (optind < argc) + policy = argv[optind++]; + else + goto out; + + break; + case 'X': delete_chain = optarg; break; case 'd': - if (!inet_pton(AF_INET, optarg, &dst)) + if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk)) break; - ip.dst = dst; - inet_pton(AF_INET, "255.255.255.255", &ip.dmsk); - if (invert) ip.invflags |= IPT_INV_DSTIP; @@ -1515,12 +1658,9 @@ static int iptables_command(int argc, char *argv[]) break; case 's': - if (!inet_pton(AF_INET, optarg, &src)) + if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk)) break; - ip.src = src; - inet_pton(AF_INET, "255.255.255.255", &ip.smsk); - if (invert) ip.invflags |= IPT_INV_SRCIP; @@ -1528,6 +1668,13 @@ static int iptables_command(int argc, char *argv[]) case 't': table_name = optarg; + + table = iptables_init(table_name); + if (table == NULL) { + ret = -EINVAL; + goto out; + } + break; case 1: @@ -1543,6 +1690,7 @@ static int iptables_command(int argc, char *argv[]) goto out; default: +#if XTABLES_VERSION_CODE > 5 if (xt_t != NULL && (xt_t->x6_parse != NULL || xt_t->parse != NULL) && (c >= (int) xt_t->option_offset && @@ -1573,27 +1721,58 @@ static int iptables_command(int argc, char *argv[]) break; } +#else + 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)) { + + for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; + tmp_xt_rm = tmp_xt_rm->next) { + xt_m_t = tmp_xt_rm->match; + if (tmp_xt_rm->completed || + xt_m_t->parse == NULL) + continue; + + if (xt_m->parse(c - xt_m->option_offset, + argv, invert, &xt_m->mflags, + NULL, &xt_m->m)) + break; + } + } +#endif break; } invert = FALSE; } +#if XTABLES_VERSION_CODE > 5 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next) xtables_option_mfcall(tmp_xt_rm->match); if (xt_t != NULL) xtables_option_tfcall(xt_t); +#else + for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; + tmp_xt_rm = tmp_xt_rm->next) + if (tmp_xt_rm->match->final_check != NULL) + tmp_xt_rm->match->final_check( + tmp_xt_rm->match->mflags); - if (table_name == NULL) - table_name = "filter"; + if (xt_t != NULL && xt_t->final_check != NULL) + xt_t->final_check(xt_t->tflags); +#endif - table = iptables_init(table_name); if (table == NULL) { - ret = -EINVAL; - goto out; + table_name = "filter"; + + table = iptables_init(table_name); + if (table == NULL) { + ret = -EINVAL; + goto out; + } } if (delete_chain != NULL) { @@ -1632,15 +1811,29 @@ static int iptables_command(int argc, char *argv[]) } if (chain) { + if (policy != NULL) { + printf("Changing policy of %s to %s\n", chain, policy); + + iptables_change_policy(table, chain, policy); + + goto out; + } + if (xt_t == NULL) goto out; + if (compare == TRUE) { + ret = iptables_compare_rule(table, &ip, chain, + target_name, xt_t, xt_m, xt_rm); + goto out; + } + if (delete == TRUE) { DBG("Deleting %s to %s (match %s)\n", target_name, chain, match_name); ret = iptables_delete_rule(table, &ip, chain, - target_name, xt_t, match_name, xt_m); + target_name, xt_t, xt_m, xt_rm); goto out; } @@ -1650,7 +1843,7 @@ static int iptables_command(int argc, char *argv[]) target_name, chain, match_name); ret = iptables_insert_rule(table, &ip, chain, - target_name, xt_t, match_name, xt_m); + target_name, xt_t, xt_rm); goto out; } else { @@ -1658,7 +1851,7 @@ static int iptables_command(int argc, char *argv[]) target_name, chain, match_name); ret = iptables_append_rule(table, &ip, chain, - target_name, xt_t, match_name, xt_m); + target_name, xt_t, xt_rm); goto out; }