Commit d0928c1c authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Florian Westphal says:

====================
Netfilter updates for net-next

1. nf_tables 'brouting' support, from Sriram Yagnaraman.

2. Update bridge netfilter and ovs conntrack helpers to handle
   IPv6 Jumbo packets properly, i.e. fetch the packet length
   from hop-by-hop extension header, from Xin Long.

   This comes with a test BIG TCP test case, added to
   tools/testing/selftests/net/.

3. Fix spelling and indentation in conntrack, from Jeremy Sowden.

* 'main' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  netfilter: nat: fix indentation of function arguments
  netfilter: conntrack: fix typo
  selftests: add a selftest for big tcp
  netfilter: use nf_ip6_check_hbh_len in nf_ct_skb_network_trim
  netfilter: move br_nf_check_hbh_len to utils
  netfilter: bridge: move pskb_trim_rcsum out of br_nf_check_hbh_len
  netfilter: bridge: check len before accessing more nh data
  netfilter: bridge: call pskb_may_pull in br_nf_check_hbh_len
  netfilter: bridge: introduce broute meta statement
====================

Link: https://lore.kernel.org/r/20230308193033.13965-1-fw@strlen.de


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents b3a8df9f b0ca2000
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -197,6 +197,8 @@ static inline int nf_cookie_v6_check(const struct ipv6hdr *iph,
__sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook,
			unsigned int dataoff, u_int8_t protocol);

int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen);

int ipv6_netfilter_init(void);
void ipv6_netfilter_fini(void);

+2 −0
Original line number Diff line number Diff line
@@ -931,6 +931,7 @@ enum nft_exthdr_attributes {
 * @NFT_META_TIME_HOUR: hour of day (in seconds)
 * @NFT_META_SDIF: slave device interface index
 * @NFT_META_SDIFNAME: slave device interface name
 * @NFT_META_BRI_BROUTE: packet br_netfilter_broute bit
 */
enum nft_meta_keys {
	NFT_META_LEN,
@@ -969,6 +970,7 @@ enum nft_meta_keys {
	NFT_META_TIME_HOUR,
	NFT_META_SDIF,
	NFT_META_SDIFNAME,
	NFT_META_BRI_BROUTE,
	__NFT_META_IIFTYPE,
};

+10 −69
Original line number Diff line number Diff line
@@ -40,62 +40,6 @@
#include <linux/sysctl.h>
#endif

/* We only check the length. A bridge shouldn't do any hop-by-hop stuff
 * anyway
 */
static int br_nf_check_hbh_len(struct sk_buff *skb)
{
	unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
	u32 pkt_len;
	const unsigned char *nh = skb_network_header(skb);
	int off = raw - nh;
	int len = (raw[1] + 1) << 3;

	if ((raw + len) - skb->data > skb_headlen(skb))
		goto bad;

	off += 2;
	len -= 2;

	while (len > 0) {
		int optlen = nh[off + 1] + 2;

		switch (nh[off]) {
		case IPV6_TLV_PAD1:
			optlen = 1;
			break;

		case IPV6_TLV_PADN:
			break;

		case IPV6_TLV_JUMBO:
			if (nh[off + 1] != 4 || (off & 3) != 2)
				goto bad;
			pkt_len = ntohl(*(__be32 *)(nh + off + 2));
			if (pkt_len <= IPV6_MAXPLEN ||
			    ipv6_hdr(skb)->payload_len)
				goto bad;
			if (pkt_len > skb->len - sizeof(struct ipv6hdr))
				goto bad;
			if (pskb_trim_rcsum(skb,
					    pkt_len + sizeof(struct ipv6hdr)))
				goto bad;
			nh = skb_network_header(skb);
			break;
		default:
			if (optlen > len)
				goto bad;
			break;
		}
		off += optlen;
		len -= optlen;
	}
	if (len == 0)
		return 0;
bad:
	return -1;
}

int br_validate_ipv6(struct net *net, struct sk_buff *skb)
{
	const struct ipv6hdr *hdr;
@@ -115,8 +59,9 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb)
		goto inhdr_error;

	pkt_len = ntohs(hdr->payload_len);
	if (hdr->nexthdr == NEXTHDR_HOP && nf_ip6_check_hbh_len(skb, &pkt_len))
		goto drop;

	if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
	if (pkt_len + ip6h_len > skb->len) {
		__IP6_INC_STATS(net, idev,
				IPSTATS_MIB_INTRUNCATEDPKTS);
@@ -127,10 +72,6 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb)
				IPSTATS_MIB_INDISCARDS);
		goto drop;
	}
		hdr = ipv6_hdr(skb);
	}
	if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb))
		goto drop;

	memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
	/* No IP options in IPv6 header; however it should be
+68 −3
Original line number Diff line number Diff line
@@ -8,6 +8,9 @@
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
#include <linux/if_bridge.h>
#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */

#include "../br_private.h"

static const struct net_device *
nft_meta_get_bridge(const struct net_device *dev)
@@ -102,6 +105,50 @@ static const struct nft_expr_ops nft_meta_bridge_get_ops = {
	.reduce		= nft_meta_get_reduce,
};

static void nft_meta_bridge_set_eval(const struct nft_expr *expr,
				     struct nft_regs *regs,
				     const struct nft_pktinfo *pkt)
{
	const struct nft_meta *meta = nft_expr_priv(expr);
	u32 *sreg = &regs->data[meta->sreg];
	struct sk_buff *skb = pkt->skb;
	u8 value8;

	switch (meta->key) {
	case NFT_META_BRI_BROUTE:
		value8 = nft_reg_load8(sreg);
		BR_INPUT_SKB_CB(skb)->br_netfilter_broute = !!value8;
		break;
	default:
		nft_meta_set_eval(expr, regs, pkt);
	}
}

static int nft_meta_bridge_set_init(const struct nft_ctx *ctx,
				    const struct nft_expr *expr,
				    const struct nlattr * const tb[])
{
	struct nft_meta *priv = nft_expr_priv(expr);
	unsigned int len;
	int err;

	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
	switch (priv->key) {
	case NFT_META_BRI_BROUTE:
		len = sizeof(u8);
		break;
	default:
		return nft_meta_set_init(ctx, expr, tb);
	}

	priv->len = len;
	err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
	if (err < 0)
		return err;

	return 0;
}

static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
				       const struct nft_expr *expr)
{
@@ -120,15 +167,33 @@ static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
	return false;
}

static int nft_meta_bridge_set_validate(const struct nft_ctx *ctx,
					const struct nft_expr *expr,
					const struct nft_data **data)
{
	struct nft_meta *priv = nft_expr_priv(expr);
	unsigned int hooks;

	switch (priv->key) {
	case NFT_META_BRI_BROUTE:
		hooks = 1 << NF_BR_PRE_ROUTING;
		break;
	default:
		return nft_meta_set_validate(ctx, expr, data);
	}

	return nft_chain_validate_hooks(ctx->chain, hooks);
}

static const struct nft_expr_ops nft_meta_bridge_set_ops = {
	.type		= &nft_meta_bridge_type,
	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)),
	.eval		= nft_meta_set_eval,
	.init		= nft_meta_set_init,
	.eval		= nft_meta_bridge_set_eval,
	.init		= nft_meta_bridge_set_init,
	.destroy	= nft_meta_set_destroy,
	.dump		= nft_meta_set_dump,
	.reduce		= nft_meta_bridge_set_reduce,
	.validate	= nft_meta_set_validate,
	.validate	= nft_meta_bridge_set_validate,
};

static const struct nft_expr_ops *
+1 −1
Original line number Diff line number Diff line
@@ -1294,7 +1294,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(__nf_conntrack_confirm);

/* Returns true if a connection correspondings to the tuple (required
/* Returns true if a connection corresponds to the tuple (required
   for NAT). */
int
nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
Loading