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 your net-next
tree, most relevantly they are:

1) Extend nft_exthdr to allow to match TCP options bitfields, from
   Manuel Messner.

2) Allow to check if IPv6 extension header is present in nf_tables,
   from Phil Sutter.

3) Allow to set and match conntrack zone in nf_tables, patches from
   Florian Westphal.

4) Several patches for the nf_tables set infrastructure, this includes
   cleanup and preparatory patches to add the new bitmap set type.

5) Add optional ruleset generation ID check to nf_tables and allow to
   delete rules that got no public handle yet via NFTA_RULE_ID. These
   patches add the missing kernel infrastructure to support rule
   deletion by description from userspace.

6) Missing NFT_SET_OBJECT flag to select the right backend when sets
   stores an object map.

7) A couple of cleanups for the expectation and SIP helper, from Gao
   feng.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-02-12 22:11:43 -05:00
Родитель 87b60cfacf 7286ff7fde
Коммит 7c92d61eca
16 изменённых файлов: 832 добавлений и 122 удалений

Просмотреть файл

@ -8587,9 +8587,8 @@ F: Documentation/networking/s2io.txt
F: Documentation/networking/vxge.txt F: Documentation/networking/vxge.txt
F: drivers/net/ethernet/neterion/ F: drivers/net/ethernet/neterion/
NETFILTER ({IP,IP6,ARP,EB,NF}TABLES) NETFILTER
M: Pablo Neira Ayuso <pablo@netfilter.org> M: Pablo Neira Ayuso <pablo@netfilter.org>
M: Patrick McHardy <kaber@trash.net>
M: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> M: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
L: netfilter-devel@vger.kernel.org L: netfilter-devel@vger.kernel.org
L: coreteam@netfilter.org L: coreteam@netfilter.org

Просмотреть файл

@ -28,6 +28,7 @@ struct nfnetlink_subsystem {
const struct nfnl_callback *cb; /* callback for individual types */ const struct nfnl_callback *cb; /* callback for individual types */
int (*commit)(struct net *net, struct sk_buff *skb); int (*commit)(struct net *net, struct sk_buff *skb);
int (*abort)(struct net *net, struct sk_buff *skb); int (*abort)(struct net *net, struct sk_buff *skb);
bool (*valid_genid)(struct net *net, u32 genid);
}; };
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);

Просмотреть файл

@ -203,6 +203,7 @@ struct nft_set_elem {
struct nft_set; struct nft_set;
struct nft_set_iter { struct nft_set_iter {
u8 genmask; u8 genmask;
bool flush;
unsigned int count; unsigned int count;
unsigned int skip; unsigned int skip;
int err; int err;
@ -243,11 +244,13 @@ enum nft_set_class {
* characteristics * characteristics
* *
* @size: required memory * @size: required memory
* @class: lookup performance class * @lookup: lookup performance class
* @space: memory class
*/ */
struct nft_set_estimate { struct nft_set_estimate {
unsigned int size; unsigned int size;
enum nft_set_class class; enum nft_set_class lookup;
enum nft_set_class space;
}; };
struct nft_set_ext; struct nft_set_ext;
@ -260,7 +263,7 @@ struct nft_expr;
* @insert: insert new element into set * @insert: insert new element into set
* @activate: activate new element in the next generation * @activate: activate new element in the next generation
* @deactivate: lookup for element and deactivate it in the next generation * @deactivate: lookup for element and deactivate it in the next generation
* @deactivate_one: deactivate element in the next generation * @flush: deactivate element in the next generation
* @remove: remove element from set * @remove: remove element from set
* @walk: iterate over all set elemeennts * @walk: iterate over all set elemeennts
* @privsize: function to return size of set private data * @privsize: function to return size of set private data
@ -295,10 +298,11 @@ struct nft_set_ops {
void * (*deactivate)(const struct net *net, void * (*deactivate)(const struct net *net,
const struct nft_set *set, const struct nft_set *set,
const struct nft_set_elem *elem); const struct nft_set_elem *elem);
bool (*deactivate_one)(const struct net *net, bool (*flush)(const struct net *net,
const struct nft_set *set, const struct nft_set *set,
void *priv); void *priv);
void (*remove)(const struct nft_set *set, void (*remove)(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem); const struct nft_set_elem *elem);
void (*walk)(const struct nft_ctx *ctx, void (*walk)(const struct nft_ctx *ctx,
struct nft_set *set, struct nft_set *set,
@ -1198,10 +1202,13 @@ struct nft_trans {
struct nft_trans_rule { struct nft_trans_rule {
struct nft_rule *rule; struct nft_rule *rule;
u32 rule_id;
}; };
#define nft_trans_rule(trans) \ #define nft_trans_rule(trans) \
(((struct nft_trans_rule *)trans->data)->rule) (((struct nft_trans_rule *)trans->data)->rule)
#define nft_trans_rule_id(trans) \
(((struct nft_trans_rule *)trans->data)->rule_id)
struct nft_trans_set { struct nft_trans_set {
struct nft_set *set; struct nft_set *set;

Просмотреть файл

@ -207,6 +207,7 @@ enum nft_chain_attributes {
* @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes) * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes)
* @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64) * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64)
* @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN) * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN)
* @NFTA_RULE_ID: uniquely identifies a rule in a transaction (NLA_U32)
*/ */
enum nft_rule_attributes { enum nft_rule_attributes {
NFTA_RULE_UNSPEC, NFTA_RULE_UNSPEC,
@ -218,6 +219,7 @@ enum nft_rule_attributes {
NFTA_RULE_POSITION, NFTA_RULE_POSITION,
NFTA_RULE_USERDATA, NFTA_RULE_USERDATA,
NFTA_RULE_PAD, NFTA_RULE_PAD,
NFTA_RULE_ID,
__NFTA_RULE_MAX __NFTA_RULE_MAX
}; };
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
@ -704,13 +706,32 @@ enum nft_payload_attributes {
}; };
#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1) #define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1)
enum nft_exthdr_flags {
NFT_EXTHDR_F_PRESENT = (1 << 0),
};
/** /**
* enum nft_exthdr_attributes - nf_tables IPv6 extension header expression netlink attributes * enum nft_exthdr_op - nf_tables match options
*
* @NFT_EXTHDR_OP_IPV6: match against ipv6 extension headers
* @NFT_EXTHDR_OP_TCP: match against tcp options
*/
enum nft_exthdr_op {
NFT_EXTHDR_OP_IPV6,
NFT_EXTHDR_OP_TCPOPT,
__NFT_EXTHDR_OP_MAX
};
#define NFT_EXTHDR_OP_MAX (__NFT_EXTHDR_OP_MAX - 1)
/**
* enum nft_exthdr_attributes - nf_tables extension header expression netlink attributes
* *
* @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers) * @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers)
* @NFTA_EXTHDR_TYPE: extension header type (NLA_U8) * @NFTA_EXTHDR_TYPE: extension header type (NLA_U8)
* @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32) * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32)
* @NFTA_EXTHDR_LEN: extension header length (NLA_U32) * @NFTA_EXTHDR_LEN: extension header length (NLA_U32)
* @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32)
* @NFTA_EXTHDR_OP: option match type (NLA_U8)
*/ */
enum nft_exthdr_attributes { enum nft_exthdr_attributes {
NFTA_EXTHDR_UNSPEC, NFTA_EXTHDR_UNSPEC,
@ -718,6 +739,8 @@ enum nft_exthdr_attributes {
NFTA_EXTHDR_TYPE, NFTA_EXTHDR_TYPE,
NFTA_EXTHDR_OFFSET, NFTA_EXTHDR_OFFSET,
NFTA_EXTHDR_LEN, NFTA_EXTHDR_LEN,
NFTA_EXTHDR_FLAGS,
NFTA_EXTHDR_OP,
__NFTA_EXTHDR_MAX __NFTA_EXTHDR_MAX
}; };
#define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1) #define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1)
@ -864,6 +887,7 @@ enum nft_rt_attributes {
* @NFT_CT_PKTS: conntrack packets * @NFT_CT_PKTS: conntrack packets
* @NFT_CT_BYTES: conntrack bytes * @NFT_CT_BYTES: conntrack bytes
* @NFT_CT_AVGPKT: conntrack average bytes per packet * @NFT_CT_AVGPKT: conntrack average bytes per packet
* @NFT_CT_ZONE: conntrack zone
*/ */
enum nft_ct_keys { enum nft_ct_keys {
NFT_CT_STATE, NFT_CT_STATE,
@ -883,6 +907,7 @@ enum nft_ct_keys {
NFT_CT_PKTS, NFT_CT_PKTS,
NFT_CT_BYTES, NFT_CT_BYTES,
NFT_CT_AVGPKT, NFT_CT_AVGPKT,
NFT_CT_ZONE,
}; };
/** /**

Просмотреть файл

@ -65,4 +65,16 @@ struct nfgenmsg {
#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE #define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 #define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1
/**
* enum nfnl_batch_attributes - nfnetlink batch netlink attributes
*
* @NFNL_BATCH_GENID: generation ID for this changeset (NLA_U32)
*/
enum nfnl_batch_attributes {
NFNL_BATCH_UNSPEC,
NFNL_BATCH_GENID,
__NFNL_BATCH_MAX
};
#define NFNL_BATCH_MAX (__NFNL_BATCH_MAX - 1)
#endif /* _UAPI_NFNETLINK_H */ #endif /* _UAPI_NFNETLINK_H */

Просмотреть файл

@ -467,10 +467,10 @@ config NF_TABLES_NETDEV
This option enables support for the "netdev" table. This option enables support for the "netdev" table.
config NFT_EXTHDR config NFT_EXTHDR
tristate "Netfilter nf_tables IPv6 exthdr module" tristate "Netfilter nf_tables exthdr module"
help help
This option adds the "exthdr" expression that you can use to match This option adds the "exthdr" expression that you can use to match
IPv6 extension headers. IPv6 extension headers and tcp options.
config NFT_META config NFT_META
tristate "Netfilter nf_tables meta module" tristate "Netfilter nf_tables meta module"
@ -509,6 +509,12 @@ config NFT_SET_HASH
This option adds the "hash" set type that is used to build one-way This option adds the "hash" set type that is used to build one-way
mappings between matchings and actions. mappings between matchings and actions.
config NFT_SET_BITMAP
tristate "Netfilter nf_tables bitmap set module"
help
This option adds the "bitmap" set type that is used to build sets
whose keys are smaller or equal to 16 bits.
config NFT_COUNTER config NFT_COUNTER
tristate "Netfilter nf_tables counter module" tristate "Netfilter nf_tables counter module"
help help

Просмотреть файл

@ -93,6 +93,7 @@ obj-$(CONFIG_NFT_REJECT) += nft_reject.o
obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o
obj-$(CONFIG_NFT_SET_RBTREE) += nft_set_rbtree.o obj-$(CONFIG_NFT_SET_RBTREE) += nft_set_rbtree.o
obj-$(CONFIG_NFT_SET_HASH) += nft_set_hash.o obj-$(CONFIG_NFT_SET_HASH) += nft_set_hash.o
obj-$(CONFIG_NFT_SET_BITMAP) += nft_set_bitmap.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
obj-$(CONFIG_NFT_LOG) += nft_log.o obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o obj-$(CONFIG_NFT_MASQ) += nft_masq.o

Просмотреть файл

@ -353,7 +353,7 @@ void nf_ct_expect_put(struct nf_conntrack_expect *exp)
} }
EXPORT_SYMBOL_GPL(nf_ct_expect_put); EXPORT_SYMBOL_GPL(nf_ct_expect_put);
static int nf_ct_expect_insert(struct nf_conntrack_expect *exp) static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
{ {
struct nf_conn_help *master_help = nfct_help(exp->master); struct nf_conn_help *master_help = nfct_help(exp->master);
struct nf_conntrack_helper *helper; struct nf_conntrack_helper *helper;
@ -380,7 +380,6 @@ static int nf_ct_expect_insert(struct nf_conntrack_expect *exp)
add_timer(&exp->timeout); add_timer(&exp->timeout);
NF_CT_STAT_INC(net, expect_create); NF_CT_STAT_INC(net, expect_create);
return 0;
} }
/* Race with expectations being used means we could have none to find; OK. */ /* Race with expectations being used means we could have none to find; OK. */
@ -464,9 +463,8 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
if (ret <= 0) if (ret <= 0)
goto out; goto out;
ret = nf_ct_expect_insert(expect); nf_ct_expect_insert(expect);
if (ret < 0)
goto out;
spin_unlock_bh(&nf_conntrack_expect_lock); spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report); nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
return ret; return ret;

Просмотреть файл

@ -809,13 +809,11 @@ static int refresh_signalling_expectation(struct nf_conn *ct,
exp->tuple.dst.protonum != proto || exp->tuple.dst.protonum != proto ||
exp->tuple.dst.u.udp.port != port) exp->tuple.dst.u.udp.port != port)
continue; continue;
if (!del_timer(&exp->timeout)) if (mod_timer_pending(&exp->timeout, jiffies + expires * HZ)) {
continue; exp->flags &= ~NF_CT_EXPECT_INACTIVE;
exp->flags &= ~NF_CT_EXPECT_INACTIVE; found = 1;
exp->timeout.expires = jiffies + expires * HZ; break;
add_timer(&exp->timeout); }
found = 1;
break;
} }
spin_unlock_bh(&nf_conntrack_expect_lock); spin_unlock_bh(&nf_conntrack_expect_lock);
return found; return found;

Просмотреть файл

@ -240,6 +240,10 @@ static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
if (trans == NULL) if (trans == NULL)
return NULL; return NULL;
if (msg_type == NFT_MSG_NEWRULE && ctx->nla[NFTA_RULE_ID] != NULL) {
nft_trans_rule_id(trans) =
ntohl(nla_get_be32(ctx->nla[NFTA_RULE_ID]));
}
nft_trans_rule(trans) = rule; nft_trans_rule(trans) = rule;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
@ -2293,6 +2297,22 @@ err1:
return err; return err;
} }
static struct nft_rule *nft_rule_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_rule *rule = nft_trans_rule(trans);
if (trans->msg_type == NFT_MSG_NEWRULE &&
id == nft_trans_rule_id(trans))
return rule;
}
return ERR_PTR(-ENOENT);
}
static int nf_tables_delrule(struct net *net, struct sock *nlsk, static int nf_tables_delrule(struct net *net, struct sock *nlsk,
struct sk_buff *skb, const struct nlmsghdr *nlh, struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
@ -2330,6 +2350,12 @@ static int nf_tables_delrule(struct net *net, struct sock *nlsk,
if (IS_ERR(rule)) if (IS_ERR(rule))
return PTR_ERR(rule); return PTR_ERR(rule);
err = nft_delrule(&ctx, rule);
} else if (nla[NFTA_RULE_ID]) {
rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
if (IS_ERR(rule))
return PTR_ERR(rule);
err = nft_delrule(&ctx, rule); err = nft_delrule(&ctx, rule);
} else { } else {
err = nft_delrule_by_chain(&ctx); err = nft_delrule_by_chain(&ctx);
@ -2398,12 +2424,14 @@ nft_select_set_ops(const struct nlattr * const nla[],
features = 0; features = 0;
if (nla[NFTA_SET_FLAGS] != NULL) { if (nla[NFTA_SET_FLAGS] != NULL) {
features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT; features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT |
NFT_SET_OBJECT;
} }
bops = NULL; bops = NULL;
best.size = ~0; best.size = ~0;
best.class = ~0; best.lookup = ~0;
best.space = ~0;
list_for_each_entry(ops, &nf_tables_set_ops, list) { list_for_each_entry(ops, &nf_tables_set_ops, list) {
if ((ops->features & features) != features) if ((ops->features & features) != features)
@ -2413,16 +2441,27 @@ nft_select_set_ops(const struct nlattr * const nla[],
switch (policy) { switch (policy) {
case NFT_SET_POL_PERFORMANCE: case NFT_SET_POL_PERFORMANCE:
if (est.class < best.class) if (est.lookup < best.lookup)
break;
if (est.class == best.class && est.size < best.size)
break; break;
if (est.lookup == best.lookup) {
if (!desc->size) {
if (est.space < best.space)
break;
} else if (est.size < best.size) {
break;
}
}
continue; continue;
case NFT_SET_POL_MEMORY: case NFT_SET_POL_MEMORY:
if (est.size < best.size) if (!desc->size) {
break; if (est.space < best.space)
if (est.size == best.size && est.class < best.class) break;
if (est.space == best.space &&
est.lookup < best.lookup)
break;
} else if (est.size < best.size) {
break; break;
}
continue; continue;
default: default:
break; break;
@ -3121,6 +3160,7 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
iter.count = 0; iter.count = 0;
iter.err = 0; iter.err = 0;
iter.fn = nf_tables_bind_check_setelem; iter.fn = nf_tables_bind_check_setelem;
iter.flush = false;
set->ops->walk(ctx, set, &iter); set->ops->walk(ctx, set, &iter);
if (iter.err < 0) if (iter.err < 0)
@ -3374,6 +3414,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
args.iter.count = 0; args.iter.count = 0;
args.iter.err = 0; args.iter.err = 0;
args.iter.fn = nf_tables_dump_setelem; args.iter.fn = nf_tables_dump_setelem;
args.iter.flush = false;
set->ops->walk(&ctx, set, &args.iter); set->ops->walk(&ctx, set, &args.iter);
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
@ -3752,7 +3793,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return 0; return 0;
err6: err6:
set->ops->remove(set, &elem); set->ops->remove(ctx->net, set, &elem);
err5: err5:
kfree(trans); kfree(trans);
err4: err4:
@ -3898,7 +3939,7 @@ static int nft_flush_set(const struct nft_ctx *ctx,
if (!trans) if (!trans)
return -ENOMEM; return -ENOMEM;
if (!set->ops->deactivate_one(ctx->net, set, elem->priv)) { if (!set->ops->flush(ctx->net, set, elem->priv)) {
err = -ENOENT; err = -ENOENT;
goto err1; goto err1;
} }
@ -3936,15 +3977,14 @@ static int nf_tables_delsetelem(struct net *net, struct sock *nlsk,
return -EBUSY; return -EBUSY;
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) { if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL) {
struct nft_set_dump_args args = { struct nft_set_iter iter = {
.iter = { .genmask = genmask,
.genmask = genmask, .fn = nft_flush_set,
.fn = nft_flush_set, .flush = true,
},
}; };
set->ops->walk(&ctx, set, &args.iter); set->ops->walk(&ctx, set, &iter);
return args.iter.err; return iter.err;
} }
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
@ -4804,7 +4844,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nf_tables_setelem_notify(&trans->ctx, te->set, nf_tables_setelem_notify(&trans->ctx, te->set,
&te->elem, &te->elem,
NFT_MSG_DELSETELEM, 0); NFT_MSG_DELSETELEM, 0);
te->set->ops->remove(te->set, &te->elem); te->set->ops->remove(net, te->set, &te->elem);
atomic_dec(&te->set->nelems); atomic_dec(&te->set->nelems);
te->set->ndeact--; te->set->ndeact--;
break; break;
@ -4925,7 +4965,7 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
case NFT_MSG_NEWSETELEM: case NFT_MSG_NEWSETELEM:
te = (struct nft_trans_elem *)trans->data; te = (struct nft_trans_elem *)trans->data;
te->set->ops->remove(te->set, &te->elem); te->set->ops->remove(net, te->set, &te->elem);
atomic_dec(&te->set->nelems); atomic_dec(&te->set->nelems);
break; break;
case NFT_MSG_DELSETELEM: case NFT_MSG_DELSETELEM:
@ -4959,6 +4999,11 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
return 0; return 0;
} }
static bool nf_tables_valid_genid(struct net *net, u32 genid)
{
return net->nft.base_seq == genid;
}
static const struct nfnetlink_subsystem nf_tables_subsys = { static const struct nfnetlink_subsystem nf_tables_subsys = {
.name = "nf_tables", .name = "nf_tables",
.subsys_id = NFNL_SUBSYS_NFTABLES, .subsys_id = NFNL_SUBSYS_NFTABLES,
@ -4966,6 +5011,7 @@ static const struct nfnetlink_subsystem nf_tables_subsys = {
.cb = nf_tables_cb, .cb = nf_tables_cb,
.commit = nf_tables_commit, .commit = nf_tables_commit,
.abort = nf_tables_abort, .abort = nf_tables_abort,
.valid_genid = nf_tables_valid_genid,
}; };
int nft_chain_validate_dependency(const struct nft_chain *chain, int nft_chain_validate_dependency(const struct nft_chain *chain,
@ -5091,6 +5137,7 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
iter.count = 0; iter.count = 0;
iter.err = 0; iter.err = 0;
iter.fn = nf_tables_loop_check_setelem; iter.fn = nf_tables_loop_check_setelem;
iter.flush = false;
set->ops->walk(ctx, set, &iter); set->ops->walk(ctx, set, &iter);
if (iter.err < 0) if (iter.err < 0)

Просмотреть файл

@ -3,7 +3,7 @@
* *
* (C) 2001 by Jay Schulist <jschlst@samba.org>, * (C) 2001 by Jay Schulist <jschlst@samba.org>,
* (C) 2002-2005 by Harald Welte <laforge@gnumonks.org> * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
* (C) 2005,2007 by Pablo Neira Ayuso <pablo@netfilter.org> * (C) 2005-2017 by Pablo Neira Ayuso <pablo@netfilter.org>
* *
* Initial netfilter messages via netlink development funded and * Initial netfilter messages via netlink development funded and
* generally made possible by Network Robots, Inc. (www.networkrobots.com) * generally made possible by Network Robots, Inc. (www.networkrobots.com)
@ -100,9 +100,9 @@ int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
} }
EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister); EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t type) static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u16 type)
{ {
u_int8_t subsys_id = NFNL_SUBSYS_ID(type); u8 subsys_id = NFNL_SUBSYS_ID(type);
if (subsys_id >= NFNL_SUBSYS_COUNT) if (subsys_id >= NFNL_SUBSYS_COUNT)
return NULL; return NULL;
@ -111,9 +111,9 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
} }
static inline const struct nfnl_callback * static inline const struct nfnl_callback *
nfnetlink_find_client(u_int16_t type, const struct nfnetlink_subsystem *ss) nfnetlink_find_client(u16 type, const struct nfnetlink_subsystem *ss)
{ {
u_int8_t cb_id = NFNL_MSG_TYPE(type); u8 cb_id = NFNL_MSG_TYPE(type);
if (cb_id >= ss->cb_count) if (cb_id >= ss->cb_count)
return NULL; return NULL;
@ -185,7 +185,7 @@ replay:
{ {
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *attr = (void *)nlh + min_len; struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len; int attrlen = nlh->nlmsg_len - min_len;
@ -273,7 +273,7 @@ enum {
}; };
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
u_int16_t subsys_id) u16 subsys_id, u32 genid)
{ {
struct sk_buff *oskb = skb; struct sk_buff *oskb = skb;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
@ -315,6 +315,12 @@ replay:
return kfree_skb(skb); return kfree_skb(skb);
} }
if (genid && ss->valid_genid && !ss->valid_genid(net, genid)) {
nfnl_unlock(subsys_id);
netlink_ack(oskb, nlh, -ERESTART);
return kfree_skb(skb);
}
while (skb->len >= nlmsg_total_size(0)) { while (skb->len >= nlmsg_total_size(0)) {
int msglen, type; int msglen, type;
@ -365,7 +371,7 @@ replay:
{ {
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); u8 cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *attr = (void *)nlh + min_len; struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len; int attrlen = nlh->nlmsg_len - min_len;
@ -436,11 +442,51 @@ done:
kfree_skb(skb); kfree_skb(skb);
} }
static const struct nla_policy nfnl_batch_policy[NFNL_BATCH_MAX + 1] = {
[NFNL_BATCH_GENID] = { .type = NLA_U32 },
};
static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)
{
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
struct nlattr *attr = (void *)nlh + min_len;
struct nlattr *cda[NFNL_BATCH_MAX + 1];
int attrlen = nlh->nlmsg_len - min_len;
struct nfgenmsg *nfgenmsg;
int msglen, err;
u32 gen_id = 0;
u16 res_id;
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
if (nlh->nlmsg_len < NLMSG_HDRLEN ||
skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
return;
err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy);
if (err < 0) {
netlink_ack(skb, nlh, err);
return;
}
if (cda[NFNL_BATCH_GENID])
gen_id = ntohl(nla_get_be32(cda[NFNL_BATCH_GENID]));
nfgenmsg = nlmsg_data(nlh);
skb_pull(skb, msglen);
/* Work around old nft using host byte order */
if (nfgenmsg->res_id == NFNL_SUBSYS_NFTABLES)
res_id = NFNL_SUBSYS_NFTABLES;
else
res_id = ntohs(nfgenmsg->res_id);
nfnetlink_rcv_batch(skb, nlh, res_id, gen_id);
}
static void nfnetlink_rcv(struct sk_buff *skb) static void nfnetlink_rcv(struct sk_buff *skb)
{ {
struct nlmsghdr *nlh = nlmsg_hdr(skb); struct nlmsghdr *nlh = nlmsg_hdr(skb);
u_int16_t res_id;
int msglen;
if (nlh->nlmsg_len < NLMSG_HDRLEN || if (nlh->nlmsg_len < NLMSG_HDRLEN ||
skb->len < nlh->nlmsg_len) skb->len < nlh->nlmsg_len)
@ -451,28 +497,10 @@ static void nfnetlink_rcv(struct sk_buff *skb)
return; return;
} }
if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) { if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN)
struct nfgenmsg *nfgenmsg; nfnetlink_rcv_skb_batch(skb, nlh);
else
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
if (nlh->nlmsg_len < NLMSG_HDRLEN ||
skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
return;
nfgenmsg = nlmsg_data(nlh);
skb_pull(skb, msglen);
/* Work around old nft using host byte order */
if (nfgenmsg->res_id == NFNL_SUBSYS_NFTABLES)
res_id = NFNL_SUBSYS_NFTABLES;
else
res_id = ntohs(nfgenmsg->res_id);
nfnetlink_rcv_batch(skb, nlh, res_id);
} else {
netlink_rcv_skb(skb, &nfnetlink_rcv_msg); netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
}
} }
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES

Просмотреть файл

@ -32,6 +32,11 @@ struct nft_ct {
}; };
}; };
#ifdef CONFIG_NF_CONNTRACK_ZONES
static DEFINE_PER_CPU(struct nf_conn *, nft_ct_pcpu_template);
static unsigned int nft_ct_pcpu_template_refcnt __read_mostly;
#endif
static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c, static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c,
enum nft_ct_keys k, enum nft_ct_keys k,
enum ip_conntrack_dir d) enum ip_conntrack_dir d)
@ -151,6 +156,18 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
case NFT_CT_PROTOCOL: case NFT_CT_PROTOCOL:
*dest = nf_ct_protonum(ct); *dest = nf_ct_protonum(ct);
return; return;
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE: {
const struct nf_conntrack_zone *zone = nf_ct_zone(ct);
if (priv->dir < IP_CT_DIR_MAX)
*dest = nf_ct_zone_id(zone, priv->dir);
else
*dest = zone->id;
return;
}
#endif
default: default:
break; break;
} }
@ -179,6 +196,53 @@ err:
regs->verdict.code = NFT_BREAK; regs->verdict.code = NFT_BREAK;
} }
#ifdef CONFIG_NF_CONNTRACK_ZONES
static void nft_ct_set_zone_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nf_conntrack_zone zone = { .dir = NF_CT_DEFAULT_ZONE_DIR };
const struct nft_ct *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb;
enum ip_conntrack_info ctinfo;
u16 value = regs->data[priv->sreg];
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (ct) /* already tracked */
return;
zone.id = value;
switch (priv->dir) {
case IP_CT_DIR_ORIGINAL:
zone.dir = NF_CT_ZONE_DIR_ORIG;
break;
case IP_CT_DIR_REPLY:
zone.dir = NF_CT_ZONE_DIR_REPL;
break;
default:
break;
}
ct = this_cpu_read(nft_ct_pcpu_template);
if (likely(atomic_read(&ct->ct_general.use) == 1)) {
nf_ct_zone_add(ct, &zone);
} else {
/* previous skb got queued to userspace */
ct = nf_ct_tmpl_alloc(nft_net(pkt), &zone, GFP_ATOMIC);
if (!ct) {
regs->verdict.code = NF_DROP;
return;
}
}
atomic_inc(&ct->ct_general.use);
nf_ct_set(skb, ct, IP_CT_NEW);
}
#endif
static void nft_ct_set_eval(const struct nft_expr *expr, static void nft_ct_set_eval(const struct nft_expr *expr,
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
@ -257,6 +321,45 @@ static void nft_ct_netns_put(struct net *net, uint8_t family)
nf_ct_netns_put(net, family); nf_ct_netns_put(net, family);
} }
#ifdef CONFIG_NF_CONNTRACK_ZONES
static void nft_ct_tmpl_put_pcpu(void)
{
struct nf_conn *ct;
int cpu;
for_each_possible_cpu(cpu) {
ct = per_cpu(nft_ct_pcpu_template, cpu);
if (!ct)
break;
nf_ct_put(ct);
per_cpu(nft_ct_pcpu_template, cpu) = NULL;
}
}
static bool nft_ct_tmpl_alloc_pcpu(void)
{
struct nf_conntrack_zone zone = { .id = 0 };
struct nf_conn *tmp;
int cpu;
if (nft_ct_pcpu_template_refcnt)
return true;
for_each_possible_cpu(cpu) {
tmp = nf_ct_tmpl_alloc(&init_net, &zone, GFP_KERNEL);
if (!tmp) {
nft_ct_tmpl_put_pcpu();
return false;
}
atomic_set(&tmp->ct_general.use, 1);
per_cpu(nft_ct_pcpu_template, cpu) = tmp;
}
return true;
}
#endif
static int nft_ct_get_init(const struct nft_ctx *ctx, static int nft_ct_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
@ -266,6 +369,7 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
int err; int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
priv->dir = IP_CT_DIR_MAX;
switch (priv->key) { switch (priv->key) {
case NFT_CT_DIRECTION: case NFT_CT_DIRECTION:
if (tb[NFTA_CT_DIRECTION] != NULL) if (tb[NFTA_CT_DIRECTION] != NULL)
@ -333,11 +437,13 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
case NFT_CT_BYTES: case NFT_CT_BYTES:
case NFT_CT_PKTS: case NFT_CT_PKTS:
case NFT_CT_AVGPKT: case NFT_CT_AVGPKT:
/* no direction? return sum of original + reply */
if (tb[NFTA_CT_DIRECTION] == NULL)
priv->dir = IP_CT_DIR_MAX;
len = sizeof(u64); len = sizeof(u64);
break; break;
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
len = sizeof(u16);
break;
#endif
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -371,15 +477,33 @@ static int nft_ct_get_init(const struct nft_ctx *ctx,
return 0; return 0;
} }
static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv)
{
switch (priv->key) {
#ifdef CONFIG_NF_CONNTRACK_LABELS
case NFT_CT_LABELS:
nf_connlabels_put(ctx->net);
break;
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
if (--nft_ct_pcpu_template_refcnt == 0)
nft_ct_tmpl_put_pcpu();
#endif
default:
break;
}
}
static int nft_ct_set_init(const struct nft_ctx *ctx, static int nft_ct_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_ct *priv = nft_expr_priv(expr); struct nft_ct *priv = nft_expr_priv(expr);
bool label_got = false;
unsigned int len; unsigned int len;
int err; int err;
priv->dir = IP_CT_DIR_MAX;
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
switch (priv->key) { switch (priv->key) {
#ifdef CONFIG_NF_CONNTRACK_MARK #ifdef CONFIG_NF_CONNTRACK_MARK
@ -397,13 +521,30 @@ static int nft_ct_set_init(const struct nft_ctx *ctx,
err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1); err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1);
if (err) if (err)
return err; return err;
label_got = true; break;
#endif
#ifdef CONFIG_NF_CONNTRACK_ZONES
case NFT_CT_ZONE:
if (!nft_ct_tmpl_alloc_pcpu())
return -ENOMEM;
nft_ct_pcpu_template_refcnt++;
break; break;
#endif #endif
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (tb[NFTA_CT_DIRECTION]) {
priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
switch (priv->dir) {
case IP_CT_DIR_ORIGINAL:
case IP_CT_DIR_REPLY:
break;
default:
return -EINVAL;
}
}
priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]); priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]);
err = nft_validate_register_load(priv->sreg, len); err = nft_validate_register_load(priv->sreg, len);
if (err < 0) if (err < 0)
@ -416,8 +557,7 @@ static int nft_ct_set_init(const struct nft_ctx *ctx,
return 0; return 0;
err1: err1:
if (label_got) __nft_ct_set_destroy(ctx, priv);
nf_connlabels_put(ctx->net);
return err; return err;
} }
@ -432,16 +572,7 @@ static void nft_ct_set_destroy(const struct nft_ctx *ctx,
{ {
struct nft_ct *priv = nft_expr_priv(expr); struct nft_ct *priv = nft_expr_priv(expr);
switch (priv->key) { __nft_ct_set_destroy(ctx, priv);
#ifdef CONFIG_NF_CONNTRACK_LABELS
case NFT_CT_LABELS:
nf_connlabels_put(ctx->net);
break;
#endif
default:
break;
}
nft_ct_netns_put(ctx->net, ctx->afi->family); nft_ct_netns_put(ctx->net, ctx->afi->family);
} }
@ -465,6 +596,7 @@ static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
case NFT_CT_BYTES: case NFT_CT_BYTES:
case NFT_CT_PKTS: case NFT_CT_PKTS:
case NFT_CT_AVGPKT: case NFT_CT_AVGPKT:
case NFT_CT_ZONE:
if (priv->dir < IP_CT_DIR_MAX && if (priv->dir < IP_CT_DIR_MAX &&
nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
goto nla_put_failure; goto nla_put_failure;
@ -487,6 +619,17 @@ static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
goto nla_put_failure; goto nla_put_failure;
switch (priv->key) {
case NFT_CT_ZONE:
if (priv->dir < IP_CT_DIR_MAX &&
nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
goto nla_put_failure;
break;
default:
break;
}
return 0; return 0;
nla_put_failure: nla_put_failure:
@ -512,6 +655,17 @@ static const struct nft_expr_ops nft_ct_set_ops = {
.dump = nft_ct_set_dump, .dump = nft_ct_set_dump,
}; };
#ifdef CONFIG_NF_CONNTRACK_ZONES
static const struct nft_expr_ops nft_ct_set_zone_ops = {
.type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_set_zone_eval,
.init = nft_ct_set_init,
.destroy = nft_ct_set_destroy,
.dump = nft_ct_set_dump,
};
#endif
static const struct nft_expr_ops * static const struct nft_expr_ops *
nft_ct_select_ops(const struct nft_ctx *ctx, nft_ct_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
@ -525,8 +679,13 @@ nft_ct_select_ops(const struct nft_ctx *ctx,
if (tb[NFTA_CT_DREG]) if (tb[NFTA_CT_DREG])
return &nft_ct_get_ops; return &nft_ct_get_ops;
if (tb[NFTA_CT_SREG]) if (tb[NFTA_CT_SREG]) {
#ifdef CONFIG_NF_CONNTRACK_ZONES
if (nla_get_be32(tb[NFTA_CT_KEY]) == htonl(NFT_CT_ZONE))
return &nft_ct_set_zone_ops;
#endif
return &nft_ct_set_ops; return &nft_ct_set_ops;
}
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }

Просмотреть файл

@ -15,19 +15,29 @@
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
// FIXME: #include <net/tcp.h>
#include <net/ipv6.h>
struct nft_exthdr { struct nft_exthdr {
u8 type; u8 type;
u8 offset; u8 offset;
u8 len; u8 len;
u8 op;
enum nft_registers dreg:8; enum nft_registers dreg:8;
u8 flags;
}; };
static void nft_exthdr_eval(const struct nft_expr *expr, static unsigned int optlen(const u8 *opt, unsigned int offset)
struct nft_regs *regs, {
const struct nft_pktinfo *pkt) /* Beware zero-length options: make finite progress */
if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
return 1;
else
return opt[offset + 1];
}
static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{ {
struct nft_exthdr *priv = nft_expr_priv(expr); struct nft_exthdr *priv = nft_expr_priv(expr);
u32 *dest = &regs->data[priv->dreg]; u32 *dest = &regs->data[priv->dreg];
@ -35,8 +45,12 @@ static void nft_exthdr_eval(const struct nft_expr *expr,
int err; int err;
err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL); err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
if (err < 0) if (priv->flags & NFT_EXTHDR_F_PRESENT) {
*dest = (err >= 0);
return;
} else if (err < 0) {
goto err; goto err;
}
offset += priv->offset; offset += priv->offset;
dest[priv->len / NFT_REG32_SIZE] = 0; dest[priv->len / NFT_REG32_SIZE] = 0;
@ -47,11 +61,59 @@ err:
regs->verdict.code = NFT_BREAK; regs->verdict.code = NFT_BREAK;
} }
static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
struct nft_exthdr *priv = nft_expr_priv(expr);
unsigned int i, optl, tcphdr_len, offset;
u32 *dest = &regs->data[priv->dreg];
struct tcphdr *tcph;
u8 *opt;
if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
goto err;
tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buff);
if (!tcph)
goto err;
tcphdr_len = __tcp_hdrlen(tcph);
if (tcphdr_len < sizeof(*tcph))
goto err;
tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, tcphdr_len, buff);
if (!tcph)
goto err;
opt = (u8 *)tcph;
for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
optl = optlen(opt, i);
if (priv->type != opt[i])
continue;
if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
goto err;
offset = i + priv->offset;
dest[priv->len / NFT_REG32_SIZE] = 0;
memcpy(dest, opt + offset, priv->len);
return;
}
err:
regs->verdict.code = NFT_BREAK;
}
static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
[NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, [NFTA_EXTHDR_DREG] = { .type = NLA_U32 },
[NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 },
[NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 },
[NFTA_EXTHDR_LEN] = { .type = NLA_U32 }, [NFTA_EXTHDR_LEN] = { .type = NLA_U32 },
[NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 },
}; };
static int nft_exthdr_init(const struct nft_ctx *ctx, static int nft_exthdr_init(const struct nft_ctx *ctx,
@ -59,13 +121,13 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_exthdr *priv = nft_expr_priv(expr); struct nft_exthdr *priv = nft_expr_priv(expr);
u32 offset, len; u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
int err; int err;
if (tb[NFTA_EXTHDR_DREG] == NULL || if (!tb[NFTA_EXTHDR_DREG] ||
tb[NFTA_EXTHDR_TYPE] == NULL || !tb[NFTA_EXTHDR_TYPE] ||
tb[NFTA_EXTHDR_OFFSET] == NULL || !tb[NFTA_EXTHDR_OFFSET] ||
tb[NFTA_EXTHDR_LEN] == NULL) !tb[NFTA_EXTHDR_LEN])
return -EINVAL; return -EINVAL;
err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset); err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
@ -76,10 +138,27 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
if (err < 0) if (err < 0)
return err; return err;
if (tb[NFTA_EXTHDR_FLAGS]) {
err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
if (err < 0)
return err;
if (flags & ~NFT_EXTHDR_F_PRESENT)
return -EINVAL;
}
if (tb[NFTA_EXTHDR_OP]) {
err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
if (err < 0)
return err;
}
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
priv->offset = offset; priv->offset = offset;
priv->len = len; priv->len = len;
priv->dreg = nft_parse_register(tb[NFTA_EXTHDR_DREG]); priv->dreg = nft_parse_register(tb[NFTA_EXTHDR_DREG]);
priv->flags = flags;
priv->op = op;
return nft_validate_register_store(ctx, priv->dreg, NULL, return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, priv->len); NFT_DATA_VALUE, priv->len);
@ -97,6 +176,10 @@ static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len))) if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
goto nla_put_failure; goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
goto nla_put_failure;
return 0; return 0;
nla_put_failure: nla_put_failure:
@ -104,17 +187,45 @@ nla_put_failure:
} }
static struct nft_expr_type nft_exthdr_type; static struct nft_expr_type nft_exthdr_type;
static const struct nft_expr_ops nft_exthdr_ops = { static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
.type = &nft_exthdr_type, .type = &nft_exthdr_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
.eval = nft_exthdr_eval, .eval = nft_exthdr_ipv6_eval,
.init = nft_exthdr_init, .init = nft_exthdr_init,
.dump = nft_exthdr_dump, .dump = nft_exthdr_dump,
}; };
static const struct nft_expr_ops nft_exthdr_tcp_ops = {
.type = &nft_exthdr_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
.eval = nft_exthdr_tcp_eval,
.init = nft_exthdr_init,
.dump = nft_exthdr_dump,
};
static const struct nft_expr_ops *
nft_exthdr_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
u32 op;
if (!tb[NFTA_EXTHDR_OP])
return &nft_exthdr_ipv6_ops;
op = ntohl(nla_get_u32(tb[NFTA_EXTHDR_OP]));
switch (op) {
case NFT_EXTHDR_OP_TCPOPT:
return &nft_exthdr_tcp_ops;
case NFT_EXTHDR_OP_IPV6:
return &nft_exthdr_ipv6_ops;
}
return ERR_PTR(-EOPNOTSUPP);
}
static struct nft_expr_type nft_exthdr_type __read_mostly = { static struct nft_expr_type nft_exthdr_type __read_mostly = {
.name = "exthdr", .name = "exthdr",
.ops = &nft_exthdr_ops, .select_ops = &nft_exthdr_select_ops,
.policy = nft_exthdr_policy, .policy = nft_exthdr_policy,
.maxattr = NFTA_EXTHDR_MAX, .maxattr = NFTA_EXTHDR_MAX,
.owner = THIS_MODULE, .owner = THIS_MODULE,

Просмотреть файл

@ -0,0 +1,314 @@
/*
* Copyright (c) 2017 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
/* This bitmap uses two bits to represent one element. These two bits determine
* the element state in the current and the future generation.
*
* An element can be in three states. The generation cursor is represented using
* the ^ character, note that this cursor shifts on every succesful transaction.
* If no transaction is going on, we observe all elements are in the following
* state:
*
* 11 = this element is active in the current generation. In case of no updates,
* ^ it stays active in the next generation.
* 00 = this element is inactive in the current generation. In case of no
* ^ updates, it stays inactive in the next generation.
*
* On transaction handling, we observe these two temporary states:
*
* 01 = this element is inactive in the current generation and it becomes active
* ^ in the next one. This happens when the element is inserted but commit
* path has not yet been executed yet, so activation is still pending. On
* transaction abortion, the element is removed.
* 10 = this element is active in the current generation and it becomes inactive
* ^ in the next one. This happens when the element is deactivated but commit
* path has not yet been executed yet, so removal is still pending. On
* transation abortion, the next generation bit is reset to go back to
* restore its previous state.
*/
struct nft_bitmap {
u16 bitmap_size;
u8 bitmap[];
};
static inline void nft_bitmap_location(u32 key, u32 *idx, u32 *off)
{
u32 k = (key << 1);
*idx = k / BITS_PER_BYTE;
*off = k % BITS_PER_BYTE;
}
/* Fetch the two bits that represent the element and check if it is active based
* on the generation mask.
*/
static inline bool
nft_bitmap_active(const u8 *bitmap, u32 idx, u32 off, u8 genmask)
{
return (bitmap[idx] & (0x3 << off)) & (genmask << off);
}
static bool nft_bitmap_lookup(const struct net *net, const struct nft_set *set,
const u32 *key, const struct nft_set_ext **ext)
{
const struct nft_bitmap *priv = nft_set_priv(set);
u8 genmask = nft_genmask_cur(net);
u32 idx, off;
nft_bitmap_location(*key, &idx, &off);
return nft_bitmap_active(priv->bitmap, idx, off, genmask);
}
static int nft_bitmap_insert(const struct net *net, const struct nft_set *set,
const struct nft_set_elem *elem,
struct nft_set_ext **_ext)
{
struct nft_bitmap *priv = nft_set_priv(set);
struct nft_set_ext *ext = elem->priv;
u8 genmask = nft_genmask_next(net);
u32 idx, off;
nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off);
if (nft_bitmap_active(priv->bitmap, idx, off, genmask))
return -EEXIST;
/* Enter 01 state. */
priv->bitmap[idx] |= (genmask << off);
return 0;
}
static void nft_bitmap_remove(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_bitmap *priv = nft_set_priv(set);
struct nft_set_ext *ext = elem->priv;
u8 genmask = nft_genmask_next(net);
u32 idx, off;
nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off);
/* Enter 00 state. */
priv->bitmap[idx] &= ~(genmask << off);
}
static void nft_bitmap_activate(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_bitmap *priv = nft_set_priv(set);
struct nft_set_ext *ext = elem->priv;
u8 genmask = nft_genmask_next(net);
u32 idx, off;
nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off);
/* Enter 11 state. */
priv->bitmap[idx] |= (genmask << off);
}
static bool nft_bitmap_flush(const struct net *net,
const struct nft_set *set, void *ext)
{
struct nft_bitmap *priv = nft_set_priv(set);
u8 genmask = nft_genmask_next(net);
u32 idx, off;
nft_bitmap_location(nft_set_ext_key(ext)->data[0], &idx, &off);
/* Enter 10 state, similar to deactivation. */
priv->bitmap[idx] &= ~(genmask << off);
return true;
}
static struct nft_set_ext *nft_bitmap_ext_alloc(const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_set_ext_tmpl tmpl;
struct nft_set_ext *ext;
nft_set_ext_prepare(&tmpl);
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
ext = kzalloc(tmpl.len, GFP_KERNEL);
if (!ext)
return NULL;
nft_set_ext_init(ext, &tmpl);
memcpy(nft_set_ext_key(ext), elem->key.val.data, set->klen);
return ext;
}
static void *nft_bitmap_deactivate(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_bitmap *priv = nft_set_priv(set);
u8 genmask = nft_genmask_next(net);
struct nft_set_ext *ext;
u32 idx, off, key = 0;
memcpy(&key, elem->key.val.data, set->klen);
nft_bitmap_location(key, &idx, &off);
if (!nft_bitmap_active(priv->bitmap, idx, off, genmask))
return NULL;
/* We have no real set extension since this is a bitmap, allocate this
* dummy object that is released from the commit/abort path.
*/
ext = nft_bitmap_ext_alloc(set, elem);
if (!ext)
return NULL;
/* Enter 10 state. */
priv->bitmap[idx] &= ~(genmask << off);
return ext;
}
static void nft_bitmap_walk(const struct nft_ctx *ctx,
struct nft_set *set,
struct nft_set_iter *iter)
{
const struct nft_bitmap *priv = nft_set_priv(set);
struct nft_set_ext_tmpl tmpl;
struct nft_set_elem elem;
struct nft_set_ext *ext;
int idx, off;
u16 key;
nft_set_ext_prepare(&tmpl);
nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
for (idx = 0; idx < priv->bitmap_size; idx++) {
for (off = 0; off < BITS_PER_BYTE; off += 2) {
if (iter->count < iter->skip)
goto cont;
if (!nft_bitmap_active(priv->bitmap, idx, off,
iter->genmask))
goto cont;
ext = kzalloc(tmpl.len, GFP_KERNEL);
if (!ext) {
iter->err = -ENOMEM;
return;
}
nft_set_ext_init(ext, &tmpl);
key = ((idx * BITS_PER_BYTE) + off) >> 1;
memcpy(nft_set_ext_key(ext), &key, set->klen);
elem.priv = ext;
iter->err = iter->fn(ctx, set, iter, &elem);
/* On set flush, this dummy extension object is released
* from the commit/abort path.
*/
if (!iter->flush)
kfree(ext);
if (iter->err < 0)
return;
cont:
iter->count++;
}
}
}
/* The bitmap size is pow(2, key length in bits) / bits per byte. This is
* multiplied by two since each element takes two bits. For 8 bit keys, the
* bitmap consumes 66 bytes. For 16 bit keys, 16388 bytes.
*/
static inline u32 nft_bitmap_size(u32 klen)
{
return ((2 << ((klen * BITS_PER_BYTE) - 1)) / BITS_PER_BYTE) << 1;
}
static inline u32 nft_bitmap_total_size(u32 klen)
{
return sizeof(struct nft_bitmap) + nft_bitmap_size(klen);
}
static unsigned int nft_bitmap_privsize(const struct nlattr * const nla[])
{
u32 klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
return nft_bitmap_total_size(klen);
}
static int nft_bitmap_init(const struct nft_set *set,
const struct nft_set_desc *desc,
const struct nlattr * const nla[])
{
struct nft_bitmap *priv = nft_set_priv(set);
priv->bitmap_size = nft_bitmap_total_size(set->klen);
return 0;
}
static void nft_bitmap_destroy(const struct nft_set *set)
{
}
static bool nft_bitmap_estimate(const struct nft_set_desc *desc, u32 features,
struct nft_set_estimate *est)
{
/* Make sure bitmaps we don't get bitmaps larger than 16 Kbytes. */
if (desc->klen > 2)
return false;
est->size = nft_bitmap_total_size(desc->klen);
est->lookup = NFT_SET_CLASS_O_1;
est->space = NFT_SET_CLASS_O_1;
return true;
}
static struct nft_set_ops nft_bitmap_ops __read_mostly = {
.privsize = nft_bitmap_privsize,
.estimate = nft_bitmap_estimate,
.init = nft_bitmap_init,
.destroy = nft_bitmap_destroy,
.insert = nft_bitmap_insert,
.remove = nft_bitmap_remove,
.deactivate = nft_bitmap_deactivate,
.flush = nft_bitmap_flush,
.activate = nft_bitmap_activate,
.lookup = nft_bitmap_lookup,
.walk = nft_bitmap_walk,
.owner = THIS_MODULE,
};
static int __init nft_bitmap_module_init(void)
{
return nft_register_set(&nft_bitmap_ops);
}
static void __exit nft_bitmap_module_exit(void)
{
nft_unregister_set(&nft_bitmap_ops);
}
module_init(nft_bitmap_module_init);
module_exit(nft_bitmap_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_ALIAS_NFT_SET();

Просмотреть файл

@ -167,8 +167,8 @@ static void nft_hash_activate(const struct net *net, const struct nft_set *set,
nft_set_elem_clear_busy(&he->ext); nft_set_elem_clear_busy(&he->ext);
} }
static bool nft_hash_deactivate_one(const struct net *net, static bool nft_hash_flush(const struct net *net,
const struct nft_set *set, void *priv) const struct nft_set *set, void *priv)
{ {
struct nft_hash_elem *he = priv; struct nft_hash_elem *he = priv;
@ -195,7 +195,7 @@ static void *nft_hash_deactivate(const struct net *net,
rcu_read_lock(); rcu_read_lock();
he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params); he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
if (he != NULL && if (he != NULL &&
!nft_hash_deactivate_one(net, set, he)) !nft_hash_flush(net, set, he))
he = NULL; he = NULL;
rcu_read_unlock(); rcu_read_unlock();
@ -203,7 +203,8 @@ static void *nft_hash_deactivate(const struct net *net,
return he; return he;
} }
static void nft_hash_remove(const struct nft_set *set, static void nft_hash_remove(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem) const struct nft_set_elem *elem)
{ {
struct nft_hash *priv = nft_set_priv(set); struct nft_hash *priv = nft_set_priv(set);
@ -383,7 +384,8 @@ static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
est->size = esize + 2 * sizeof(struct nft_hash_elem *); est->size = esize + 2 * sizeof(struct nft_hash_elem *);
} }
est->class = NFT_SET_CLASS_O_1; est->lookup = NFT_SET_CLASS_O_1;
est->space = NFT_SET_CLASS_O_N;
return true; return true;
} }
@ -397,12 +399,12 @@ static struct nft_set_ops nft_hash_ops __read_mostly = {
.insert = nft_hash_insert, .insert = nft_hash_insert,
.activate = nft_hash_activate, .activate = nft_hash_activate,
.deactivate = nft_hash_deactivate, .deactivate = nft_hash_deactivate,
.deactivate_one = nft_hash_deactivate_one, .flush = nft_hash_flush,
.remove = nft_hash_remove, .remove = nft_hash_remove,
.lookup = nft_hash_lookup, .lookup = nft_hash_lookup,
.update = nft_hash_update, .update = nft_hash_update,
.walk = nft_hash_walk, .walk = nft_hash_walk,
.features = NFT_SET_MAP | NFT_SET_TIMEOUT, .features = NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };

