Commit 2ba7e7eb authored by Paolo Abeni's avatar Paolo Abeni
Browse files
Pablo Neira Ayuso says:

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

This is v3, including a crash fix for patch 01/14.

The following patchset contains Netfilter/IPVS fixes for net:

1) Fix UDP segmentation with IPVS tunneled traffic, from Terin Stock.

2) Fix chain binding transaction logic, add a bound flag to rule
   transactions. Remove incorrect logic in nft_data_hold() and
   nft_data_release().

3) Add a NFT_TRANS_PREPARE_ERROR deactivate state to deal with releasing
   the set/chain as a follow up to 1240eb93 ("netfilter: nf_tables:
   incorrect error path handling with NFT_MSG_NEWRULE")

4) Drop map element references from preparation phase instead of
   set destroy path, otherwise bogus EBUSY with transactions such as:

        flush chain ip x y
        delete chain ip x w

   where chain ip x y contains jump/goto from set elements.

5) Pipapo set type does not regard generation mask from the walk
   iteration.

6) Fix reference count underflow in set element reference to
   stateful object.

7) Several patches to tighten the nf_tables API:
   - disallow set element updates of bound anonymous set
   - disallow unbound anonymous set/chain at the end of transaction.
   - disallow updates of anonymous set.
   - disallow timeout configuration for anonymous sets.

8) Fix module reference leak in chain updates.

9) Fix nfnetlink_osf module autoload.

10) Fix deletion of basechain when NFTA_CHAIN_HOOK is specified as
    in iptables-nft.

This Netfilter batch is larger than usual at this stage, I am aware we
are fairly late in the -rc cycle, if you prefer to route them through
net-next, please let me know.

netfilter pull request 23-06-21

* tag 'nf-23-06-21' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_tables: Fix for deleting base chains with payload
  netfilter: nfnetlink_osf: fix module autoload
  netfilter: nf_tables: drop module reference after updating chain
  netfilter: nf_tables: disallow timeout for anonymous sets
  netfilter: nf_tables: disallow updates of anonymous sets
  netfilter: nf_tables: reject unbound chain set before commit phase
  netfilter: nf_tables: reject unbound anonymous set before commit phase
  netfilter: nf_tables: disallow element updates of bound anonymous sets
  netfilter: nf_tables: fix underflow in object reference counter
  netfilter: nft_set_pipapo: .walk does not deal with generations
  netfilter: nf_tables: drop map element references from preparation phase
  netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain
  netfilter: nf_tables: fix chain binding transaction logic
  ipvs: align inner_mac_header for encapsulation
====================

Link: https://lore.kernel.org/r/20230621100731.68068-1-pablo@netfilter.org


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents a9628e88 42e344f0
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -472,7 +472,8 @@ struct nft_set_ops {
	int				(*init)(const struct nft_set *set,
						const struct nft_set_desc *desc,
						const struct nlattr * const nla[]);
	void				(*destroy)(const struct nft_set *set);
	void				(*destroy)(const struct nft_ctx *ctx,
						   const struct nft_set *set);
	void				(*gc_init)(const struct nft_set *set);

	unsigned int			elemsize;
@@ -809,6 +810,8 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
			    struct nft_expr *expr_array[]);
void nft_set_elem_destroy(const struct nft_set *set, void *elem,
			  bool destroy_expr);
void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
				const struct nft_set *set, void *elem);

