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

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter updates for net-next:

1) Add nft_reg_store64() and nft_reg_load64() helpers, from Ander Juaristi.

2) Time matching support, also from Ander Juaristi.

3) VLAN support for nfnetlink_log, from Michael Braun.

4) Support for set element deletions from the packet path, also from Ander.

5) Remove __read_mostly from conntrack spinlock, from Li RongQing.

6) Support for updating stateful objects, this also includes the initial
   client for this infrastructure: the quota extension. A follow up fix
   for the control plane also comes in this batch. Patches from
   Fernando Fernandez Mancera.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-09-07 16:31:30 +02:00
commit b8f6a0eeb9
12 changed files with 287 additions and 31 deletions

View File

@ -2,6 +2,7 @@
#ifndef _NET_NF_TABLES_H
#define _NET_NF_TABLES_H
#include <asm/unaligned.h>
#include <linux/list.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
@ -102,33 +103,43 @@ struct nft_regs {
};
};
/* Store/load an u16 or u8 integer to/from the u32 data register.
/* Store/load an u8, u16 or u64 integer to/from the u32 data register.
*
* Note, when using concatenations, register allocation happens at 32-bit
* level. So for store instruction, pad the rest part with zero to avoid
* garbage values.
*/
static inline void nft_reg_store16(u32 *dreg, u16 val)
{
*dreg = 0;
*(u16 *)dreg = val;
}
static inline void nft_reg_store8(u32 *dreg, u8 val)
{
*dreg = 0;
*(u8 *)dreg = val;
}
static inline u8 nft_reg_load8(u32 *sreg)
{
return *(u8 *)sreg;
}
static inline void nft_reg_store16(u32 *dreg, u16 val)
{
*dreg = 0;
*(u16 *)dreg = val;
}
static inline u16 nft_reg_load16(u32 *sreg)
{
return *(u16 *)sreg;
}
static inline u8 nft_reg_load8(u32 *sreg)
static inline void nft_reg_store64(u32 *dreg, u64 val)
{
return *(u8 *)sreg;
put_unaligned(val, (u64 *)dreg);
}
static inline u64 nft_reg_load64(u32 *sreg)
{
return get_unaligned((u64 *)sreg);
}
static inline void nft_data_copy(u32 *dst, const struct nft_data *src,
@ -291,17 +302,23 @@ struct nft_expr;
* struct nft_set_ops - nf_tables set operations
*
* @lookup: look up an element within the set
* @update: update an element if exists, add it if doesn't exist
* @delete: delete an element
* @insert: insert new element into set
* @activate: activate new element in the next generation
* @deactivate: lookup for element and deactivate it in the next generation
* @flush: deactivate element in the next generation
* @remove: remove element from set
* @walk: iterate over all set elemeennts
* @walk: iterate over all set elements
* @get: get set elements
* @privsize: function to return size of set private data
* @init: initialize private data of new set instance
* @destroy: destroy private data of set instance
* @elemsize: element private size
*
* Operations lookup, update and delete have simpler interfaces, are faster
* and currently only used in the packet path. All the rest are slower,
* control plane functions.
*/
struct nft_set_ops {
bool (*lookup)(const struct net *net,
@ -316,6 +333,8 @@ struct nft_set_ops {
const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_set_ext **ext);
bool (*delete)(const struct nft_set *set,
const u32 *key);
int (*insert)(const struct net *net,
const struct nft_set *set,
@ -1108,6 +1127,7 @@ struct nft_object_type {
* @init: initialize object from netlink attributes
* @destroy: release existing stateful object
* @dump: netlink dump stateful object
* @update: update stateful object
*/
struct nft_object_ops {
void (*eval)(struct nft_object *obj,
@ -1122,6 +1142,8 @@ struct nft_object_ops {
int (*dump)(struct sk_buff *skb,
struct nft_object *obj,
bool reset);
void (*update)(struct nft_object *obj,
struct nft_object *newobj);
const struct nft_object_type *type;
};
@ -1410,10 +1432,16 @@ struct nft_trans_elem {
struct nft_trans_obj {
struct nft_object *obj;
struct nft_object *newobj;
bool update;
};
#define nft_trans_obj(trans) \
(((struct nft_trans_obj *)trans->data)->obj)
#define nft_trans_obj_newobj(trans) \
(((struct nft_trans_obj *)trans->data)->newobj)
#define nft_trans_obj_update(trans) \
(((struct nft_trans_obj *)trans->data)->update)
struct nft_trans_flowtable {
struct nft_flowtable *flowtable;

View File

@ -636,6 +636,7 @@ enum nft_lookup_attributes {
enum nft_dynset_ops {
NFT_DYNSET_OP_ADD,
NFT_DYNSET_OP_UPDATE,
NFT_DYNSET_OP_DELETE,
};
enum nft_dynset_flags {
@ -799,6 +800,9 @@ enum nft_exthdr_attributes {
* @NFT_META_OIFKIND: packet output interface kind name (dev->rtnl_link_ops->kind)
* @NFT_META_BRI_IIFPVID: packet input bridge port pvid
* @NFT_META_BRI_IIFVPROTO: packet input bridge vlan proto
* @NFT_META_TIME_NS: time since epoch (in nanoseconds)
* @NFT_META_TIME_DAY: day of week (from 0 = Sunday to 6 = Saturday)
* @NFT_META_TIME_HOUR: hour of day (in seconds)
*/
enum nft_meta_keys {
NFT_META_LEN,
@ -831,6 +835,9 @@ enum nft_meta_keys {
NFT_META_OIFKIND,
NFT_META_BRI_IIFPVID,
NFT_META_BRI_IIFVPROTO,
NFT_META_TIME_NS,
NFT_META_TIME_DAY,
NFT_META_TIME_HOUR,
};
/**

View File

@ -33,6 +33,15 @@ struct nfulnl_msg_packet_timestamp {
__aligned_be64 usec;
};
enum nfulnl_vlan_attr {
NFULA_VLAN_UNSPEC,
NFULA_VLAN_PROTO, /* __be16 skb vlan_proto */
NFULA_VLAN_TCI, /* __be16 skb htons(vlan_tci) */
__NFULA_VLAN_MAX,
};
#define NFULA_VLAN_MAX (__NFULA_VLAN_MAX + 1)
enum nfulnl_attr_type {
NFULA_UNSPEC,
NFULA_PACKET_HDR,
@ -54,6 +63,8 @@ enum nfulnl_attr_type {
NFULA_HWLEN, /* hardware header length */
NFULA_CT, /* nf_conntrack_netlink.h */
NFULA_CT_INFO, /* enum ip_conntrack_info */
NFULA_VLAN, /* nested attribute: packet vlan info */
NFULA_L2HDR, /* full L2 header */
__NFULA_MAX
};

View File

@ -73,8 +73,7 @@ struct conntrack_gc_work {
};
static __read_mostly struct kmem_cache *nf_conntrack_cachep;
static __read_mostly spinlock_t nf_conntrack_locks_all_lock;
static __read_mostly DEFINE_SPINLOCK(nf_conntrack_locks_all_lock);
static DEFINE_SPINLOCK(nf_conntrack_locks_all_lock);
static __read_mostly bool nf_conntrack_locks_all;
/* every gc cycle scans at most 1/GC_MAX_BUCKETS_DIV part of table */

View File

@ -11,7 +11,7 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_labels.h>
static __read_mostly DEFINE_SPINLOCK(nf_connlabels_lock);
static DEFINE_SPINLOCK(nf_connlabels_lock);
static int replace_u32(u32 *address, u32 mask, u32 new)
{

View File

@ -5131,6 +5131,41 @@ nft_obj_type_get(struct net *net, u32 objtype)
return ERR_PTR(-ENOENT);
}
static int nf_tables_updobj(const struct nft_ctx *ctx,
const struct nft_object_type *type,
const struct nlattr *attr,
struct nft_object *obj)
{
struct nft_object *newobj;
struct nft_trans *trans;
int err;
if (!obj->ops->update)
return -EOPNOTSUPP;
trans = nft_trans_alloc(ctx, NFT_MSG_NEWOBJ,
sizeof(struct nft_trans_obj));
if (!trans)
return -ENOMEM;
newobj = nft_obj_init(ctx, type, attr);
if (IS_ERR(newobj)) {
err = PTR_ERR(newobj);
goto err1;
}
nft_trans_obj(trans) = obj;
nft_trans_obj_update(trans) = true;
nft_trans_obj_newobj(trans) = newobj;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
err1:
kfree(trans);
kfree(newobj);
return err;
}
static int nf_tables_newobj(struct net *net, struct sock *nlsk,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
@ -5170,7 +5205,13 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk,
NL_SET_BAD_ATTR(extack, nla[NFTA_OBJ_NAME]);
return -EEXIST;
}
return 0;
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
type = nft_obj_type_get(net, objtype);
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
return nf_tables_updobj(&ctx, type, nla[NFTA_OBJ_DATA], obj);
}
nft_ctx_init(&ctx, net, skb, nlh, family, table, NULL, nla);
@ -6431,6 +6472,19 @@ static void nft_chain_commit_update(struct nft_trans *trans)
}
}
static void nft_obj_commit_update(struct nft_trans *trans)
{
struct nft_object *newobj;
struct nft_object *obj;
obj = nft_trans_obj(trans);
newobj = nft_trans_obj_newobj(trans);
obj->ops->update(obj, newobj);
kfree(newobj);
}
static void nft_commit_release(struct nft_trans *trans)
{
switch (trans->msg_type) {
@ -6795,10 +6849,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
te->set->ndeact--;
break;
case NFT_MSG_NEWOBJ:
nft_clear(net, nft_trans_obj(trans));
nf_tables_obj_notify(&trans->ctx, nft_trans_obj(trans),
NFT_MSG_NEWOBJ);
nft_trans_destroy(trans);
if (nft_trans_obj_update(trans)) {
nft_obj_commit_update(trans);
nf_tables_obj_notify(&trans->ctx,
nft_trans_obj(trans),
NFT_MSG_NEWOBJ);
} else {
nft_clear(net, nft_trans_obj(trans));
nf_tables_obj_notify(&trans->ctx,
nft_trans_obj(trans),
NFT_MSG_NEWOBJ);
nft_trans_destroy(trans);
}
break;
case NFT_MSG_DELOBJ:
nft_obj_del(nft_trans_obj(trans));
@ -6945,8 +7007,13 @@ static int __nf_tables_abort(struct net *net)
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWOBJ:
trans->ctx.table->use--;
nft_obj_del(nft_trans_obj(trans));
if (nft_trans_obj_update(trans)) {
kfree(nft_trans_obj_newobj(trans));
nft_trans_destroy(trans);
} else {
trans->ctx.table->use--;
nft_obj_del(nft_trans_obj(trans));
}
break;
case NFT_MSG_DELOBJ:
trans->ctx.table->use++;

View File

@ -385,6 +385,57 @@ nfulnl_timer(struct timer_list *t)
instance_put(inst);
}
static u32 nfulnl_get_bridge_size(const struct sk_buff *skb)
{
u32 size = 0;
if (!skb_mac_header_was_set(skb))
return 0;
if (skb_vlan_tag_present(skb)) {
size += nla_total_size(0); /* nested */
size += nla_total_size(sizeof(u16)); /* id */
size += nla_total_size(sizeof(u16)); /* tag */
}
if (skb->network_header > skb->mac_header)
size += nla_total_size(skb->network_header - skb->mac_header);
return size;
}
static int nfulnl_put_bridge(struct nfulnl_instance *inst, const struct sk_buff *skb)
{
if (!skb_mac_header_was_set(skb))
return 0;
if (skb_vlan_tag_present(skb)) {
struct nlattr *nest;
nest = nla_nest_start(inst->skb, NFULA_VLAN);
if (!nest)
goto nla_put_failure;
if (nla_put_be16(inst->skb, NFULA_VLAN_TCI, htons(skb->vlan_tci)) ||
nla_put_be16(inst->skb, NFULA_VLAN_PROTO, skb->vlan_proto))
goto nla_put_failure;
nla_nest_end(inst->skb, nest);
}
if (skb->mac_header < skb->network_header) {
int len = (int)(skb->network_header - skb->mac_header);
if (nla_put(inst->skb, NFULA_L2HDR, len, skb_mac_header(skb)))
goto nla_put_failure;
}
return 0;
nla_put_failure:
return -1;
}
/* This is an inline function, we don't really care about a long
* list of arguments */
static inline int
@ -580,6 +631,10 @@ __build_packet_message(struct nfnl_log_net *log,
NFULA_CT, NFULA_CT_INFO) < 0)
goto nla_put_failure;
if ((pf == NFPROTO_NETDEV || pf == NFPROTO_BRIDGE) &&
nfulnl_put_bridge(inst, skb) < 0)
goto nla_put_failure;
if (data_len) {
struct nlattr *nla;
int size = nla_attr_size(data_len);
@ -687,6 +742,8 @@ nfulnl_log_packet(struct net *net,
size += nfnl_ct->build_size(ct);
}
}
if (pf == NFPROTO_NETDEV || pf == NFPROTO_BRIDGE)
size += nfulnl_get_bridge_size(skb);
qthreshold = inst->qthreshold;
/* per-rule qthreshold overrides per-instance */

View File

@ -43,14 +43,15 @@ void nft_byteorder_eval(const struct nft_expr *expr,
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 8; i++) {
src64 = get_unaligned((u64 *)&src[i]);
put_unaligned_be64(src64, &dst[i]);
src64 = nft_reg_load64(&src[i]);
nft_reg_store64(&dst[i], be64_to_cpu(src64));
}
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 8; i++) {
src64 = get_unaligned_be64(&src[i]);
put_unaligned(src64, (u64 *)&dst[i]);
src64 = (__force __u64)
cpu_to_be64(nft_reg_load64(&src[i]));
nft_reg_store64(&dst[i], src64);
}
break;
}

View File

@ -84,6 +84,11 @@ void nft_dynset_eval(const struct nft_expr *expr,
const struct nft_expr *sexpr;
u64 timeout;
if (priv->op == NFT_DYNSET_OP_DELETE) {
set->ops->delete(set, &regs->data[priv->sreg_key]);
return;
}
if (set->ops->update(set, &regs->data[priv->sreg_key], nft_dynset_new,
expr, regs, &ext)) {
sexpr = NULL;
@ -161,6 +166,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
switch (priv->op) {
case NFT_DYNSET_OP_ADD:
case NFT_DYNSET_OP_DELETE:
break;
case NFT_DYNSET_OP_UPDATE:
if (!(set->flags & NFT_SET_TIMEOUT))

View File

@ -26,8 +26,36 @@
#include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */
#define NFT_META_SECS_PER_MINUTE 60
#define NFT_META_SECS_PER_HOUR 3600
#define NFT_META_SECS_PER_DAY 86400
#define NFT_META_DAYS_PER_WEEK 7
static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state);
static u8 nft_meta_weekday(unsigned long secs)
{
unsigned int dse;
u8 wday;
secs -= NFT_META_SECS_PER_MINUTE * sys_tz.tz_minuteswest;
dse = secs / NFT_META_SECS_PER_DAY;
wday = (4 + dse) % NFT_META_DAYS_PER_WEEK;
return wday;
}
static u32 nft_meta_hour(unsigned long secs)
{
struct tm tm;
time64_to_tm(secs, 0, &tm);
return tm.tm_hour * NFT_META_SECS_PER_HOUR
+ tm.tm_min * NFT_META_SECS_PER_MINUTE
+ tm.tm_sec;
}
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
@ -218,6 +246,15 @@ void nft_meta_get_eval(const struct nft_expr *expr,
goto err;
strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ);
break;
case NFT_META_TIME_NS:
nft_reg_store64(dest, ktime_get_real_ns());
break;
case NFT_META_TIME_DAY:
nft_reg_store8(dest, nft_meta_weekday(get_seconds()));
break;
case NFT_META_TIME_HOUR:
*dest = nft_meta_hour(get_seconds());
break;
default:
WARN_ON(1);
goto err;
@ -330,6 +367,15 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
len = sizeof(u8);
break;
#endif
case NFT_META_TIME_NS:
len = sizeof(u64);
break;
case NFT_META_TIME_DAY:
len = sizeof(u8);
break;
case NFT_META_TIME_HOUR:
len = sizeof(u32);
break;
default:
return -EOPNOTSUPP;
}

View File

@ -13,7 +13,7 @@
#include <net/netfilter/nf_tables.h>
struct nft_quota {
u64 quota;
atomic64_t quota;
unsigned long flags;
atomic64_t consumed;
};
@ -21,7 +21,8 @@ struct nft_quota {
static inline bool nft_overquota(struct nft_quota *priv,
const struct sk_buff *skb)
{
return atomic64_add_return(skb->len, &priv->consumed) >= priv->quota;
return atomic64_add_return(skb->len, &priv->consumed) >=
atomic64_read(&priv->quota);
}
static inline bool nft_quota_invert(struct nft_quota *priv)
@ -89,7 +90,7 @@ static int nft_quota_do_init(const struct nlattr * const tb[],
return -EOPNOTSUPP;
}
priv->quota = quota;
atomic64_set(&priv->quota, quota);
priv->flags = flags;
atomic64_set(&priv->consumed, consumed);
@ -105,10 +106,22 @@ static int nft_quota_obj_init(const struct nft_ctx *ctx,
return nft_quota_do_init(tb, priv);
}
static void nft_quota_obj_update(struct nft_object *obj,
struct nft_object *newobj)
{
struct nft_quota *newpriv = nft_obj_data(newobj);
struct nft_quota *priv = nft_obj_data(obj);
u64 newquota;
newquota = atomic64_read(&newpriv->quota);
atomic64_set(&priv->quota, newquota);
priv->flags = newpriv->flags;
}
static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv,
bool reset)
{
u64 consumed, consumed_cap;
u64 consumed, consumed_cap, quota;
u32 flags = priv->flags;
/* Since we inconditionally increment consumed quota for each packet
@ -116,14 +129,15 @@ static int nft_quota_do_dump(struct sk_buff *skb, struct nft_quota *priv,
* userspace.
*/
consumed = atomic64_read(&priv->consumed);
if (consumed >= priv->quota) {
consumed_cap = priv->quota;
quota = atomic64_read(&priv->quota);
if (consumed >= quota) {
consumed_cap = quota;
flags |= NFT_QUOTA_F_DEPLETED;
} else {
consumed_cap = consumed;
}
if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(priv->quota),
if (nla_put_be64(skb, NFTA_QUOTA_BYTES, cpu_to_be64(quota),
NFTA_QUOTA_PAD) ||
nla_put_be64(skb, NFTA_QUOTA_CONSUMED, cpu_to_be64(consumed_cap),
NFTA_QUOTA_PAD) ||
@ -155,6 +169,7 @@ static const struct nft_object_ops nft_quota_obj_ops = {
.init = nft_quota_obj_init,
.eval = nft_quota_obj_eval,
.dump = nft_quota_obj_dump,
.update = nft_quota_obj_update,
};
static struct nft_object_type nft_quota_obj_type __read_mostly = {

View File

@ -234,6 +234,24 @@ static void nft_rhash_remove(const struct net *net,
rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params);
}
static bool nft_rhash_delete(const struct nft_set *set,
const u32 *key)
{
struct nft_rhash *priv = nft_set_priv(set);
struct nft_rhash_cmp_arg arg = {
.genmask = NFT_GENMASK_ANY,
.set = set,
.key = key,
};
struct nft_rhash_elem *he;
he = rhashtable_lookup(&priv->ht, &arg, nft_rhash_params);
if (he == NULL)
return false;
return rhashtable_remove_fast(&priv->ht, &he->node, nft_rhash_params) == 0;
}
static void nft_rhash_walk(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_iter *iter)
{
@ -662,6 +680,7 @@ struct nft_set_type nft_set_rhash_type __read_mostly = {
.remove = nft_rhash_remove,
.lookup = nft_rhash_lookup,
.update = nft_rhash_update,
.delete = nft_rhash_delete,
.walk = nft_rhash_walk,
.get = nft_rhash_get,
},