1
0
Fork 0

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

Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

1) Missing dependencies in NFT_BRIDGE_REJECT, from Randy Dunlap.

2) Use atomic_inc_return() instead of atomic_add_return() in IPVS,
   from Yejune Deng.

3) Simplify check for overquota in xt_nfacct, from Kaixu Xia.

4) Move nfnl_acct_list away from struct net, from Miao Wang.

5) Pass actual sk in reject actions, from Jan Engelhardt.

6) Add timeout and protoinfo to ctnetlink destroy events,
   from Florian Westphal.

7) Four patches to generalize set infrastructure to support
   for multiple expressions per set element.

* git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next:
  netfilter: nftables: netlink support for several set element expressions
  netfilter: nftables: generalize set extension to support for several expressions
  netfilter: nftables: move nft_expr before nft_set
  netfilter: nftables: generalize set expressions support
  netfilter: ctnetlink: add timeout and protoinfo to destroy events
  netfilter: use actual socket sk for REJECT action
  netfilter: nfnl_acct: remove data from struct net
  netfilter: Remove unnecessary conversion to bool
  ipvs: replace atomic_add_return()
  netfilter: nft_reject_bridge: fix build errors due to code movement
====================

Link: https://lore.kernel.org/r/20201212230513.3465-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
zero-sugar-mainline-defconfig
Jakub Kicinski 2020-12-14 15:43:20 -08:00
commit 7bca5021a4
25 changed files with 537 additions and 163 deletions

View File

@ -151,9 +151,6 @@ struct net {
#endif
struct sock *nfnl;
struct sock *nfnl_stash;
#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
struct list_head nfnl_acct_list;
#endif
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
struct list_head nfct_timeout_list;
#endif

View File

@ -8,8 +8,8 @@
#include <net/netfilter/nf_reject.h>
void nf_send_unreach(struct sk_buff *skb_in, int code, int hook);
void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook);
void nf_send_reset(struct net *net, struct sock *, struct sk_buff *oldskb,
int hook);
const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *_oth, int hook);
struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb,

View File

@ -7,9 +7,8 @@
void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
unsigned int hooknum);
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook);
void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
int hook);
const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
struct tcphdr *otcph,
unsigned int *otcplen, int hook);

View File

@ -32,7 +32,7 @@ struct nf_conntrack_l4proto {
/* convert protoinfo to nfnetink attributes */
int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla,
struct nf_conn *ct);
struct nf_conn *ct, bool destroy);
/* convert nfnetlink attributes to protoinfo */
int (*from_nlattr)(struct nlattr *tb[], struct nf_conn *ct);

View File