/**
 *	struct nft_set_gc_batch_head - nf_tables set garbage collection batch
@@ -901,6 +904,7 @@ struct nft_expr_type {

enum nft_trans_phase {
	NFT_TRANS_PREPARE,
	NFT_TRANS_PREPARE_ERROR,
	NFT_TRANS_ABORT,
	NFT_TRANS_COMMIT,
	NFT_TRANS_RELEASE
@@ -1009,7 +1013,10 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
	return (void *)&rule->data[rule->dlen];
}

void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule);
void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule);
void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
			      enum nft_trans_phase phase);
void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule);

static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
					    struct nft_regs *regs,
@@ -1104,6 +1111,8 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
			 const struct nft_set_iter *iter,
			 struct nft_set_elem *elem);
int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set);
int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);

enum nft_chain_types {
	NFT_CHAIN_T_DEFAULT = 0,
@@ -1140,11 +1149,17 @@ int nft_chain_validate_dependency(const struct nft_chain *chain,
int nft_chain_validate_hooks(const struct nft_chain *chain,
                             unsigned int hook_flags);

static inline bool nft_chain_binding(const struct nft_chain *chain)
{
	return chain->flags & NFT_CHAIN_BINDING;
}

static inline bool nft_chain_is_bound(struct nft_chain *chain)
{
	return (chain->flags & NFT_CHAIN_BINDING) && chain->bound;
}

int nft_chain_add(struct nft_table *table, struct nft_chain *chain);
void nft_chain_del(struct nft_chain *chain);
void nf_tables_chain_destroy(struct nft_ctx *ctx);

@@ -1558,6 +1573,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
 *	struct nft_trans - nf_tables object update in transaction
 *
 *	@list: used internally
 *	@binding_list: list of objects with possible bindings
 *	@msg_type: message type
 *	@put_net: ctx->net needs to be put
 *	@ctx: transaction context
@@ -1565,6 +1581,7 @@ static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
 */
