Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 255d0dc3 authored by Eric Dumazet's avatar Eric Dumazet Committed by Pablo Neira Ayuso
Browse files

netfilter: x_table: speedup compat operations



One iptables invocation with 135000 rules takes 35 seconds of cpu time
on a recent server, using a 32bit distro and a 64bit kernel.

We eventually trigger NMI/RCU watchdog.

INFO: rcu_sched_state detected stall on CPU 3 (t=6000 jiffies)

COMPAT mode has quadratic behavior and consume 16 bytes of memory per
rule.

Switch the xt_compat algos to use an array instead of list, and use a
binary search to locate an offset in the sorted array.

This halves memory need (8 bytes per rule), and removes quadratic
behavior [ O(N*N) -> O(N*log2(N)) ]

Time of iptables goes from 35 s to 150 ms.

Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent b017900a
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -611,8 +611,9 @@ struct _compat_xt_align {
extern void xt_compat_lock(u_int8_t af);
extern void xt_compat_lock(u_int8_t af);
extern void xt_compat_unlock(u_int8_t af);
extern void xt_compat_unlock(u_int8_t af);


extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, short delta);
extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta);
extern void xt_compat_flush_offsets(u_int8_t af);
extern void xt_compat_flush_offsets(u_int8_t af);
extern void xt_compat_init_offsets(u_int8_t af, unsigned int number);
extern int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
extern int xt_compat_calc_jump(u_int8_t af, unsigned int offset);


extern int xt_compat_match_offset(const struct xt_match *match);
extern int xt_compat_match_offset(const struct xt_match *match);
+1 −0
Original line number Original line Diff line number Diff line
@@ -1764,6 +1764,7 @@ static int compat_table_info(const struct ebt_table_info *info,


	newinfo->entries_size = size;
	newinfo->entries_size = size;


	xt_compat_init_offsets(AF_INET, info->nentries);
	return EBT_ENTRY_ITERATE(entries, size, compat_calc_entry, info,
	return EBT_ENTRY_ITERATE(entries, size, compat_calc_entry, info,
							entries, newinfo);
							entries, newinfo);
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -883,6 +883,7 @@ static int compat_table_info(const struct xt_table_info *info,
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	newinfo->initial_entries = 0;
	newinfo->initial_entries = 0;
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	xt_compat_init_offsets(NFPROTO_ARP, info->number);
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		if (ret != 0)
		if (ret != 0)
@@ -1350,6 +1351,7 @@ static int translate_compat_table(const char *name,
	duprintf("translate_compat_table: size %u\n", info->size);
	duprintf("translate_compat_table: size %u\n", info->size);
	j = 0;
	j = 0;
	xt_compat_lock(NFPROTO_ARP);
	xt_compat_lock(NFPROTO_ARP);
	xt_compat_init_offsets(NFPROTO_ARP, number);
	/* Walk through entries, checking offsets. */
	/* Walk through entries, checking offsets. */
	xt_entry_foreach(iter0, entry0, total_size) {
	xt_entry_foreach(iter0, entry0, total_size) {
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+2 −0
Original line number Original line Diff line number Diff line
@@ -1080,6 +1080,7 @@ static int compat_table_info(const struct xt_table_info *info,
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	newinfo->initial_entries = 0;
	newinfo->initial_entries = 0;
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	xt_compat_init_offsets(AF_INET, info->number);
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		if (ret != 0)
		if (ret != 0)
@@ -1681,6 +1682,7 @@ translate_compat_table(struct net *net,
	duprintf("translate_compat_table: size %u\n", info->size);
	duprintf("translate_compat_table: size %u\n", info->size);
	j = 0;
	j = 0;
	xt_compat_lock(AF_INET);
	xt_compat_lock(AF_INET);
	xt_compat_init_offsets(AF_INET, number);
	/* Walk through entries, checking offsets. */
	/* Walk through entries, checking offsets. */
	xt_entry_foreach(iter0, entry0, total_size) {
	xt_entry_foreach(iter0, entry0, total_size) {
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+2 −0
Original line number Original line Diff line number Diff line
@@ -1093,6 +1093,7 @@ static int compat_table_info(const struct xt_table_info *info,
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
	newinfo->initial_entries = 0;
	newinfo->initial_entries = 0;
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	loc_cpu_entry = info->entries[raw_smp_processor_id()];
	xt_compat_init_offsets(AF_INET6, info->number);
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
		if (ret != 0)
		if (ret != 0)
@@ -1696,6 +1697,7 @@ translate_compat_table(struct net *net,
	duprintf("translate_compat_table: size %u\n", info->size);
	duprintf("translate_compat_table: size %u\n", info->size);
	j = 0;
	j = 0;
	xt_compat_lock(AF_INET6);
	xt_compat_lock(AF_INET6);
	xt_compat_init_offsets(AF_INET6, number);
	/* Walk through entries, checking offsets. */
	/* Walk through entries, checking offsets. */
	xt_entry_foreach(iter0, entry0, total_size) {
	xt_entry_foreach(iter0, entry0, total_size) {
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
Loading