@ -305,8 +305,33 @@ struct nft_set_estimate {
enum nft_set_class space;
};
#define NFT_EXPR_MAXATTR 16
#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \
ALIGN(size, __alignof__(struct nft_expr)))
/**
* struct nft_expr - nf_tables expression
*
* @ops: expression ops
* @data: expression private data
*/
struct nft_expr {
const struct nft_expr_ops *ops;
unsigned char data[]
__attribute__((aligned(__alignof__(u64))));
};
static inline void *nft_expr_priv(const struct nft_expr *expr)
{
return (void *)expr->data;
}
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,
const struct nft_expr *expr);
struct nft_set_ext;
struct nft_expr;
/**
* struct nft_set_ops - nf_tables set operations
@ -396,6 +421,22 @@ struct nft_set_type {
};
#define to_set_type(o) container_of(o, struct nft_set_type, ops)
struct nft_set_elem_expr {
u8 size;
unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr))));
};
#define nft_setelem_expr_at(__elem_expr, __offset) \
((struct nft_expr *)&__elem_expr->data[__offset])
#define nft_setelem_expr_foreach(__expr, __elem_expr, __size) \
for (__expr = nft_setelem_expr_at(__elem_expr, 0), __size = 0; \
__size < (__elem_expr)->size; \
__size += (__expr)->ops->size, __expr = ((void *)(__expr)) + (__expr)->ops->size)
#define NFT_SET_EXPR_MAX 2
/**
* struct nft_set - nf_tables set instance
*
@ -448,13 +489,14 @@ struct nft_set {
u16 policy;
u16 udlen;
unsigned char *udata;
struct nft_expr *expr;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
u16 flags:14,
genmask:2;
u8 klen;
u8 dlen;
u8 num_exprs;
struct nft_expr *exprs[NFT_SET_EXPR_MAX];
unsigned char data[]
__attribute__((aligned(__alignof__(u64))));
};
@ -519,7 +561,7 @@ void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
* @NFT_SET_EXT_TIMEOUT: element timeout
* @NFT_SET_EXT_EXPIRATION: element expiration time
* @NFT_SET_EXT_USERDATA: user data associated with the element
* @NFT_SET_EXT_EXPR: expression assiociated with the element
* @NFT_SET_EXT_EXPRESSIONS: expressions assiciated with the element
* @NFT_SET_EXT_OBJREF: stateful object reference associated with element
* @NFT_SET_EXT_NUM: number of extension types
*/
@ -531,7 +573,7 @@ enum nft_set_extensions {
NFT_SET_EXT_TIMEOUT,
NFT_SET_EXT_EXPIRATION,
NFT_SET_EXT_USERDATA,
NFT_SET_EXT_EXPR,
NFT_SET_EXT_EXPRESSIONS,
NFT_SET_EXT_OBJREF,
NFT_SET_EXT_NUM
};
@ -649,9 +691,9 @@ static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext
return nft_set_ext(ext, NFT_SET_EXT_USERDATA);
}
static inline struct nft_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
static inline struct nft_set_elem_expr *nft_set_ext_expr(const struct nft_set_ext *ext)
{
return nft_set_ext(ext, NFT_SET_EXT_EXPR);
return nft_set_ext(ext, NFT_SET_EXT_EXPRESSIONS);
}
static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
@ -794,7 +836,6 @@ struct nft_offload_ctx;
* @validate: validate expression, called during loop detection
* @data: extra data to attach to this expression operation
*/
struct nft_expr;
struct nft_expr_ops {
void (*eval)(const struct nft_expr *expr,
struct nft_regs *regs,
@ -830,32 +871,6 @@ struct nft_expr_ops {
void *data;
};
#define NFT_EXPR_MAXATTR 16
#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \
ALIGN(size, __alignof__(struct nft_expr)))
/**
* struct nft_expr - nf_tables expression
*
* @ops: expression ops
* @data: expression private data
*/
struct nft_expr {
const struct nft_expr_ops *ops;
unsigned char data[]
__attribute__((aligned(__alignof__(u64))));
};
static inline void *nft_expr_priv(const struct nft_expr *expr)
{
return (void *)expr->data;
}
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,
const struct nft_expr *expr);
/**
* struct nft_rule - nf_tables rule
*
@ -908,11 +923,17 @@ static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_set_elem_expr *elem_expr;
struct nft_expr *expr;
u32 size;
if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) {
expr = nft_set_ext_expr(ext);
expr->ops->eval(expr, regs, pkt);
if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) {
elem_expr = nft_set_ext_expr(ext);
nft_setelem_expr_foreach(expr, elem_expr, size) {
expr->ops->eval(expr, regs, pkt);
if (regs->verdict.code == NFT_BREAK)
return;
}
}
}

View File

@ -361,6 +361,7 @@ enum nft_set_field_attributes {
* @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*)
* @NFTA_SET_HANDLE: set handle (NLA_U64)
* @NFTA_SET_EXPR: set expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_SET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_set_attributes {
NFTA_SET_UNSPEC,
@ -381,6 +382,7 @@ enum nft_set_attributes {
NFTA_SET_OBJ_TYPE,
NFTA_SET_HANDLE,
NFTA_SET_EXPR,
NFTA_SET_EXPRESSIONS,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
@ -406,6 +408,7 @@ enum nft_set_elem_flags {
* @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING)
* @NFTA_SET_ELEM_KEY_END: closing key value (NLA_NESTED: nft_data)
* @NFTA_SET_ELEM_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC,
@ -419,6 +422,7 @@ enum nft_set_elem_attributes {
NFTA_SET_ELEM_PAD,
NFTA_SET_ELEM_OBJREF,
NFTA_SET_ELEM_KEY_END,
NFTA_SET_ELEM_EXPRESSIONS,
__NFTA_SET_ELEM_MAX
};
#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
@ -715,6 +719,7 @@ enum nft_dynset_flags {
* @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
* @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
* @NFTA_DYNSET_FLAGS: flags (NLA_U32)
* @NFTA_DYNSET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
*/
enum nft_dynset_attributes {
NFTA_DYNSET_UNSPEC,
@ -727,6 +732,7 @@ enum nft_dynset_attributes {
NFTA_DYNSET_EXPR,
NFTA_DYNSET_PAD,
NFTA_DYNSET_FLAGS,
NFTA_DYNSET_EXPRESSIONS,
__NFTA_DYNSET_MAX,
};
#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)

View File

@ -18,6 +18,8 @@ config NFT_BRIDGE_META
config NFT_BRIDGE_REJECT
tristate "Netfilter nf_tables bridge reject support"
depends on NFT_REJECT
depends on NF_REJECT_IPV4
depends on NF_REJECT_IPV6
help
Add support to reject packets.

View File

@ -56,7 +56,8 @@ reject_tg(struct sk_buff *skb, const struct xt_action_param *par)
nf_send_unreach(skb, ICMP_PKT_FILTERED, hook);
break;
case IPT_TCP_RESET:
nf_send_reset(xt_net(par), skb, hook);
nf_send_reset(xt_net(par), par->state->sk, skb, hook);
break;
case IPT_ICMP_ECHOREPLY:
/* Doesn't happen. */
break;

View File

@ -234,7 +234,8 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
}
/* Send RST reply */
void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
int hook)
{
struct net_device *br_indev __maybe_unused;
struct sk_buff *nskb;
@ -267,8 +268,7 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
ip4_dst_hoplimit(skb_dst(nskb)));
nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
if (ip_route_me_harder(net, nskb->sk, nskb, RTN_UNSPEC))
if (ip_route_me_harder(net, sk, nskb, RTN_UNSPEC))
goto free_nskb;
niph = ip_hdr(nskb);

View File

@ -27,7 +27,8 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr,
nf_send_unreach(pkt->skb, priv->icmp_code, nft_hook(pkt));
break;
case NFT_REJECT_TCP_RST:
nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt));
nf_send_reset(nft_net(pkt), pkt->xt.state->sk, pkt->skb,
nft_hook(pkt));
break;
default:
break;

View File

@ -61,7 +61,7 @@ reject_tg6(struct sk_buff *skb, const struct xt_action_param *par)
/* Do nothing */
break;
case IP6T_TCP_RESET:
nf_send_reset6(net, skb, xt_hooknum(par));
nf_send_reset6(net, par->state->sk, skb, xt_hooknum(par));
break;
case IP6T_ICMP6_POLICY_FAIL:
nf_send_unreach6(net, skb, ICMPV6_POLICY_FAIL, xt_hooknum(par));

View File

