mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 20:51:44 +00:00
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.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
12dee519d4
@ -24,6 +24,7 @@ struct module;
|
||||
enum {
|
||||
NFT_PKTINFO_L4PROTO = (1 << 0),
|
||||
NFT_PKTINFO_INNER = (1 << 1),
|
||||
NFT_PKTINFO_INNER_FULL = (1 << 2),
|
||||
};
|
||||
|
||||
struct nft_pktinfo {
|
||||
@ -32,8 +33,8 @@ struct nft_pktinfo {
|
||||
u8 flags;
|
||||
u8 tprot;
|
||||
u16 fragoff;
|
||||
unsigned int thoff;
|
||||
unsigned int inneroff;
|
||||
u16 thoff;
|
||||
u16 inneroff;
|
||||
};
|
||||
|
||||
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)
|
||||
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);
|
||||
void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
|
||||
int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
|
||||
@ -864,6 +869,7 @@ struct nft_expr_type {
|
||||
const struct nlattr * const tb[]);
|
||||
void (*release_ops)(const struct nft_expr_ops *ops);
|
||||
const struct nft_expr_ops *ops;
|
||||
const struct nft_expr_ops *inner_ops;
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
struct module *owner;
|
||||
|
@ -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_exthdr_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
|
||||
extern struct nft_object_type nft_secmark_obj_type;
|
||||
@ -66,16 +68,6 @@ struct nft_payload {
|
||||
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_bitwise_fast_ops;
|
||||
@ -148,4 +140,28 @@ void nft_rt_get_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs, const struct nft_pktinfo *pkt);
|
||||
void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs,
|
||||
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 */
|
||||
|
@ -35,6 +35,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
|
||||
return -1;
|
||||
else if (len < thoff)
|
||||
return -1;
|
||||
else if (thoff < sizeof(*iph))
|
||||
return -1;
|
||||
|
||||
pkt->flags = NFT_PKTINFO_L4PROTO;
|
||||
pkt->tprot = iph->protocol;
|
||||
@ -69,6 +71,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
|
||||
return -1;
|
||||
} else if (len < thoff) {
|
||||
goto inhdr_error;
|
||||
} else if (thoff < sizeof(*iph)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkt->flags = NFT_PKTINFO_L4PROTO;
|
||||
|
@ -13,7 +13,7 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)
|
||||
unsigned short frag_off;
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -47,7 +47,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
|
||||
return -1;
|
||||
|
||||
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
|
||||
if (protohdr < 0)
|
||||
if (protohdr < 0 || thoff > U16_MAX)
|
||||
return -1;
|
||||
|
||||
pkt->flags = NFT_PKTINFO_L4PROTO;
|
||||
@ -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);
|
||||
if (protohdr < 0)
|
||||
if (protohdr < 0 || thoff > U16_MAX)
|
||||
goto inhdr_error;
|
||||
|
||||
pkt->flags = NFT_PKTINFO_L4PROTO;
|
||||
|
@ -46,4 +46,10 @@ int nft_meta_set_validate(const struct nft_ctx *ctx,
|
||||
|
||||
bool nft_meta_get_reduce(struct nft_regs_track *track,
|
||||
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
|
||||
|
@ -760,6 +760,7 @@ enum nft_payload_bases {
|
||||
NFT_PAYLOAD_NETWORK_HEADER,
|
||||
NFT_PAYLOAD_TRANSPORT_HEADER,
|
||||
NFT_PAYLOAD_INNER_HEADER,
|
||||
NFT_PAYLOAD_TUN_HEADER,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -779,6 +780,32 @@ enum nft_payload_csum_flags {
|
||||
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
|
||||
*
|
||||
|
@ -568,12 +568,6 @@ config NFT_TUNNEL
|
||||
This option adds the "tunnel" expression that you can use to set
|
||||
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
|
||||
depends on NETFILTER_NETLINK_QUEUE
|
||||
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 \
|
||||
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_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_pipapo.o
|
||||
|
||||
@ -104,7 +105,6 @@ obj-$(CONFIG_NFT_CT) += nft_ct.o
|
||||
obj-$(CONFIG_NFT_FLOW_OFFLOAD) += nft_flow_offload.o
|
||||
obj-$(CONFIG_NFT_LIMIT) += nft_limit.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_QUOTA) += nft_quota.o
|
||||
obj-$(CONFIG_NFT_REJECT) += nft_reject.o
|
||||
|
@ -2857,6 +2857,43 @@ err1:
|
||||
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,
|
||||
const struct nft_expr_info *expr_info,
|
||||
struct nft_expr *expr)
|
||||
|
@ -340,6 +340,8 @@ static struct nft_expr_type *nft_basic_types[] = {
|
||||
&nft_exthdr_type,
|
||||
&nft_last_type,
|
||||
&nft_counter_type,
|
||||
&nft_objref_type,
|
||||
&nft_inner_type,
|
||||
};
|
||||
|
||||
static struct nft_object_type *nft_basic_objects[] = {
|
||||
|
384
net/netfilter/nft_inner.c
Normal file
384
net/netfilter/nft_inner.c
Normal file
@ -0,0 +1,384 @@
|
||||
// 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,
|
||||
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 = ®s->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 = {
|
||||
.name = "meta",
|
||||
.select_ops = nft_meta_select_ops,
|
||||
.inner_ops = &nft_meta_inner_ops,
|
||||
.policy = nft_meta_policy,
|
||||
.maxattr = NFTA_META_MAX,
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -82,7 +82,6 @@ static void nft_objref_activate(const struct nft_ctx *ctx,
|
||||
obj->use++;
|
||||
}
|
||||
|
||||
static struct nft_expr_type nft_objref_type;
|
||||
static const struct nft_expr_ops nft_objref_ops = {
|
||||
.type = &nft_objref_type,
|
||||
.size = NFT_EXPR_SIZE(sizeof(struct nft_object *)),
|
||||
@ -195,7 +194,6 @@ static void nft_objref_map_destroy(const struct nft_ctx *ctx,
|
||||
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 = {
|
||||
.type = &nft_objref_type,
|
||||
.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] = {
|
||||
[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",
|
||||
.select_ops = nft_objref_select_ops,
|
||||
.policy = nft_objref_policy,
|
||||
.maxattr = NFTA_OBJREF_MAX,
|
||||
.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 @@
|
||||
/* For layer 4 checksum field offset. */
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/gre.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
@ -100,6 +101,40 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt)
|
||||
pkt->inneroff = thoff + __tcp_hdrlen(th);
|
||||
}
|
||||
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:
|
||||
return -1;
|
||||
}
|
||||
@ -109,7 +144,7 @@ static int __nft_payload_inner_offset(struct nft_pktinfo *pkt)
|
||||
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) &&
|
||||
__nft_payload_inner_offset((struct nft_pktinfo *)pkt) < 0)
|
||||
@ -552,6 +587,92 @@ const struct nft_expr_ops nft_payload_fast_ops = {
|
||||
.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 = ®s->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)
|
||||
{
|
||||
*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,
|
||||
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,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
@ -885,6 +1016,7 @@ nft_payload_select_ops(const struct nft_ctx *ctx,
|
||||
struct nft_expr_type nft_payload_type __read_mostly = {
|
||||
.name = "payload",
|
||||
.select_ops = nft_payload_select_ops,
|
||||
.inner_ops = &nft_payload_inner_ops,
|
||||
.policy = nft_payload_policy,
|
||||
.maxattr = NFTA_PAYLOAD_MAX,
|
||||
.owner = THIS_MODULE,
|
||||
|
Loading…
Reference in New Issue
Block a user