Skip to content

Commit

Permalink
Merge branch 'main' of git://git.kernel.org/pub/scm/linux/kernel/git/…
Browse files Browse the repository at this point in the history
…netfilter/nf-next

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: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Mar 10, 2023
2 parents b3a8df9 + b0ca200 commit d0928c1
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 77 deletions.
2 changes: 2 additions & 0 deletions include/linux/netfilter_ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
};

Expand Down
79 changes: 10 additions & 69 deletions net/bridge/br_netfilter_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -115,22 +59,19 @@ 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);
goto drop;
}
if (pskb_trim_rcsum(skb, pkt_len + ip6h_len)) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INDISCARDS);
goto drop;
}
hdr = ipv6_hdr(skb);
if (pkt_len + ip6h_len > skb->len) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
}
if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb))
if (pskb_trim_rcsum(skb, pkt_len + ip6h_len)) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INDISCARDS);
goto drop;
}

memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
/* No IP options in IPv6 header; however it should be
Expand Down
71 changes: 68 additions & 3 deletions net/bridge/netfilter/nft_meta_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand All @@ -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 *
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nf_conntrack_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 9 additions & 2 deletions net/netfilter/nf_conntrack_ovs.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
#include <net/ipv6_frag.h>
#include <net/ip.h>
#include <linux/netfilter_ipv6.h>

/* 'skb' should already be pulled to nh_ofs. */
int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
Expand Down Expand Up @@ -120,8 +121,14 @@ int nf_ct_skb_network_trim(struct sk_buff *skb, int family)
len = skb_ip_totlen(skb);
break;
case NFPROTO_IPV6:
len = sizeof(struct ipv6hdr)
+ ntohs(ipv6_hdr(skb)->payload_len);
len = ntohs(ipv6_hdr(skb)->payload_len);
if (ipv6_hdr(skb)->nexthdr == NEXTHDR_HOP) {
int err = nf_ip6_check_hbh_len(skb, &len);

if (err)
return err;
}
len += sizeof(struct ipv6hdr);
break;
default:
len = skb->len;
Expand Down
4 changes: 2 additions & 2 deletions net/netfilter/nf_nat_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,8 @@ get_unique_tuple(struct nf_conntrack_tuple *tuple,
if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
if (!(range->flags & NF_NAT_RANGE_PROTO_OFFSET) &&
l4proto_in_range(tuple, maniptype,
&range->min_proto,
&range->max_proto) &&
&range->min_proto,
&range->max_proto) &&
(range->min_proto.all == range->max_proto.all ||
!nf_nat_used_tuple(tuple, ct)))
return;
Expand Down
52 changes: 52 additions & 0 deletions net/netfilter/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,55 @@ int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry)
}
return ret;
}

/* Only get and check the lengths, not do any hop-by-hop stuff. */
int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen)
{
int len, off = sizeof(struct ipv6hdr);
unsigned char *nh;

if (!pskb_may_pull(skb, off + 8))
return -ENOMEM;
nh = (unsigned char *)(ipv6_hdr(skb) + 1);
len = (nh[1] + 1) << 3;

if (!pskb_may_pull(skb, off + len))
return -ENOMEM;
nh = skb_network_header(skb);

off += 2;
len -= 2;
while (len > 0) {
int optlen;

if (nh[off] == IPV6_TLV_PAD1) {
off++;
len--;
continue;
}
if (len < 2)
return -EBADMSG;
optlen = nh[off + 1] + 2;
if (optlen > len)
return -EBADMSG;

if (nh[off] == IPV6_TLV_JUMBO) {
u32 pkt_len;

if (nh[off + 1] != 4 || (off & 3) != 2)
return -EBADMSG;
pkt_len = ntohl(*(__be32 *)(nh + off + 2));
if (pkt_len <= IPV6_MAXPLEN ||
ipv6_hdr(skb)->payload_len)
return -EBADMSG;
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
return -EBADMSG;
*plen = pkt_len;
}
off += optlen;
len -= optlen;
}

return len ? -EBADMSG : 0;
}
EXPORT_SYMBOL_GPL(nf_ip6_check_hbh_len);
1 change: 1 addition & 0 deletions tools/testing/selftests/net/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ TEST_PROGS += l2_tos_ttl_inherit.sh
TEST_PROGS += bind_bhash.sh
TEST_PROGS += ip_local_port_range.sh
TEST_PROGS += rps_default_mask.sh
TEST_PROGS += big_tcp.sh
TEST_PROGS_EXTENDED := in_netns.sh setup_loopback.sh setup_veth.sh
TEST_PROGS_EXTENDED += toeplitz_client.sh toeplitz.sh
TEST_GEN_FILES = socket nettest
Expand Down
Loading

0 comments on commit d0928c1

Please sign in to comment.