static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft,
                                  struct nf_conn *ct,
-                                 bool tcp)
+                                 bool tcp, bool bidirectional)
 {
        struct nf_conn_act_ct_ext *act_ct_ext;
        struct flow_offload *entry;
                ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
                ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
        }
+       if (bidirectional)
+               __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &entry->flags);
 
        act_ct_ext = nf_conn_act_ct_ext_find(ct);
        if (act_ct_ext) {
                                           struct nf_conn *ct,
                                           enum ip_conntrack_info ctinfo)
 {
-       bool tcp = false;
-
-       if ((ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) ||
-           !test_bit(IPS_ASSURED_BIT, &ct->status))
-               return;
+       bool tcp = false, bidirectional = true;
 
        switch (nf_ct_protonum(ct)) {
        case IPPROTO_TCP:
-               tcp = true;
-               if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
+               if ((ctinfo != IP_CT_ESTABLISHED &&
+                    ctinfo != IP_CT_ESTABLISHED_REPLY) ||
+                   !test_bit(IPS_ASSURED_BIT, &ct->status) ||
+                   ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
                        return;
+
+               tcp = true;
                break;
        case IPPROTO_UDP:
+               if (!nf_ct_is_confirmed(ct))
+                       return;
+               if (!test_bit(IPS_ASSURED_BIT, &ct->status))
+                       bidirectional = false;
                break;
 #ifdef CONFIG_NF_CT_PROTO_GRE
        case IPPROTO_GRE: {
                struct nf_conntrack_tuple *tuple;
 
-               if (ct->status & IPS_NAT_MASK)
+               if ((ctinfo != IP_CT_ESTABLISHED &&
+                    ctinfo != IP_CT_ESTABLISHED_REPLY) ||
+                   !test_bit(IPS_ASSURED_BIT, &ct->status) ||
+                   ct->status & IPS_NAT_MASK)
                        return;
+
                tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
                /* No support for GRE v1 */
                if (tuple->src.u.gre.key || tuple->dst.u.gre.key)
            ct->status & IPS_SEQ_ADJUST)
                return;
 
-       tcf_ct_flow_table_add(ct_ft, ct, tcp);
+       tcf_ct_flow_table_add(ct_ft, ct, tcp, bidirectional);
 }
 
 static bool
        flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
        ct = flow->ct;
 
+       if (dir == FLOW_OFFLOAD_DIR_REPLY &&
+           !test_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags)) {
+               /* Only offload reply direction after connection became
+                * assured.
+                */
+               if (test_bit(IPS_ASSURED_BIT, &ct->status))
+                       set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
+               else if (test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags))
+                       /* If flow_table flow has already been updated to the
+                        * established state, then don't refresh.
+                        */
+                       return false;
+       }
+
        if (tcph && (unlikely(tcph->fin || tcph->rst))) {
                flow_offload_teardown(flow);
                return false;
        }
 
-       ctinfo = dir == FLOW_OFFLOAD_DIR_ORIGINAL ? IP_CT_ESTABLISHED :
-                                                   IP_CT_ESTABLISHED_REPLY;
+       if (dir == FLOW_OFFLOAD_DIR_ORIGINAL)
+               ctinfo = test_bit(IPS_SEEN_REPLY_BIT, &ct->status) ?
+                       IP_CT_ESTABLISHED : IP_CT_NEW;
+       else
+               ctinfo = IP_CT_ESTABLISHED_REPLY;
 
        flow_offload_refresh(nf_ft, flow);
        nf_conntrack_get(&ct->ct_general);