[PATCH v2] netfilter: ipset: Fix calling ip_set() macro at dumping
The ip_set() macro is called when either ip_set_ref_lock held only or no lock/nfnl mutex is held at dumping. Take this into account properly. Also, use Pablo's suggestion to use rcu_dereference_raw(), the ref_netlink protects the set. Signed-off-by: Jozsef Kadlecsik --- net/netfilter/ipset/ip_set_core.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 68db946..1577f2f 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -55,11 +55,15 @@ MODULE_AUTHOR("Jozsef Kadlecsik "); MODULE_DESCRIPTION("core IP set support"); MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); -/* When the nfnl mutex is held: */ +/* When the nfnl mutex or ip_set_ref_lock is held: */ #define ip_set_dereference(p) \ - rcu_dereference_protected(p, lockdep_nfnl_is_held(NFNL_SUBSYS_IPSET)) + rcu_dereference_protected(p,\ + lockdep_nfnl_is_held(NFNL_SUBSYS_IPSET) || \ + lockdep_is_held(_set_ref_lock)) #define ip_set(inst, id) \ ip_set_dereference((inst)->ip_set_list)[id] +#define ip_set_ref_netlink(inst,id)\ + rcu_dereference_raw((inst)->ip_set_list)[id] /* The set types are implemented in modules and registered set types * can be found in ip_set_type_list. Adding/deleting types is @@ -1251,7 +1255,7 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set(inst, index); + struct ip_set *set = ip_set_ref_netlink(inst, index); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1440,7 +1444,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { - set = ip_set(inst, index); + set = ip_set_ref_netlink(inst, index); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); -- 2.1.4
Re: [PATCH nf 1/2] netfilter: nf_conncount: use spin_lock_bh instead of spin_lock
Thanks to all reviewer! On Tue, 30 Oct 2018 at 08:41, Florian Westphal wrote: > > Pablo Neira Ayuso wrote: > > On Thu, Oct 25, 2018 at 11:56:12PM +0900, Taehee Yoo wrote: > > > conn_free() holds lock with spin_lock(). and it is called by both > > > nf_conncount_lookup() and nf_conncount_gc_list(). > > > nf_conncount_lookup() is bottom-half context and nf_conncount_gc_list() > > > is process context. so that spin_lock() is not safe. > > > Hence conn_free() should use spin_lock_bh() instead of spin_lock(). > > > > > > test commands: > > >%nft add table ip filter > > >%nft add chain ip filter input { type filter hook input priority 0\; } > > >%nft add rule filter input meter test { ip saddr ct count over 2 } \ > > >counter > > > > > > splat looks like: > > > [ 461.996507] > > > [ 461.998999] WARNING: inconsistent lock state > > > [ 461.998999] 4.19.0-rc6+ #22 Not tainted > > > [ 461.998999] > > > [ 461.998999] inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. > > > [ 461.998999] kworker/0:2/134 [HC0[0]:SC0[0]:HE1:SE1] takes: > > > [ 461.998999] a71a559a (&(>list_lock)->rlock){+.?.}, at: > > > conn_free+0x69/0x2b0 [nf_conncount] > > > [ 461.998999] {IN-SOFTIRQ-W} state was registered at: > > > [ 461.998999] _raw_spin_lock+0x30/0x70 > > > [ 461.998999] nf_conncount_add+0x28a/0x520 [nf_conncount] > > > [ 461.998999] nft_connlimit_eval+0x401/0x580 [nft_connlimit] > > > [ 461.998999] nft_dynset_eval+0x32b/0x590 [nf_tables] > > > [ 461.998999] nft_do_chain+0x497/0x1430 [nf_tables] > > > [ 461.998999] nft_do_chain_ipv4+0x255/0x330 [nf_tables] > > > [ 461.998999] nf_hook_slow+0xb1/0x160 > > > [ ... ] > > > [ 461.998999] other info that might help us debug this: > > > [ 461.998999] Possible unsafe locking scenario: > > > [ 461.998999] > > > [ 461.998999]CPU0 > > > [ 461.998999] > > > [ 461.998999] lock(&(>list_lock)->rlock); > > > [ 461.998999] > > > [ 461.998999] lock(&(>list_lock)->rlock); > > > [ 461.998999] > > > [ 461.998999] *** DEADLOCK *** > > > [ 461.998999] > > > [ ... ] > > > > nf_conncount_add() also holds spin_lock while allocate from there is > > GFP_ATOMIC given this is called from packet path. > > Good catch, yes, this needs spin_lock_bh variant too. > > > tree_nodes_free() is also called from user context without _bh > > disabled. > > This one is fine, both call sites hold spin_lock_bh(_conncount_locks[x]). I will test then I will send v2 patch! Thanks!
Re: [PATCH nf] netfilter: xt_RATEEST: remove netns exit routine
On Tue, 30 Oct 2018 at 08:00, Pablo Neira Ayuso wrote: > Hi Pablo, Thank you for review! > On Fri, Oct 19, 2018 at 12:27:57AM +0900, Taehee Yoo wrote: > > xt_rateest_net_exit() was added to check whether rules are flushed > > successfully. but ->net_exit() callback is called earlier than > > ->destroy() callback. > > So that ->net_exit() callback can't check that. > > > > test commands: > >%ip netns add vm1 > >%ip netns exec vm1 iptables -t mangle -I PREROUTING -p udp \ > > --dport -j RATEEST --rateest-name ap \ > > --rateest-interval 250ms --rateest-ewma 0.5s > >%ip netns del vm1 > > Hm, I cannot reproduce this here. > > I can see iptables-tests.py with -N fails to load entries: > > # ip netns exec test xtables-legacy-multi iptables -A INPUT -m rateest > --rateest RE1 --rateest-lt --rateest-bps 8bit > iptables: No chain/target/match by that name. > > but not this warning, probably I'm missing instrumention, something > not enabled here. > I think you need RE1 RATEEST entry because rateest match needs RATEEST entry. So that below command is needed. %ip netns exec test xtables-legacy-multi iptables -t mangle -I PREROUTING -p udp \ --dport -j RATEEST --rateest-name RE1 --rateest-interval 250ms --rateest-ewma 0.5s RE1 entry is created by above command. Then, your command would not be failed. Thanks! > > splat looks like: > > [ 668.813518] WARNING: CPU: 0 PID: 87 at net/netfilter/xt_RATEEST.c:210 > > xt_rateest_net_exit+0x210/0x340 [xt_RATEEST] > > [ 668.813518] CPU: 0 PID: 87 Comm: kworker/u4:2 Not tainted 4.19.0-rc7+ #21 > > [ 668.813518] Workqueue: netns cleanup_net > > [ 668.813518] RIP: 0010:xt_rateest_net_exit+0x210/0x340 [xt_RATEEST] > > [ 668.813518] Code: 00 48 8b 85 30 ff ff ff 4c 8b 23 80 38 00 0f 85 24 01 > > 00 00 48 8b 85 30 ff ff ff 4d 85 e4 4c 89 a5 58 ff ff ff c6 00 f8 74 b2 > > <0f> 0b 48 83 c3 08 4c 39 f3 75 b0 48 b8 00 00 00 00 00 fc ff df 49 > > [ 668.813518] RSP: 0018:8801156c73f8 EFLAGS: 00010282 > > [ 668.813518] RAX: ed0022ad8e85 RBX: 880118928e98 RCX: > > 5db8012a > > [ 668.813518] RDX: 8801156c7428 RSI: cb1d185f RDI: > > 880115663b74 > > [ 668.813518] RBP: 8801156c74d0 R08: 8801156633c0 R09: > > 1100236440be > > [ 668.813518] R10: 0001 R11: ed002367d852 R12: > > 880115142b08 > > [ 668.813518] R13: 110022ad8e81 R14: 880118928ea8 R15: > > dc00 > > [ 668.813518] FS: () GS:88011b20() > > knlGS: > > [ 668.813518] CS: 0010 DS: ES: CR0: 80050033 > > [ 668.813518] CR2: 563aa69f4f28 CR3: 000105a16000 CR4: > > 001006f0 > > [ 668.813518] Call Trace: > > [ 668.813518] ? unregister_netdevice_many+0xe0/0xe0 > > [ 668.813518] ? xt_rateest_net_init+0x2c0/0x2c0 [xt_RATEEST] > > [ 668.813518] ? default_device_exit+0x1ca/0x270 > > [ 668.813518] ? remove_proc_entry+0x1cd/0x390 > > [ 668.813518] ? dev_change_net_namespace+0xd00/0xd00 > > [ 668.813518] ? __init_waitqueue_head+0x130/0x130 > > [ 668.813518] ops_exit_list.isra.10+0x94/0x140 > > [ 668.813518] cleanup_net+0x45b/0x900 > > [ 668.813518] ? net_drop_ns+0x110/0x110 > > [ 668.813518] ? swapgs_restore_regs_and_return_to_usermode+0x3c/0x80 > > [ 668.813518] ? save_trace+0x300/0x300 > > [ 668.813518] ? lock_acquire+0x196/0x470 > > [ 668.813518] ? lock_acquire+0x196/0x470 > > [ 668.813518] ? process_one_work+0xb60/0x1de0 > > [ 668.813518] ? _raw_spin_unlock_irq+0x29/0x40 > > [ 668.813518] ? _raw_spin_unlock_irq+0x29/0x40 > > [ 668.813518] ? __lock_acquire+0x4500/0x4500 > > [ 668.813518] ? __lock_is_held+0xb4/0x140 > > [ 668.813518] process_one_work+0xc13/0x1de0 > > [ 668.813518] ? pwq_dec_nr_in_flight+0x3c0/0x3c0 > > [ 668.813518] ? set_load_weight+0x270/0x270 > > [ ... ] > > > > Fixes: 3427b2ab63fa ("netfilter: make xt_rateest hash table per net") > > Signed-off-by: Taehee Yoo > > --- > > net/netfilter/xt_RATEEST.c | 10 -- > > 1 file changed, 10 deletions(-) > > > > diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c > > index dec843cadf46..9e05c86ba5c4 100644 > > --- a/net/netfilter/xt_RATEEST.c > > +++ b/net/netfilter/xt_RATEEST.c > > @@ -201,18 +201,8 @@ static __net_init int xt_rateest_net_init(struct net > > *net) > > return 0; > > } > > > > -static void __net_exit xt_rateest_net_exit(struct net *net) > > -{ > > - struct xt_rateest_net *xn = net_generic(net, xt_rateest_id); > > - int i; > > - > > - for (i = 0; i < ARRAY_SIZE(xn->hash); i++) > > - WARN_ON_ONCE(!hlist_empty(>hash[i])); > > -} > > - > > static struct pernet_operations xt_rateest_net_ops = { > > .init = xt_rateest_net_init, > > - .exit = xt_rateest_net_exit, > > .id = _rateest_id, > > .size = sizeof(struct xt_rateest_net), > > }; > > -- > > 2.17.1 > >
Re: [iptables PATCH] xtables: Fix for matching rules with wildcard interfaces
On Tue, Oct 30, 2018 at 06:45:20PM +0100, Phil Sutter wrote: > Hi Pablo, > > On Tue, Oct 30, 2018 at 06:01:19PM +0100, Pablo Neira Ayuso wrote: > > On Tue, Oct 30, 2018 at 05:57:53PM +0100, Phil Sutter wrote: > > > Due to xtables_parse_interface() and parse_ifname() being misaligned > > > regarding interface mask setting, rules containing a wildcard interface > > > added with iptables-nft could neither be checked nor deleted. > > > > > > Signed-off-by: Phil Sutter > > > --- > > > iptables/nft-shared.c| 2 +- > > > .../shell/testcases/nft-only/0004wildcard-iface_0| 12 > > > 2 files changed, 13 insertions(+), 1 deletion(-) > > > create mode 100755 > > > iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > > > > > diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c > > > index 492e4ec124a79..7b8ca5e4becaf 100644 > > > --- a/iptables/nft-shared.c > > > +++ b/iptables/nft-shared.c > > > @@ -249,7 +249,7 @@ static void parse_ifname(const char *name, unsigned > > > int len, char *dst, unsigned > > > return; > > > dst[len++] = 0; > > > if (mask) > > > - memset(mask, 0xff, len + 1); > > > + memset(mask, 0xff, len - 2); > > > } > > > > > > int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface, > > > diff --git a/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > > b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > > new file mode 100755 > > > index 0..b7c398ecbb29c > > > --- /dev/null > > > +++ b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > > @@ -0,0 +1,12 @@ > > > +#!/bin/bash > > > + > > > +# Make sure rules containing wildcard interfaces are found again. > > > + > > > +set -e > > > + > > > +[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit > > > 0; } > > > + > > > +lname='alongifacename+' > > > +$XT_MULTI iptables -A FORWARD -i eth+ -o $lname -j ACCEPT > > > +$XT_MULTI iptables -C FORWARD -i eth+ -o $lname -j ACCEPT > > > +$XT_MULTI iptables -D FORWARD -i eth+ -o $lname -j ACCEPT > > > > Suggestion: Probably we can catch this through tests/py/, just a > > suggestion. -C and -D operations, very much look the same from > > interface perspective, so just checking for -I then -D should be fine > > as tests/py. > > Yes, testing for -C and -D is kind of redundant, though shouldn't matter > much as it's just one more command. What do you mean with tests/py? > There's no such thing in iptables repository? :) Refering to iptables-tests.py, eg. extensions/libxt_mark.t, we can add a iptables.t file to test for built-in selectors, eg. -s, -d, -i, -o and so on. Also update iptables-tests.py to check for iptables.t. If you don't like iptables/iptables.t, just pick a better name / location in the tree :)
Re: [iptables PATCH] xtables: Fix for matching rules with wildcard interfaces
Hi Pablo, On Tue, Oct 30, 2018 at 06:01:19PM +0100, Pablo Neira Ayuso wrote: > On Tue, Oct 30, 2018 at 05:57:53PM +0100, Phil Sutter wrote: > > Due to xtables_parse_interface() and parse_ifname() being misaligned > > regarding interface mask setting, rules containing a wildcard interface > > added with iptables-nft could neither be checked nor deleted. > > > > Signed-off-by: Phil Sutter > > --- > > iptables/nft-shared.c| 2 +- > > .../shell/testcases/nft-only/0004wildcard-iface_0| 12 > > 2 files changed, 13 insertions(+), 1 deletion(-) > > create mode 100755 > > iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > > > diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c > > index 492e4ec124a79..7b8ca5e4becaf 100644 > > --- a/iptables/nft-shared.c > > +++ b/iptables/nft-shared.c > > @@ -249,7 +249,7 @@ static void parse_ifname(const char *name, unsigned int > > len, char *dst, unsigned > > return; > > dst[len++] = 0; > > if (mask) > > - memset(mask, 0xff, len + 1); > > + memset(mask, 0xff, len - 2); > > } > > > > int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface, > > diff --git a/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > new file mode 100755 > > index 0..b7c398ecbb29c > > --- /dev/null > > +++ b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > @@ -0,0 +1,12 @@ > > +#!/bin/bash > > + > > +# Make sure rules containing wildcard interfaces are found again. > > + > > +set -e > > + > > +[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; > > } > > + > > +lname='alongifacename+' > > +$XT_MULTI iptables -A FORWARD -i eth+ -o $lname -j ACCEPT > > +$XT_MULTI iptables -C FORWARD -i eth+ -o $lname -j ACCEPT > > +$XT_MULTI iptables -D FORWARD -i eth+ -o $lname -j ACCEPT > > Suggestion: Probably we can catch this through tests/py/, just a > suggestion. -C and -D operations, very much look the same from > interface perspective, so just checking for -I then -D should be fine > as tests/py. Yes, testing for -C and -D is kind of redundant, though shouldn't matter much as it's just one more command. What do you mean with tests/py? There's no such thing in iptables repository? :) Thanks, Phil
Re: [iptables PATCH] xtables: Fix for matching rules with wildcard interfaces
On Tue, Oct 30, 2018 at 05:57:53PM +0100, Phil Sutter wrote: > Due to xtables_parse_interface() and parse_ifname() being misaligned > regarding interface mask setting, rules containing a wildcard interface > added with iptables-nft could neither be checked nor deleted. > > Signed-off-by: Phil Sutter > --- > iptables/nft-shared.c| 2 +- > .../shell/testcases/nft-only/0004wildcard-iface_0| 12 > 2 files changed, 13 insertions(+), 1 deletion(-) > create mode 100755 > iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > > diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c > index 492e4ec124a79..7b8ca5e4becaf 100644 > --- a/iptables/nft-shared.c > +++ b/iptables/nft-shared.c > @@ -249,7 +249,7 @@ static void parse_ifname(const char *name, unsigned int > len, char *dst, unsigned > return; > dst[len++] = 0; > if (mask) > - memset(mask, 0xff, len + 1); > + memset(mask, 0xff, len - 2); > } > > int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface, > diff --git a/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > new file mode 100755 > index 0..b7c398ecbb29c > --- /dev/null > +++ b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 > @@ -0,0 +1,12 @@ > +#!/bin/bash > + > +# Make sure rules containing wildcard interfaces are found again. > + > +set -e > + > +[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; } > + > +lname='alongifacename+' > +$XT_MULTI iptables -A FORWARD -i eth+ -o $lname -j ACCEPT > +$XT_MULTI iptables -C FORWARD -i eth+ -o $lname -j ACCEPT > +$XT_MULTI iptables -D FORWARD -i eth+ -o $lname -j ACCEPT Suggestion: Probably we can catch this through tests/py/, just a suggestion. -C and -D operations, very much look the same from interface perspective, so just checking for -I then -D should be fine as tests/py.
[iptables PATCH] xtables: Fix for matching rules with wildcard interfaces
Due to xtables_parse_interface() and parse_ifname() being misaligned regarding interface mask setting, rules containing a wildcard interface added with iptables-nft could neither be checked nor deleted. Signed-off-by: Phil Sutter --- iptables/nft-shared.c| 2 +- .../shell/testcases/nft-only/0004wildcard-iface_0| 12 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100755 iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 diff --git a/iptables/nft-shared.c b/iptables/nft-shared.c index 492e4ec124a79..7b8ca5e4becaf 100644 --- a/iptables/nft-shared.c +++ b/iptables/nft-shared.c @@ -249,7 +249,7 @@ static void parse_ifname(const char *name, unsigned int len, char *dst, unsigned return; dst[len++] = 0; if (mask) - memset(mask, 0xff, len + 1); + memset(mask, 0xff, len - 2); } int parse_meta(struct nftnl_expr *e, uint8_t key, char *iniface, diff --git a/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 new file mode 100755 index 0..b7c398ecbb29c --- /dev/null +++ b/iptables/tests/shell/testcases/nft-only/0004wildcard-iface_0 @@ -0,0 +1,12 @@ +#!/bin/bash + +# Make sure rules containing wildcard interfaces are found again. + +set -e + +[[ $XT_MULTI == */xtables-nft-multi ]] || { echo "skip $XT_MULTI"; exit 0; } + +lname='alongifacename+' +$XT_MULTI iptables -A FORWARD -i eth+ -o $lname -j ACCEPT +$XT_MULTI iptables -C FORWARD -i eth+ -o $lname -j ACCEPT +$XT_MULTI iptables -D FORWARD -i eth+ -o $lname -j ACCEPT -- 2.19.0
Re: [PATCH] ulogd2: fix build with musl libc
On Mon, Oct 29, 2018 at 05:52:51PM -0700, Cameron Norman wrote: > Yes, we can do that. Would you send me a patch? Please, add your Signed-off-by: tag. Or I can just mangle this patch here if you prefer this. Thanks. > On Mon, Oct 29, 2018 at 2:11 PM Pablo Neira Ayuso wrote: > > > > Hi, > > > > On Sat, Oct 27, 2018 at 01:05:45PM -0700, Cameron Norman wrote: > > > The attached patch fixes building ulogd2 with musl libc. It is being > > > used on Void Linux right now. > > > > > > https://bugzilla.netfilter.org/show_bug.cgi?id=1278 > > > > > > Thanks, > > > -- > > > Cameron Nemo > > > > > diff --git a/src/ulogd.c b/src/ulogd.c > > > index b8bc57c..eb119f9 100644 > > > --- a/src/ulogd.c > > > +++ b/src/ulogd.c > > > @@ -65,6 +65,7 @@ > > > #include > > > #include > > > #include > > > +#include > > > > musl has limits.h, couldn't we just do > > > > #include > > > > instead? > > > > > #include > > > #include > > > #ifdef DEBUG > >
Re: [PATCH 3/3 nft,v2] expression: always print range expression numerically
On Mon, Oct 29, 2018 at 09:58:00PM +0100, Pablo Neira Ayuso wrote: > Otherwise we end up displaying things that we cannot parse as input. > Moreover, in a range, it's relevant to the user the values that are > enclosed in the range, so let's print this numerically. > > Fixes: baa4e0e3fa5f ("src: add NFT_CTX_OUTPUT_NUMERIC_PROTO") > Reported-by: Phil Sutter > Signed-off-by: Pablo Neira Ayuso Acked-by: Phil Sutter Thanks a lot!
Re: [PATCH 1/3 nft,v3] src: get rid of nft_ctx_output_{get,set}_numeric()
On Mon, Oct 29, 2018 at 09:57:58PM +0100, Pablo Neira Ayuso wrote: > This patch adds NFT_CTX_OUTPUT_NUMERIC_SYMBOL, which replaces the last > client of the numeric level approach. > > This patch updates `-n' option semantics to display all output > numerically. > > Note that monitor code was still using the -n option to skip printing > the process name, this patch updates that path too to print it > inconditionally to simplify things. > > Given the numeric levels have no more clients after this patch, remove > that code. > > Update several tests/shell not to use -nn. > > This patch adds NFT_CTX_OUTPUT_NUMERIC_ALL which enables all flags to > provide a fully numerical output. > > Signed-off-by: Pablo Neira Ayuso Acked-by: Phil Sutter