@ -275,7 +275,8 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
return 0;
}
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
int hook)
{
struct net_device *br_indev __maybe_unused;
struct sk_buff *nskb;
@ -367,7 +368,7 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
dev_queue_xmit(nskb);
} else
#endif
ip6_local_out(net, nskb->sk, nskb);
ip6_local_out(net, sk, nskb);
}
EXPORT_SYMBOL_GPL(nf_send_reset6);

View File

@ -28,7 +28,8 @@ static void nft_reject_ipv6_eval(const struct nft_expr *expr,
nft_hook(pkt));
break;
case NFT_REJECT_TCP_RST:
nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt));
nf_send_reset6(nft_net(pkt), pkt->xt.state->sk, pkt->skb,
nft_hook(pkt));
break;
default:
break;

View File

@ -2137,7 +2137,7 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
if (cp->flags & IP_VS_CONN_F_ONE_PACKET)
pkts = sysctl_sync_threshold(ipvs);
else
pkts = atomic_add_return(1, &cp->in_pkts);
pkts = atomic_inc_return(&cp->in_pkts);
if (ipvs->sync_state & IP_VS_STATE_MASTER)
ip_vs_sync_conn(ipvs, cp, pkts);

View File

@ -615,7 +615,7 @@ static void ip_vs_sync_conn_v0(struct netns_ipvs *ipvs, struct ip_vs_conn *cp,
cp = cp->control;
if (cp) {
if (cp->flags & IP_VS_CONN_F_TEMPLATE)
pkts = atomic_add_return(1, &cp->in_pkts);
pkts = atomic_inc_return(&cp->in_pkts);
else
pkts = sysctl_sync_threshold(ipvs);
ip_vs_sync_conn(ipvs, cp, pkts);
@ -776,7 +776,7 @@ control:
if (!cp)
return;
if (cp->flags & IP_VS_CONN_F_TEMPLATE)
pkts = atomic_add_return(1, &cp->in_pkts);
pkts = atomic_inc_return(&cp->in_pkts);
else
pkts = sysctl_sync_threshold(ipvs);
goto sloop;

View File

@ -167,10 +167,14 @@ nla_put_failure:
return -1;
}
static int ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct)
static int ctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct,
bool skip_zero)
{
long timeout = nf_ct_expires(ct) / HZ;
if (skip_zero && timeout == 0)
return 0;
if (nla_put_be32(skb, CTA_TIMEOUT, htonl(timeout)))
goto nla_put_failure;
return 0;
@ -179,7 +183,8 @@ nla_put_failure:
return -1;
}
static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct)
static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct,
bool destroy)
{
const struct nf_conntrack_l4proto *l4proto;
struct nlattr *nest_proto;
@ -193,7 +198,7 @@ static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct)
if (!nest_proto)
goto nla_put_failure;
ret = l4proto->to_nlattr(skb, nest_proto, ct);
ret = l4proto->to_nlattr(skb, nest_proto, ct, destroy);
nla_nest_end(skb, nest_proto);
@ -537,8 +542,8 @@ static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct)
return -1;
if (!test_bit(IPS_OFFLOAD_BIT, &ct->status) &&
(ctnetlink_dump_timeout(skb, ct) < 0 ||
ctnetlink_dump_protoinfo(skb, ct) < 0))
(ctnetlink_dump_timeout(skb, ct, false) < 0 ||
ctnetlink_dump_protoinfo(skb, ct, false) < 0))
return -1;
return 0;
@ -780,15 +785,19 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
goto nla_put_failure;
if (events & (1 << IPCT_DESTROY)) {
if (ctnetlink_dump_acct(skb, ct, type) < 0 ||
ctnetlink_dump_timestamp(skb, ct) < 0)
goto nla_put_failure;
} else {
if (ctnetlink_dump_timeout(skb, ct) < 0)
if (ctnetlink_dump_timeout(skb, ct, true) < 0)
goto nla_put_failure;
if (events & (1 << IPCT_PROTOINFO)
&& ctnetlink_dump_protoinfo(skb, ct) < 0)
if (ctnetlink_dump_acct(skb, ct, type) < 0 ||
ctnetlink_dump_timestamp(skb, ct) < 0 ||
ctnetlink_dump_protoinfo(skb, ct, true) < 0)
goto nla_put_failure;
} else {
if (ctnetlink_dump_timeout(skb, ct, false) < 0)
goto nla_put_failure;
if (events & (1 << IPCT_PROTOINFO) &&
ctnetlink_dump_protoinfo(skb, ct, false) < 0)
goto nla_put_failure;
if ((events & (1 << IPCT_HELPER) || nfct_help(ct))
@ -2720,10 +2729,10 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct)
if (ctnetlink_dump_status(skb, ct) < 0)
goto nla_put_failure;
if (ctnetlink_dump_timeout(skb, ct) < 0)
if (ctnetlink_dump_timeout(skb, ct, false) < 0)
goto nla_put_failure;
if (ctnetlink_dump_protoinfo(skb, ct) < 0)
if (ctnetlink_dump_protoinfo(skb, ct, false) < 0)
goto nla_put_failure;
if (ctnetlink_dump_helpinfo(skb, ct) < 0)

View File

@ -589,7 +589,7 @@ static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
struct nf_conn *ct)
struct nf_conn *ct, bool destroy)
{
struct nlattr *nest_parms;
@ -597,15 +597,22 @@ static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP);
if (!nest_parms)
goto nla_put_failure;
if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state) ||
nla_put_u8(skb, CTA_PROTOINFO_DCCP_ROLE,
if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state))
goto nla_put_failure;
if (destroy)
goto skip_state;
if (nla_put_u8(skb, CTA_PROTOINFO_DCCP_ROLE,
ct->proto.dccp.role[IP_CT_DIR_ORIGINAL]) ||
nla_put_be64(skb, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
cpu_to_be64(ct->proto.dccp.handshake_seq),
CTA_PROTOINFO_DCCP_PAD))
goto nla_put_failure;
skip_state:
nla_nest_end(skb, nest_parms);
spin_unlock_bh(&ct->lock);
return 0;
nla_put_failure:

