Commit 12dee519 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

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

1) Move struct nft_payload_set definition to .c file where it is
   only used.

2) Shrink transport and inner header offset fields in the nft_pktinfo
   structure to 16-bits, from Florian Westphal.

3) Get rid of nft_objref Kbuild toggle, make it built-in into
   nf_tables. This expression is used to instantiate conntrack helpers
   in nftables. After removing the conntrack helper auto-assignment
   toggle it this feature became more important so move it to the nf_tables
   core module. Also from Florian.

4) Extend the existing function to calculate payload inner header offset
   to deal with the GRE and IPIP transport protocols.

6) Add inner expression support for nf_tables. This new expression
   provides a packet parser for tunneled packets which uses a userspace
   description of the expected inner headers. The inner expression
   invokes the payload expression (via direct call) to match on the
   inner header protocol fields using the inner link, network and
   transport header offsets.

   An example of the bytecode generated from userspace to match on
   IP source encapsulated in a VxLAN packet:

   # nft --debug=netlink add rule netdev x y udp dport 4789 vxlan ip saddr 1.2.3.4
     netdev x y
       [ meta load l4proto => reg 1 ]
       [ cmp eq reg 1 0x00000011 ]
       [ payload load 2b @ transport header + 2 => reg 1 ]
       [ cmp eq reg 1 0x0000b512 ]
       [ inner type vxlan hdrsize 8 flags f [ meta load protocol => reg 1 ] ]
       [ cmp eq reg 1 0x00000008 ]
       [ inner type vxlan hdrsize 8 flags f [ payload load 4b @ network header + 12 => reg 1 ] ]
       [ cmp eq reg 1 0x04030201 ]

7) Store inner link, network and transport header offsets in percpu
   area to parse inner packet header once only. Matching on a different
   tunnel type invalidates existing offsets in the percpu area and it
   invokes the inner tunnel parser again.

8) Add support for inner meta matching. This support for
   NFTA_META_PROTOCOL, which specifies the inner ethertype, and
   NFT_META_L4PROTO, which specifies the inner transport protocol.

9) Extend nft_inner to parse GENEVE optional fields to calculate the
   link layer offset.

10) Update inner expression so tunnel offset points to GRE header
    to normalize tunnel header handling. This also allows to perform
    different interpretations of the GRE header from userspace.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  netfilter: nft_inner: set tunnel offset to GRE header offset
  netfilter: nft_inner: add geneve support
  netfilter: nft_meta: add inner match support
  netfilter: nft_inner: add percpu inner context
  netfilter: nft_inner: support for inner tunnel header matching
  netfilter: nft_payload: access ipip payload for inner offset
  netfilter: nft_payload: access GRE payload via inner offset
  netfilter: nft_objref: make it builtin
  netfilter: nf_tables: reduce nft_pktinfo by 8 bytes
  netfilter: nft_payload: move struct nft_payload_set definition where it belongs
====================

