Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
Pablo Neira Ayuso says: ==================== Netfilter fixes for net The following patchset contains a rather large update with Netfilter fixes, specifically targeted to incorrect RCU usage in several spots and the userspace conntrack helper infrastructure (nfnetlink_cthelper), more specifically they are: 1) expect_class_max is incorrect set via cthelper, as in kernel semantics mandate that this represents the array of expectation classes minus 1. Patch from Liping Zhang. 2) Expectation policy updates via cthelper are currently broken for several reasons: This code allows illegal changes in the policy such as changing the number of expeciation classes, it is leaking the updated policy and such update occurs with no RCU protection at all. Fix this by adding a new nfnl_cthelper_update_policy() that describes what is really legal on the update path. 3) Fix several memory leaks in cthelper, from Jeffy Chen. 4) synchronize_rcu() is missing in the removal path of several modules, this may lead to races since CPU may still be running on code that has just gone. Also from Liping Zhang. 5) Don't use the helper hashtable from cthelper, it is not safe to walk over those bits without the helper mutex. Fix this by introducing a new independent list for userspace helpers. From Liping Zhang. 6) nf_ct_extend_unregister() needs synchronize_rcu() to make sure no packets are walking on any conntrack extension that is gone after module removal, again from Liping. 7) nf_nat_snmp may crash if we fail to unregister the helper due to accidental leftover code, from Gao Feng. 8) Fix leak in nfnetlink_queue with secctx support, from Liping Zhang. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
8f1f7eeb22
|
@ -1260,16 +1260,6 @@ static const struct nf_conntrack_expect_policy snmp_exp_policy = {
|
|||
.timeout = 180,
|
||||
};
|
||||
|
||||
static struct nf_conntrack_helper snmp_helper __read_mostly = {
|
||||
.me = THIS_MODULE,
|
||||
.help = help,
|
||||
.expect_policy = &snmp_exp_policy,
|
||||
.name = "snmp",
|
||||
.tuple.src.l3num = AF_INET,
|
||||
.tuple.src.u.udp.port = cpu_to_be16(SNMP_PORT),
|
||||
.tuple.dst.protonum = IPPROTO_UDP,
|
||||
};
|
||||
|
||||
static struct nf_conntrack_helper snmp_trap_helper __read_mostly = {
|
||||
.me = THIS_MODULE,
|
||||
.help = help,
|
||||
|
@ -1288,22 +1278,16 @@ static struct nf_conntrack_helper snmp_trap_helper __read_mostly = {
|
|||
|
||||
static int __init nf_nat_snmp_basic_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(nf_nat_snmp_hook != NULL);
|
||||
RCU_INIT_POINTER(nf_nat_snmp_hook, help);
|
||||
|
||||
ret = nf_conntrack_helper_register(&snmp_trap_helper);
|
||||
if (ret < 0) {
|
||||
nf_conntrack_helper_unregister(&snmp_helper);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
return nf_conntrack_helper_register(&snmp_trap_helper);
|
||||
}
|
||||
|
||||
static void __exit nf_nat_snmp_basic_fini(void)
|
||||
{
|
||||
RCU_INIT_POINTER(nf_nat_snmp_hook, NULL);
|
||||
synchronize_rcu();
|
||||
nf_conntrack_helper_unregister(&snmp_trap_helper);
|
||||
}
|
||||
|
||||
|
|
|
@ -290,6 +290,7 @@ void nf_conntrack_unregister_notifier(struct net *net,
|
|||
BUG_ON(notify != new);
|
||||
RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
|
||||
mutex_unlock(&nf_ct_ecache_mutex);
|
||||
/* synchronize_rcu() is called from ctnetlink_exit. */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
|
||||
|
||||
|
@ -326,6 +327,7 @@ void nf_ct_expect_unregister_notifier(struct net *net,
|
|||
BUG_ON(notify != new);
|
||||
RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
|
||||
mutex_unlock(&nf_ct_ecache_mutex);
|
||||
/* synchronize_rcu() is called from ctnetlink_exit. */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
|
||||
|
||||
|
|
|
@ -53,7 +53,11 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id,
|
|||
|
||||
rcu_read_lock();
|
||||
t = rcu_dereference(nf_ct_ext_types[id]);
|
||||
BUG_ON(t == NULL);
|
||||
if (!t) {
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
off = ALIGN(sizeof(struct nf_ct_ext), t->align);
|
||||
len = off + t->len + var_alloc_len;
|
||||
alloc_size = t->alloc_size + var_alloc_len;
|
||||
|
@ -88,7 +92,10 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id,
|
|||
|
||||
rcu_read_lock();
|
||||
t = rcu_dereference(nf_ct_ext_types[id]);
|
||||
BUG_ON(t == NULL);
|
||||
if (!t) {
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newoff = ALIGN(old->len, t->align);
|
||||
newlen = newoff + t->len + var_alloc_len;
|
||||
|
@ -175,6 +182,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type)
|
|||
RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL);
|
||||
update_alloc_size(type);
|
||||
mutex_unlock(&nf_ct_ext_type_mutex);
|
||||
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);
|
||||
|
|
|
@ -3442,6 +3442,7 @@ static void __exit ctnetlink_exit(void)
|
|||
#ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
|
||||
RCU_INIT_POINTER(nfnl_ct_hook, NULL);
|
||||
#endif
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
module_init(ctnetlink_init);
|
||||
|
|
|
@ -903,6 +903,8 @@ static void __exit nf_nat_cleanup(void)
|
|||
#ifdef CONFIG_XFRM
|
||||
RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL);
|
||||
#endif
|
||||
synchronize_rcu();
|
||||
|
||||
for (i = 0; i < NFPROTO_NUMPROTO; i++)
|
||||
kfree(nf_nat_l4protos[i]);
|
||||
|
||||
|
|
|
@ -32,6 +32,13 @@ MODULE_LICENSE("GPL");
|
|||
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
|
||||
MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers");
|
||||
|
||||
struct nfnl_cthelper {
|
||||
struct list_head list;
|
||||
struct nf_conntrack_helper helper;
|
||||
};
|
||||
|
||||
static LIST_HEAD(nfnl_cthelper_list);
|
||||
|
||||
static int
|
||||
nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff,
|
||||
struct nf_conn *ct, enum ip_conntrack_info ctinfo)
|
||||
|
@ -161,6 +168,7 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
|
|||
int i, ret;
|
||||
struct nf_conntrack_expect_policy *expect_policy;
|
||||
struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
|
||||
unsigned int class_max;
|
||||
|
||||
ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
|
||||
nfnl_cthelper_expect_policy_set);
|
||||
|
@ -170,19 +178,18 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
|
|||
if (!tb[NFCTH_POLICY_SET_NUM])
|
||||
return -EINVAL;
|
||||
|
||||
helper->expect_class_max =
|
||||
ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
|
||||
|
||||
if (helper->expect_class_max != 0 &&
|
||||
helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES)
|
||||
class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
|
||||
if (class_max == 0)
|
||||
return -EINVAL;
|
||||
if (class_max > NF_CT_MAX_EXPECT_CLASSES)
|
||||
return -EOVERFLOW;
|
||||
|
||||
expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
|
||||
helper->expect_class_max, GFP_KERNEL);
|
||||
class_max, GFP_KERNEL);
|
||||
if (expect_policy == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i=0; i<helper->expect_class_max; i++) {
|
||||
for (i = 0; i < class_max; i++) {
|
||||
if (!tb[NFCTH_POLICY_SET+i])
|
||||
goto err;
|
||||
|
||||
|
@ -191,6 +198,8 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper,
|
|||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
helper->expect_class_max = class_max - 1;
|
||||
helper->expect_policy = expect_policy;
|
||||
return 0;
|
||||
err:
|
||||
|
@ -203,18 +212,20 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
|
|||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct nf_conntrack_helper *helper;
|
||||
struct nfnl_cthelper *nfcth;
|
||||
int ret;
|
||||
|
||||
if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN])
|
||||
return -EINVAL;
|
||||
|
||||
helper = kzalloc(sizeof(struct nf_conntrack_helper), GFP_KERNEL);
|
||||
if (helper == NULL)
|
||||
nfcth = kzalloc(sizeof(*nfcth), GFP_KERNEL);
|
||||
if (nfcth == NULL)
|
||||
return -ENOMEM;
|
||||
helper = &nfcth->helper;
|
||||
|
||||
ret = nfnl_cthelper_parse_expect_policy(helper, tb[NFCTH_POLICY]);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
goto err1;
|
||||
|
||||
strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN);
|
||||
helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN]));
|
||||
|
@ -245,12 +256,98 @@ nfnl_cthelper_create(const struct nlattr * const tb[],
|
|||
|
||||
ret = nf_conntrack_helper_register(helper);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
goto err2;
|
||||
|
||||
list_add_tail(&nfcth->list, &nfnl_cthelper_list);
|
||||
return 0;
|
||||
err2:
|
||||
kfree(helper->expect_policy);
|
||||
err1:
|
||||
kfree(nfcth);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy,
|
||||
struct nf_conntrack_expect_policy *new_policy,
|
||||
const struct nlattr *attr)
|
||||
{
|
||||
struct nlattr *tb[NFCTH_POLICY_MAX + 1];
|
||||
int err;
|
||||
|
||||
err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr,
|
||||
nfnl_cthelper_expect_pol);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!tb[NFCTH_POLICY_NAME] ||
|
||||
!tb[NFCTH_POLICY_EXPECT_MAX] ||
|
||||
!tb[NFCTH_POLICY_EXPECT_TIMEOUT])
|
||||
return -EINVAL;
|
||||
|
||||
if (nla_strcmp(tb[NFCTH_POLICY_NAME], policy->name))
|
||||
return -EBUSY;
|
||||
|
||||
new_policy->max_expected =
|
||||
ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX]));
|
||||
new_policy->timeout =
|
||||
ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT]));
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree(helper);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfnl_cthelper_update_policy_all(struct nlattr *tb[],
|
||||
struct nf_conntrack_helper *helper)
|
||||
{
|
||||
struct nf_conntrack_expect_policy new_policy[helper->expect_class_max + 1];
|
||||
struct nf_conntrack_expect_policy *policy;
|
||||
int i, err;
|
||||
|
||||
/* Check first that all policy attributes are well-formed, so we don't
|
||||
* leave things in inconsistent state on errors.
|
||||
*/
|
||||
for (i = 0; i < helper->expect_class_max + 1; i++) {
|
||||
|
||||
if (!tb[NFCTH_POLICY_SET + i])
|
||||
return -EINVAL;
|
||||
|
||||
err = nfnl_cthelper_update_policy_one(&helper->expect_policy[i],
|
||||
&new_policy[i],
|
||||
tb[NFCTH_POLICY_SET + i]);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
/* Now we can safely update them. */
|
||||
for (i = 0; i < helper->expect_class_max + 1; i++) {
|
||||
policy = (struct nf_conntrack_expect_policy *)
|
||||
&helper->expect_policy[i];
|
||||
policy->max_expected = new_policy->max_expected;
|
||||
policy->timeout = new_policy->timeout;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfnl_cthelper_update_policy(struct nf_conntrack_helper *helper,
|
||||
const struct nlattr *attr)
|
||||
{
|
||||
struct nlattr *tb[NFCTH_POLICY_SET_MAX + 1];
|
||||
unsigned int class_max;
|
||||
int err;
|
||||
|
||||
err = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
|
||||
nfnl_cthelper_expect_policy_set);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!tb[NFCTH_POLICY_SET_NUM])
|
||||
return -EINVAL;
|
||||
|
||||
class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
|
||||
if (helper->expect_class_max + 1 != class_max)
|
||||
return -EBUSY;
|
||||
|
||||
return nfnl_cthelper_update_policy_all(tb, helper);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -263,8 +360,7 @@ nfnl_cthelper_update(const struct nlattr * const tb[],
|
|||
return -EBUSY;
|
||||
|
||||
if (tb[NFCTH_POLICY]) {
|
||||
ret = nfnl_cthelper_parse_expect_policy(helper,
|
||||
tb[NFCTH_POLICY]);
|
||||
ret = nfnl_cthelper_update_policy(helper, tb[NFCTH_POLICY]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -293,7 +389,8 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
|
|||
const char *helper_name;
|
||||
struct nf_conntrack_helper *cur, *helper = NULL;
|
||||
struct nf_conntrack_tuple tuple;
|
||||
int ret = 0, i;
|
||||
struct nfnl_cthelper *nlcth;
|
||||
int ret = 0;
|
||||
|
||||
if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
|
||||
return -EINVAL;
|
||||
|
@ -304,31 +401,22 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < nf_ct_helper_hsize && !helper; i++) {
|
||||
hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
|
||||
list_for_each_entry(nlcth, &nfnl_cthelper_list, list) {
|
||||
cur = &nlcth->helper;
|
||||
|
||||
/* skip non-userspace conntrack helpers. */
|
||||
if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
|
||||
continue;
|
||||
|
||||
if (strncmp(cur->name, helper_name,
|
||||
NF_CT_HELPER_NAME_LEN) != 0)
|
||||
if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
|
||||
continue;
|
||||
|
||||
if ((tuple.src.l3num != cur->tuple.src.l3num ||
|
||||
tuple.dst.protonum != cur->tuple.dst.protonum))
|
||||
continue;
|
||||
|
||||
if (nlh->nlmsg_flags & NLM_F_EXCL) {
|
||||
ret = -EEXIST;
|
||||
goto err;
|
||||
}
|
||||
if (nlh->nlmsg_flags & NLM_F_EXCL)
|
||||
return -EEXIST;
|
||||
|
||||
helper = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (helper == NULL)
|
||||
ret = nfnl_cthelper_create(tb, &tuple);
|
||||
|
@ -336,9 +424,6 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
|
|||
ret = nfnl_cthelper_update(tb, helper);
|
||||
|
||||
return ret;
|
||||
err:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -377,10 +462,10 @@ nfnl_cthelper_dump_policy(struct sk_buff *skb,
|
|||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
|
||||
htonl(helper->expect_class_max)))
|
||||
htonl(helper->expect_class_max + 1)))
|
||||
goto nla_put_failure;
|
||||
|
||||
for (i=0; i<helper->expect_class_max; i++) {
|
||||
for (i = 0; i < helper->expect_class_max + 1; i++) {
|
||||
nest_parms2 = nla_nest_start(skb,
|
||||
(NFCTH_POLICY_SET+i) | NLA_F_NESTED);
|
||||
if (nest_parms2 == NULL)
|
||||
|
@ -502,11 +587,12 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
|
|||
struct sk_buff *skb, const struct nlmsghdr *nlh,
|
||||
const struct nlattr * const tb[])
|
||||
{
|
||||
int ret = -ENOENT, i;
|
||||
int ret = -ENOENT;
|
||||
struct nf_conntrack_helper *cur;
|
||||
struct sk_buff *skb2;
|
||||
char *helper_name = NULL;
|
||||
struct nf_conntrack_tuple tuple;
|
||||
struct nfnl_cthelper *nlcth;
|
||||
bool tuple_set = false;
|
||||
|
||||
if (nlh->nlmsg_flags & NLM_F_DUMP) {
|
||||
|
@ -527,17 +613,12 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
|
|||
tuple_set = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < nf_ct_helper_hsize; i++) {
|
||||
hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[i], hnode) {
|
||||
|
||||
/* skip non-userspace conntrack helpers. */
|
||||
if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
|
||||
list_for_each_entry(nlcth, &nfnl_cthelper_list, list) {
|
||||
cur = &nlcth->helper;
|
||||
if (helper_name &&
|
||||
strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
|
||||
continue;
|
||||
|
||||
if (helper_name && strncmp(cur->name, helper_name,
|
||||
NF_CT_HELPER_NAME_LEN) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (tuple_set &&
|
||||
(tuple.src.l3num != cur->tuple.src.l3num ||
|
||||
tuple.dst.protonum != cur->tuple.dst.protonum))
|
||||
|
@ -566,7 +647,6 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
|
|||
/* this avoids a loop in nfnetlink. */
|
||||
return ret == -EAGAIN ? -ENOBUFS : ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -576,10 +656,10 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
|
|||
{
|
||||
char *helper_name = NULL;
|
||||
struct nf_conntrack_helper *cur;
|
||||
struct hlist_node *tmp;
|
||||
struct nf_conntrack_tuple tuple;
|
||||
bool tuple_set = false, found = false;
|
||||
int i, j = 0, ret;
|
||||
struct nfnl_cthelper *nlcth, *n;
|
||||
int j = 0, ret;
|
||||
|
||||
if (tb[NFCTH_NAME])
|
||||
helper_name = nla_data(tb[NFCTH_NAME]);
|
||||
|
@ -592,19 +672,14 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
|
|||
tuple_set = true;
|
||||
}
|
||||
|
||||
for (i = 0; i < nf_ct_helper_hsize; i++) {
|
||||
hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
|
||||
hnode) {
|
||||
/* skip non-userspace conntrack helpers. */
|
||||
if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) {
|
||||
cur = &nlcth->helper;
|
||||
j++;
|
||||
|
||||
if (helper_name && strncmp(cur->name, helper_name,
|
||||
NF_CT_HELPER_NAME_LEN) != 0) {
|
||||
if (helper_name &&
|
||||
strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tuple_set &&
|
||||
(tuple.src.l3num != cur->tuple.src.l3num ||
|
||||
tuple.dst.protonum != cur->tuple.dst.protonum))
|
||||
|
@ -612,8 +687,12 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
|
|||
|
||||
found = true;
|
||||
nf_conntrack_helper_unregister(cur);
|
||||
kfree(cur->expect_policy);
|
||||
|
||||
list_del(&nlcth->list);
|
||||
kfree(nlcth);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we return success if we flush and there is no helpers */
|
||||
return (found || j == 0) ? 0 : -ENOENT;
|
||||
}
|
||||
|
@ -662,20 +741,16 @@ err_out:
|
|||
static void __exit nfnl_cthelper_exit(void)
|
||||
{
|
||||
struct nf_conntrack_helper *cur;
|
||||
struct hlist_node *tmp;
|
||||
int i;
|
||||
struct nfnl_cthelper *nlcth, *n;
|
||||
|
||||
nfnetlink_subsys_unregister(&nfnl_cthelper_subsys);
|
||||
|
||||
for (i=0; i<nf_ct_helper_hsize; i++) {
|
||||
hlist_for_each_entry_safe(cur, tmp, &nf_ct_helper_hash[i],
|
||||
hnode) {
|
||||
/* skip non-userspace conntrack helpers. */
|
||||
if (!(cur->flags & NF_CT_HELPER_F_USERSPACE))
|
||||
continue;
|
||||
list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) {
|
||||
cur = &nlcth->helper;
|
||||
|
||||
nf_conntrack_helper_unregister(cur);
|
||||
}
|
||||
kfree(cur->expect_policy);
|
||||
kfree(nlcth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -646,8 +646,8 @@ static void __exit cttimeout_exit(void)
|
|||
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
|
||||
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
|
||||
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
|
||||
synchronize_rcu();
|
||||
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
|
||||
rcu_barrier();
|
||||
}
|
||||
|
||||
module_init(cttimeout_init);
|
||||
|
|
|
@ -443,7 +443,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
|||
skb = alloc_skb(size, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
skb_tx_error(entskb);
|
||||
return NULL;
|
||||
goto nlmsg_failure;
|
||||
}
|
||||
|
||||
nlh = nlmsg_put(skb, 0, 0,
|
||||
|
@ -452,7 +452,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
|||
if (!nlh) {
|
||||
skb_tx_error(entskb);
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
goto nlmsg_failure;
|
||||
}
|
||||
nfmsg = nlmsg_data(nlh);
|
||||
nfmsg->nfgen_family = entry->state.pf;
|
||||
|
@ -598,12 +598,17 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
|
|||
}
|
||||
|
||||
nlh->nlmsg_len = skb->len;
|
||||
if (seclen)
|
||||
security_release_secctx(secdata, seclen);
|
||||
return skb;
|
||||
|
||||
nla_put_failure:
|
||||
skb_tx_error(entskb);
|
||||
kfree_skb(skb);
|
||||
net_err_ratelimited("nf_queue: error creating packet message\n");
|
||||
nlmsg_failure:
|
||||
if (seclen)
|
||||
security_release_secctx(secdata, seclen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue