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

Commit 3ae55826 authored by David S. Miller's avatar David S. Miller
Browse files


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: default avatarDavid S. Miller <davem@davemloft.net>
parents e6b02be8 f5553c19
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;
+6 −23
Original line number Diff line number Diff line
@@ -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,
+22 −11
Original line number Diff line number Diff line
@@ -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 ||
+26 −2
Original line number Diff line number Diff line
@@ -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
+17 −9
Original line number Diff line number Diff line
@@ -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>");
Loading