struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper;
/* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(*pskb, &ctinfo);
return NF_ACCEPT;
help = nfct_help(ct);
- if (!help || !help->helper)
+ if (!help)
return NF_ACCEPT;
-
- return help->helper->help(pskb,
- skb_network_offset(*pskb) + ip_hdrlen(*pskb),
- ct, ctinfo);
+ /* rcu_read_lock()ed by nf_hook_slow */
+ helper = rcu_dereference(help->helper);
+ if (!helper)
+ return NF_ACCEPT;
+ return helper->help(pskb, skb_network_offset(*pskb) + ip_hdrlen(*pskb),
+ ct, ctinfo);
}
static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
{
struct nf_conn *ct;
struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper;
enum ip_conntrack_info ctinfo;
unsigned int ret, protoff;
unsigned int extoff = (u8 *)(ipv6_hdr(*pskb) + 1) - (*pskb)->data;
goto out;
help = nfct_help(ct);
- if (!help || !help->helper)
+ if (!help)
+ goto out;
+ /* rcu_read_lock()ed by nf_hook_slow */
+ helper = rcu_dereference(help->helper);
+ if (!helper)
goto out;
protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
return NF_ACCEPT;
}
- ret = help->helper->help(pskb, protoff, ct, ctinfo);
+ ret = helper->help(pskb, protoff, ct, ctinfo);
if (ret != NF_ACCEPT)
return ret;
out:
{
struct nf_conn *ct = (void *)ul_conntrack;
struct nf_conn_help *help = nfct_help(ct);
+ struct nf_conntrack_helper *helper;
- if (help && help->helper && help->helper->destroy)
- help->helper->destroy(ct);
+ if (help) {
+ rcu_read_lock();
+ helper = rcu_dereference(help->helper);
+ if (helper && helper->destroy)
+ helper->destroy(ct);
+ rcu_read_unlock();
+ }
write_lock_bh(&nf_conntrack_lock);
/* Inside lock so preempt is disabled on module removal path.
unsigned int dataoff)
{
struct nf_conn *conntrack;
+ struct nf_conn_help *help;
struct nf_conntrack_tuple repl_tuple;
struct nf_conntrack_expect *exp;
u_int32_t features = 0;
write_lock_bh(&nf_conntrack_lock);
exp = find_expectation(tuple);
+ help = nfct_help(conntrack);
if (exp) {
DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
conntrack, exp);
__set_bit(IPS_EXPECTED_BIT, &conntrack->status);
conntrack->master = exp->master;
if (exp->helper)
- nfct_help(conntrack)->helper = exp->helper;
+ rcu_assign_pointer(help->helper, exp->helper);
#ifdef CONFIG_NF_CONNTRACK_MARK
conntrack->mark = exp->master->mark;
#endif
nf_conntrack_get(&conntrack->master->ct_general);
NF_CT_STAT_INC(expect_new);
} else {
- struct nf_conn_help *help = nfct_help(conntrack);
-
- if (help)
- help->helper = __nf_ct_helper_find(&repl_tuple);
+ if (help) {
+ /* not in hash table yet, so not strictly necessary */
+ rcu_assign_pointer(help->helper,
+ __nf_ct_helper_find(&repl_tuple));
+ }
NF_CT_STAT_INC(new);
}
helper = __nf_ct_helper_find(newreply);
if (helper)
memset(&help->help, 0, sizeof(help->help));
- help->helper = helper;
+ /* not in hash table yet, so not strictly necessary */
+ rcu_assign_pointer(help->helper, helper);
}
write_unlock_bh(&nf_conntrack_lock);
}
NF_CT_ASSERT(master_help);
write_lock_bh(&nf_conntrack_lock);
+ if (!master_help->helper) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
list_for_each_entry(i, &nf_conntrack_expect_list, list) {
if (expect_matches(i, expect)) {
/* Refresh timer: if it's dying, ignore.. */
if (help && help->helper == me) {
nf_conntrack_event(IPCT_HELPER, ct);
- help->helper = NULL;
+ rcu_assign_pointer(help->helper, NULL);
}
return 0;
}
{
struct nfattr *nest_helper;
const struct nf_conn_help *help = nfct_help(ct);
+ struct nf_conntrack_helper *helper;
- if (!help || !help->helper)
+ if (!help)
return 0;
+ rcu_read_lock();
+ helper = rcu_dereference(help->helper);
+ if (!helper)
+ goto out;
+
nest_helper = NFA_NEST(skb, CTA_HELP);
- NFA_PUT(skb, CTA_HELP_NAME, strlen(help->helper->name), help->helper->name);
+ NFA_PUT(skb, CTA_HELP_NAME, strlen(helper->name), helper->name);
- if (help->helper->to_nfattr)
- help->helper->to_nfattr(skb, ct);
+ if (helper->to_nfattr)
+ helper->to_nfattr(skb, ct);
NFA_NEST_END(skb, nest_helper);
-
+out:
+ rcu_read_unlock();
return 0;
nfattr_failure:
+ rcu_read_unlock();
return -1;
}
if (help && help->helper) {
/* we had a helper before ... */
nf_ct_remove_expectations(ct);
- help->helper = NULL;
+ rcu_assign_pointer(help->helper, NULL);
}
return 0;
/* need to zero data of old helper */
memset(&help->help, 0, sizeof(help->help));
- help->helper = helper;
+ rcu_assign_pointer(help->helper, helper);
return 0;
}
struct nf_conn *ct;
int err = -EINVAL;
struct nf_conn_help *help;
+ struct nf_conntrack_helper *helper = NULL;
ct = nf_conntrack_alloc(otuple, rtuple);
if (ct == NULL || IS_ERR(ct))
#endif
help = nfct_help(ct);
- if (help)
- help->helper = nf_ct_helper_find_get(rtuple);
+ if (help) {
+ helper = nf_ct_helper_find_get(rtuple);
+ /* not in hash table yet so not strictly necessary */
+ rcu_assign_pointer(help->helper, helper);
+ }
add_timer(&ct->timeout);
nf_conntrack_hash_insert(ct);
- if (help && help->helper)
- nf_ct_helper_put(help->helper);
+ if (helper)
+ nf_ct_helper_put(helper);
return 0;
struct nf_conn_help *help = nfct_help(ct);
struct nf_ct_gre_keymap **kmp, *km;
- BUG_ON(strcmp(help->helper->name, "pptp"));
kmp = &help->help.ct_pptp_info.keymap[dir];
if (*kmp) {
/* check whether it's a retransmission */
enum ip_conntrack_dir dir;
DEBUGP("entering for ct %p\n", ct);
- BUG_ON(strcmp(help->helper->name, "pptp"));
write_lock_bh(&nf_ct_gre_lock);
for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {