#include <sys/errno.h>
#include <sys/socket.h>
#include <xtables.h>
+#include <inttypes.h>
#include <linux/netfilter_ipv4/ip_tables.h>
struct connman_iptables_entry {
int offset;
int builtin;
+ int counter_idx;
struct ipt_entry *entry;
};
{
iterate_entries_cb_t cb = user_data;
- DBG("entry %p hook %d offset %d size %d", entry, hook,
- offset, entry->next_offset);
+ DBG("entry %p hook %u offset %u size %u packets %"PRIu64" bytes %"PRIu64,
+ entry, hook, offset, (unsigned int) entry->next_offset,
+ (uint64_t) entry->counters.pcnt, (uint64_t) entry->counters.bcnt);
return cb(entry, builtin, hook, size, offset, NULL);
}
static int iptables_add_entry(struct connman_iptables *table,
struct ipt_entry *entry, GList *before,
- int builtin)
+ int builtin, int counter_idx)
{
struct connman_iptables_entry *e, *entry_before;
e->entry = entry;
e->builtin = builtin;
+ e->counter_idx = counter_idx;
table->entries = g_list_insert_before(table->entries, before, e);
table->num_entries++;
error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
g_stpcpy(error->error, name);
- if (iptables_add_entry(table, entry_head, last, -1) < 0)
+ if (iptables_add_entry(table, entry_head, last, -1, -1) < 0)
goto err_head;
/* tail entry */
ALIGN(sizeof(struct ipt_standard_target));
standard->verdict = XT_RETURN;
- if (iptables_add_entry(table, entry_return, last, -1) < 0)
+ if (iptables_add_entry(table, entry_return, last, -1, -1) < 0)
goto err;
return 0;
if (!new_entry)
return -EINVAL;
- ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
+ ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin, -1);
if (ret < 0)
g_free(new_entry);
if (builtin == -1)
chain_head = chain_head->next;
- ret = iptables_add_entry(table, new_entry, chain_head, builtin);
+ ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1);
if (ret < 0)
g_free(new_entry);
target = ipt_get_target(entry->entry);
t = (struct xt_standard_target *)target;
+ if (t->verdict != verdict)
+ entry->counter_idx = -1;
t->verdict = verdict;
return 0;
return 0;
}
+static int iptables_add_counters(struct connman_iptables *table,
+ struct xt_counters_info *c)
+{
+ int err;
+
+ err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS, c,
+ sizeof(*c) + sizeof(struct xt_counters) * c->num_counters);
+ if (err < 0)
+ return -errno;
+
+ return 0;
+}
+
static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
size_t size, unsigned offset, void *user_data)
{
memcpy(new_entry, entry, entry->next_offset);
- return iptables_add_entry(table, new_entry, NULL, builtin);
+ return iptables_add_entry(table, new_entry, NULL, builtin,
+ table->num_entries);
}
static void table_cleanup(struct connman_iptables *table)
.option_offset = 0,
.opts = iptables_opts,
.orig_opts = iptables_opts,
+#if XTABLES_VERSION_CODE > 10
+ .compat_rev = xtables_compatible_revision,
+#endif
};
static struct xtables_target *prepare_target(struct connman_iptables *table,
struct xtables_target *xt_t;
GList *xt_m;
struct xtables_rule_match *xt_rm;
+ int proto;
};
static int prepare_getopt_args(const char *str, struct parse_context *ctx)
{
struct xtables_match *m;
struct xtables_rule_match *rm;
+ struct ipt_entry fw;
+
+ memset(&fw, 0, sizeof(fw));
+
+ /* The SNAT parser wants to know the protocol. */
+ if (ctx->proto == 0)
+ ctx->proto = IPPROTO_IP;
+ fw.ip.proto = ctx->proto;
for (rm = ctx->xt_rm; rm; rm = rm->next) {
if (rm->completed != 0)
+ XT_OPTION_OFFSET_SCALE)
continue;
- xtables_option_mpcall(c, ctx->argv, invert, m, NULL);
+ xtables_option_mpcall(c, ctx->argv, invert, m, &fw);
}
if (!ctx->xt_t)
+ XT_OPTION_OFFSET_SCALE)
return 0;
- xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, NULL);
+ xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw);
return 0;
}
ctx->xt_m = g_list_append(ctx->xt_m, xt_m);
break;
+ case 'p':
+ ctx->proto = xtables_parse_protocol(optarg);
+ break;
case 'j':
/* Target */
ctx->xt_t = prepare_target(table, optarg);
struct connman_iptables *table;
struct ipt_replace *repl;
int err;
+ struct xt_counters_info *counters;
+ struct connman_iptables_entry *e;
+ GList *list;
+ unsigned int cnt;
DBG("%s", table_name);
return -EINVAL;
repl = iptables_blob(table);
+ if (!repl)
+ return -ENOMEM;
if (debug_enabled)
dump_ipt_replace(repl);
err = iptables_replace(table, repl);
- g_free(repl->counters);
- g_free(repl);
+ if (err < 0)
+ goto out_free;
+
+ counters = g_try_malloc0(sizeof(*counters) +
+ sizeof(struct xt_counters) * table->num_entries);
+ if (!counters) {
+ err = -ENOMEM;
+ goto out_hash_remove;
+ }
+ g_stpcpy(counters->name, table->info->name);
+ counters->num_counters = table->num_entries;
+ for (list = table->entries, cnt = 0; list; list = list->next, cnt++) {
+ e = list->data;
+ if (e->counter_idx >= 0)
+ counters->counters[cnt] = repl->counters[e->counter_idx];
+ }
+ err = iptables_add_counters(table, counters);
+ g_free(counters);
if (err < 0)
- return err;
+ goto out_hash_remove;
- g_hash_table_remove(table_hash, table_name);
+ err = 0;
- return 0;
+out_hash_remove:
+ g_hash_table_remove(table_hash, table_name);
+out_free:
+ g_free(repl->counters);
+ g_free(repl);
+ return err;
}
static void remove_table(gpointer user_data)
struct connman_iptables *table;
table = get_table(table_name);
- if (!table)
+ if (!table) {
+ g_free(cbd);
return -EINVAL;
+ }
iterate_entries(table->blob_entries->entrytable,
table->info->valid_hooks,