OvS ct action has this 'force' flag, which basically forces ConnTrack to
consider that this packet, this specific direction, is the original one.

Implement that similarly: if the ct entry is there and the direction is not
the expected one, destroy it and create a new one.

Signed-off-by: Marcelo Ricardo Leitner <mleit...@redhat.com>
---
 include/uapi/linux/tc_act/tc_ct.h |  1 +
 net/sched/act_ct.c                | 16 +++++++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/tc_act/tc_ct.h 
b/include/uapi/linux/tc_act/tc_ct.h
index 
37b95cda1dedd283b0244a03a20860ba22966dfa..009e53ee83fb3125bc5c4ca86954af3bf6a0287a
 100644
--- a/include/uapi/linux/tc_act/tc_ct.h
+++ b/include/uapi/linux/tc_act/tc_ct.h
@@ -25,6 +25,7 @@ enum {
 
 enum {
        TC_CT_COMMIT,
+       TC_CT_FORCE,
        __TC_CT_MAX
 };
 #define TC_CT_MAX (__TC_CT_MAX - 1)
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index 
f69509954149a0c8be710916a5289a4448049b5d..8a1b5d6a7cd8360c50011d992368464db213a020
 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -165,6 +165,7 @@ static int tcf_ct_act(struct sk_buff *skb, const struct 
tc_action *a,
                      struct tcf_result *res)
 {
        struct tcf_ct *p = to_tcf_ct(a);
+       enum ip_conntrack_info ctinfo;
        struct nf_hook_state state = {
                .hook = NF_INET_PRE_ROUTING,
        };
@@ -173,6 +174,8 @@ static int tcf_ct_act(struct sk_buff *skb, const struct 
tc_action *a,
        int action, err;
        int nh_ofs;
 
+       /* Again needs to be here because we need a new ref on the ct. */
+again:
        spin_lock(&p->tcf_lock);
 
        tcf_lastuse_update(&p->tcf_tm);
@@ -218,8 +221,19 @@ static int tcf_ct_act(struct sk_buff *skb, const struct 
tc_action *a,
        if (err != NF_ACCEPT)
                goto drop;
 
-       new_ct = (struct nf_conn *)skb_nfct(skb);
+       new_ct = nf_ct_get(skb, &ctinfo);
        if (new_ct) {
+               /* Force conntrack entry direction. */
+               if (flags & BIT(TC_CT_FORCE) &&
+                   CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) {
+                       if (nf_ct_is_confirmed(new_ct))
+                               nf_ct_delete(new_ct, 0, 0);
+
+                       nf_conntrack_put(&new_ct->ct_general);
+                       nf_ct_set(skb, NULL, 0);
+                       goto again;
+               }
+
                if (mark_mask) {
                        new_ct->mark = (new_ct->mark &~ mark_mask) | (mark & 
mark_mask);
                        if (nf_ct_is_confirmed(new_ct))
-- 
2.20.1

Reply via email to