OFFLOAD tables not flushed on FW reload

Hi,
maybe it is by-design, but if a flow is offloaded to either SW or HW flow offload and the FW rules are changed to DROP a flow that is currently offloaded, the flow tables are not flushed and the offloaded flow keep running.

I have had to implement the patch below so that I at least periodically can check if offloaded flows are allowed to continue to run. I do it periodically because a FW rule could be a time schedule and therefor only flushing on a FW reload trigger is not enough.

Note: The patch only work on SW OFFLOAD, the HW OFFLOAD (on mt762x) flush doesn't seem to work even though it get instructed to remove the flow from its tables.

Does anyone have any comments or hints to why the HW_OFFLOAD doesn't flush as SW_OFFLOAD does?

Any comments in general to my approach of periodically retire flows from the offload tables so that the FW rules are checked periodically... Could it be done in another way?

Thanks

--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -169,6 +169,7 @@ struct flow_offload {
 	unsigned long				flags;
 	u16					type;
 	u32					timeout;
+        u32                                     handover;
 	struct rcu_head				rcu_head;
 };

--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -283,6 +283,7 @@ int flow_offload_add(struct nf_flowtable
 	int err;

 	flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
+        flow->handover = nf_flowtable_time_stamp + (30 * HZ);

 	err = rhashtable_insert_fast(&flow_table->rhashtable,
 				     &flow->tuplehash[0].node,
@@ -337,6 +338,7 @@ static inline bool nf_flow_has_expired(c
 static void flow_offload_del(struct nf_flowtable *flow_table,
 			     struct flow_offload *flow)
 {
+   	clear_bit(IPS_OFFLOAD_BIT, &flow->ct->status);
 	rhashtable_remove_fast(&flow_table->rhashtable,
 			       &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node,
 			       nf_flow_offload_rhash_params);
@@ -421,12 +423,15 @@ static void nf_flow_offload_gc_step(stru
 	    nf_ct_is_dying(flow->ct))
 		flow_offload_teardown(flow);

-	if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
+ 	if (test_bit(NF_FLOW_TEARDOWN, &flow->flags) ||
+ 	    nf_flow_timeout_delta(flow->handover) <= 0) {
 		if (test_bit(NF_FLOW_HW, &flow->flags)) {
 			if (!test_bit(NF_FLOW_HW_DYING, &flow->flags))
 				nf_flow_offload_del(flow_table, flow);
-			else if (test_bit(NF_FLOW_HW_DEAD, &flow->flags))
-				flow_offload_del(flow_table, flow);
+			else if (test_bit(NF_FLOW_HW_DEAD, &flow->flags)) {
+			  clear_bit(NF_FLOW_HW, &flow->flags);
+			  flow_offload_del(flow_table, flow);
+			}
 		} else {
 			flow_offload_del(flow_table, flow);
 		}

1 Like