View File

@ -543,7 +543,7 @@ static bool sctp_can_early_drop(const struct nf_conn *ct)
#include <linux/netfilter/nfnetlink_conntrack.h>
static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
struct nf_conn *ct)
struct nf_conn *ct, bool destroy)
{
struct nlattr *nest_parms;
@ -552,15 +552,20 @@ static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
if (!nest_parms)
goto nla_put_failure;
if (nla_put_u8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state) ||
nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
if (nla_put_u8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state))
goto nla_put_failure;
if (destroy)
goto skip_state;
if (nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL]) ||
nla_put_be32(skb, CTA_PROTOINFO_SCTP_VTAG_REPLY,
ct->proto.sctp.vtag[IP_CT_DIR_REPLY]))
goto nla_put_failure;
skip_state:
spin_unlock_bh(&ct->lock);
nla_nest_end(skb, nest_parms);
return 0;

View File

@ -1186,7 +1186,7 @@ static bool tcp_can_early_drop(const struct nf_conn *ct)
#include <linux/netfilter/nfnetlink_conntrack.h>
static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
struct nf_conn *ct)
struct nf_conn *ct, bool destroy)
{
struct nlattr *nest_parms;
struct nf_ct_tcp_flags tmp = {};
@ -1196,8 +1196,13 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
if (!nest_parms)
goto nla_put_failure;
if (nla_put_u8(skb, CTA_PROTOINFO_TCP_STATE, ct->proto.tcp.state) ||
nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
if (nla_put_u8(skb, CTA_PROTOINFO_TCP_STATE, ct->proto.tcp.state))
goto nla_put_failure;
if (destroy)
goto skip_state;
if (nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
ct->proto.tcp.seen[0].td_scale) ||
nla_put_u8(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY,
ct->proto.tcp.seen[1].td_scale))
@ -1212,8 +1217,8 @@ static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
if (nla_put(skb, CTA_PROTOINFO_TCP_FLAGS_REPLY,
sizeof(struct nf_ct_tcp_flags), &tmp))
goto nla_put_failure;
skip_state:
spin_unlock_bh(&ct->lock);
nla_nest_end(skb, nest_parms);
return 0;

View File

@ -3571,6 +3571,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
[NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 },
[NFTA_SET_HANDLE] = { .type = NLA_U64 },
[NFTA_SET_EXPR] = { .type = NLA_NESTED },
[NFTA_SET_EXPRESSIONS] = { .type = NLA_NESTED },
};
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
@ -3778,6 +3779,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
u32 portid = ctx->portid;
struct nlattr *nest;
u32 seq = ctx->seq;
int i;
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event);
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
@ -3846,12 +3848,23 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
nla_nest_end(skb, nest);
if (set->expr) {
if (set->num_exprs == 1) {
nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR);
if (nf_tables_fill_expr_info(skb, set->expr) < 0)
if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
} else if (set->num_exprs > 1) {
nest = nla_nest_start_noflag(skb, NFTA_SET_EXPRESSIONS);
if (nest == NULL)
goto nla_put_failure;
for (i = 0; i < set->num_exprs; i++) {
if (nft_expr_dump(skb, NFTA_LIST_ELEM,
set->exprs[i]) < 0)
goto nla_put_failure;
}
nla_nest_end(skb, nest);
}
nlmsg_end(skb, nlh);
@ -4220,7 +4233,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
return err;
}
if (nla[NFTA_SET_EXPR])
if (nla[NFTA_SET_EXPR] || nla[NFTA_SET_EXPRESSIONS])
desc.expr = true;
table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask);
@ -4284,6 +4297,31 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
err = PTR_ERR(expr);
goto err_set_alloc_name;
}
set->exprs[0] = expr;
set->num_exprs++;
} else if (nla[NFTA_SET_EXPRESSIONS]) {
struct nft_expr *expr;
struct nlattr *tmp;
int left;
i = 0;
nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
if (i == NFT_SET_EXPR_MAX) {
err = -E2BIG;
goto err_set_init;
}
if (nla_type(tmp) != NFTA_LIST_ELEM) {
err = -EINVAL;
goto err_set_init;
}
expr = nft_set_elem_expr_alloc(&ctx, set, tmp);
if (IS_ERR(expr)) {
err = PTR_ERR(expr);
goto err_set_init;
}
set->exprs[i++] = expr;
set->num_exprs++;
}
}
udata = NULL;
@ -4301,7 +4339,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
set->dtype = dtype;
set->objtype = objtype;
set->dlen = desc.dlen;
set->expr = expr;
set->flags = flags;
set->size = desc.size;
set->policy = policy;
@ -4330,8 +4367,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
err_set_trans:
ops->destroy(set);
err_set_init:
if (expr)
nft_expr_destroy(&ctx, expr);
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(&ctx, set->exprs[i]);
err_set_alloc_name:
kfree(set->name);
err_set_name:
@ -4341,11 +4378,13 @@ err_set_name:
static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
int i;
if (WARN_ON(set->use > 0))
return;
if (set->expr)
nft_expr_destroy(ctx, set->expr);
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(ctx, set->exprs[i]);
set->ops->destroy(set);
kfree(set->name);
@ -4498,8 +4537,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
[NFT_SET_EXT_DATA] = {
.align = __alignof__(u32),
},
[NFT_SET_EXT_EXPR] = {
.align = __alignof__(struct nft_expr),
[NFT_SET_EXT_EXPRESSIONS] = {
.align = __alignof__(struct nft_set_elem_expr),
},
[NFT_SET_EXT_OBJREF] = {
.len = sizeof(struct nft_object *),
@ -4542,6 +4581,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
[NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING,
.len = NFT_OBJ_MAXNAMELEN - 1 },
[NFTA_SET_ELEM_KEY_END] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_EXPRESSIONS] = { .type = NLA_NESTED },
};
static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@ -4575,6 +4615,43 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
return 0;
}
static int nft_set_elem_expr_dump(struct sk_buff *skb,
const struct nft_set *set,
const struct nft_set_ext *ext)
{
struct nft_set_elem_expr *elem_expr;
u32 size, num_exprs = 0;
struct nft_expr *expr;
struct nlattr *nest;
elem_expr = nft_set_ext_expr(ext);
nft_setelem_expr_foreach(expr, elem_expr, size)
num_exprs++;
if (num_exprs == 1) {
expr = nft_setelem_expr_at(elem_expr, 0);
if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0)
return -1;
return 0;
} else if (num_exprs > 1) {
nest = nla_nest_start_noflag(skb, NFTA_SET_ELEM_EXPRESSIONS);
if (nest == NULL)
goto nla_put_failure;
nft_setelem_expr_foreach(expr, elem_expr, size) {
expr = nft_setelem_expr_at(elem_expr, size);
if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0)
goto nla_put_failure;
}
nla_nest_end(skb, nest);
}
return 0;
nla_put_failure:
return -1;
}
static int nf_tables_fill_setelem(struct sk_buff *skb,
const struct nft_set *set,
const struct nft_set_elem *elem)
@ -4602,8 +4679,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
set->dlen) < 0)
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) &&
nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0)
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) &&
nft_set_elem_expr_dump(skb, set, ext))
goto nla_put_failure;
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
@ -5098,8 +5175,8 @@ void *nft_set_elem_init(const struct nft_set *set,
return elem;
}
static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_expr *expr)
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_expr *expr)
{
if (expr->ops->destroy_clone) {
expr->ops->destroy_clone(ctx, expr);
@ -5109,6 +5186,16 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
}
}
static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
struct nft_set_elem_expr *elem_expr)
{
struct nft_expr *expr;
u32 size;
nft_setelem_expr_foreach(expr, elem_expr, size)
__nft_set_elem_expr_destroy(ctx, expr);
}
void nft_set_elem_destroy(const struct nft_set *set, void *elem,
bool destroy_expr)
{
@ -5121,7 +5208,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
nft_data_release(nft_set_ext_data(ext), set->dtype);
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
@ -5138,15 +5225,57 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
{
struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));
kfree(elem);
}
static int nft_set_elem_expr_clone(const struct nft_ctx *ctx,
struct nft_set *set,
struct nft_expr *expr_array[])
{
struct nft_expr *expr;
int err, i, k;
for (i = 0; i < set->num_exprs; i++) {
expr = kzalloc(set->exprs[i]->ops->size, GFP_KERNEL);
if (!expr)
goto err_expr;
err = nft_expr_clone(expr, set->exprs[i]);
if (err < 0) {
nft_expr_destroy(ctx, expr);
goto err_expr;
}
expr_array[i] = expr;
}
return 0;
err_expr:
for (k = i - 1; k >= 0; k++)
nft_expr_destroy(ctx, expr_array[i]);
return -ENOMEM;
}
static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i,
struct nft_expr *expr_array[])
{
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
struct nft_expr *expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
memcpy(expr, expr_array[i], expr_array[i]->ops->size);
elem_expr->size += expr_array[i]->ops->size;
kfree(expr_array[i]);
expr_array[i] = NULL;
}
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr, u32 nlmsg_flags)
{
struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {};
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
u8 genmask = nft_genmask_next(ctx->net);
struct nft_set_ext_tmpl tmpl;
@ -5154,16 +5283,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_elem elem;
struct nft_set_binding *binding;
struct nft_object *obj = NULL;
struct nft_expr *expr = NULL;
struct nft_userdata *udata;
struct nft_data_desc desc;
enum nft_registers dreg;
struct nft_trans *trans;
u32 flags = 0;
u32 flags = 0, size = 0;
u64 timeout;
u64 expiration;
int err, i;
u8 ulen;
int err;
err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr,
nft_set_elem_policy, NULL);
@ -5196,7 +5324,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nla[NFTA_SET_ELEM_TIMEOUT] ||
nla[NFTA_SET_ELEM_EXPIRATION] ||
nla[NFTA_SET_ELEM_USERDATA] ||
nla[NFTA_SET_ELEM_EXPR]))
nla[NFTA_SET_ELEM_EXPR] ||
nla[NFTA_SET_ELEM_EXPRESSIONS]))
return -EINVAL;
timeout = 0;
@ -5221,23 +5350,62 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err;
}
if (nla[NFTA_SET_ELEM_EXPR] != NULL) {
if (nla[NFTA_SET_ELEM_EXPR]) {
struct nft_expr *expr;
if (set->num_exprs != 1)
return -EOPNOTSUPP;
expr = nft_set_elem_expr_alloc(ctx, set,
nla[NFTA_SET_ELEM_EXPR]);
if (IS_ERR(expr))
return PTR_ERR(expr);
err = -EOPNOTSUPP;
if (set->expr && set->expr->ops != expr->ops)
goto err_set_elem_expr;
} else if (set->expr) {
expr = kzalloc(set->expr->ops->size, GFP_KERNEL);
if (!expr)
return -ENOMEM;
expr_array[0] = expr;
err = nft_expr_clone(expr, set->expr);
if (err < 0)
if (set->exprs[0] && set->exprs[0]->ops != expr->ops) {
err = -EOPNOTSUPP;
goto err_set_elem_expr;
}
} else if (nla[NFTA_SET_ELEM_EXPRESSIONS]) {
struct nft_expr *expr;
struct nlattr *tmp;
int left;
if (set->num_exprs == 0)
return -EOPNOTSUPP;
i = 0;
nla_for_each_nested(tmp, nla[NFTA_SET_ELEM_EXPRESSIONS], left) {
if (i == set->num_exprs) {
err = -E2BIG;
goto err_set_elem_expr;
}
if (nla_type(tmp) != NFTA_LIST_ELEM) {
err = -EINVAL;
goto err_set_elem_expr;
}
expr = nft_set_elem_expr_alloc(ctx, set, tmp);
if (IS_ERR(expr)) {
err = PTR_ERR(expr);
goto err_set_elem_expr;
}
expr_array[i] = expr;
if (expr->ops != set->exprs[i]->ops) {
err = -EOPNOTSUPP;
goto err_set_elem_expr;
}
i++;
}
if (set->num_exprs != i) {
err = -EOPNOTSUPP;
goto err_set_elem_expr;
}
} else if (set->num_exprs > 0) {
err = nft_set_elem_expr_clone(ctx, set, expr_array);
if (err < 0)
goto err_set_elem_expr_clone;
}
err = nft_setelem_parse_key(ctx, set, &elem.key.val,
@ -5262,9 +5430,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
}
if (expr)
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR,
expr->ops->size);
if (set->num_exprs) {
for (i = 0; i < set->num_exprs; i++)
size += expr_array[i]->ops->size;
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS,
sizeof(struct nft_set_elem_expr) +
size);
}
if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
if (!(set->flags & NFT_SET_OBJECT)) {
@ -5346,11 +5519,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
*nft_set_ext_obj(ext) = obj;
obj->use++;
}
if (expr) {
memcpy(nft_set_ext_expr(ext), expr, expr->ops->size);
kfree(expr);
expr = NULL;
}
for (i = 0; i < set->num_exprs; i++)
nft_set_elem_expr_setup(ext, i, expr_array);
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL)
@ -5411,9 +5581,9 @@ err_parse_key_end:
err_parse_key:
nft_data_release(&elem.key.val, NFT_DATA_VALUE);
err_set_elem_expr:
if (expr != NULL)
nft_expr_destroy(ctx, expr);
for (i = 0; i < set->num_exprs && expr_array[i]; i++)
nft_expr_destroy(ctx, expr_array[i]);
err_set_elem_expr_clone:
return err;
}

View File

@ -16,6 +16,7 @@
#include <linux/errno.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <net/netns/generic.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
@ -41,6 +42,17 @@ struct nfacct_filter {
u32 mask;
};
struct nfnl_acct_net {
struct list_head nfnl_acct_list;
};
static unsigned int nfnl_acct_net_id __read_mostly;
static inline struct nfnl_acct_net *nfnl_acct_pernet(struct net *net)
{
return net_generic(net, nfnl_acct_net_id);
}
#define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES)
#define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */
@ -49,6 +61,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
const struct nlattr * const tb[],
struct netlink_ext_ack *extack)
{
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
struct nf_acct *nfacct, *matching = NULL;
char *acct_name;
unsigned int size = 0;
@ -61,7 +74,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
if (strlen(acct_name) == 0)
return -EINVAL;
list_for_each_entry(nfacct, &net->nfnl_acct_list, head) {
list_for_each_entry(nfacct, &nfnl_acct_net->nfnl_acct_list, head) {
if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0)
continue;
@ -123,7 +136,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl,
be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS])));
}
refcount_set(&nfacct->refcnt, 1);
list_add_tail_rcu(&nfacct->head, &net->nfnl_acct_list);
list_add_tail_rcu(&nfacct->head, &nfnl_acct_net->nfnl_acct_list);
return 0;
}
@ -188,6 +201,7 @@ static int
nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
struct nf_acct *cur, *last;
const struct nfacct_filter *filter = cb->data;
@ -199,7 +213,7 @@ nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[1] = 0;
rcu_read_lock();
list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) {
list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) {
if (last) {
if (cur != last)
continue;
@ -269,6 +283,7 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl,
const struct nlattr * const tb[],
struct netlink_ext_ack *extack)
{
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
int ret = -ENOENT;
struct nf_acct *cur;
char *acct_name;
@ -288,7 +303,7 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl,
return -EINVAL;
acct_name = nla_data(tb[NFACCT_NAME]);
list_for_each_entry(cur, &net->nfnl_acct_list, head) {
list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) {
struct sk_buff *skb2;
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
@ -342,19 +357,20 @@ static int nfnl_acct_del(struct net *net, struct sock *nfnl,
const struct nlattr * const tb[],
struct netlink_ext_ack *extack)
{
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
struct nf_acct *cur, *tmp;
int ret = -ENOENT;
char *acct_name;
if (!tb[NFACCT_NAME]) {
list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head)
list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head)
nfnl_acct_try_del(cur);
return 0;
}
acct_name = nla_data(tb[NFACCT_NAME]);
list_for_each_entry(cur, &net->nfnl_acct_list, head) {
list_for_each_entry(cur, &nfnl_acct_net->nfnl_acct_list, head) {
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0)
continue;
@ -402,10 +418,11 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT);
struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name)
{
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
struct nf_acct *cur, *acct = NULL;
rcu_read_lock();
list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) {
list_for_each_entry_rcu(cur, &nfnl_acct_net->nfnl_acct_list, head) {
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
continue;
@ -488,16 +505,17 @@ EXPORT_SYMBOL_GPL(nfnl_acct_overquota);
static int __net_init nfnl_acct_net_init(struct net *net)
{
INIT_LIST_HEAD(&net->nfnl_acct_list);
INIT_LIST_HEAD(&nfnl_acct_pernet(net)->nfnl_acct_list);
return 0;
}
static void __net_exit nfnl_acct_net_exit(struct net *net)
{
struct nfnl_acct_net *nfnl_acct_net = nfnl_acct_pernet(net);
struct nf_acct *cur, *tmp;
list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) {
list_for_each_entry_safe(cur, tmp, &nfnl_acct_net->nfnl_acct_list, head) {
list_del_rcu(&cur->head);
if (refcount_dec_and_test(&cur->refcnt))
@ -508,6 +526,8 @@ static void __net_exit nfnl_acct_net_exit(struct net *net)
static struct pernet_operations nfnl_acct_ops = {
.init = nfnl_acct_net_init,
.exit = nfnl_acct_net_exit,
.id = &nfnl_acct_net_id,
.size = sizeof(struct nfnl_acct_net),
};
static int __init nfnl_acct_init(void)

View File

@ -19,11 +19,30 @@ struct nft_dynset {
enum nft_registers sreg_key:8;
enum nft_registers sreg_data:8;
bool invert;
u8 num_exprs;
u64 timeout;
struct nft_expr *expr;
struct nft_expr *expr_array[NFT_SET_EXPR_MAX];
struct nft_set_binding binding;
};
static int nft_dynset_expr_setup(const struct nft_dynset *priv,
const struct nft_set_ext *ext)
{
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
struct nft_expr *expr;
int i;
for (i = 0; i < priv->num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
if (nft_expr_clone(expr, priv->expr_array[i]) < 0)
return -1;
elem_expr->size += priv->expr_array[i]->ops->size;
}
return 0;
}
static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
struct nft_regs *regs)
{
@ -44,8 +63,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
goto err1;
ext = nft_set_elem_ext(set, elem);
if (priv->expr != NULL &&
nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0)
if (priv->num_exprs && nft_dynset_expr_setup(priv, ext) < 0)
goto err2;
return elem;
@ -90,6 +108,41 @@ void nft_dynset_eval(const struct nft_expr *expr,
regs->verdict.code = NFT_BREAK;
}
static void nft_dynset_ext_add_expr(struct nft_dynset *priv)
{
u8 size = 0;
int i;
for (i = 0; i < priv->num_exprs; i++)
size += priv->expr_array[i]->ops->size;
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPRESSIONS,
sizeof(struct nft_set_elem_expr) + size);
}
static struct nft_expr *
nft_dynset_expr_alloc(const struct nft_ctx *ctx, const struct nft_set *set,
const struct nlattr *attr, int pos)
{
struct nft_expr *expr;
int err;
expr = nft_set_elem_expr_alloc(ctx, set, attr);
if (IS_ERR(expr))
return expr;
if (set->exprs[pos] && set->exprs[pos]->ops != expr->ops) {
err = -EOPNOTSUPP;
goto err_dynset_expr;
}
return expr;
err_dynset_expr:
nft_expr_destroy(ctx, expr);
return ERR_PTR(err);
}
static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
[NFTA_DYNSET_SET_NAME] = { .type = NLA_STRING,
.len = NFT_SET_MAXNAMELEN - 1 },
@ -100,6 +153,7 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
[NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
[NFTA_DYNSET_EXPR] = { .type = NLA_NESTED },
[NFTA_DYNSET_FLAGS] = { .type = NLA_U32 },
[NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED },
};
static int nft_dynset_init(const struct nft_ctx *ctx,
@ -110,7 +164,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
u8 genmask = nft_genmask_next(ctx->net);
struct nft_set *set;
u64 timeout;
int err;
int err, i;
lockdep_assert_held(&ctx->net->nft.commit_mutex);
@ -181,16 +235,58 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
} else if (set->flags & NFT_SET_MAP)
return -EINVAL;
if (tb[NFTA_DYNSET_EXPR] != NULL) {
if (!(set->flags & NFT_SET_EVAL))
return -EINVAL;
if ((tb[NFTA_DYNSET_EXPR] || tb[NFTA_DYNSET_EXPRESSIONS]) &&
!(set->flags & NFT_SET_EVAL))
return -EINVAL;
priv->expr = nft_set_elem_expr_alloc(ctx, set,
tb[NFTA_DYNSET_EXPR]);
if (IS_ERR(priv->expr))
return PTR_ERR(priv->expr);
if (tb[NFTA_DYNSET_EXPR]) {
struct nft_expr *dynset_expr;
if (set->expr && set->expr->ops != priv->expr->ops) {
dynset_expr = nft_dynset_expr_alloc(ctx, set,
tb[NFTA_DYNSET_EXPR], 0);
if (IS_ERR(dynset_expr))
return PTR_ERR(dynset_expr);
priv->num_exprs++;
priv->expr_array[0] = dynset_expr;
if (set->num_exprs > 1 ||
(set->num_exprs == 1 &&
dynset_expr->ops != set->exprs[0]->ops)) {
err = -EOPNOTSUPP;
goto err_expr_free;
}
} else if (tb[NFTA_DYNSET_EXPRESSIONS]) {
struct nft_expr *dynset_expr;
struct nlattr *tmp;
int left;
i = 0;
nla_for_each_nested(tmp, tb[NFTA_DYNSET_EXPRESSIONS], left) {
if (i == NFT_SET_EXPR_MAX) {
err = -E2BIG;
goto err_expr_free;
}
if (nla_type(tmp) != NFTA_LIST_ELEM) {
err = -EINVAL;
goto err_expr_free;
}
dynset_expr = nft_dynset_expr_alloc(ctx, set, tmp, i);
if (IS_ERR(dynset_expr)) {
err = PTR_ERR(dynset_expr);
goto err_expr_free;
}
priv->expr_array[i] = dynset_expr;
priv->num_exprs++;
if (set->num_exprs &&
dynset_expr->ops != set->exprs[i]->ops) {
err = -EOPNOTSUPP;
goto err_expr_free;
}
i++;
}
if (set->num_exprs && set->num_exprs != i) {
err = -EOPNOTSUPP;
goto err_expr_free;
}
@ -200,9 +296,10 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
if (set->flags & NFT_SET_MAP)
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
if (priv->expr != NULL)
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_EXPR,
priv->expr->ops->size);
if (priv->num_exprs)
nft_dynset_ext_add_expr(priv);
if (set->flags & NFT_SET_TIMEOUT) {
if (timeout || set->timeout)
nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
@ -221,8 +318,8 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
return 0;
err_expr_free:
if (priv->expr != NULL)
nft_expr_destroy(ctx, priv->expr);
for (i = 0; i < priv->num_exprs; i++)
nft_expr_destroy(ctx, priv->expr_array[i]);
return err;
}
@ -247,9 +344,10 @@ static void nft_dynset_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_dynset *priv = nft_expr_priv(expr);
int i;
if (priv->expr != NULL)
nft_expr_destroy(ctx, priv->expr);
for (i = 0; i < priv->num_exprs; i++)
nft_expr_destroy(ctx, priv->expr_array[i]);
nf_tables_destroy_set(ctx, priv->set);
}
@ -258,6 +356,7 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_dynset *priv = nft_expr_priv(expr);
u32 flags = priv->invert ? NFT_DYNSET_F_INV : 0;
int i;
if (nft_dump_register(skb, NFTA_DYNSET_SREG_KEY, priv->sreg_key))
goto nla_put_failure;
@ -272,8 +371,23 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
nf_jiffies64_to_msecs(priv->timeout),
NFTA_DYNSET_PAD))
goto nla_put_failure;
if (priv->expr && nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr))
goto nla_put_failure;
if (priv->num_exprs == 1) {
if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr_array[0]))
goto nla_put_failure;
} else if (priv->num_exprs > 1) {
struct nlattr *nest;
nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS);
if (!nest)
goto nla_put_failure;
for (i = 0; i < priv->num_exprs; i++) {
if (nft_expr_dump(skb, NFTA_LIST_ELEM,
priv->expr_array[i]))
goto nla_put_failure;
}
nla_nest_end(skb, nest);
}
if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
goto nla_put_failure;
return 0;

