Commit 8b5b8c29 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf into netfilter

Pablo Neira Ayuso says:

====================
The following patchset contains late netfilter fixes for your net
tree, they are:

* Don't drop segmented TCP packets in the SIP helper, we've got reports
  from users that this was breaking communications when the SIP phone
  messages are larger than the MTU, from Patrick McHardy.

* Fix refcount leak in the ipset list set, from Jozsef Kadlecsik.

* On hash set resizing, the nomatch flag was lost, thus entirely inverting
  the logic of the set matching, from Jozsef Kadlecsik.

* Fix crash on NAT modules removal. Timer expiration may race with the
  module cleanup exit path while deleting conntracks, from Florian
  Westphal.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 50bceae9 c2d421e1
...@@ -291,6 +291,7 @@ ip_set_hash_destroy(struct ip_set *set) ...@@ -291,6 +291,7 @@ ip_set_hash_destroy(struct ip_set *set)
#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist) #define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
#define type_pf_data_next TOKEN(TYPE, PF, _data_next) #define type_pf_data_next TOKEN(TYPE, PF, _data_next)
#define type_pf_data_flags TOKEN(TYPE, PF, _data_flags) #define type_pf_data_flags TOKEN(TYPE, PF, _data_flags)
#define type_pf_data_reset_flags TOKEN(TYPE, PF, _data_reset_flags)
#ifdef IP_SET_HASH_WITH_NETS #ifdef IP_SET_HASH_WITH_NETS
#define type_pf_data_match TOKEN(TYPE, PF, _data_match) #define type_pf_data_match TOKEN(TYPE, PF, _data_match)
#else #else
...@@ -385,9 +386,9 @@ type_pf_resize(struct ip_set *set, bool retried) ...@@ -385,9 +386,9 @@ type_pf_resize(struct ip_set *set, bool retried)
struct ip_set_hash *h = set->data; struct ip_set_hash *h = set->data;
struct htable *t, *orig = h->table; struct htable *t, *orig = h->table;
u8 htable_bits = orig->htable_bits; u8 htable_bits = orig->htable_bits;
const struct type_pf_elem *data; struct type_pf_elem *data;
struct hbucket *n, *m; struct hbucket *n, *m;
u32 i, j; u32 i, j, flags = 0;
int ret; int ret;
retry: retry:
...@@ -412,9 +413,16 @@ type_pf_resize(struct ip_set *set, bool retried) ...@@ -412,9 +413,16 @@ type_pf_resize(struct ip_set *set, bool retried)
n = hbucket(orig, i); n = hbucket(orig, i);
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_data(n, j); data = ahash_data(n, j);
#ifdef IP_SET_HASH_WITH_NETS
flags = 0;
type_pf_data_reset_flags(data, &flags);
#endif
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_add(m, data, AHASH_MAX(h), 0); ret = type_pf_elem_add(m, data, AHASH_MAX(h), flags);
if (ret < 0) { if (ret < 0) {
#ifdef IP_SET_HASH_WITH_NETS
type_pf_data_flags(data, flags);
#endif
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
ahash_destroy(t); ahash_destroy(t);
if (ret == -EAGAIN) if (ret == -EAGAIN)
...@@ -836,9 +844,9 @@ type_pf_tresize(struct ip_set *set, bool retried) ...@@ -836,9 +844,9 @@ type_pf_tresize(struct ip_set *set, bool retried)
struct ip_set_hash *h = set->data; struct ip_set_hash *h = set->data;
struct htable *t, *orig = h->table; struct htable *t, *orig = h->table;
u8 htable_bits = orig->htable_bits; u8 htable_bits = orig->htable_bits;
const struct type_pf_elem *data; struct type_pf_elem *data;
struct hbucket *n, *m; struct hbucket *n, *m;
u32 i, j; u32 i, j, flags = 0;
int ret; int ret;
/* Try to cleanup once */ /* Try to cleanup once */
...@@ -873,10 +881,17 @@ type_pf_tresize(struct ip_set *set, bool retried) ...@@ -873,10 +881,17 @@ type_pf_tresize(struct ip_set *set, bool retried)
n = hbucket(orig, i); n = hbucket(orig, i);
for (j = 0; j < n->pos; j++) { for (j = 0; j < n->pos; j++) {
data = ahash_tdata(n, j); data = ahash_tdata(n, j);
#ifdef IP_SET_HASH_WITH_NETS
flags = 0;
type_pf_data_reset_flags(data, &flags);
#endif
m = hbucket(t, HKEY(data, h->initval, htable_bits)); m = hbucket(t, HKEY(data, h->initval, htable_bits));
ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), 0, ret = type_pf_elem_tadd(m, data, AHASH_MAX(h), flags,
ip_set_timeout_get(type_pf_data_timeout(data))); ip_set_timeout_get(type_pf_data_timeout(data)));
if (ret < 0) { if (ret < 0) {
#ifdef IP_SET_HASH_WITH_NETS
type_pf_data_flags(data, flags);
#endif
read_unlock_bh(&set->lock); read_unlock_bh(&set->lock);
ahash_destroy(t); ahash_destroy(t);
if (ret == -EAGAIN) if (ret == -EAGAIN)
...@@ -1187,6 +1202,7 @@ type_pf_gc_init(struct ip_set *set) ...@@ -1187,6 +1202,7 @@ type_pf_gc_init(struct ip_set *set)
#undef type_pf_data_tlist #undef type_pf_data_tlist
#undef type_pf_data_next #undef type_pf_data_next
#undef type_pf_data_flags #undef type_pf_data_flags
#undef type_pf_data_reset_flags
#undef type_pf_data_match #undef type_pf_data_match
#undef type_pf_elem #undef type_pf_elem
......
...@@ -104,6 +104,15 @@ hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags) ...@@ -104,6 +104,15 @@ hash_ipportnet4_data_flags(struct hash_ipportnet4_elem *dst, u32 flags)
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
} }
static inline void
hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int static inline int
hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem) hash_ipportnet4_data_match(const struct hash_ipportnet4_elem *elem)
{ {
...@@ -414,6 +423,15 @@ hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags) ...@@ -414,6 +423,15 @@ hash_ipportnet6_data_flags(struct hash_ipportnet6_elem *dst, u32 flags)
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
} }
static inline void
hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int static inline int
hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem) hash_ipportnet6_data_match(const struct hash_ipportnet6_elem *elem)
{ {
......
...@@ -87,7 +87,16 @@ hash_net4_data_copy(struct hash_net4_elem *dst, ...@@ -87,7 +87,16 @@ hash_net4_data_copy(struct hash_net4_elem *dst,
static inline void static inline void
hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags) hash_net4_data_flags(struct hash_net4_elem *dst, u32 flags)
{ {
dst->nomatch = flags & IPSET_FLAG_NOMATCH; dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_net4_data_reset_flags(struct hash_net4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
} }
static inline int static inline int
...@@ -308,7 +317,16 @@ hash_net6_data_copy(struct hash_net6_elem *dst, ...@@ -308,7 +317,16 @@ hash_net6_data_copy(struct hash_net6_elem *dst,
static inline void static inline void
hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags) hash_net6_data_flags(struct hash_net6_elem *dst, u32 flags)
{ {
dst->nomatch = flags & IPSET_FLAG_NOMATCH; dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_net6_data_reset_flags(struct hash_net6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
} }
static inline int static inline int
......
...@@ -198,7 +198,16 @@ hash_netiface4_data_copy(struct hash_netiface4_elem *dst, ...@@ -198,7 +198,16 @@ hash_netiface4_data_copy(struct hash_netiface4_elem *dst,
static inline void static inline void
hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags) hash_netiface4_data_flags(struct hash_netiface4_elem *dst, u32 flags)
{ {
dst->nomatch = flags & IPSET_FLAG_NOMATCH; dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
}
static inline void
hash_netiface4_data_reset_flags(struct hash_netiface4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
} }
static inline int static inline int
...@@ -494,7 +503,7 @@ hash_netiface6_data_copy(struct hash_netiface6_elem *dst, ...@@ -494,7 +503,7 @@ hash_netiface6_data_copy(struct hash_netiface6_elem *dst,
static inline void static inline void
hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags) hash_netiface6_data_flags(struct hash_netiface6_elem *dst, u32 flags)
{ {
dst->nomatch = flags & IPSET_FLAG_NOMATCH; dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
} }
static inline int static inline int
...@@ -503,6 +512,15 @@ hash_netiface6_data_match(const struct hash_netiface6_elem *elem) ...@@ -503,6 +512,15 @@ hash_netiface6_data_match(const struct hash_netiface6_elem *elem)
return elem->nomatch ? -ENOTEMPTY : 1; return elem->nomatch ? -ENOTEMPTY : 1;
} }
static inline void
hash_netiface6_data_reset_flags(struct hash_netiface6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline void static inline void
hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem) hash_netiface6_data_zero_out(struct hash_netiface6_elem *elem)
{ {
......
...@@ -104,6 +104,15 @@ hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags) ...@@ -104,6 +104,15 @@ hash_netport4_data_flags(struct hash_netport4_elem *dst, u32 flags)
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
} }
static inline void
hash_netport4_data_reset_flags(struct hash_netport4_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int static inline int
hash_netport4_data_match(const struct hash_netport4_elem *elem) hash_netport4_data_match(const struct hash_netport4_elem *elem)
{ {
...@@ -375,6 +384,15 @@ hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags) ...@@ -375,6 +384,15 @@ hash_netport6_data_flags(struct hash_netport6_elem *dst, u32 flags)
dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH); dst->nomatch = !!(flags & IPSET_FLAG_NOMATCH);
} }
static inline void
hash_netport6_data_reset_flags(struct hash_netport6_elem *dst, u32 *flags)
{
if (dst->nomatch) {
*flags = IPSET_FLAG_NOMATCH;
dst->nomatch = 0;
}
}
static inline int static inline int
hash_netport6_data_match(const struct hash_netport6_elem *elem) hash_netport6_data_match(const struct hash_netport6_elem *elem)
{ {
......
...@@ -174,9 +174,13 @@ list_set_add(struct list_set *map, u32 i, ip_set_id_t id, ...@@ -174,9 +174,13 @@ list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
{ {
const struct set_elem *e = list_set_elem(map, i); const struct set_elem *e = list_set_elem(map, i);
if (i == map->size - 1 && e->id != IPSET_INVALID_ID) if (e->id != IPSET_INVALID_ID) {
/* Last element replaced: e.g. add new,before,last */ const struct set_elem *x = list_set_elem(map, map->size - 1);
ip_set_put_byindex(e->id);
/* Last element replaced or pushed off */
if (x->id != IPSET_INVALID_ID)
ip_set_put_byindex(x->id);
}
if (with_timeout(map->timeout)) if (with_timeout(map->timeout))
list_elem_tadd(map, i, id, ip_set_timeout_set(timeout)); list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
else else
......
...@@ -1593,10 +1593,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, ...@@ -1593,10 +1593,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
end += strlen("\r\n\r\n") + clen; end += strlen("\r\n\r\n") + clen;
msglen = origlen = end - dptr; msglen = origlen = end - dptr;
if (msglen > datalen) { if (msglen > datalen)
nf_ct_helper_log(skb, ct, "incomplete/bad SIP message"); return NF_ACCEPT;
return NF_DROP;
}
ret = process_sip_msg(skb, ct, protoff, dataoff, ret = process_sip_msg(skb, ct, protoff, dataoff,
&dptr, &msglen); &dptr, &msglen);
......
...@@ -467,33 +467,22 @@ EXPORT_SYMBOL_GPL(nf_nat_packet); ...@@ -467,33 +467,22 @@ EXPORT_SYMBOL_GPL(nf_nat_packet);
struct nf_nat_proto_clean { struct nf_nat_proto_clean {
u8 l3proto; u8 l3proto;
u8 l4proto; u8 l4proto;
bool hash;
}; };
/* Clear NAT section of all conntracks, in case we're loaded again. */ /* kill conntracks with affected NAT section */
static int nf_nat_proto_clean(struct nf_conn *i, void *data) static int nf_nat_proto_remove(struct nf_conn *i, void *data)
{ {
const struct nf_nat_proto_clean *clean = data; const struct nf_nat_proto_clean *clean = data;
struct nf_conn_nat *nat = nfct_nat(i); struct nf_conn_nat *nat = nfct_nat(i);
if (!nat) if (!nat)
return 0; return 0;
if (!(i->status & IPS_SRC_NAT_DONE))
return 0;
if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) || if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) ||
(clean->l4proto && nf_ct_protonum(i) != clean->l4proto)) (clean->l4proto && nf_ct_protonum(i) != clean->l4proto))
return 0; return 0;
if (clean->hash) { return i->status & IPS_NAT_MASK ? 1 : 0;
spin_lock_bh(&nf_nat_lock);
hlist_del_rcu(&nat->bysource);
spin_unlock_bh(&nf_nat_lock);
} else {
memset(nat, 0, sizeof(*nat));
i->status &= ~(IPS_NAT_MASK | IPS_NAT_DONE_MASK |
IPS_SEQ_ADJUST);
}
return 0;
} }
static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto) static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
...@@ -505,16 +494,8 @@ static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto) ...@@ -505,16 +494,8 @@ static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
struct net *net; struct net *net;
rtnl_lock(); rtnl_lock();
/* Step 1 - remove from bysource hash */
clean.hash = true;
for_each_net(net) for_each_net(net)
nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean);
synchronize_rcu();
/* Step 2 - clean NAT section */
clean.hash = false;
for_each_net(net)
nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -526,16 +507,9 @@ static void nf_nat_l3proto_clean(u8 l3proto) ...@@ -526,16 +507,9 @@ static void nf_nat_l3proto_clean(u8 l3proto)
struct net *net; struct net *net;
rtnl_lock(); rtnl_lock();
/* Step 1 - remove from bysource hash */
clean.hash = true;
for_each_net(net)
nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean);
synchronize_rcu();
/* Step 2 - clean NAT section */
clean.hash = false;
for_each_net(net) for_each_net(net)
nf_ct_iterate_cleanup(net, nf_nat_proto_clean, &clean); nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -773,7 +747,7 @@ static void __net_exit nf_nat_net_exit(struct net *net) ...@@ -773,7 +747,7 @@ static void __net_exit nf_nat_net_exit(struct net *net)
{ {
struct nf_nat_proto_clean clean = {}; struct nf_nat_proto_clean clean = {};
nf_ct_iterate_cleanup(net, &nf_nat_proto_clean, &clean); nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean);
synchronize_rcu(); synchronize_rcu();
nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size); nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment