| From 54d132db57c16b6cf4c7eea61f8a56e45a28e886 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 3 Nov 2023 16:14:10 +0100 |
| Subject: net/sched: act_ct: Always fill offloading tuple iifidx |
| |
| From: Vlad Buslov <vladbu@nvidia.com> |
| |
| [ Upstream commit 9bc64bd0cd765f696fcd40fc98909b1f7c73b2ba ] |
| |
| Referenced commit doesn't always set iifidx when offloading the flow to |
| hardware. Fix the following cases: |
| |
| - nf_conn_act_ct_ext_fill() is called before extension is created with |
| nf_conn_act_ct_ext_add() in tcf_ct_act(). This can cause rule offload with |
| unspecified iifidx when connection is offloaded after only single |
| original-direction packet has been processed by tc data path. Always fill |
| the new nf_conn_act_ct_ext instance after creating it in |
| nf_conn_act_ct_ext_add(). |
| |
| - Offloading of unidirectional UDP NEW connections is now supported, but ct |
| flow iifidx field is not updated when connection is promoted to |
| bidirectional which can result reply-direction iifidx to be zero when |
| refreshing the connection. Fill in the extension and update flow iifidx |
| before calling flow_offload_refresh(). |
| |
| Fixes: 9795ded7f924 ("net/sched: act_ct: Fill offloading tuple iifidx") |
| Reviewed-by: Paul Blakey <paulb@nvidia.com> |
| Signed-off-by: Vlad Buslov <vladbu@nvidia.com> |
| Reviewed-by: Simon Horman <horms@kernel.org> |
| Fixes: 6a9bad0069cf ("net/sched: act_ct: offload UDP NEW connections") |
| Link: https://lore.kernel.org/r/20231103151410.764271-1-vladbu@nvidia.com |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/net/netfilter/nf_conntrack_act_ct.h | 30 ++++++++++++--------- |
| net/openvswitch/conntrack.c | 2 +- |
| net/sched/act_ct.c | 15 ++++++++++- |
| 3 files changed, 32 insertions(+), 15 deletions(-) |
| |
| diff --git a/include/net/netfilter/nf_conntrack_act_ct.h b/include/net/netfilter/nf_conntrack_act_ct.h |
| index 078d3c52c03f9..e5f2f0b73a9a0 100644 |
| --- a/include/net/netfilter/nf_conntrack_act_ct.h |
| +++ b/include/net/netfilter/nf_conntrack_act_ct.h |
| @@ -20,7 +20,22 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_find(const struct nf |
| #endif |
| } |
| |
| -static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn *ct) |
| +static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct, |
| + enum ip_conntrack_info ctinfo) |
| +{ |
| +#if IS_ENABLED(CONFIG_NET_ACT_CT) |
| + struct nf_conn_act_ct_ext *act_ct_ext; |
| + |
| + act_ct_ext = nf_conn_act_ct_ext_find(ct); |
| + if (dev_net(skb->dev) == &init_net && act_ct_ext) |
| + act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex; |
| +#endif |
| +} |
| + |
| +static inline struct |
| +nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct sk_buff *skb, |
| + struct nf_conn *ct, |
| + enum ip_conntrack_info ctinfo) |
| { |
| #if IS_ENABLED(CONFIG_NET_ACT_CT) |
| struct nf_conn_act_ct_ext *act_ct = nf_ct_ext_find(ct, NF_CT_EXT_ACT_CT); |
| @@ -29,22 +44,11 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn * |
| return act_ct; |
| |
| act_ct = nf_ct_ext_add(ct, NF_CT_EXT_ACT_CT, GFP_ATOMIC); |
| + nf_conn_act_ct_ext_fill(skb, ct, ctinfo); |
| return act_ct; |
| #else |
| return NULL; |
| #endif |
| } |
| |
| -static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct, |
| - enum ip_conntrack_info ctinfo) |
| -{ |
| -#if IS_ENABLED(CONFIG_NET_ACT_CT) |
| - struct nf_conn_act_ct_ext *act_ct_ext; |
| - |
| - act_ct_ext = nf_conn_act_ct_ext_find(ct); |
| - if (dev_net(skb->dev) == &init_net && act_ct_ext) |
| - act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex; |
| -#endif |
| -} |
| - |
| #endif /* _NF_CONNTRACK_ACT_CT_H */ |
| diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c |
| index 0b9a785dea459..3019a4406ca4f 100644 |
| --- a/net/openvswitch/conntrack.c |
| +++ b/net/openvswitch/conntrack.c |
| @@ -985,7 +985,7 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, |
| if (err) |
| return err; |
| |
| - nf_conn_act_ct_ext_add(ct); |
| + nf_conn_act_ct_ext_add(skb, ct, ctinfo); |
| } else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && |
| labels_nonzero(&info->labels.mask)) { |
| err = ovs_ct_set_labels(ct, key, &info->labels.value, |
| diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c |
| index fb52d6f9aff93..3922d825ef2d8 100644 |
| --- a/net/sched/act_ct.c |
| +++ b/net/sched/act_ct.c |
| @@ -376,6 +376,17 @@ static void tcf_ct_flow_tc_ifidx(struct flow_offload *entry, |
| entry->tuplehash[dir].tuple.tc.iifidx = act_ct_ext->ifindex[dir]; |
| } |
| |
| +static void tcf_ct_flow_ct_ext_ifidx_update(struct flow_offload *entry) |
| +{ |
| + struct nf_conn_act_ct_ext *act_ct_ext; |
| + |
| + act_ct_ext = nf_conn_act_ct_ext_find(entry->ct); |
| + if (act_ct_ext) { |
| + tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_ORIGINAL); |
| + tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_REPLY); |
| + } |
| +} |
| + |
| static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft, |
| struct nf_conn *ct, |
| bool tcp, bool bidirectional) |
| @@ -671,6 +682,8 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p, |
| else |
| ctinfo = IP_CT_ESTABLISHED_REPLY; |
| |
| + nf_conn_act_ct_ext_fill(skb, ct, ctinfo); |
| + tcf_ct_flow_ct_ext_ifidx_update(flow); |
| flow_offload_refresh(nf_ft, flow, force_refresh); |
| if (!test_bit(IPS_ASSURED_BIT, &ct->status)) { |
| /* Process this flow in SW to allow promoting to ASSURED */ |
| @@ -1030,7 +1043,7 @@ TC_INDIRECT_SCOPE int tcf_ct_act(struct sk_buff *skb, const struct tc_action *a, |
| tcf_ct_act_set_labels(ct, p->labels, p->labels_mask); |
| |
| if (!nf_ct_is_confirmed(ct)) |
| - nf_conn_act_ct_ext_add(ct); |
| + nf_conn_act_ct_ext_add(skb, ct, ctinfo); |
| |
| /* This will take care of sending queued events |
| * even if the connection is already confirmed. |
| -- |
| 2.42.0 |
| |