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, they are: 1) Validate hooks for nf_tables NAT expressions, otherwise users can crash the kernel when using them from the wrong hook. We already got one user trapped on this when configuring masquerading. 2) Fix a BUG splat in nf_tables with CONFIG_DEBUG_PREEMPT=y. Reported by Andreas Schultz. 3) Avoid unnecessary reroute of traffic in the local input path in IPVS that triggers a crash in in xfrm. Reported by Florian Wiessner and fixes by Julian Anastasov. 4) Fix memory and module refcount leak from the error path of nf_tables_newchain(). ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
3ae55826ae
7 changed files with 121 additions and 64 deletions
|
@ -530,6 +530,8 @@ enum nft_chain_type {
|
|||
|
||||
int nft_chain_validate_dependency(const struct nft_chain *chain,
|
||||
enum nft_chain_type type);
|
||||
int nft_chain_validate_hooks(const struct nft_chain *chain,
|
||||
unsigned int hook_flags);
|
||||
|
||||
struct nft_stats {
|
||||
u64 bytes;
|
||||
|
|
|
@ -265,22 +265,12 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
|
|||
data[NFT_REG_VERDICT].verdict = NF_DROP;
|
||||
}
|
||||
|
||||
static int nft_reject_bridge_validate_hooks(const struct nft_chain *chain)
|
||||
static int nft_reject_bridge_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
struct nft_base_chain *basechain;
|
||||
|
||||
if (chain->flags & NFT_BASE_CHAIN) {
|
||||
basechain = nft_base_chain(chain);
|
||||
|
||||
switch (basechain->ops[0].hooknum) {
|
||||
case NF_BR_PRE_ROUTING:
|
||||
case NF_BR_LOCAL_IN:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return nft_chain_validate_hooks(ctx->chain, (1 << NF_BR_PRE_ROUTING) |
|
||||
(1 << NF_BR_LOCAL_IN));
|
||||
}
|
||||
|
||||
static int nft_reject_bridge_init(const struct nft_ctx *ctx,
|
||||
|
@ -290,7 +280,7 @@ static int nft_reject_bridge_init(const struct nft_ctx *ctx,
|
|||
struct nft_reject *priv = nft_expr_priv(expr);
|
||||
int icmp_code, err;
|
||||
|
||||
err = nft_reject_bridge_validate_hooks(ctx->chain);
|
||||
err = nft_reject_bridge_validate(ctx, expr, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -341,13 +331,6 @@ static int nft_reject_bridge_dump(struct sk_buff *skb,
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int nft_reject_bridge_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
return nft_reject_bridge_validate_hooks(ctx->chain);
|
||||
}
|
||||
|
||||
static struct nft_expr_type nft_reject_bridge_type;
|
||||
static const struct nft_expr_ops nft_reject_bridge_ops = {
|
||||
.type = &nft_reject_bridge_type,
|
||||
|
|
|
@ -659,16 +659,24 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ip_vs_route_me_harder(int af, struct sk_buff *skb)
|
||||
static int ip_vs_route_me_harder(int af, struct sk_buff *skb,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
if (!sysctl_snat_reroute(skb))
|
||||
return 0;
|
||||
/* Reroute replies only to remote clients (FORWARD and LOCAL_OUT) */
|
||||
if (NF_INET_LOCAL_IN == hooknum)
|
||||
return 0;
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6) {
|
||||
if (sysctl_snat_reroute(skb) && ip6_route_me_harder(skb) != 0)
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
|
||||
if (dst->dev && !(dst->dev->flags & IFF_LOOPBACK) &&
|
||||
ip6_route_me_harder(skb) != 0)
|
||||
return 1;
|
||||
} else
|
||||
#endif
|
||||
if ((sysctl_snat_reroute(skb) ||
|
||||
skb_rtable(skb)->rt_flags & RTCF_LOCAL) &&
|
||||
if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL) &&
|
||||
ip_route_me_harder(skb, RTN_LOCAL) != 0)
|
||||
return 1;
|
||||
|
||||
|
@ -791,7 +799,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
|
|||
union nf_inet_addr *snet,
|
||||
__u8 protocol, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp,
|
||||
unsigned int offset, unsigned int ihl)
|
||||
unsigned int offset, unsigned int ihl,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
unsigned int verdict = NF_DROP;
|
||||
|
||||
|
@ -821,7 +830,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
|
|||
#endif
|
||||
ip_vs_nat_icmp(skb, pp, cp, 1);
|
||||
|
||||
if (ip_vs_route_me_harder(af, skb))
|
||||
if (ip_vs_route_me_harder(af, skb, hooknum))
|
||||
goto out;
|
||||
|
||||
/* do the statistics and put it back */
|
||||
|
@ -916,7 +925,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
|
|||
|
||||
snet.ip = iph->saddr;
|
||||
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
|
||||
pp, ciph.len, ihl);
|
||||
pp, ciph.len, ihl, hooknum);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
|
@ -981,7 +990,8 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
|
|||
snet.in6 = ciph.saddr.in6;
|
||||
writable = ciph.len;
|
||||
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
|
||||
pp, writable, sizeof(struct ipv6hdr));
|
||||
pp, writable, sizeof(struct ipv6hdr),
|
||||
hooknum);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1040,7 +1050,8 @@ static inline bool is_new_conn(const struct sk_buff *skb,
|
|||
*/
|
||||
static unsigned int
|
||||
handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
|
||||
struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
|
||||
struct ip_vs_conn *cp, struct ip_vs_iphdr *iph,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
struct ip_vs_protocol *pp = pd->pp;
|
||||
|
||||
|
@ -1078,7 +1089,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
|
|||
* if it came from this machine itself. So re-compute
|
||||
* the routing information.
|
||||
*/
|
||||
if (ip_vs_route_me_harder(af, skb))
|
||||
if (ip_vs_route_me_harder(af, skb, hooknum))
|
||||
goto drop;
|
||||
|
||||
IP_VS_DBG_PKT(10, af, pp, skb, 0, "After SNAT");
|
||||
|
@ -1181,7 +1192,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
|
|||
cp = pp->conn_out_get(af, skb, &iph, 0);
|
||||
|
||||
if (likely(cp))
|
||||
return handle_response(af, skb, pd, cp, &iph);
|
||||
return handle_response(af, skb, pd, cp, &iph, hooknum);
|
||||
if (sysctl_nat_icmp_send(net) &&
|
||||
(pp->protocol == IPPROTO_TCP ||
|
||||
pp->protocol == IPPROTO_UDP ||
|
||||
|
|
|
@ -1134,9 +1134,11 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
|
|||
/* Restore old counters on this cpu, no problem. Per-cpu statistics
|
||||
* are not exposed to userspace.
|
||||
*/
|
||||
preempt_disable();
|
||||
stats = this_cpu_ptr(newstats);
|
||||
stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
|
||||
stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
|
||||
preempt_enable();
|
||||
|
||||
return newstats;
|
||||
}
|
||||
|
@ -1262,8 +1264,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|||
nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
|
||||
trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
|
||||
sizeof(struct nft_trans_chain));
|
||||
if (trans == NULL)
|
||||
if (trans == NULL) {
|
||||
free_percpu(stats);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
nft_trans_chain_stats(trans) = stats;
|
||||
nft_trans_chain_update(trans) = true;
|
||||
|
@ -1319,8 +1323,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|||
hookfn = type->hooks[hooknum];
|
||||
|
||||
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
|
||||
if (basechain == NULL)
|
||||
if (basechain == NULL) {
|
||||
module_put(type->owner);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (nla[NFTA_CHAIN_COUNTERS]) {
|
||||
stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
|
||||
|
@ -3753,6 +3759,24 @@ int nft_chain_validate_dependency(const struct nft_chain *chain,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nft_chain_validate_dependency);
|
||||
|
||||
int nft_chain_validate_hooks(const struct nft_chain *chain,
|
||||
unsigned int hook_flags)
|
||||
{
|
||||
struct nft_base_chain *basechain;
|
||||
|
||||
if (chain->flags & NFT_BASE_CHAIN) {
|
||||
basechain = nft_base_chain(chain);
|
||||
|
||||
if ((1 << basechain->ops[0].hooknum) & hook_flags)
|
||||
return 0;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_chain_validate_hooks);
|
||||
|
||||
/*
|
||||
* Loop detection - walk through the ruleset beginning at the destination chain
|
||||
* of a new jump until either the source chain is reached (loop) or all
|
||||
|
|
|
@ -21,6 +21,21 @@ const struct nla_policy nft_masq_policy[NFTA_MASQ_MAX + 1] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(nft_masq_policy);
|
||||
|
||||
int nft_masq_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return nft_chain_validate_hooks(ctx->chain,
|
||||
(1 << NF_INET_POST_ROUTING));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_masq_validate);
|
||||
|
||||
int nft_masq_init(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[])
|
||||
|
@ -28,8 +43,8 @@ int nft_masq_init(const struct nft_ctx *ctx,
|
|||
struct nft_masq *priv = nft_expr_priv(expr);
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
if (err < 0)
|
||||
err = nft_masq_validate(ctx, expr, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tb[NFTA_MASQ_FLAGS] == NULL)
|
||||
|
@ -60,12 +75,5 @@ int nft_masq_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nft_masq_dump);
|
||||
|
||||
int nft_masq_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_masq_validate);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
|
||||
|
|
|
@ -88,6 +88,33 @@ static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
|
|||
[NFTA_NAT_FLAGS] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static int nft_nat_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
struct nft_nat *priv = nft_expr_priv(expr);
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
switch (priv->type) {
|
||||
case NFT_NAT_SNAT:
|
||||
err = nft_chain_validate_hooks(ctx->chain,
|
||||
(1 << NF_INET_POST_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_IN));
|
||||
break;
|
||||
case NFT_NAT_DNAT:
|
||||
err = nft_chain_validate_hooks(ctx->chain,
|
||||
(1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_OUT));
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[])
|
||||
{
|
||||
|
@ -95,10 +122,6 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
|||
u32 family;
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tb[NFTA_NAT_TYPE] == NULL ||
|
||||
(tb[NFTA_NAT_REG_ADDR_MIN] == NULL &&
|
||||
tb[NFTA_NAT_REG_PROTO_MIN] == NULL))
|
||||
|
@ -115,6 +138,10 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nft_nat_validate(ctx, expr, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (tb[NFTA_NAT_FAMILY] == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -219,13 +246,6 @@ static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int nft_nat_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
}
|
||||
|
||||
static struct nft_expr_type nft_nat_type;
|
||||
static const struct nft_expr_ops nft_nat_ops = {
|
||||
.type = &nft_nat_type,
|
||||
|
|
|
@ -23,6 +23,22 @@ const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(nft_redir_policy);
|
||||
|
||||
int nft_redir_validate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return nft_chain_validate_hooks(ctx->chain,
|
||||
(1 << NF_INET_PRE_ROUTING) |
|
||||
(1 << NF_INET_LOCAL_OUT));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_redir_validate);
|
||||
|
||||
int nft_redir_init(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
const struct nlattr * const tb[])
|
||||
|
@ -30,7 +46,7 @@ int nft_redir_init(const struct nft_ctx *ctx,
|
|||
struct nft_redir *priv = nft_expr_priv(expr);
|
||||
int err;
|
||||
|
||||
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
err = nft_redir_validate(ctx, expr, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -88,12 +104,5 @@ int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nft_redir_dump);
|
||||
|
||||
int nft_redir_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
const struct nft_data **data)
|
||||
{
|
||||
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nft_redir_validate);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
|
||||
|
|
Loading…
Reference in a new issue