return key;
}
-/* add a single keymap entry, associate with specified master ct */
-int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
- struct nf_conntrack_tuple *t)
+enum nf_ct_gre_km_act {
+ NF_CT_GRE_KM_NEW,
+ NF_CT_GRE_KM_BAD,
+ NF_CT_GRE_KM_DUP
+};
+
+static enum nf_ct_gre_km_act
+nf_ct_gre_km_acceptable(const struct nf_ct_pptp_master *ct_pptp_info,
+ const struct nf_conntrack_tuple *orig,
+ const struct nf_conntrack_tuple *repl)
+{
+ struct nf_ct_gre_keymap *km_orig, *km_repl;
+
+ lockdep_assert_held(&keymap_lock);
+
+ km_orig = ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL];
+ km_repl = ct_pptp_info->keymap[IP_CT_DIR_REPLY];
+
+ if (km_orig && km_repl) {
+ if (!gre_key_cmpfn(km_orig, orig))
+ return NF_CT_GRE_KM_BAD;
+
+ if (!gre_key_cmpfn(km_repl, repl))
+ return NF_CT_GRE_KM_BAD;
+
+ return NF_CT_GRE_KM_DUP;
+ }
+
+ DEBUG_NET_WARN_ON_ONCE(km_orig);
+ DEBUG_NET_WARN_ON_ONCE(km_repl);
+ return NF_CT_GRE_KM_NEW;
+}
+
+/* add keymap entries, associate with specified master ct */
+bool nf_ct_gre_keymap_add(struct nf_conn *ct,
+ const struct nf_conntrack_tuple *orig,
+ const struct nf_conntrack_tuple *repl)
{
struct net *net = nf_ct_net(ct);
struct nf_gre_net *net_gre = gre_pernet(net);
struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
- struct nf_ct_gre_keymap **kmp, *km;
-
- kmp = &ct_pptp_info->keymap[dir];
- if (*kmp) {
- /* check whether it's a retransmission */
- list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
- if (gre_key_cmpfn(km, t) && km == *kmp)
- return 0;
- }
- pr_debug("trying to override keymap_%s for ct %p\n",
- dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
- return -EEXIST;
- }
+ struct nf_ct_gre_keymap *km_orig, *km_repl;
+ bool ret = false;
- km = kmalloc_obj(*km, GFP_ATOMIC);
- if (!km)
- return -ENOMEM;
- memcpy(&km->tuple, t, sizeof(*t));
- *kmp = km;
+ km_orig = kmalloc_obj(*km_orig, GFP_ATOMIC);
+ if (!km_orig)
+ return false;
+ km_repl = kmalloc_obj(*km_repl, GFP_ATOMIC);
+ if (!km_repl)
+ goto km_free;
- pr_debug("adding new entry %p: ", km);
- nf_ct_dump_tuple(&km->tuple);
+ memcpy(&km_orig->tuple, orig, sizeof(*orig));
+ memcpy(&km_repl->tuple, repl, sizeof(*repl));
spin_lock_bh(&keymap_lock);
- list_add_tail(&km->list, &net_gre->keymap_list);
+ if (nf_ct_is_dying(ct))
+ goto unlock_free;
+
+ switch (nf_ct_gre_km_acceptable(ct_pptp_info, orig, repl)) {
+ case NF_CT_GRE_KM_NEW:
+ break;
+ case NF_CT_GRE_KM_DUP:
+ ret = true;
+ goto unlock_free;
+ case NF_CT_GRE_KM_BAD:
+ pr_debug("trying to override keymap for ct %p\n", ct);
+ goto unlock_free;
+ }
+
+ if (ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] ||
+ ct_pptp_info->keymap[IP_CT_DIR_REPLY])
+ goto unlock_free;
+
+ pr_debug("adding new entries %p,%p: ", km_orig, km_repl);
+ nf_ct_dump_tuple(&km_orig->tuple);
+ nf_ct_dump_tuple(&km_repl->tuple);
+
+ list_add_tail_rcu(&km_orig->list, &net_gre->keymap_list);
+ list_add_tail_rcu(&km_repl->list, &net_gre->keymap_list);
+ ct_pptp_info->keymap[IP_CT_DIR_ORIGINAL] = km_orig;
+ ct_pptp_info->keymap[IP_CT_DIR_REPLY] = km_repl;
spin_unlock_bh(&keymap_lock);
- return 0;
+ return true;
+
+unlock_free:
+ spin_unlock_bh(&keymap_lock);
+km_free:
+ kfree(km_orig);
+ kfree(km_repl);
+ return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);