Commit ff44a837 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

The following patchset contains Netfilter/IPVS fixes for your net tree:

1) The nftnl mutex is now per-netns, therefore use reference counter
   for matches and targets to deal with concurrent updates from netns.
   Moreover, place extensions in a pernet list. Patches from Florian Westphal.

2) Bail out with EINVAL in case of negative timeouts via setsockopt()
   through ip_vs_set_timeout(), from ZhangXiaoxu.

3) Spurious EINVAL on ebtables 32bit binary with 64bit kernel, also
   from Florian.

4) Reset TCP option header parser in case of fingerprint mismatch,
   otherwise follow up overlapping fingerprint definitions including
   TCP options do not work, from Fernando Fernandez Mancera.

5) Compilation warning in ipt_CLUSTER with CONFIG_PROC_FS unset.
   From Anders Roxell.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6fb6e637 206b8cc5
...@@ -2293,9 +2293,12 @@ static int compat_do_replace(struct net *net, void __user *user, ...@@ -2293,9 +2293,12 @@ static int compat_do_replace(struct net *net, void __user *user,
xt_compat_lock(NFPROTO_BRIDGE); xt_compat_lock(NFPROTO_BRIDGE);
if (tmp.nentries) {
ret = xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries); ret = xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries);
if (ret < 0) if (ret < 0)
goto out_unlock; goto out_unlock;
}
ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state); ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state);
if (ret < 0) if (ret < 0)
goto out_unlock; goto out_unlock;
......
...@@ -846,9 +846,9 @@ static int clusterip_net_init(struct net *net) ...@@ -846,9 +846,9 @@ static int clusterip_net_init(struct net *net)
static void clusterip_net_exit(struct net *net) static void clusterip_net_exit(struct net *net)
{ {
#ifdef CONFIG_PROC_FS
struct clusterip_net *cn = clusterip_pernet(net); struct clusterip_net *cn = clusterip_pernet(net);
#ifdef CONFIG_PROC_FS
mutex_lock(&cn->mutex); mutex_lock(&cn->mutex);
proc_remove(cn->procdir); proc_remove(cn->procdir);
cn->procdir = NULL; cn->procdir = NULL;
......
...@@ -2220,6 +2220,18 @@ static int ip_vs_set_timeout(struct netns_ipvs *ipvs, struct ip_vs_timeout_user ...@@ -2220,6 +2220,18 @@ static int ip_vs_set_timeout(struct netns_ipvs *ipvs, struct ip_vs_timeout_user
u->tcp_fin_timeout, u->tcp_fin_timeout,
u->udp_timeout); u->udp_timeout);
#ifdef CONFIG_IP_VS_PROTO_TCP
if (u->tcp_timeout < 0 || u->tcp_timeout > (INT_MAX / HZ) ||
u->tcp_fin_timeout < 0 || u->tcp_fin_timeout > (INT_MAX / HZ)) {
return -EINVAL;
}
#endif
#ifdef CONFIG_IP_VS_PROTO_UDP
if (u->udp_timeout < 0 || u->udp_timeout > (INT_MAX / HZ))
return -EINVAL;
#endif
#ifdef CONFIG_IP_VS_PROTO_TCP #ifdef CONFIG_IP_VS_PROTO_TCP
if (u->tcp_timeout) { if (u->tcp_timeout) {
pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP); pd = ip_vs_proto_data_get(ipvs, IPPROTO_TCP);
......
...@@ -66,6 +66,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb, ...@@ -66,6 +66,7 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
int ttl_check, int ttl_check,
struct nf_osf_hdr_ctx *ctx) struct nf_osf_hdr_ctx *ctx)
{ {
const __u8 *optpinit = ctx->optp;
unsigned int check_WSS = 0; unsigned int check_WSS = 0;
int fmatch = FMATCH_WRONG; int fmatch = FMATCH_WRONG;
int foptsize, optnum; int foptsize, optnum;
...@@ -155,6 +156,9 @@ static bool nf_osf_match_one(const struct sk_buff *skb, ...@@ -155,6 +156,9 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
} }
} }
if (fmatch != FMATCH_OK)
ctx->optp = optpinit;
return fmatch == FMATCH_OK; return fmatch == FMATCH_OK;
} }
......
...@@ -22,11 +22,15 @@ ...@@ -22,11 +22,15 @@
#include <linux/netfilter_bridge/ebtables.h> #include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter_arp/arp_tables.h> #include <linux/netfilter_arp/arp_tables.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netns/generic.h>
struct nft_xt { struct nft_xt {
struct list_head head; struct list_head head;
struct nft_expr_ops ops; struct nft_expr_ops ops;
unsigned int refcnt; refcount_t refcnt;
/* used only when transaction mutex is locked */
unsigned int listcnt;
/* Unlike other expressions, ops doesn't have static storage duration. /* Unlike other expressions, ops doesn't have static storage duration.
* nft core assumes they do. We use kfree_rcu so that nft core can * nft core assumes they do. We use kfree_rcu so that nft core can
...@@ -43,10 +47,24 @@ struct nft_xt_match_priv { ...@@ -43,10 +47,24 @@ struct nft_xt_match_priv {
void *info; void *info;
}; };
struct nft_compat_net {
struct list_head nft_target_list;
struct list_head nft_match_list;
};
static unsigned int nft_compat_net_id __read_mostly;
static struct nft_expr_type nft_match_type;
static struct nft_expr_type nft_target_type;
static struct nft_compat_net *nft_compat_pernet(struct net *net)
{
return net_generic(net, nft_compat_net_id);
}
static bool nft_xt_put(struct nft_xt *xt) static bool nft_xt_put(struct nft_xt *xt)
{ {
if (--xt->refcnt == 0) { if (refcount_dec_and_test(&xt->refcnt)) {
list_del(&xt->head); WARN_ON_ONCE(!list_empty(&xt->head));
kfree_rcu(xt, rcu_head); kfree_rcu(xt, rcu_head);
return true; return true;
} }
...@@ -273,7 +291,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -273,7 +291,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return -EINVAL; return -EINVAL;
nft_xt = container_of(expr->ops, struct nft_xt, ops); nft_xt = container_of(expr->ops, struct nft_xt, ops);
nft_xt->refcnt++; refcount_inc(&nft_xt->refcnt);
return 0; return 0;
} }
...@@ -486,7 +504,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -486,7 +504,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return ret; return ret;
nft_xt = container_of(expr->ops, struct nft_xt, ops); nft_xt = container_of(expr->ops, struct nft_xt, ops);
nft_xt->refcnt++; refcount_inc(&nft_xt->refcnt);
return 0; return 0;
} }
...@@ -540,6 +558,43 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) ...@@ -540,6 +558,43 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
__nft_match_destroy(ctx, expr, nft_expr_priv(expr)); __nft_match_destroy(ctx, expr, nft_expr_priv(expr));
} }
static void nft_compat_activate(const struct nft_ctx *ctx,
const struct nft_expr *expr,
struct list_head *h)
{
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
if (xt->listcnt == 0)
list_add(&xt->head, h);
xt->listcnt++;
}
static void nft_compat_activate_mt(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
nft_compat_activate(ctx, expr, &cn->nft_match_list);
}
static void nft_compat_activate_tg(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
nft_compat_activate(ctx, expr, &cn->nft_target_list);
}
static void nft_compat_deactivate(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
if (--xt->listcnt == 0)
list_del_init(&xt->head);
}
static void static void
nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) nft_match_large_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
{ {
...@@ -734,10 +789,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = { ...@@ -734,10 +789,6 @@ static const struct nfnetlink_subsystem nfnl_compat_subsys = {
.cb = nfnl_nft_compat_cb, .cb = nfnl_nft_compat_cb,
}; };
static LIST_HEAD(nft_match_list);
static struct nft_expr_type nft_match_type;
static bool nft_match_cmp(const struct xt_match *match, static bool nft_match_cmp(const struct xt_match *match,
const char *name, u32 rev, u32 family) const char *name, u32 rev, u32 family)
{ {
...@@ -749,6 +800,7 @@ static const struct nft_expr_ops * ...@@ -749,6 +800,7 @@ static const struct nft_expr_ops *
nft_match_select_ops(const struct nft_ctx *ctx, nft_match_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_compat_net *cn;
struct nft_xt *nft_match; struct nft_xt *nft_match;
struct xt_match *match; struct xt_match *match;
unsigned int matchsize; unsigned int matchsize;
...@@ -765,8 +817,10 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -765,8 +817,10 @@ nft_match_select_ops(const struct nft_ctx *ctx,
rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV])); rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV]));
family = ctx->family; family = ctx->family;
cn = nft_compat_pernet(ctx->net);
/* Re-use the existing match if it's already loaded. */ /* Re-use the existing match if it's already loaded. */
list_for_each_entry(nft_match, &nft_match_list, head) { list_for_each_entry(nft_match, &cn->nft_match_list, head) {
struct xt_match *match = nft_match->ops.data; struct xt_match *match = nft_match->ops.data;
if (nft_match_cmp(match, mt_name, rev, family)) if (nft_match_cmp(match, mt_name, rev, family))
...@@ -789,11 +843,13 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -789,11 +843,13 @@ nft_match_select_ops(const struct nft_ctx *ctx,
goto err; goto err;
} }
nft_match->refcnt = 0; refcount_set(&nft_match->refcnt, 0);
nft_match->ops.type = &nft_match_type; nft_match->ops.type = &nft_match_type;
nft_match->ops.eval = nft_match_eval; nft_match->ops.eval = nft_match_eval;
nft_match->ops.init = nft_match_init; nft_match->ops.init = nft_match_init;
nft_match->ops.destroy = nft_match_destroy; nft_match->ops.destroy = nft_match_destroy;
nft_match->ops.activate = nft_compat_activate_mt;
nft_match->ops.deactivate = nft_compat_deactivate;
nft_match->ops.dump = nft_match_dump; nft_match->ops.dump = nft_match_dump;
nft_match->ops.validate = nft_match_validate; nft_match->ops.validate = nft_match_validate;
nft_match->ops.data = match; nft_match->ops.data = match;
...@@ -810,7 +866,8 @@ nft_match_select_ops(const struct nft_ctx *ctx, ...@@ -810,7 +866,8 @@ nft_match_select_ops(const struct nft_ctx *ctx,
nft_match->ops.size = matchsize; nft_match->ops.size = matchsize;
list_add(&nft_match->head, &nft_match_list); nft_match->listcnt = 1;
list_add(&nft_match->head, &cn->nft_match_list);
return &nft_match->ops; return &nft_match->ops;
err: err:
...@@ -826,10 +883,6 @@ static struct nft_expr_type nft_match_type __read_mostly = { ...@@ -826,10 +883,6 @@ static struct nft_expr_type nft_match_type __read_mostly = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static LIST_HEAD(nft_target_list);
static struct nft_expr_type nft_target_type;
static bool nft_target_cmp(const struct xt_target *tg, static bool nft_target_cmp(const struct xt_target *tg,
const char *name, u32 rev, u32 family) const char *name, u32 rev, u32 family)
{ {
...@@ -841,6 +894,7 @@ static const struct nft_expr_ops * ...@@ -841,6 +894,7 @@ static const struct nft_expr_ops *
nft_target_select_ops(const struct nft_ctx *ctx, nft_target_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_compat_net *cn;
struct nft_xt *nft_target; struct nft_xt *nft_target;
struct xt_target *target; struct xt_target *target;
char *tg_name; char *tg_name;
...@@ -861,8 +915,9 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -861,8 +915,9 @@ nft_target_select_ops(const struct nft_ctx *ctx,
strcmp(tg_name, "standard") == 0) strcmp(tg_name, "standard") == 0)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
cn = nft_compat_pernet(ctx->net);
/* Re-use the existing target if it's already loaded. */ /* Re-use the existing target if it's already loaded. */
list_for_each_entry(nft_target, &nft_target_list, head) { list_for_each_entry(nft_target, &cn->nft_target_list, head) {
struct xt_target *target = nft_target->ops.data; struct xt_target *target = nft_target->ops.data;
if (!target->target) if (!target->target)
...@@ -893,11 +948,13 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -893,11 +948,13 @@ nft_target_select_ops(const struct nft_ctx *ctx,
goto err; goto err;
} }
nft_target->refcnt = 0; refcount_set(&nft_target->refcnt, 0);
nft_target->ops.type = &nft_target_type; nft_target->ops.type = &nft_target_type;
nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize)); nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
nft_target->ops.init = nft_target_init; nft_target->ops.init = nft_target_init;
nft_target->ops.destroy = nft_target_destroy; nft_target->ops.destroy = nft_target_destroy;
nft_target->ops.activate = nft_compat_activate_tg;
nft_target->ops.deactivate = nft_compat_deactivate;
nft_target->ops.dump = nft_target_dump; nft_target->ops.dump = nft_target_dump;
nft_target->ops.validate = nft_target_validate; nft_target->ops.validate = nft_target_validate;
nft_target->ops.data = target; nft_target->ops.data = target;
...@@ -907,7 +964,8 @@ nft_target_select_ops(const struct nft_ctx *ctx, ...@@ -907,7 +964,8 @@ nft_target_select_ops(const struct nft_ctx *ctx,
else else
nft_target->ops.eval = nft_target_eval_xt; nft_target->ops.eval = nft_target_eval_xt;
list_add(&nft_target->head, &nft_target_list); nft_target->listcnt = 1;
list_add(&nft_target->head, &cn->nft_target_list);
return &nft_target->ops; return &nft_target->ops;
err: err:
...@@ -923,13 +981,74 @@ static struct nft_expr_type nft_target_type __read_mostly = { ...@@ -923,13 +981,74 @@ static struct nft_expr_type nft_target_type __read_mostly = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int __net_init nft_compat_init_net(struct net *net)
{
struct nft_compat_net *cn = nft_compat_pernet(net);
INIT_LIST_HEAD(&cn->nft_target_list);
INIT_LIST_HEAD(&cn->nft_match_list);
return 0;
}
static void __net_exit nft_compat_exit_net(struct net *net)
{
struct nft_compat_net *cn = nft_compat_pernet(net);
struct nft_xt *xt, *next;
if (list_empty(&cn->nft_match_list) &&
list_empty(&cn->nft_target_list))
return;
/* If there was an error that caused nft_xt expr to not be initialized
* fully and noone else requested the same expression later, the lists
* contain 0-refcount entries that still hold module reference.
*
* Clean them here.
*/
mutex_lock(&net->nft.commit_mutex);
list_for_each_entry_safe(xt, next, &cn->nft_target_list, head) {
struct xt_target *target = xt->ops.data;
list_del_init(&xt->head);
if (refcount_read(&xt->refcnt))
continue;
module_put(target->me);
kfree(xt);
}
list_for_each_entry_safe(xt, next, &cn->nft_match_list, head) {
struct xt_match *match = xt->ops.data;
list_del_init(&xt->head);
if (refcount_read(&xt->refcnt))
continue;
module_put(match->me);
kfree(xt);
}
mutex_unlock(&net->nft.commit_mutex);
}
static struct pernet_operations nft_compat_net_ops = {
.init = nft_compat_init_net,
.exit = nft_compat_exit_net,
.id = &nft_compat_net_id,
.size = sizeof(struct nft_compat_net),
};
static int __init nft_compat_module_init(void) static int __init nft_compat_module_init(void)
{ {
int ret; int ret;
ret = register_pernet_subsys(&nft_compat_net_ops);
if (ret < 0)
goto err_target;
ret = nft_register_expr(&nft_match_type); ret = nft_register_expr(&nft_match_type);
if (ret < 0) if (ret < 0)
return ret; goto err_pernet;
ret = nft_register_expr(&nft_target_type); ret = nft_register_expr(&nft_target_type);
if (ret < 0) if (ret < 0)
...@@ -942,45 +1061,21 @@ static int __init nft_compat_module_init(void) ...@@ -942,45 +1061,21 @@ static int __init nft_compat_module_init(void)
} }
return ret; return ret;
err_target: err_target:
nft_unregister_expr(&nft_target_type); nft_unregister_expr(&nft_target_type);
err_match: err_match:
nft_unregister_expr(&nft_match_type); nft_unregister_expr(&nft_match_type);
err_pernet:
unregister_pernet_subsys(&nft_compat_net_ops);
return ret; return ret;
} }
static void __exit nft_compat_module_exit(void) static void __exit nft_compat_module_exit(void)
{ {
struct nft_xt *xt, *next;
/* list should be empty here, it can be non-empty only in case there
* was an error that caused nft_xt expr to not be initialized fully
* and noone else requested the same expression later.
*
* In this case, the lists contain 0-refcount entries that still
* hold module reference.
*/
list_for_each_entry_safe(xt, next, &nft_target_list, head) {
struct xt_target *target = xt->ops.data;
if (WARN_ON_ONCE(xt->refcnt))
continue;
module_put(target->me);
kfree(xt);
}
list_for_each_entry_safe(xt, next, &nft_match_list, head) {
struct xt_match *match = xt->ops.data;
if (WARN_ON_ONCE(xt->refcnt))
continue;
module_put(match->me);
kfree(xt);
}
nfnetlink_subsys_unregister(&nfnl_compat_subsys); nfnetlink_subsys_unregister(&nfnl_compat_subsys);
nft_unregister_expr(&nft_target_type); nft_unregister_expr(&nft_target_type);
nft_unregister_expr(&nft_match_type); nft_unregister_expr(&nft_match_type);
unregister_pernet_subsys(&nft_compat_net_ops);
} }
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT); MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT);
......
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