Link: https://lore.kernel.org/r/20221026132227.3287-1-pablo@netfilter.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 148b811c 91619eb6
...@@ -24,6 +24,7 @@ struct module; ...@@ -24,6 +24,7 @@ struct module;
enum { enum {
NFT_PKTINFO_L4PROTO = (1 << 0), NFT_PKTINFO_L4PROTO = (1 << 0),
NFT_PKTINFO_INNER = (1 << 1), NFT_PKTINFO_INNER = (1 << 1),
NFT_PKTINFO_INNER_FULL = (1 << 2),
}; };
struct nft_pktinfo { struct nft_pktinfo {
...@@ -32,8 +33,8 @@ struct nft_pktinfo { ...@@ -32,8 +33,8 @@ struct nft_pktinfo {
u8 flags; u8 flags;
u8 tprot; u8 tprot;
u16 fragoff; u16 fragoff;
unsigned int thoff; u16 thoff;
unsigned int inneroff; u16 inneroff;
}; };
static inline struct sock *nft_sk(const struct nft_pktinfo *pkt) static inline struct sock *nft_sk(const struct nft_pktinfo *pkt)
...@@ -375,6 +376,10 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) ...@@ -375,6 +376,10 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
return (void *)expr->data; return (void *)expr->data;
} }
struct nft_expr_info;
int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
struct nft_expr_info *info);
int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src); int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src);
void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr); void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
int nft_expr_dump(struct sk_buff *skb, unsigned int attr, int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
...@@ -864,6 +869,7 @@ struct nft_expr_type { ...@@ -864,6 +869,7 @@ struct nft_expr_type {
const struct nlattr * const tb[]); const struct nlattr * const tb[]);
void (*release_ops)(const struct nft_expr_ops *ops); void (*release_ops)(const struct nft_expr_ops *ops);
const struct nft_expr_ops *ops; const struct nft_expr_ops *ops;
const struct nft_expr_ops *inner_ops;
struct list_head list; struct list_head list;
const char *name; const char *name;
struct module *owner; struct module *owner;
......
...@@ -18,6 +18,8 @@ extern struct nft_expr_type nft_meta_type; ...@@ -18,6 +18,8 @@ extern struct nft_expr_type nft_meta_type;
extern struct nft_expr_type nft_rt_type; extern struct nft_expr_type nft_rt_type;
extern struct nft_expr_type nft_exthdr_type; extern struct nft_expr_type nft_exthdr_type;
extern struct nft_expr_type nft_last_type; extern struct nft_expr_type nft_last_type;
extern struct nft_expr_type nft_objref_type;
extern struct nft_expr_type nft_inner_type;
#ifdef CONFIG_NETWORK_SECMARK #ifdef CONFIG_NETWORK_SECMARK
extern struct nft_object_type nft_secmark_obj_type; extern struct nft_object_type nft_secmark_obj_type;
...@@ -66,16 +68,6 @@ struct nft_payload { ...@@ -66,16 +68,6 @@ struct nft_payload {
u8 dreg; u8 dreg;
}; };
struct nft_payload_set {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
u8 sreg;
u8 csum_type;
u8 csum_offset;
u8 csum_flags;
};
extern const struct nft_expr_ops nft_payload_fast_ops; extern const struct nft_expr_ops nft_payload_fast_ops;
extern const struct nft_expr_ops nft_bitwise_fast_ops; extern const struct nft_expr_ops nft_bitwise_fast_ops;
...@@ -148,4 +140,28 @@ void nft_rt_get_eval(const struct nft_expr *expr, ...@@ -148,4 +140,28 @@ void nft_rt_get_eval(const struct nft_expr *expr,
struct nft_regs *regs, const struct nft_pktinfo *pkt); struct nft_regs *regs, const struct nft_pktinfo *pkt);
void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs, void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt); const struct nft_pktinfo *pkt);
enum {
NFT_PAYLOAD_CTX_INNER_TUN = (1 << 0),
NFT_PAYLOAD_CTX_INNER_LL = (1 << 1),
NFT_PAYLOAD_CTX_INNER_NH = (1 << 2),
NFT_PAYLOAD_CTX_INNER_TH = (1 << 3),
};
struct nft_inner_tun_ctx {
u16 type;
u16 inner_tunoff;
u16 inner_lloff;
u16 inner_nhoff;
u16 inner_thoff;
__be16 llproto;
u8 l4proto;
u8 flags;
};
int nft_payload_inner_offset(const struct nft_pktinfo *pkt);
void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *ctx);
#endif /* _NET_NF_TABLES_CORE_H */ #endif /* _NET_NF_TABLES_CORE_H */
...@@ -35,6 +35,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) ...@@ -35,6 +35,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
return -1; return -1;
else if (len < thoff) else if (len < thoff)
return -1; return -1;
else if (thoff < sizeof(*iph))
return -1;
pkt->flags = NFT_PKTINFO_L4PROTO; pkt->flags = NFT_PKTINFO_L4PROTO;
pkt->tprot = iph->protocol; pkt->tprot = iph->protocol;
...@@ -69,6 +71,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) ...@@ -69,6 +71,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
return -1; return -1;
} else if (len < thoff) { } else if (len < thoff) {
goto inhdr_error; goto inhdr_error;
} else if (thoff < sizeof(*iph)) {
return -1;
} }
pkt->flags = NFT_PKTINFO_L4PROTO; pkt->flags = NFT_PKTINFO_L4PROTO;
......
...@@ -13,7 +13,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt) ...@@ -13,7 +13,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)
unsigned short frag_off; unsigned short frag_off;
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
if (protohdr < 0) { if (protohdr < 0 || thoff > U16_MAX) {
nft_set_pktinfo_unspec(pkt); nft_set_pktinfo_unspec(pkt);
return; return;
} }
...@@ -47,7 +47,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) ...@@ -47,7 +47,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
return -1; return -1;
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
if (protohdr < 0) if (protohdr < 0 || thoff > U16_MAX)
return -1; return -1;
pkt->flags = NFT_PKTINFO_L4PROTO; pkt->flags = NFT_PKTINFO_L4PROTO;
...@@ -93,7 +93,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt) ...@@ -93,7 +93,7 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)
} }
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags); protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
if (protohdr < 0) if (protohdr < 0 || thoff > U16_MAX)
goto inhdr_error; goto inhdr_error;
pkt->flags = NFT_PKTINFO_L4PROTO; pkt->flags = NFT_PKTINFO_L4PROTO;
......
...@@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx, ...@@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,
bool nft_meta_get_reduce(struct nft_regs_track *track, bool nft_meta_get_reduce(struct nft_regs_track *track,
const struct nft_expr *expr); const struct nft_expr *expr);
struct nft_inner_tun_ctx;
void nft_meta_inner_eval(const struct nft_expr *expr,
struct nft_regs *regs, const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx);
#endif #endif
...@@ -760,6 +760,7 @@ enum nft_payload_bases { ...@@ -760,6 +760,7 @@ enum nft_payload_bases {
NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_NETWORK_HEADER,
NFT_PAYLOAD_TRANSPORT_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER,
NFT_PAYLOAD_INNER_HEADER, NFT_PAYLOAD_INNER_HEADER,
NFT_PAYLOAD_TUN_HEADER,
}; };
/** /**
...@@ -779,6 +780,32 @@ enum nft_payload_csum_flags { ...@@ -779,6 +780,32 @@ enum nft_payload_csum_flags {
NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0), NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0),
}; };
enum nft_inner_type {
NFT_INNER_UNSPEC = 0,
NFT_INNER_VXLAN,
NFT_INNER_GENEVE,
};
enum nft_inner_flags {
NFT_INNER_HDRSIZE = (1 << 0),
NFT_INNER_LL = (1 << 1),
NFT_INNER_NH = (1 << 2),
NFT_INNER_TH = (1 << 3),
};
#define NFT_INNER_MASK (NFT_INNER_HDRSIZE | NFT_INNER_LL | \
NFT_INNER_NH | NFT_INNER_TH)
enum nft_inner_attributes {
NFTA_INNER_UNSPEC,
NFTA_INNER_NUM,
NFTA_INNER_TYPE,
NFTA_INNER_FLAGS,
NFTA_INNER_HDRSIZE,
NFTA_INNER_EXPR,
__NFTA_INNER_MAX
};
#define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1)
/** /**
* enum nft_payload_attributes - nf_tables payload expression netlink attributes * enum nft_payload_attributes - nf_tables payload expression netlink attributes
* *
......
...@@ -568,12 +568,6 @@ config NFT_TUNNEL ...@@ -568,12 +568,6 @@ config NFT_TUNNEL
This option adds the "tunnel" expression that you can use to set This option adds the "tunnel" expression that you can use to set
tunneling policies. tunneling policies.
config NFT_OBJREF
tristate "Netfilter nf_tables stateful object reference module"
help
This option adds the "objref" expression that allows you to refer to
stateful objects, such as counters and quotas.
config NFT_QUEUE config NFT_QUEUE
depends on NETFILTER_NETLINK_QUEUE depends on NETFILTER_NETLINK_QUEUE
tristate "Netfilter nf_tables queue module" tristate "Netfilter nf_tables queue module"
......
...@@ -86,7 +86,8 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \ ...@@ -86,7 +86,8 @@ nf_tables-objs := nf_tables_core.o nf_tables_api.o nft_chain_filter.o \
nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \ nf_tables_trace.o nft_immediate.o nft_cmp.o nft_range.o \
nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \ nft_bitwise.o nft_byteorder.o nft_payload.o nft_lookup.o \
nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \ nft_dynset.o nft_meta.o nft_rt.o nft_exthdr.o nft_last.o \
nft_counter.o nft_chain_route.o nf_tables_offload.o \ nft_counter.o nft_objref.o nft_inner.o \
nft_chain_route.o nf_tables_offload.o \
nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o \ nft_set_hash.o nft_set_bitmap.o nft_set_rbtree.o \
nft_set_pipapo.o nft_set_pipapo.o
...@@ -104,7 +105,6 @@ obj-$(CONFIG_NFT_CT) += nft_ct.o ...@@ -104,7 +105,6 @@ obj-$(CONFIG_NFT_CT) += nft_ct.o
obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o
obj-$(CONFIG_NFT_LIMIT) += nft_limit.o obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
obj-$(CONFIG_NFT_NAT) += nft_nat.o obj-$(CONFIG_NFT_NAT) += nft_nat.o
obj-$(CONFIG_NFT_OBJREF) += nft_objref.o
obj-$(CONFIG_NFT_QUEUE) += nft_queue.o obj-$(CONFIG_NFT_QUEUE) += nft_queue.o
obj-$(CONFIG_NFT_QUOTA) += nft_quota.o obj-$(CONFIG_NFT_QUOTA) += nft_quota.o
obj-$(CONFIG_NFT_REJECT) += nft_reject.o obj-$(CONFIG_NFT_REJECT) += nft_reject.o
......
...@@ -2857,6 +2857,43 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx, ...@@ -2857,6 +2857,43 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
return err; return err;
} }
int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
struct nft_expr_info *info)
{
struct nlattr *tb[NFTA_EXPR_MAX + 1];
const struct nft_expr_type *type;
int err;
err = nla_parse_nested_deprecated(tb, NFTA_EXPR_MAX, nla,
nft_expr_policy, NULL);
if (err < 0)
return err;
if (!tb[NFTA_EXPR_DATA])
return -EINVAL;
type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]);
if (IS_ERR(type))
return PTR_ERR(type);
if (!type->inner_ops)
return -EOPNOTSUPP;
err = nla_parse_nested_deprecated(info->tb, type->maxattr,
tb[NFTA_EXPR_DATA],
type->policy, NULL);
if (err < 0)
goto err_nla_parse;
info->attr = nla;
info->ops = type->inner_ops;
return 0;
err_nla_parse:
return err;
}
static int nf_tables_newexpr(const struct nft_ctx *ctx, static int nf_tables_newexpr(const struct nft_ctx *ctx,
const struct nft_expr_info *expr_info, const struct nft_expr_info *expr_info,
struct nft_expr *expr) struct nft_expr *expr)
......
...@@ -340,6 +340,8 @@ static struct nft_expr_type *nft_basic_types[] = { ...@@ -340,6 +340,8 @@ static struct nft_expr_type *nft_basic_types[] = {
&nft_exthdr_type, &nft_exthdr_type,
&nft_last_type, &nft_last_type,
&nft_counter_type, &nft_counter_type,
&nft_objref_type,
&nft_inner_type,
}; };
static struct nft_object_type *nft_basic_objects[] = { static struct nft_object_type *nft_basic_objects[] = {
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org>
*/
#include <linux/kernel.h>
#include <linux/if_vlan.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
#include <net/netfilter/nf_tables_offload.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <net/gre.h>
#include <net/geneve.h>
#include <net/ip.h>
#include <linux/icmpv6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
static DEFINE_PER_CPU(struct nft_inner_tun_ctx, nft_pcpu_tun_ctx);
/* Same layout as nft_expr but it embeds the private expression data area. */
struct __nft_expr {
const struct nft_expr_ops *ops;
union {
struct nft_payload payload;
struct nft_meta meta;
} __attribute__((aligned(__alignof__(u64))));
};
enum {
NFT_INNER_EXPR_PAYLOAD,
NFT_INNER_EXPR_META,
};
struct nft_inner {
u8 flags;
u8 hdrsize;
u8 type;
u8 expr_type;
struct __nft_expr expr;
};
static int nft_inner_parse_l2l3(const struct nft_inner *priv,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *ctx, u32 off)
{
__be16 llproto, outer_llproto;
u32 nhoff, thoff;
if (priv->flags & NFT_INNER_LL) {
struct vlan_ethhdr *veth, _veth;
struct ethhdr *eth, _eth;
u32 hdrsize;
eth = skb_header_pointer(pkt->skb, off, sizeof(_eth), &_eth);
if (!eth)
return -1;
switch (eth->h_proto) {
case htons(ETH_P_IP):
case htons(ETH_P_IPV6):
llproto = eth->h_proto;
hdrsize = sizeof(_eth);
break;
case htons(ETH_P_8021Q):
veth = skb_header_pointer(pkt->skb, off, sizeof(_veth), &_veth);
if (!eth)
return -1;
outer_llproto = veth->h_vlan_encapsulated_proto;
llproto = veth->h_vlan_proto;
hdrsize = sizeof(_veth);
break;
default:
return -1;
}
ctx->inner_lloff = off;
ctx->flags |= NFT_PAYLOAD_CTX_INNER_LL;
off += hdrsize;
} else {
struct iphdr *iph;
u32 _version;
iph = skb_header_pointer(pkt->skb, off, sizeof(_version), &_version);
if (!iph)
return -1;
switch (iph->version) {
case 4:
llproto = htons(ETH_P_IP);
break;
case 6:
llproto = htons(ETH_P_IPV6);
break;
default:
return -1;
}
}
ctx->llproto = llproto;
if (llproto == htons(ETH_P_8021Q))
llproto = outer_llproto;
nhoff = off;
switch (llproto) {
case htons(ETH_P_IP): {
struct iphdr *iph, _iph;
iph = skb_header_pointer(pkt->skb, nhoff, sizeof(_iph), &_iph);
if (!iph)
return -1;
if (iph->ihl < 5 || iph->version != 4)
return -1;
ctx->inner_nhoff = nhoff;
ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
thoff = nhoff + (iph->ihl * 4);
if ((ntohs(iph->frag_off) & IP_OFFSET) == 0) {
ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
ctx->inner_thoff = thoff;
ctx->l4proto = iph->protocol;
}
}
break;
case htons(ETH_P_IPV6): {
struct ipv6hdr *ip6h, _ip6h;
int fh_flags = IP6_FH_F_AUTH;
unsigned short fragoff;
int l4proto;
ip6h = skb_header_pointer(pkt->skb, nhoff, sizeof(_ip6h), &_ip6h);
if (!ip6h)
return -1;
if (ip6h->version != 6)
return -1;
ctx->inner_nhoff = nhoff;
ctx->flags |= NFT_PAYLOAD_CTX_INNER_NH;
thoff = nhoff;
l4proto = ipv6_find_hdr(pkt->skb, &thoff, -1, &fragoff, &fh_flags);
if (l4proto < 0 || thoff > U16_MAX)
return -1;
if (fragoff == 0) {
thoff = nhoff + sizeof(_ip6h);
ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH;
ctx->inner_thoff = thoff;
ctx->l4proto = l4proto;
}
}
break;
default:
return -1;
}
return 0;
}
static int nft_inner_parse_tunhdr(const struct nft_inner *priv,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *ctx, u32 *off)
{
if (pkt->tprot == IPPROTO_GRE) {
ctx->inner_tunoff = pkt->thoff;
ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
return 0;
}
if (pkt->tprot != IPPROTO_UDP)
return -1;
ctx->inner_tunoff = *off;
ctx->flags |= NFT_PAYLOAD_CTX_INNER_TUN;
*off += priv->hdrsize;
switch (priv->type) {
case NFT_INNER_GENEVE: {
struct genevehdr *gnvh, _gnvh;
gnvh = skb_header_pointer(pkt->skb, pkt->inneroff,
sizeof(_gnvh), &_gnvh);
if (!gnvh)
return -1;
*off += gnvh->opt_len * 4;
}
break;
default:
break;
}
return 0;
}
static int nft_inner_parse(const struct nft_inner *priv,
struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
struct nft_inner_tun_ctx ctx = {};
u32 off = pkt->inneroff;
if (priv->flags & NFT_INNER_HDRSIZE &&
nft_inner_parse_tunhdr(priv, pkt, &ctx, &off) < 0)
return -1;
if (priv->flags & (NFT_INNER_LL | NFT_INNER_NH)) {
if (nft_inner_parse_l2l3(priv, pkt, &ctx, off) < 0)
return -1;
} else if (priv->flags & NFT_INNER_TH) {
ctx.inner_thoff = off;
ctx.flags |= NFT_PAYLOAD_CTX_INNER_TH;
}
*tun_ctx = ctx;
tun_ctx->type = priv->type;
pkt->flags |= NFT_PKTINFO_INNER_FULL;
return 0;
}
static bool nft_inner_parse_needed(const struct nft_inner *priv,
const struct nft_pktinfo *pkt,
const struct nft_inner_tun_ctx *tun_ctx)
{
if (!(pkt->flags & NFT_PKTINFO_INNER_FULL))
return true;
if (priv->type != tun_ctx->type)
return true;
return false;
}
static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_inner_tun_ctx *tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx);
const struct nft_inner *priv = nft_expr_priv(expr);
if (nft_payload_inner_offset(pkt) < 0)
goto err;
if (nft_inner_parse_needed(priv, pkt, tun_ctx) &&
nft_inner_parse(priv, (struct nft_pktinfo *)pkt, tun_ctx) < 0)
goto err;
switch (priv->expr_type) {
case NFT_INNER_EXPR_PAYLOAD:
nft_payload_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
break;
case NFT_INNER_EXPR_META:
nft_meta_inner_eval((struct nft_expr *)&priv->expr, regs, pkt, tun_ctx);
break;
default:
WARN_ON_ONCE(1);
goto err;
}
return;
err:
regs->verdict.code = NFT_BREAK;
}
static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = {
[NFTA_INNER_NUM] = { .type = NLA_U32 },
[NFTA_INNER_FLAGS] = { .type = NLA_U32 },
[NFTA_INNER_HDRSIZE] = { .type = NLA_U32 },
[NFTA_INNER_TYPE] = { .type = NLA_U32 },
[NFTA_INNER_EXPR] = { .type = NLA_NESTED },
};
struct nft_expr_info {
const struct nft_expr_ops *ops;
const struct nlattr *attr;
struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
};
static int nft_inner_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_inner *priv = nft_expr_priv(expr);
u32 flags, hdrsize, type, num;
struct nft_expr_info expr_info;
int err;
if (!tb[NFTA_INNER_FLAGS] ||
!tb[NFTA_INNER_HDRSIZE] ||
!tb[NFTA_INNER_TYPE] ||
!tb[NFTA_INNER_EXPR])
return -EINVAL;
flags = ntohl(nla_get_be32(tb[NFTA_INNER_FLAGS]));
if (flags & ~NFT_INNER_MASK)
return -EOPNOTSUPP;
num = ntohl(nla_get_be32(tb[NFTA_INNER_NUM]));
if (num != 0)
return -EOPNOTSUPP;
hdrsize = ntohl(nla_get_be32(tb[NFTA_INNER_HDRSIZE]));
type = ntohl(nla_get_be32(tb[NFTA_INNER_TYPE]));
if (type > U8_MAX)
return -EINVAL;
if (flags & NFT_INNER_HDRSIZE) {
if (hdrsize == 0 || hdrsize > 64)
return -EOPNOTSUPP;
}
priv->flags = flags;
priv->hdrsize = hdrsize;
priv->type = type;
err = nft_expr_inner_parse(ctx, tb[NFTA_INNER_EXPR], &expr_info);
if (err < 0)
return err;
priv->expr.ops = expr_info.ops;
if (!strcmp(expr_info.ops->type->name, "payload"))
priv->expr_type = NFT_INNER_EXPR_PAYLOAD;
else if (!strcmp(expr_info.ops->type->name, "meta"))
priv->expr_type = NFT_INNER_EXPR_META;
else
return -EINVAL;
err = expr_info.ops->init(ctx, (struct nft_expr *)&priv->expr,
(const struct nlattr * const*)expr_info.tb);
if (err < 0)
return err;
return 0;
}
static int nft_inner_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_inner *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_INNER_NUM, htonl(0)) ||
nla_put_be32(skb, NFTA_INNER_TYPE, htonl(priv->type)) ||
nla_put_be32(skb, NFTA_INNER_FLAGS, htonl(priv->flags)) ||
nla_put_be32(skb, NFTA_INNER_HDRSIZE, htonl(priv->hdrsize)))
goto nla_put_failure;
if (nft_expr_dump(skb, NFTA_INNER_EXPR,
(struct nft_expr *)&priv->expr) < 0)
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static const struct nft_expr_ops nft_inner_ops = {
.type = &nft_inner_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_inner)),
.eval = nft_inner_eval,
.init = nft_inner_init,
.dump = nft_inner_dump,
};
struct nft_expr_type nft_inner_type __read_mostly = {
.name = "inner",
.ops = &nft_inner_ops,
.policy = nft_inner_policy,
.maxattr = NFTA_INNER_MAX,
.owner = THIS_MODULE,
};
...@@ -831,9 +831,71 @@ nft_meta_select_ops(const struct nft_ctx *ctx, ...@@ -831,9 +831,71 @@ nft_meta_select_ops(const struct nft_ctx *ctx,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
static int nft_meta_inner_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;
priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
switch (priv->key) {
case NFT_META_PROTOCOL:
len = sizeof(u16);
break;
case NFT_META_L4PROTO:
len = sizeof(u32);
break;
default:
return -EOPNOTSUPP;
}
priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len);
}
void nft_meta_inner_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
const struct nft_meta *priv = nft_expr_priv(expr);
u32 *dest = &regs->data[priv->dreg];
switch (priv->key) {
case NFT_META_PROTOCOL:
nft_reg_store16(dest, (__force u16)tun_ctx->llproto);
break;
case NFT_META_L4PROTO:
if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH))
goto err;
nft_reg_store8(dest, tun_ctx->l4proto);
break;
default:
WARN_ON_ONCE(1);
goto err;
}
return;
err:
regs->verdict.code = NFT_BREAK;
}
EXPORT_SYMBOL_GPL(nft_meta_inner_eval);
static const struct nft_expr_ops nft_meta_inner_ops = {
.type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.init = nft_meta_inner_init,
.dump = nft_meta_get_dump,
/* direct call to nft_meta_inner_eval(). */
};
struct nft_expr_type nft_meta_type __read_mostly = { struct nft_expr_type nft_meta_type __read_mostly = {
.name = "meta", .name = "meta",
.select_ops = nft_meta_select_ops, .select_ops = nft_meta_select_ops,
.inner_ops = &nft_meta_inner_ops,
.policy = nft_meta_policy, .policy = nft_meta_policy,
.maxattr = NFTA_META_MAX, .maxattr = NFTA_META_MAX,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -82,7 +82,6 @@ static void nft_objref_activate(const struct nft_ctx *ctx, ...@@ -82,7 +82,6 @@ static void nft_objref_activate(const struct nft_ctx *ctx,
obj->use++; obj->use++;
} }
static struct nft_expr_type nft_objref_type;
static const struct nft_expr_ops nft_objref_ops = { static const struct nft_expr_ops nft_objref_ops = {
.type = &nft_objref_type, .type = &nft_objref_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_object *)), .size = NFT_EXPR_SIZE(sizeof(struct nft_object *)),
...@@ -195,7 +194,6 @@ static void nft_objref_map_destroy(const struct nft_ctx *ctx, ...@@ -195,7 +194,6 @@ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
nf_tables_destroy_set(ctx, priv->set); nf_tables_destroy_set(ctx, priv->set);
} }
static struct nft_expr_type nft_objref_type;
static const struct nft_expr_ops nft_objref_map_ops = { static const struct nft_expr_ops nft_objref_map_ops = {
.type = &nft_objref_type, .type = &nft_objref_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)), .size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
...@@ -233,28 +231,10 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { ...@@ -233,28 +231,10 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = {
[NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 },
}; };
static struct nft_expr_type nft_objref_type __read_mostly = { struct nft_expr_type nft_objref_type __read_mostly = {
.name = "objref", .name = "objref",
.select_ops = nft_objref_select_ops, .select_ops = nft_objref_select_ops,
.policy = nft_objref_policy, .policy = nft_objref_policy,
.maxattr = NFTA_OBJREF_MAX, .maxattr = NFTA_OBJREF_MAX,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int __init nft_objref_module_init(void)
{
return nft_register_expr(&nft_objref_type);
}
static void __exit nft_objref_module_exit(void)
{
nft_unregister_expr(&nft_objref_type);
}
module_init(nft_objref_module_init);
module_exit(nft_objref_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_ALIAS_NFT_EXPR("objref");
MODULE_DESCRIPTION("nftables stateful object reference module");
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
/* For layer 4 checksum field offset. */ /* For layer 4 checksum field offset. */
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <net/gre.h>
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
...@@ -100,6 +101,40 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) ...@@ -100,6 +101,40 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt)
pkt->inneroff = thoff + __tcp_hdrlen(th); pkt->inneroff = thoff + __tcp_hdrlen(th);
} }
break; break;
case IPPROTO_GRE: {
u32 offset = sizeof(struct gre_base_hdr), version;
struct gre_base_hdr *gre, _gre;
gre = skb_header_pointer(pkt->skb, thoff, sizeof(_gre), &_gre);
if (!gre)
return -1;
version = gre->flags & GRE_VERSION;
switch (version) {
case GRE_VERSION_0:
if (gre->flags & GRE_ROUTING)
return -1;
if (gre->flags & GRE_CSUM) {
offset += sizeof_field(struct gre_full_hdr, csum) +
sizeof_field(struct gre_full_hdr, reserved1);
}
if (gre->flags & GRE_KEY)
offset += sizeof_field(struct gre_full_hdr, key);
if (gre->flags & GRE_SEQ)
offset += sizeof_field(struct gre_full_hdr, seq);
break;
default:
return -1;
}
pkt->inneroff = thoff + offset;
}
break;
case IPPROTO_IPIP:
pkt->inneroff = thoff;
break;
default: default:
return -1; return -1;
} }
...@@ -109,7 +144,7 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt) ...@@ -109,7 +144,7 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt)
return 0; return 0;
} }
static int nft_payload_inner_offset(const struct nft_pktinfo *pkt) int nft_payload_inner_offset(const struct nft_pktinfo *pkt)
{ {
if (!(pkt->flags & NFT_PKTINFO_INNER) && if (!(pkt->flags & NFT_PKTINFO_INNER) &&
__nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0) __nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0)
...@@ -552,6 +587,92 @@ const struct nft_expr_ops nft_payload_fast_ops = { ...@@ -552,6 +587,92 @@ const struct nft_expr_ops nft_payload_fast_ops = {
.offload = nft_payload_offload, .offload = nft_payload_offload,
}; };
void nft_payload_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt,
struct nft_inner_tun_ctx *tun_ctx)
{
const struct nft_payload *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb;
u32 *dest = &regs->data[priv->dreg];
int offset;
if (priv->len % NFT_REG32_SIZE)
dest[priv->len / NFT_REG32_SIZE] = 0;
switch (priv->base) {
case NFT_PAYLOAD_TUN_HEADER:
if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TUN))
goto err;
offset = tun_ctx->inner_tunoff;
break;
case NFT_PAYLOAD_LL_HEADER:
if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_LL))
goto err;
offset = tun_ctx->inner_lloff;
break;
case NFT_PAYLOAD_NETWORK_HEADER:
if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_NH))
goto err;
offset = tun_ctx->inner_nhoff;
break;
case NFT_PAYLOAD_TRANSPORT_HEADER:
if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH))
goto err;
offset = tun_ctx->inner_thoff;
break;
default:
WARN_ON_ONCE(1);
goto err;
}
offset += priv->offset;
if (skb_copy_bits(skb, offset, dest, priv->len) < 0)
goto err;
return;
err:
regs->verdict.code = NFT_BREAK;
}
static int nft_payload_inner_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_payload *priv = nft_expr_priv(expr);
u32 base;
base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE]));
switch (base) {
case NFT_PAYLOAD_TUN_HEADER:
case NFT_PAYLOAD_LL_HEADER:
case NFT_PAYLOAD_NETWORK_HEADER:
case NFT_PAYLOAD_TRANSPORT_HEADER:
break;
default:
return -EOPNOTSUPP;
}
priv->base = base;
priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET]));
priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN]));
return nft_parse_register_store(ctx, tb[NFTA_PAYLOAD_DREG],
&priv->dreg, NULL, NFT_DATA_VALUE,
priv->len);
}
static const struct nft_expr_ops nft_payload_inner_ops = {
.type = &nft_payload_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_payload)),
.init = nft_payload_inner_init,
.dump = nft_payload_dump,
/* direct call to nft_payload_inner_eval(). */
};
static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum) static inline void nft_csum_replace(__sum16 *sum, __wsum fsum, __wsum tsum)
{ {
*sum = csum_fold(csum_add(csum_sub(~csum_unfold(*sum), fsum), tsum)); *sum = csum_fold(csum_add(csum_sub(~csum_unfold(*sum), fsum), tsum));
...@@ -665,6 +786,16 @@ static int nft_payload_csum_inet(struct sk_buff *skb, const u32 *src, ...@@ -665,6 +786,16 @@ static int nft_payload_csum_inet(struct sk_buff *skb, const u32 *src,
return 0; return 0;
} }
struct nft_payload_set {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
u8 sreg;
u8 csum_type;
u8 csum_offset;
u8 csum_flags;
};
static void nft_payload_set_eval(const struct nft_expr *expr, static void nft_payload_set_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
...@@ -885,6 +1016,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx, ...@@ -885,6 +1016,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
struct nft_expr_type nft_payload_type __read_mostly = { struct nft_expr_type nft_payload_type __read_mostly = {
.name = "payload", .name = "payload",
.select_ops = nft_payload_select_ops, .select_ops = nft_payload_select_ops,
.inner_ops = &nft_payload_inner_ops,
.policy = nft_payload_policy, .policy = nft_payload_policy,
.maxattr = NFTA_PAYLOAD_MAX, .maxattr = NFTA_PAYLOAD_MAX,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment