Commit e80a07b2 authored by David S. Miller's avatar David S. Miller

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

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter updates for net-next:

1) Support for rejecting packets from the prerouting chain, from
   Laura Garcia Liebana.

2) Remove useless assignment in pipapo, from Stefano Brivio.

3) On demand hook registration in IPVS, from Julian Anastasov.

4) Expire IPVS connection from process context to not overload
   timers, also from Julian.

5) Fallback to conntrack TCP tracker to handle connection reuse
   in IPVS, from Julian Anastasov.

6) Several patches to support for chain bindings.

7) Expose enum nft_chain_flags through UAPI.

8) Reject unsupported chain flags from the netlink control plane.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d07ae5f c1f79a2e
......@@ -874,6 +874,7 @@ struct netns_ipvs {
struct ip_vs_stats tot_stats; /* Statistics & est. */
int num_services; /* no of virtual services */
int num_services6; /* IPv6 virtual services */
/* Trash for destinations */
struct list_head dest_trash;
......@@ -960,6 +961,7 @@ struct netns_ipvs {
* are not supported when synchronization is enabled.
*/
unsigned int mixed_address_family_dests;
unsigned int hooks_afmask; /* &1=AF_INET, &2=AF_INET6 */
};
#define DEFAULT_SYNC_THRESHOLD 3
......@@ -1624,18 +1626,16 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp)
}
#endif /* CONFIG_IP_VS_NFCT */
/* Really using conntrack? */
static inline bool ip_vs_conn_uses_conntrack(struct ip_vs_conn *cp,
struct sk_buff *skb)
/* Using old conntrack that can not be redirected to another real server? */
static inline bool ip_vs_conn_uses_old_conntrack(struct ip_vs_conn *cp,
struct sk_buff *skb)
{
#ifdef CONFIG_IP_VS_NFCT
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
if (!(cp->flags & IP_VS_CONN_F_NFCT))
return false;
ct = nf_ct_get(skb, &ctinfo);
if (ct)
if (ct && nf_ct_is_confirmed(ct))
return true;
#endif
return false;
......@@ -1670,6 +1670,9 @@ static inline void ip_vs_unregister_conntrack(struct ip_vs_service *svc)
#endif
}
int ip_vs_register_hooks(struct netns_ipvs *ipvs, unsigned int af);
void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af);
static inline int
ip_vs_dest_conn_overhead(struct ip_vs_dest *dest)
{
......
......@@ -899,6 +899,8 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
return (void *)&rule->data[rule->dlen];
}
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule);
static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
......@@ -921,11 +923,6 @@ static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
(expr) != (last); \
(expr) = nft_expr_next(expr))
enum nft_chain_flags {
NFT_BASE_CHAIN = 0x1,
NFT_CHAIN_HW_OFFLOAD = 0x2,
};
#define NFT_CHAIN_POLICY_UNSET U8_MAX
/**
......@@ -949,7 +946,8 @@ struct nft_chain {
struct nft_table *table;
u64 handle;
u32 use;
u8 flags:6,
u8 flags:5,
bound:1,
genmask:2;
char *name;
......@@ -994,6 +992,14 @@ int nft_chain_validate_dependency(const struct nft_chain *chain,
int nft_chain_validate_hooks(const struct nft_chain *chain,
unsigned int hook_flags);
static inline bool nft_chain_is_bound(struct nft_chain *chain)
{
return (chain->flags & NFT_CHAIN_BINDING) && chain->bound;
}
void nft_chain_del(struct nft_chain *chain);
void nf_tables_chain_destroy(struct nft_ctx *ctx);
struct nft_stats {
u64 bytes;
u64 pkts;
......@@ -1036,7 +1042,7 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai
static inline bool nft_is_base_chain(const struct nft_chain *chain)
{
return chain->flags & NFT_BASE_CHAIN;
return chain->flags & NFT_CHAIN_BASE;
}
int __nft_release_basechain(struct nft_ctx *ctx);
......@@ -1433,6 +1439,7 @@ struct nft_trans_chain {
char *name;
struct nft_stats __percpu *stats;
u8 policy;
u32 chain_id;
};
#define nft_trans_chain_update(trans) \
......@@ -1443,6 +1450,8 @@ struct nft_trans_chain {
(((struct nft_trans_chain *)trans->data)->stats)
#define nft_trans_chain_policy(trans) \
(((struct nft_trans_chain *)trans->data)->policy)
#define nft_trans_chain_id(trans) \
(((struct nft_trans_chain *)trans->data)->chain_id)
struct nft_trans_table {
bool update;
......
......@@ -184,6 +184,15 @@ enum nft_table_attributes {
};
#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1)
enum nft_chain_flags {
NFT_CHAIN_BASE = (1 << 0),
NFT_CHAIN_HW_OFFLOAD = (1 << 1),
NFT_CHAIN_BINDING = (1 << 2),
};
#define NFT_CHAIN_FLAGS (NFT_CHAIN_BASE | \
NFT_CHAIN_HW_OFFLOAD | \
NFT_CHAIN_BINDING)
/**
* enum nft_chain_attributes - nf_tables chain netlink attributes
*
......@@ -196,6 +205,7 @@ enum nft_table_attributes {
* @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
* @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
* @NFTA_CHAIN_FLAGS: chain flags
* @NFTA_CHAIN_ID: uniquely identifies a chain in a transaction (NLA_U32)
*/
enum nft_chain_attributes {
NFTA_CHAIN_UNSPEC,
......@@ -209,6 +219,7 @@ enum nft_chain_attributes {
NFTA_CHAIN_COUNTERS,
NFTA_CHAIN_PAD,
NFTA_CHAIN_FLAGS,
NFTA_CHAIN_ID,
__NFTA_CHAIN_MAX
};
#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1)
......@@ -238,6 +249,7 @@ enum nft_rule_attributes {
NFTA_RULE_PAD,
NFTA_RULE_ID,
NFTA_RULE_POSITION_ID,
NFTA_RULE_CHAIN_ID,
__NFTA_RULE_MAX
};
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
......@@ -468,11 +480,13 @@ enum nft_data_attributes {
*
* @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
* @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
* @NFTA_VERDICT_CHAIN_ID: jump target chain ID (NLA_U32)
*/
enum nft_verdict_attributes {
NFTA_VERDICT_UNSPEC,
NFTA_VERDICT_CODE,
NFTA_VERDICT_CHAIN,
NFTA_VERDICT_CHAIN_ID,
__NFTA_VERDICT_MAX
};
#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1)
......
......@@ -96,6 +96,21 @@ void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb,
}
EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put);
static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
{
struct dst_entry *dst = NULL;
struct flowi fl;
memset(&fl, 0, sizeof(struct flowi));
fl.u.ip4.daddr = ip_hdr(skb_in)->saddr;
nf_ip_route(dev_net(skb_in->dev), &dst, &fl, false);
if (!dst)
return -1;
skb_dst_set(skb_in, dst);
return 0;
}
/* Send RST reply */
void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
{
......@@ -109,6 +124,9 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook)
if (!oth)
return;
if (hook == NF_INET_PRE_ROUTING && nf_reject_fill_skb_dst(oldskb))
return;
if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
return;
......@@ -175,6 +193,9 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (iph->frag_off & htons(IP_OFFSET))
return;
if (hook == NF_INET_PRE_ROUTING && nf_reject_fill_skb_dst(skb_in))
return;
if (skb_csum_unnecessary(skb_in) || !nf_reject_verify_csum(proto)) {
icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
return;
......
......@@ -126,6 +126,21 @@ void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb,
}
EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put);
static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
{
struct dst_entry *dst = NULL;
struct flowi fl;
memset(&fl, 0, sizeof(struct flowi));
fl.u.ip6.daddr = ipv6_hdr(skb_in)->saddr;
nf_ip6_route(dev_net(skb_in->dev), &dst, &fl, false);
if (!dst)
return -1;
skb_dst_set(skb_in, dst);
return 0;
}
void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
{
struct net_device *br_indev __maybe_unused;
......@@ -154,6 +169,14 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
fl6.daddr = oip6h->saddr;
fl6.fl6_sport = otcph->dest;
fl6.fl6_dport = otcph->source;
if (hook == NF_INET_PRE_ROUTING) {
nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
if (!dst)
return;
skb_dst_set(oldskb, dst);
}
fl6.flowi6_oif = l3mdev_master_ifindex(skb_dst(oldskb)->dev);
fl6.flowi6_mark = IP6_REPLY_MARK(net, oldskb->mark);
security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
......@@ -245,6 +268,9 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
if (hooknum == NF_INET_PRE_ROUTING && nf_reject6_fill_skb_dst(skb_in))
return;
icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
}
EXPORT_SYMBOL_GPL(nf_send_unreach6);
......
......@@ -807,6 +807,31 @@ static void ip_vs_conn_rcu_free(struct rcu_head *head)
kmem_cache_free(ip_vs_conn_cachep, cp);
}
/* Try to delete connection while not holding reference */
static void ip_vs_conn_del(struct ip_vs_conn *cp)
{
if (del_timer(&cp->timer)) {
/* Drop cp->control chain too */
if (cp->control)
cp->timeout = 0;
ip_vs_conn_expire(&cp->timer);
}
}
/* Try to delete connection while holding reference */
static void ip_vs_conn_del_put(struct ip_vs_conn *cp)
{
if (del_timer(&cp->timer)) {
/* Drop cp->control chain too */
if (cp->control)
cp->timeout = 0;
__ip_vs_conn_put(cp);
ip_vs_conn_expire(&cp->timer);
} else {
__ip_vs_conn_put(cp);
}
}
static void ip_vs_conn_expire(struct timer_list *t)
{
struct ip_vs_conn *cp = from_timer(cp, t, timer);
......@@ -827,14 +852,17 @@ static void ip_vs_conn_expire(struct timer_list *t)
/* does anybody control me? */
if (ct) {
bool has_ref = !cp->timeout && __ip_vs_conn_get(ct);
ip_vs_control_del(cp);
/* Drop CTL or non-assured TPL if not used anymore */
if (!cp->timeout && !atomic_read(&ct->n_control) &&
if (has_ref && !atomic_read(&ct->n_control) &&
(!(ct->flags & IP_VS_CONN_F_TEMPLATE) ||
!(ct->state & IP_VS_CTPL_S_ASSURED))) {
IP_VS_DBG(4, "drop controlling connection\n");
ct->timeout = 0;
ip_vs_conn_expire_now(ct);
ip_vs_conn_del_put(ct);
} else if (has_ref) {
__ip_vs_conn_put(ct);
}
}
......@@ -1317,8 +1345,7 @@ void ip_vs_random_dropentry(struct netns_ipvs *ipvs)
drop:
IP_VS_DBG(4, "drop connection\n");
cp->timeout = 0;
ip_vs_conn_expire_now(cp);
ip_vs_conn_del(cp);
}
cond_resched_rcu();
}
......@@ -1341,19 +1368,15 @@ static void ip_vs_conn_flush(struct netns_ipvs *ipvs)
hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
if (cp->ipvs != ipvs)
continue;
/* As timers are expired in LIFO order, restart
* the timer of controlling connection first, so
* that it is expired after us.
*/
if (atomic_read(&cp->n_control))
continue;
cp_c = cp->control;
/* cp->control is valid only with reference to cp */
if (cp_c && __ip_vs_conn_get(cp)) {
IP_VS_DBG(4, "del connection\n");
ip_vs_conn_del(cp);
if (cp_c && !atomic_read(&cp_c->n_control)) {
IP_VS_DBG(4, "del controlling connection\n");
ip_vs_conn_expire_now(cp_c);
__ip_vs_conn_put(cp);
ip_vs_conn_del(cp_c);
}
IP_VS_DBG(4, "del connection\n");
ip_vs_conn_expire_now(cp);
}
cond_resched_rcu();
}
......
......@@ -2066,14 +2066,14 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
conn_reuse_mode = sysctl_conn_reuse_mode(ipvs);
if (conn_reuse_mode && !iph.fragoffs && is_new_conn(skb, &iph) && cp) {
bool uses_ct = false, resched = false;
bool old_ct = false, resched = false;
if (unlikely(sysctl_expire_nodest_conn(ipvs)) && cp->dest &&
unlikely(!atomic_read(&cp->dest->weight))) {
resched = true;
uses_ct = ip_vs_conn_uses_conntrack(cp, skb);
old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
} else if (is_new_conn_expected(cp, conn_reuse_mode)) {
uses_ct = ip_vs_conn_uses_conntrack(cp, skb);
old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
if (!atomic_read(&cp->n_control)) {
resched = true;
} else {
......@@ -2081,15 +2081,17 @@ ip_vs_in(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, int
* that uses conntrack while it is still
* referenced by controlled connection(s).
*/
resched = !uses_ct;
resched = !old_ct;
}
}
if (resched) {
if (!old_ct)
cp->flags &= ~IP_VS_CONN_F_NFCT;
if (!atomic_read(&cp->n_control))
ip_vs_conn_expire_now(cp);
__ip_vs_conn_put(cp);
if (uses_ct)
if (old_ct)
return NF_DROP;
cp = NULL;
}
......@@ -2256,7 +2258,7 @@ ip_vs_forward_icmp_v6(void *priv, struct sk_buff *skb,
#endif
static const struct nf_hook_ops ip_vs_ops[] = {
static const struct nf_hook_ops ip_vs_ops4[] = {
/* After packet filtering, change source only for VS/NAT */
{
.hook = ip_vs_reply4,
......@@ -2302,7 +2304,10 @@ static const struct nf_hook_ops ip_vs_ops[] = {
.hooknum = NF_INET_FORWARD,
.priority = 100,
},
};
#ifdef CONFIG_IP_VS_IPV6
static const struct nf_hook_ops ip_vs_ops6[] = {
/* After packet filtering, change source only for VS/NAT */
{
.hook = ip_vs_reply6,
......@@ -2348,8 +2353,64 @@ static const struct nf_hook_ops ip_vs_ops[] = {
.hooknum = NF_INET_FORWARD,
.priority = 100,
},
#endif
};
#endif
int ip_vs_register_hooks(struct netns_ipvs *ipvs, unsigned int af)
{
const struct nf_hook_ops *ops;
unsigned int count;
unsigned int afmask;
int ret = 0;
if (af == AF_INET6) {
#ifdef CONFIG_IP_VS_IPV6
ops = ip_vs_ops6;
count = ARRAY_SIZE(ip_vs_ops6);
afmask = 2;
#else
return -EINVAL;
#endif
} else {
ops = ip_vs_ops4;
count = ARRAY_SIZE(ip_vs_ops4);
afmask = 1;
}
if (!(ipvs->hooks_afmask & afmask)) {
ret = nf_register_net_hooks(ipvs->net, ops, count);
if (ret >= 0)
ipvs->hooks_afmask |= afmask;
}
return ret;
}
void ip_vs_unregister_hooks(struct netns_ipvs *ipvs, unsigned int af)
{
const struct nf_hook_ops *ops;
unsigned int count;
unsigned int afmask;
if (af == AF_INET6) {
#ifdef CONFIG_IP_VS_IPV6
ops = ip_vs_ops6;
count = ARRAY_SIZE(ip_vs_ops6);
afmask = 2;
#else
return;
#endif
} else {
ops = ip_vs_ops4;
count = ARRAY_SIZE(ip_vs_ops4);
afmask = 1;
}
if (ipvs->hooks_afmask & afmask) {
nf_unregister_net_hooks(ipvs->net, ops, count);
ipvs->hooks_afmask &= ~afmask;
}
}
/*
* Initialize IP Virtual Server netns mem.
*/
......@@ -2425,19 +2486,6 @@ static void __net_exit __ip_vs_cleanup_batch(struct list_head *net_list)
}
}
static int __net_init __ip_vs_dev_init(struct net *net)
{
int ret;
ret = nf_register_net_hooks(net, ip_vs_ops, ARRAY_SIZE(ip_vs_ops));
if (ret < 0)
goto hook_fail;
return 0;
hook_fail:
return ret;
}
static void __net_exit __ip_vs_dev_cleanup_batch(struct list_head *net_list)
{
struct netns_ipvs *ipvs;
......@@ -2446,7 +2494,8 @@ static void __net_exit __ip_vs_dev_cleanup_batch(struct list_head *net_list)
EnterFunction(2);
list_for_each_entry(net, net_list, exit_list) {
ipvs = net_ipvs(net);
nf_unregister_net_hooks(net, ip_vs_ops, ARRAY_SIZE(ip_vs_ops));
ip_vs_unregister_hooks(ipvs, AF_INET);
ip_vs_unregister_hooks(ipvs, AF_INET6);
ipvs->enable = 0; /* Disable packet reception */
smp_wmb();
ip_vs_sync_net_cleanup(ipvs);
......@@ -2462,7 +2511,6 @@ static struct pernet_operations ipvs_core_ops = {
};
static struct pernet_operations ipvs_core_dev_ops = {
.init = __ip_vs_dev_init,
.exit_batch = __ip_vs_dev_cleanup_batch,
};
......
......@@ -224,7 +224,8 @@ static void defense_work_handler(struct work_struct *work)
update_defense_level(ipvs);
if (atomic_read(&ipvs->dropentry))
ip_vs_random_dropentry(ipvs);
schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD);
queue_delayed_work(system_long_wq, &ipvs->defense_work,
DEFENSE_TIMER_PERIOD);
}
#endif
......@@ -1272,6 +1273,7 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
struct ip_vs_scheduler *sched = NULL;
struct ip_vs_pe *pe = NULL;
struct ip_vs_service *svc = NULL;
int ret_hooks = -1;
/* increase the module use count */
if (!ip_vs_use_count_inc())
......@@ -1313,6 +1315,14 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
}
#endif
if ((u->af == AF_INET && !ipvs->num_services) ||
(u->af == AF_INET6 && !ipvs->num_services6)) {
ret = ip_vs_register_hooks(ipvs, u->af);
if (ret < 0)
goto out_err;
ret_hooks = ret;
}
svc = kzalloc(sizeof(struct ip_vs_service), GFP_KERNEL);
if (svc == NULL) {
IP_VS_DBG(1, "%s(): no memory\n", __func__);
......@@ -1374,6 +1384,8 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
/* Count only IPv4 services for old get/setsockopt interface */
if (svc->af == AF_INET)
ipvs->num_services++;
else if (svc->af == AF_INET6)
ipvs->num_services6++;
/* Hash the service into the service table */
ip_vs_svc_hash(svc);
......@@ -1385,6 +1397,8 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
out_err:
if (ret_hooks >= 0)
ip_vs_unregister_hooks(ipvs, u->af);
if (svc != NULL) {
ip_vs_unbind_scheduler(svc, sched);
ip_vs_service_free(svc);
......@@ -1500,9 +1514,15 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
struct ip_vs_pe *old_pe;
struct netns_ipvs *ipvs = svc->ipvs;
/* Count only IPv4 services for old get/setsockopt interface */
if (svc->af == AF_INET)
if (svc->af == AF_INET) {
ipvs->num_services--;
if (!ipvs->num_services)
ip_vs_unregister_hooks(ipvs, svc->af);
} else if (svc->af == AF_INET6) {
ipvs->num_services6--;
if (!ipvs->num_services6)
ip_vs_unregister_hooks(ipvs, svc->af);
}
ip_vs_stop_estimator(svc->ipvs, &svc->stats);
......@@ -4063,7 +4083,8 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
ipvs->sysctl_tbl = tbl;
/* Schedule defense work */
INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler);
schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD);
queue_delayed_work(system_long_wq, &ipvs->defense_work,
DEFENSE_TIMER_PERIOD);
return 0;
}
......
......@@ -280,9 +280,15 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
if (trans == NULL)
return ERR_PTR(-ENOMEM);
if (msg_type == NFT_MSG_NEWCHAIN)
if (msg_type == NFT_MSG_NEWCHAIN) {
nft_activate_next(ctx->net, ctx->chain);
if (ctx->nla[NFTA_CHAIN_ID]) {
nft_trans_chain_id(trans) =
ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID]));
}
}
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return trans;
}
......@@ -1050,6 +1056,9 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (!nft_is_active_next(ctx->net, chain))
continue;
if (nft_chain_is_bound(chain))
continue;
ctx->chain = chain;
err = nft_delrule_by_chain(ctx);
......@@ -1092,6 +1101,9 @@ static int nft_flush_table(struct nft_ctx *ctx)
if (!nft_is_active_next(ctx->net, chain))
continue;
if (nft_chain_is_bound(chain))
continue;
ctx->chain = chain;
err = nft_delchain(ctx);
......@@ -1274,6 +1286,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
.len = NFT_MODULE_AUTOLOAD_LIMIT },
[NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
[NFTA_CHAIN_FLAGS] = { .type = NLA_U32 },
[NFTA_CHAIN_ID] = { .type = NLA_U32 },
};
static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = {
......@@ -1406,13 +1419,12 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net,
lockdep_commit_lock_is_held(net));
if (nft_dump_stats(skb, stats))
goto nla_put_failure;
if ((chain->flags & NFT_CHAIN_HW_OFFLOAD) &&
nla_put_be32(skb, NFTA_CHAIN_FLAGS,
htonl(NFT_CHAIN_HW_OFFLOAD)))
goto nla_put_failure;
}
if (chain->flags &&
nla_put_be32(skb, NFTA_CHAIN_FLAGS, htonl(chain->flags)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use)))
goto nla_put_failure;
......@@ -1614,7 +1626,7 @@ static void nf_tables_chain_free_chain_rules(struct nft_chain *chain)
kvfree(chain->rules_next);
}
static void nf_tables_chain_destroy(struct nft_ctx *ctx)
void nf_tables_chain_destroy(struct nft_ctx *ctx)
{
struct nft_chain *chain = ctx->chain;
struct nft_hook *hook, *next;
......@@ -1896,7 +1908,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
nft_basechain_hook_init(&basechain->ops, family, hook, chain);
}
chain->flags |= NFT_BASE_CHAIN | flags;
chain->flags |= NFT_CHAIN_BASE | flags;
basechain->policy = NF_ACCEPT;
if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
nft_chain_offload_priority(basechain) < 0)
......@@ -1907,6 +1919,22 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
return 0;
}
static int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
{
int err;
err = rhltable_insert_key(&table->chains_ht, chain->name,
&chain->rhlhead, nft_chain_ht_params);
if (err)
return err;
list_add_tail_rcu(&chain->list, &table->chains);
return 0;
}
static u64 chain_id;
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
u8 policy, u32 flags)
{
......@@ -1915,6 +1943,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
struct nft_base_chain *basechain;
struct nft_stats __percpu *stats;
struct net *net = ctx->net;
char name[NFT_NAME_MAXLEN];
struct nft_trans *trans;
struct nft_chain *chain;
struct nft_rule **rules;
......@@ -1926,6 +1955,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
if (nla[NFTA_CHAIN_HOOK]) {
struct nft_chain_hook hook;
if (flags & NFT_CHAIN_BINDING)
return -EOPNOTSUPP;
err = nft_chain_parse_hook(net, nla, &hook, family, true);
if (err < 0)
return err;
......@@ -1955,16 +1987,33 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
return err;
}
} else {
if (flags & NFT_CHAIN_BASE)
return -EINVAL;
if (flags & NFT_CHAIN_HW_OFFLOAD)
return -EOPNOTSUPP;
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
return -ENOMEM;
chain->flags = flags;
}
ctx->chain = chain;
INIT_LIST_HEAD(&chain->rules);
chain->handle = nf_tables_alloc_handle(table);
chain->table = table;
chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
if (nla[NFTA_CHAIN_NAME]) {
chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL);
} else {
if (!(flags & NFT_CHAIN_BINDING))
return -EINVAL;
snprintf(name, sizeof(name), "__chain%llu", ++chain_id);
chain->name = kstrdup(name, GFP_KERNEL);
}
if (!chain->name) {
err = -ENOMEM;
goto err1;
......@@ -1984,16 +2033,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
if (err < 0)
goto err1;
err = rhltable_insert_key(&table->chains_ht, chain->name,
&chain->rhlhead, nft_chain_ht_params);
if (err)
goto err2;
trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
if (IS_ERR(trans)) {
err = PTR_ERR(trans);
rhltable_remove(&table->chains_ht, &chain->rhlhead,
nft_chain_ht_params);
goto err2;
}
......@@ -2001,8 +2043,13 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
if (nft_is_base_chain(chain))
nft_trans_chain_policy(trans) = policy;
err = nft_chain_add(table, chain);
if (err < 0) {
nft_trans_destroy(trans);
goto err2;
}
table->use++;
list_add_tail_rcu(&chain->list, &table->chains);
return 0;
err2:
......@@ -2146,6 +2193,22 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
return err;
}
static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
const struct nlattr *nla)
{
u32 id = ntohl(nla_get_be32(nla));
struct nft_trans *trans;
list_for_each_entry(trans, &net->nft.commit_list, list) {
struct nft_chain *chain = trans->ctx.chain;
if (trans->msg_type == NFT_MSG_NEWCHAIN &&
id == nft_trans_chain_id(trans))
return chain;
}
return ERR_PTR(-ENOENT);
}
static int nf_tables_newchain(struct net *net, struct sock *nlsk,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
......@@ -2154,9 +2217,9 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
u8 genmask = nft_genmask_next(net);
int family = nfmsg->nfgen_family;
struct nft_chain *chain = NULL;
const struct nlattr *attr;
struct nft_table *table;
struct nft_chain *chain;
u8 policy = NF_ACCEPT;
struct nft_ctx ctx;
u64 handle = 0;
......@@ -2181,7 +2244,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
return PTR_ERR(chain);
}
attr = nla[NFTA_CHAIN_HANDLE];
} else {
} else if (nla[NFTA_CHAIN_NAME]) {
chain = nft_chain_lookup(net, table, attr, genmask);
if (IS_ERR(chain)) {
if (PTR_ERR(chain) != -ENOENT) {
......@@ -2190,6 +2253,8 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
}
chain = NULL;
}
} else if (!nla[NFTA_CHAIN_ID]) {
return -EINVAL;
}
if (nla[NFTA_CHAIN_POLICY]) {
......@@ -2220,6 +2285,9 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
else if (chain)
flags = chain->flags;
if (flags & ~NFT_CHAIN_FLAGS)
return -EOPNOTSUPP;
nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
if (chain != NULL) {
......@@ -2230,7 +2298,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
flags |= chain->flags & NFT_BASE_CHAIN;
flags |= chain->flags & NFT_CHAIN_BASE;
return nf_tables_updchain(&ctx, genmask, policy, flags);
}
......@@ -2624,6 +2692,7 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
.len = NFT_USERDATA_MAXLEN },
[NFTA_RULE_ID] = { .type = NLA_U32 },
[NFTA_RULE_POSITION_ID] = { .type = NLA_U32 },
[NFTA_RULE_CHAIN_ID] = { .type = NLA_U32 },
};
static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
......@@ -2938,8 +3007,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
kfree(rule);
}
static void nf_tables_rule_release(const struct nft_ctx *ctx,
struct nft_rule *rule)
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
{
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
nf_tables_rule_destroy(ctx, rule);
......@@ -3030,10 +3098,24 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
return PTR_ERR(table);
}
chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], genmask);
if (IS_ERR(chain)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
return PTR_ERR(chain);
if (nla[NFTA_RULE_CHAIN]) {
chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN],
genmask);
if (IS_ERR(chain)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
return PTR_ERR(chain);
}
if (nft_chain_is_bound(chain))
return -EOPNOTSUPP;
} else if (nla[NFTA_RULE_CHAIN_ID]) {
chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]);
if (IS_ERR(chain)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
return PTR_ERR(chain);
}
} else {
return -EINVAL;
}
if (nla[NFTA_RULE_HANDLE]) {
......@@ -3245,6 +3327,8 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]);
return PTR_ERR(chain);
}
if (nft_chain_is_bound(chain))
return -EOPNOTSUPP;
}
nft_ctx_init(&ctx, net, skb, nlh, family, table, chain, nla);
......@@ -5281,11 +5365,24 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
*/
void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
{
struct nft_chain *chain;
struct nft_rule *rule;
if (type == NFT_DATA_VERDICT) {
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
data->verdict.chain->use++;
chain = data->verdict.chain;
chain->use++;
if (!nft_chain_is_bound(chain))
break;
chain->table->use++;
list_for_each_entry(rule, &chain->rules, list)
chain->use++;
nft_chain_add(chain->table, chain);
break;
}
}
......@@ -7425,7 +7522,7 @@ static void nft_obj_del(struct nft_object *obj)
list_del_rcu(&obj->list);
}
static void nft_chain_del(struct nft_chain *chain)
void nft_chain_del(struct nft_chain *chain)
{
struct nft_table *table = chain->table;
......@@ -7776,6 +7873,10 @@ static int __nf_tables_abort(struct net *net, bool autoload)
kfree(nft_trans_chain_name(trans));
nft_trans_destroy(trans);
} else {
if (nft_chain_is_bound(trans->ctx.chain)) {
nft_trans_destroy(trans);
break;
}
trans->ctx.table->use--;
nft_chain_del(trans->ctx.chain);
nf_tables_unregister_hook(trans->ctx.net,
......@@ -8205,6 +8306,7 @@ static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = {
[NFTA_VERDICT_CODE] = { .type = NLA_U32 },
[NFTA_VERDICT_CHAIN] = { .type = NLA_STRING,
.len = NFT_CHAIN_MAXNAMELEN - 1 },
[NFTA_VERDICT_CHAIN_ID] = { .type = NLA_U32 },
};
static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
......@@ -8241,10 +8343,19 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
break;
case NFT_JUMP:
case NFT_GOTO:
if (!tb[NFTA_VERDICT_CHAIN])
if (tb[NFTA_VERDICT_CHAIN]) {
chain = nft_chain_lookup(ctx->net, ctx->table,
tb[NFTA_VERDICT_CHAIN],
genmask);
} else if (tb[NFTA_VERDICT_CHAIN_ID]) {
chain = nft_chain_lookup_byid(ctx->net,
tb[NFTA_VERDICT_CHAIN_ID]);
if (IS_ERR(chain))
return PTR_ERR(chain);
} else {
return -EINVAL;
chain = nft_chain_lookup(ctx->net, ctx->table,
tb[NFTA_VERDICT_CHAIN], genmask);
}
if (IS_ERR(chain))
return PTR_ERR(chain);
if (nft_is_base_chain(chain))
......@@ -8262,10 +8373,23 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
static void nft_verdict_uninit(const struct nft_data *data)
{
struct nft_chain *chain;
struct nft_rule *rule;
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
data->verdict.chain->use--;
chain = data->verdict.chain;
chain->use--;
if (!nft_chain_is_bound(chain))
break;
chain->table->use--;
list_for_each_entry(rule, &chain->rules, list)
chain->use--;
nft_chain_del(chain);
break;
}
}
......
......@@ -54,6 +54,23 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
if (err < 0)
goto err1;
if (priv->dreg == NFT_REG_VERDICT) {
struct nft_chain *chain = priv->data.verdict.chain;
switch (priv->data.verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
if (nft_chain_is_bound(chain)) {
err = -EBUSY;
goto err1;
}
chain->bound = true;
break;
default:
break;
}
}
return 0;
err1:
......@@ -81,6 +98,39 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg));
}
static void nft_immediate_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
const struct nft_data *data = &priv->data;
struct nft_ctx chain_ctx;
struct nft_chain *chain;
struct nft_rule *rule;
if (priv->dreg != NFT_REG_VERDICT)
return;
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
chain = data->verdict.chain;
if (!nft_chain_is_bound(chain))
break;
chain_ctx = *ctx;
chain_ctx.chain = chain;
list_for_each_entry(rule, &chain->rules, list)
nf_tables_rule_release(&chain_ctx, rule);
nf_tables_chain_destroy(&chain_ctx);
break;
default:
break;
}
}
static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
......@@ -170,6 +220,7 @@ static const struct nft_expr_ops nft_imm_ops = {
.init = nft_immediate_init,
.activate = nft_immediate_activate,
.deactivate = nft_immediate_deactivate,
.destroy = nft_immediate_destroy,
.dump = nft_immediate_dump,
.validate = nft_immediate_validate,
.offload = nft_immediate_offload,
......
......@@ -30,7 +30,8 @@ int nft_reject_validate(const struct nft_ctx *ctx,
return nft_chain_validate_hooks(ctx->chain,
(1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_FORWARD) |
(1 << NF_INET_LOCAL_OUT));
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_PRE_ROUTING));
}
EXPORT_SYMBOL_GPL(nft_reject_validate);
......
......@@ -1249,8 +1249,6 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
if (err)
return err;
this_cpu_write(nft_pipapo_scratch_index, false);
m->bsize_max = bsize_max;
} else {
put_cpu_ptr(m->scratch);
......
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