Просмотреть файл

@ -151,7 +151,8 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,
return err; return err;
} }
static void nft_rbtree_remove(const struct nft_set *set, static void nft_rbtree_remove(const struct net *net,
const struct nft_set *set,
const struct nft_set_elem *elem) const struct nft_set_elem *elem)
{ {
struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree *priv = nft_set_priv(set);
@ -171,8 +172,8 @@ static void nft_rbtree_activate(const struct net *net,
nft_set_elem_change_active(net, set, &rbe->ext); nft_set_elem_change_active(net, set, &rbe->ext);
} }
static bool nft_rbtree_deactivate_one(const struct net *net, static bool nft_rbtree_flush(const struct net *net,
const struct nft_set *set, void *priv) const struct nft_set *set, void *priv)
{ {
struct nft_rbtree_elem *rbe = priv; struct nft_rbtree_elem *rbe = priv;
@ -213,7 +214,7 @@ static void *nft_rbtree_deactivate(const struct net *net,
parent = parent->rb_right; parent = parent->rb_right;
continue; continue;
} }
nft_rbtree_deactivate_one(net, set, rbe); nft_rbtree_flush(net, set, rbe);
return rbe; return rbe;
} }
} }
@ -290,7 +291,8 @@ static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
else else
est->size = nsize; est->size = nsize;
est->class = NFT_SET_CLASS_O_LOG_N; est->lookup = NFT_SET_CLASS_O_LOG_N;
est->space = NFT_SET_CLASS_O_N;
return true; return true;
} }
@ -304,11 +306,11 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = {
.insert = nft_rbtree_insert, .insert = nft_rbtree_insert,
.remove = nft_rbtree_remove, .remove = nft_rbtree_remove,
.deactivate = nft_rbtree_deactivate, .deactivate = nft_rbtree_deactivate,
.deactivate_one = nft_rbtree_deactivate_one, .flush = nft_rbtree_flush,
.activate = nft_rbtree_activate, .activate = nft_rbtree_activate,
.lookup = nft_rbtree_lookup, .lookup = nft_rbtree_lookup,
.walk = nft_rbtree_walk, .walk = nft_rbtree_walk,
.features = NFT_SET_INTERVAL | NFT_SET_MAP, .features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };