|
@@ -124,37 +124,43 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
|
|
return ++table->hgenerator;
|
|
return ++table->hgenerator;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
|
|
|
|
|
|
+static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
|
|
|
|
|
|
-static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
|
|
|
|
|
|
+static const struct nf_chain_type *
|
|
|
|
+__nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- for (i=0; i<NFT_CHAIN_T_MAX; i++) {
|
|
|
|
|
|
+ for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
|
|
if (chain_type[family][i] != NULL &&
|
|
if (chain_type[family][i] != NULL &&
|
|
!nla_strcmp(nla, chain_type[family][i]->name))
|
|
!nla_strcmp(nla, chain_type[family][i]->name))
|
|
- return i;
|
|
|
|
|
|
+ return chain_type[family][i];
|
|
}
|
|
}
|
|
- return -1;
|
|
|
|
|
|
+ return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
|
|
|
|
- const struct nlattr *nla,
|
|
|
|
- bool autoload)
|
|
|
|
|
|
+static const struct nf_chain_type *
|
|
|
|
+nf_tables_chain_type_lookup(const struct nft_af_info *afi,
|
|
|
|
+ const struct nlattr *nla,
|
|
|
|
+ bool autoload)
|
|
{
|
|
{
|
|
- int type;
|
|
|
|
|
|
+ const struct nf_chain_type *type;
|
|
|
|
|
|
type = __nf_tables_chain_type_lookup(afi->family, nla);
|
|
type = __nf_tables_chain_type_lookup(afi->family, nla);
|
|
|
|
+ if (type != NULL)
|
|
|
|
+ return type;
|
|
#ifdef CONFIG_MODULES
|
|
#ifdef CONFIG_MODULES
|
|
- if (type < 0 && autoload) {
|
|
|
|
|
|
+ if (autoload) {
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
request_module("nft-chain-%u-%*.s", afi->family,
|
|
request_module("nft-chain-%u-%*.s", afi->family,
|
|
nla_len(nla)-1, (const char *)nla_data(nla));
|
|
nla_len(nla)-1, (const char *)nla_data(nla));
|
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
|
type = __nf_tables_chain_type_lookup(afi->family, nla);
|
|
type = __nf_tables_chain_type_lookup(afi->family, nla);
|
|
|
|
+ if (type != NULL)
|
|
|
|
+ return ERR_PTR(-EAGAIN);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
- return type;
|
|
|
|
|
|
+ return ERR_PTR(-ENOENT);
|
|
}
|
|
}
|
|
|
|
|
|
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
|
|
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
|
|
@@ -307,7 +313,8 @@ err:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-static int nf_tables_table_enable(struct nft_table *table)
|
|
|
|
|
|
+static int nf_tables_table_enable(const struct nft_af_info *afi,
|
|
|
|
+ struct nft_table *table)
|
|
{
|
|
{
|
|
struct nft_chain *chain;
|
|
struct nft_chain *chain;
|
|
int err, i = 0;
|
|
int err, i = 0;
|
|
@@ -316,7 +323,7 @@ static int nf_tables_table_enable(struct nft_table *table)
|
|
if (!(chain->flags & NFT_BASE_CHAIN))
|
|
if (!(chain->flags & NFT_BASE_CHAIN))
|
|
continue;
|
|
continue;
|
|
|
|
|
|
- err = nf_register_hook(&nft_base_chain(chain)->ops);
|
|
|
|
|
|
+ err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
@@ -331,18 +338,20 @@ err:
|
|
if (i-- <= 0)
|
|
if (i-- <= 0)
|
|
break;
|
|
break;
|
|
|
|
|
|
- nf_unregister_hook(&nft_base_chain(chain)->ops);
|
|
|
|
|
|
+ nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
|
|
}
|
|
}
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-static int nf_tables_table_disable(struct nft_table *table)
|
|
|
|
|
|
+static int nf_tables_table_disable(const struct nft_af_info *afi,
|
|
|
|
+ struct nft_table *table)
|
|
{
|
|
{
|
|
struct nft_chain *chain;
|
|
struct nft_chain *chain;
|
|
|
|
|
|
list_for_each_entry(chain, &table->chains, list) {
|
|
list_for_each_entry(chain, &table->chains, list) {
|
|
if (chain->flags & NFT_BASE_CHAIN)
|
|
if (chain->flags & NFT_BASE_CHAIN)
|
|
- nf_unregister_hook(&nft_base_chain(chain)->ops);
|
|
|
|
|
|
+ nf_unregister_hooks(nft_base_chain(chain)->ops,
|
|
|
|
+ afi->nops);
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -357,7 +366,7 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
|
|
int family = nfmsg->nfgen_family, ret = 0;
|
|
int family = nfmsg->nfgen_family, ret = 0;
|
|
|
|
|
|
if (nla[NFTA_TABLE_FLAGS]) {
|
|
if (nla[NFTA_TABLE_FLAGS]) {
|
|
- __be32 flags;
|
|
|
|
|
|
+ u32 flags;
|
|
|
|
|
|
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
|
|
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
|
|
if (flags & ~NFT_TABLE_F_DORMANT)
|
|
if (flags & ~NFT_TABLE_F_DORMANT)
|
|
@@ -365,12 +374,12 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
|
|
|
|
|
|
if ((flags & NFT_TABLE_F_DORMANT) &&
|
|
if ((flags & NFT_TABLE_F_DORMANT) &&
|
|
!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
!(table->flags & NFT_TABLE_F_DORMANT)) {
|
|
- ret = nf_tables_table_disable(table);
|
|
|
|
|
|
+ ret = nf_tables_table_disable(afi, table);
|
|
if (ret >= 0)
|
|
if (ret >= 0)
|
|
table->flags |= NFT_TABLE_F_DORMANT;
|
|
table->flags |= NFT_TABLE_F_DORMANT;
|
|
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
|
|
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
|
|
table->flags & NFT_TABLE_F_DORMANT) {
|
|
table->flags & NFT_TABLE_F_DORMANT) {
|
|
- ret = nf_tables_table_enable(table);
|
|
|
|
|
|
+ ret = nf_tables_table_enable(afi, table);
|
|
if (ret >= 0)
|
|
if (ret >= 0)
|
|
table->flags &= ~NFT_TABLE_F_DORMANT;
|
|
table->flags &= ~NFT_TABLE_F_DORMANT;
|
|
}
|
|
}
|
|
@@ -393,6 +402,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
|
|
struct nft_table *table;
|
|
struct nft_table *table;
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net *net = sock_net(skb->sk);
|
|
int family = nfmsg->nfgen_family;
|
|
int family = nfmsg->nfgen_family;
|
|
|
|
+ u32 flags = 0;
|
|
|
|
|
|
afi = nf_tables_afinfo_lookup(net, family, true);
|
|
afi = nf_tables_afinfo_lookup(net, family, true);
|
|
if (IS_ERR(afi))
|
|
if (IS_ERR(afi))
|
|
@@ -414,25 +424,25 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
|
|
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
|
|
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (nla[NFTA_TABLE_FLAGS]) {
|
|
|
|
+ flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
|
|
|
|
+ if (flags & ~NFT_TABLE_F_DORMANT)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!try_module_get(afi->owner))
|
|
|
|
+ return -EAFNOSUPPORT;
|
|
|
|
+
|
|
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
|
|
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
|
|
- if (table == NULL)
|
|
|
|
|
|
+ if (table == NULL) {
|
|
|
|
+ module_put(afi->owner);
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
|
|
nla_strlcpy(table->name, name, nla_len(name));
|
|
nla_strlcpy(table->name, name, nla_len(name));
|
|
INIT_LIST_HEAD(&table->chains);
|
|
INIT_LIST_HEAD(&table->chains);
|
|
INIT_LIST_HEAD(&table->sets);
|
|
INIT_LIST_HEAD(&table->sets);
|
|
-
|
|
|
|
- if (nla[NFTA_TABLE_FLAGS]) {
|
|
|
|
- __be32 flags;
|
|
|
|
-
|
|
|
|
- flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
|
|
|
|
- if (flags & ~NFT_TABLE_F_DORMANT) {
|
|
|
|
- kfree(table);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- table->flags |= flags;
|
|
|
|
- }
|
|
|
|
|
|
+ table->flags = flags;
|
|
|
|
|
|
list_add_tail(&table->list, &afi->tables);
|
|
list_add_tail(&table->list, &afi->tables);
|
|
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
|
|
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
|
|
@@ -457,16 +467,17 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
|
|
if (IS_ERR(table))
|
|
if (IS_ERR(table))
|
|
return PTR_ERR(table);
|
|
return PTR_ERR(table);
|
|
|
|
|
|
- if (table->use)
|
|
|
|
|
|
+ if (!list_empty(&table->chains) || !list_empty(&table->sets))
|
|
return -EBUSY;
|
|
return -EBUSY;
|
|
|
|
|
|
list_del(&table->list);
|
|
list_del(&table->list);
|
|
nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
|
|
nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
|
|
kfree(table);
|
|
kfree(table);
|
|
|
|
+ module_put(afi->owner);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-int nft_register_chain_type(struct nf_chain_type *ctype)
|
|
|
|
|
|
+int nft_register_chain_type(const struct nf_chain_type *ctype)
|
|
{
|
|
{
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
@@ -475,10 +486,6 @@ int nft_register_chain_type(struct nf_chain_type *ctype)
|
|
err = -EBUSY;
|
|
err = -EBUSY;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (!try_module_get(ctype->me))
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
chain_type[ctype->family][ctype->type] = ctype;
|
|
chain_type[ctype->family][ctype->type] = ctype;
|
|
out:
|
|
out:
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
@@ -486,11 +493,10 @@ out:
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(nft_register_chain_type);
|
|
EXPORT_SYMBOL_GPL(nft_register_chain_type);
|
|
|
|
|
|
-void nft_unregister_chain_type(struct nf_chain_type *ctype)
|
|
|
|
|
|
+void nft_unregister_chain_type(const struct nf_chain_type *ctype)
|
|
{
|
|
{
|
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
|
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
|
chain_type[ctype->family][ctype->type] = NULL;
|
|
chain_type[ctype->family][ctype->type] = NULL;
|
|
- module_put(ctype->me);
|
|
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
|
|
EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
|
|
@@ -598,7 +604,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
|
|
|
|
|
|
if (chain->flags & NFT_BASE_CHAIN) {
|
|
if (chain->flags & NFT_BASE_CHAIN) {
|
|
const struct nft_base_chain *basechain = nft_base_chain(chain);
|
|
const struct nft_base_chain *basechain = nft_base_chain(chain);
|
|
- const struct nf_hook_ops *ops = &basechain->ops;
|
|
|
|
|
|
+ const struct nf_hook_ops *ops = &basechain->ops[0];
|
|
struct nlattr *nest;
|
|
struct nlattr *nest;
|
|
|
|
|
|
nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
|
|
nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
|
|
@@ -614,9 +620,8 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
|
|
htonl(basechain->policy)))
|
|
htonl(basechain->policy)))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
|
|
|
|
- if (nla_put_string(skb, NFTA_CHAIN_TYPE,
|
|
|
|
- chain_type[ops->pf][nft_base_chain(chain)->type]->name))
|
|
|
|
- goto nla_put_failure;
|
|
|
|
|
|
+ if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
|
|
if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
|
|
if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
@@ -757,22 +762,6 @@ err:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
-static int
|
|
|
|
-nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr)
|
|
|
|
-{
|
|
|
|
- switch (ntohl(nla_get_be32(attr))) {
|
|
|
|
- case NF_DROP:
|
|
|
|
- chain->policy = NF_DROP;
|
|
|
|
- break;
|
|
|
|
- case NF_ACCEPT:
|
|
|
|
- chain->policy = NF_ACCEPT;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
|
|
static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
|
|
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
|
|
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
|
|
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
|
|
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
|
|
@@ -831,7 +820,9 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
struct nlattr *ha[NFTA_HOOK_MAX + 1];
|
|
struct nlattr *ha[NFTA_HOOK_MAX + 1];
|
|
struct net *net = sock_net(skb->sk);
|
|
struct net *net = sock_net(skb->sk);
|
|
int family = nfmsg->nfgen_family;
|
|
int family = nfmsg->nfgen_family;
|
|
|
|
+ u8 policy = NF_ACCEPT;
|
|
u64 handle = 0;
|
|
u64 handle = 0;
|
|
|
|
+ unsigned int i;
|
|
int err;
|
|
int err;
|
|
bool create;
|
|
bool create;
|
|
|
|
|
|
@@ -845,9 +836,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
if (IS_ERR(table))
|
|
if (IS_ERR(table))
|
|
return PTR_ERR(table);
|
|
return PTR_ERR(table);
|
|
|
|
|
|
- if (table->use == UINT_MAX)
|
|
|
|
- return -EOVERFLOW;
|
|
|
|
-
|
|
|
|
chain = NULL;
|
|
chain = NULL;
|
|
name = nla[NFTA_CHAIN_NAME];
|
|
name = nla[NFTA_CHAIN_NAME];
|
|
|
|
|
|
@@ -865,6 +853,22 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (nla[NFTA_CHAIN_POLICY]) {
|
|
|
|
+ if ((chain != NULL &&
|
|
|
|
+ !(chain->flags & NFT_BASE_CHAIN)) ||
|
|
|
|
+ nla[NFTA_CHAIN_HOOK] == NULL)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ policy = nla_get_be32(nla[NFTA_CHAIN_POLICY]);
|
|
|
|
+ switch (policy) {
|
|
|
|
+ case NF_DROP:
|
|
|
|
+ case NF_ACCEPT:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if (chain != NULL) {
|
|
if (chain != NULL) {
|
|
if (nlh->nlmsg_flags & NLM_F_EXCL)
|
|
if (nlh->nlmsg_flags & NLM_F_EXCL)
|
|
return -EEXIST;
|
|
return -EEXIST;
|
|
@@ -875,16 +879,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
!IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
|
|
!IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
|
|
return -EEXIST;
|
|
return -EEXIST;
|
|
|
|
|
|
- if (nla[NFTA_CHAIN_POLICY]) {
|
|
|
|
- if (!(chain->flags & NFT_BASE_CHAIN))
|
|
|
|
- return -EOPNOTSUPP;
|
|
|
|
-
|
|
|
|
- err = nf_tables_chain_policy(nft_base_chain(chain),
|
|
|
|
- nla[NFTA_CHAIN_POLICY]);
|
|
|
|
- if (err < 0)
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
if (!(chain->flags & NFT_BASE_CHAIN))
|
|
if (!(chain->flags & NFT_BASE_CHAIN))
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
@@ -895,24 +889,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (nla[NFTA_CHAIN_POLICY])
|
|
|
|
+ nft_base_chain(chain)->policy = policy;
|
|
|
|
+
|
|
if (nla[NFTA_CHAIN_HANDLE] && name)
|
|
if (nla[NFTA_CHAIN_HANDLE] && name)
|
|
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
|
|
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
|
|
|
|
|
|
goto notify;
|
|
goto notify;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (table->use == UINT_MAX)
|
|
|
|
+ return -EOVERFLOW;
|
|
|
|
+
|
|
if (nla[NFTA_CHAIN_HOOK]) {
|
|
if (nla[NFTA_CHAIN_HOOK]) {
|
|
|
|
+ const struct nf_chain_type *type;
|
|
struct nf_hook_ops *ops;
|
|
struct nf_hook_ops *ops;
|
|
nf_hookfn *hookfn;
|
|
nf_hookfn *hookfn;
|
|
- u32 hooknum;
|
|
|
|
- int type = NFT_CHAIN_T_DEFAULT;
|
|
|
|
|
|
+ u32 hooknum, priority;
|
|
|
|
|
|
|
|
+ type = chain_type[family][NFT_CHAIN_T_DEFAULT];
|
|
if (nla[NFTA_CHAIN_TYPE]) {
|
|
if (nla[NFTA_CHAIN_TYPE]) {
|
|
type = nf_tables_chain_type_lookup(afi,
|
|
type = nf_tables_chain_type_lookup(afi,
|
|
nla[NFTA_CHAIN_TYPE],
|
|
nla[NFTA_CHAIN_TYPE],
|
|
create);
|
|
create);
|
|
- if (type < 0)
|
|
|
|
- return -ENOENT;
|
|
|
|
|
|
+ if (IS_ERR(type))
|
|
|
|
+ return PTR_ERR(type);
|
|
}
|
|
}
|
|
|
|
|
|
err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
|
|
err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
|
|
@@ -926,46 +927,23 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
|
|
hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
|
|
if (hooknum >= afi->nhooks)
|
|
if (hooknum >= afi->nhooks)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
+ priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
|
|
|
|
|
|
- hookfn = chain_type[family][type]->fn[hooknum];
|
|
|
|
- if (hookfn == NULL)
|
|
|
|
|
|
+ if (!(type->hook_mask & (1 << hooknum)))
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
+ if (!try_module_get(type->owner))
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ hookfn = type->hooks[hooknum];
|
|
|
|
|
|
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
|
|
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
|
|
if (basechain == NULL)
|
|
if (basechain == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
- basechain->type = type;
|
|
|
|
- chain = &basechain->chain;
|
|
|
|
-
|
|
|
|
- ops = &basechain->ops;
|
|
|
|
- ops->pf = family;
|
|
|
|
- ops->owner = afi->owner;
|
|
|
|
- ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
|
|
|
|
- ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
|
|
|
|
- ops->priv = chain;
|
|
|
|
- ops->hook = hookfn;
|
|
|
|
- if (afi->hooks[ops->hooknum])
|
|
|
|
- ops->hook = afi->hooks[ops->hooknum];
|
|
|
|
-
|
|
|
|
- chain->flags |= NFT_BASE_CHAIN;
|
|
|
|
-
|
|
|
|
- if (nla[NFTA_CHAIN_POLICY]) {
|
|
|
|
- err = nf_tables_chain_policy(basechain,
|
|
|
|
- nla[NFTA_CHAIN_POLICY]);
|
|
|
|
- if (err < 0) {
|
|
|
|
- free_percpu(basechain->stats);
|
|
|
|
- kfree(basechain);
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
- } else
|
|
|
|
- basechain->policy = NF_ACCEPT;
|
|
|
|
-
|
|
|
|
if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
if (nla[NFTA_CHAIN_COUNTERS]) {
|
|
err = nf_tables_counters(basechain,
|
|
err = nf_tables_counters(basechain,
|
|
nla[NFTA_CHAIN_COUNTERS]);
|
|
nla[NFTA_CHAIN_COUNTERS]);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
- free_percpu(basechain->stats);
|
|
|
|
|
|
+ module_put(type->owner);
|
|
kfree(basechain);
|
|
kfree(basechain);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
@@ -973,12 +951,33 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
struct nft_stats __percpu *newstats;
|
|
struct nft_stats __percpu *newstats;
|
|
|
|
|
|
newstats = alloc_percpu(struct nft_stats);
|
|
newstats = alloc_percpu(struct nft_stats);
|
|
- if (newstats == NULL)
|
|
|
|
|
|
+ if (newstats == NULL) {
|
|
|
|
+ module_put(type->owner);
|
|
|
|
+ kfree(basechain);
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+ rcu_assign_pointer(basechain->stats, newstats);
|
|
|
|
+ }
|
|
|
|
|
|
- rcu_assign_pointer(nft_base_chain(chain)->stats,
|
|
|
|
- newstats);
|
|
|
|
|
|
+ basechain->type = type;
|
|
|
|
+ chain = &basechain->chain;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < afi->nops; i++) {
|
|
|
|
+ ops = &basechain->ops[i];
|
|
|
|
+ ops->pf = family;
|
|
|
|
+ ops->owner = afi->owner;
|
|
|
|
+ ops->hooknum = hooknum;
|
|
|
|
+ ops->priority = priority;
|
|
|
|
+ ops->priv = chain;
|
|
|
|
+ ops->hook = afi->hooks[ops->hooknum];
|
|
|
|
+ if (hookfn)
|
|
|
|
+ ops->hook = hookfn;
|
|
|
|
+ if (afi->hook_ops_init)
|
|
|
|
+ afi->hook_ops_init(ops, i);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ chain->flags |= NFT_BASE_CHAIN;
|
|
|
|
+ basechain->policy = policy;
|
|
} else {
|
|
} else {
|
|
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
|
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
|
if (chain == NULL)
|
|
if (chain == NULL)
|
|
@@ -993,8 +992,9 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
|
|
|
|
|
|
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
|
|
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
|
|
chain->flags & NFT_BASE_CHAIN) {
|
|
chain->flags & NFT_BASE_CHAIN) {
|
|
- err = nf_register_hook(&nft_base_chain(chain)->ops);
|
|
|
|
|
|
+ err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
|
|
if (err < 0) {
|
|
if (err < 0) {
|
|
|
|
+ module_put(basechain->type->owner);
|
|
free_percpu(basechain->stats);
|
|
free_percpu(basechain->stats);
|
|
kfree(basechain);
|
|
kfree(basechain);
|
|
return err;
|
|
return err;
|
|
@@ -1015,6 +1015,7 @@ static void nf_tables_rcu_chain_destroy(struct rcu_head *head)
|
|
BUG_ON(chain->use > 0);
|
|
BUG_ON(chain->use > 0);
|
|
|
|
|
|
if (chain->flags & NFT_BASE_CHAIN) {
|
|
if (chain->flags & NFT_BASE_CHAIN) {
|
|
|
|
+ module_put(nft_base_chain(chain)->type->owner);
|
|
free_percpu(nft_base_chain(chain)->stats);
|
|
free_percpu(nft_base_chain(chain)->stats);
|
|
kfree(nft_base_chain(chain));
|
|
kfree(nft_base_chain(chain));
|
|
} else
|
|
} else
|
|
@@ -1052,7 +1053,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
|
|
|
|
|
|
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
|
|
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
|
|
chain->flags & NFT_BASE_CHAIN)
|
|
chain->flags & NFT_BASE_CHAIN)
|
|
- nf_unregister_hook(&nft_base_chain(chain)->ops);
|
|
|
|
|
|
+ nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
|
|
|
|
|
|
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
|
|
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
|
|
family);
|
|
family);
|