View File

@ -28,7 +28,8 @@ static void nft_reject_inet_eval(const struct nft_expr *expr,
nft_hook(pkt));
break;
case NFT_REJECT_TCP_RST:
nf_send_reset(nft_net(pkt), pkt->skb, nft_hook(pkt));
nf_send_reset(nft_net(pkt), pkt->xt.state->sk,
pkt->skb, nft_hook(pkt));
break;
case NFT_REJECT_ICMPX_UNREACH:
nf_send_unreach(pkt->skb,
@ -44,7 +45,8 @@ static void nft_reject_inet_eval(const struct nft_expr *expr,
priv->icmp_code, nft_hook(pkt));
break;
case NFT_REJECT_TCP_RST:
nf_send_reset6(nft_net(pkt), pkt->skb, nft_hook(pkt));
nf_send_reset6(nft_net(pkt), pkt->xt.state->sk,
pkt->skb, nft_hook(pkt));
break;
case NFT_REJECT_ICMPX_UNREACH:
nf_send_unreach6(nft_net(pkt), pkt->skb,

View File

@ -293,6 +293,22 @@ cont:
rhashtable_walk_exit(&hti);
}
static bool nft_rhash_expr_needs_gc_run(const struct nft_set *set,
struct nft_set_ext *ext)
{
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
struct nft_expr *expr;
u32 size;
nft_setelem_expr_foreach(expr, elem_expr, size) {
if (expr->ops->gc &&
expr->ops->gc(read_pnet(&set->net), expr))
return true;
}
return false;
}
static void nft_rhash_gc(struct work_struct *work)
{
struct nft_set *set;
@ -314,16 +330,13 @@ static void nft_rhash_gc(struct work_struct *work)
continue;
}
if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPR)) {
struct nft_expr *expr = nft_set_ext_expr(&he->ext);
if (nft_set_ext_exists(&he->ext, NFT_SET_EXT_EXPRESSIONS) &&
nft_rhash_expr_needs_gc_run(set, &he->ext))
goto needs_gc_run;
if (expr->ops->gc &&
expr->ops->gc(read_pnet(&set->net), expr))
goto gc;
}
if (!nft_set_elem_expired(&he->ext))
continue;
gc:
needs_gc_run:
if (nft_set_elem_mark_busy(&he->ext))
continue;

View File

@ -27,7 +27,7 @@ static bool nfacct_mt(const struct sk_buff *skb, struct xt_action_param *par)
overquota = nfnl_acct_overquota(xt_net(par), info->nfacct);
return overquota == NFACCT_UNDERQUOTA ? false : true;
return overquota != NFACCT_UNDERQUOTA;
}
static int