struct nft_trans {
	struct list_head		list;
	struct list_head		binding_list;
	int				msg_type;
	bool				put_net;
	struct nft_ctx			ctx;
@@ -1575,6 +1592,7 @@ struct nft_trans_rule {
	struct nft_rule			*rule;
	struct nft_flow_rule		*flow;
	u32				rule_id;
	bool				bound;
};

#define nft_trans_rule(trans)	\
@@ -1583,6 +1601,8 @@ struct nft_trans_rule {
	(((struct nft_trans_rule *)trans->data)->flow)
#define nft_trans_rule_id(trans)	\
	(((struct nft_trans_rule *)trans->data)->rule_id)
#define nft_trans_rule_bound(trans)	\
	(((struct nft_trans_rule *)trans->data)->bound)

struct nft_trans_set {
	struct nft_set			*set;
@@ -1607,15 +1627,19 @@ struct nft_trans_set {
	(((struct nft_trans_set *)trans->data)->gc_int)

struct nft_trans_chain {
	struct nft_chain		*chain;
	bool				update;
	char				*name;
	struct nft_stats __percpu	*stats;
	u8				policy;
	bool				bound;
	u32				chain_id;
	struct nft_base_chain		*basechain;
	struct list_head		hook_list;
};

#define nft_trans_chain(trans)	\
	(((struct nft_trans_chain *)trans->data)->chain)
#define nft_trans_chain_update(trans)	\
	(((struct nft_trans_chain *)trans->data)->update)
#define nft_trans_chain_name(trans)	\
@@ -1624,6 +1648,8 @@ struct nft_trans_chain {
	(((struct nft_trans_chain *)trans->data)->stats)
#define nft_trans_chain_policy(trans)	\
	(((struct nft_trans_chain *)trans->data)->policy)
#define nft_trans_chain_bound(trans)	\
	(((struct nft_trans_chain *)trans->data)->bound)
#define nft_trans_chain_id(trans)	\
	(((struct nft_trans_chain *)trans->data)->chain_id)
#define nft_trans_basechain(trans)	\
@@ -1700,6 +1726,7 @@ static inline int nft_request_module(struct net *net, const char *fmt, ...) { re
struct nftables_pernet {
	struct list_head	tables;
	struct list_head	commit_list;
	struct list_head	binding_list;
	struct list_head	module_list;
	struct list_head	notify_list;
	struct mutex		commit_mutex;
+2 −0
Original line number Diff line number Diff line
@@ -1207,6 +1207,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
	skb->transport_header = skb->network_header;

	skb_set_inner_ipproto(skb, next_protocol);
	skb_set_inner_mac_header(skb, skb_inner_network_offset(skb));

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		bool check = false;
@@ -1349,6 +1350,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
	skb->transport_header = skb->network_header;

	skb_set_inner_ipproto(skb, next_protocol);
	skb_set_inner_mac_header(skb, skb_inner_network_offset(skb));

	if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) {
		bool check = false;
+295 −71

File changed.

Preview size limit exceeded, changes collapsed.

+1 −0
Original line number Diff line number Diff line
@@ -439,3 +439,4 @@ module_init(nfnl_osf_init);
module_exit(nfnl_osf_fini);

MODULE_LICENSE("GPL");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);
+81 −9
Original line number Diff line number Diff line
@@ -76,11 +76,9 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
		switch (priv->data.verdict.code) {
		case NFT_JUMP:
		case NFT_GOTO:
			if (nft_chain_is_bound(chain)) {
				err = -EBUSY;
				goto err1;
			}
			chain->bound = true;
			err = nf_tables_bind_chain(ctx, chain);
			if (err < 0)
				return err;
			break;
		default:
			break;
@@ -98,6 +96,31 @@ static void nft_immediate_activate(const struct nft_ctx *ctx,
				   const struct nft_expr *expr)
{
	const struct nft_immediate_expr *priv = nft_expr_priv(expr);
	const struct nft_data *data = &priv->data;
	struct nft_ctx chain_ctx;
	struct nft_chain *chain;
	struct nft_rule *rule;

	if (priv->dreg == NFT_REG_VERDICT) {
		switch (data->verdict.code) {
		case NFT_JUMP:
		case NFT_GOTO:
			chain = data->verdict.chain;
			if (!nft_chain_binding(chain))
				break;

			chain_ctx = *ctx;
			chain_ctx.chain = chain;

			list_for_each_entry(rule, &chain->rules, list)
				nft_rule_expr_activate(&chain_ctx, rule);

			nft_clear(ctx->net, chain);
			break;
		default:
			break;
		}
	}

	return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg));
}
@@ -107,6 +130,43 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
				     enum nft_trans_phase phase)
{
	const struct nft_immediate_expr *priv = nft_expr_priv(expr);
	const struct nft_data *data = &priv->data;
	struct nft_ctx chain_ctx;
	struct nft_chain *chain;
	struct nft_rule *rule;

	if (priv->dreg == NFT_REG_VERDICT) {
		switch (data->verdict.code) {
		case NFT_JUMP:
		case NFT_GOTO:
			chain = data->verdict.chain;
			if (!nft_chain_binding(chain))
				break;

			chain_ctx = *ctx;
			chain_ctx.chain = chain;

			list_for_each_entry(rule, &chain->rules, list)
				nft_rule_expr_deactivate(&chain_ctx, rule, phase);

			switch (phase) {
			case NFT_TRANS_PREPARE_ERROR:
				nf_tables_unbind_chain(ctx, chain);
				fallthrough;
			case NFT_TRANS_PREPARE:
				nft_deactivate_next(ctx->net, chain);
				break;
			default:
				nft_chain_del(chain);
				chain->bound = false;
				chain->table->use--;
				break;
			}
			break;
		default:
			break;
		}
	}

	if (phase == NFT_TRANS_COMMIT)
		return;
@@ -131,15 +191,27 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx,
	case NFT_GOTO:
		chain = data->verdict.chain;

		if (!nft_chain_is_bound(chain))
		if (!nft_chain_binding(chain))
			break;

		/* Rule construction failed, but chain is already bound:
		 * let the transaction records release this chain and its rules.
		 */
		if (chain->bound) {
			chain->use--;
			break;
		}

		/* Rule has been deleted, release chain and its rules. */
		chain_ctx = *ctx;
		chain_ctx.chain = chain;

		list_for_each_entry_safe(rule, n, &chain->rules, list)
			nf_tables_rule_release(&chain_ctx, rule);

		chain->use--;
		list_for_each_entry_safe(rule, n, &chain->rules, list) {
			chain->use--;
			list_del(&rule->list);
			nf_tables_rule_destroy(&chain_ctx, rule);
		}
		nf_tables_chain_destroy(&chain_ctx);
		break;
	default:
Loading