diff mbox series

[net,v2] openvswitch: always update flow key after nat

Message ID 20220318124319.3056455-1-aconole@redhat.com (mailing list archive)
State Accepted
Commit 60b44ca6bd7518dd38fa2719bc9240378b6172c3
Delegated to: Netdev Maintainers
Headers show
Series [net,v2] openvswitch: always update flow key after nat | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers fail 1 blamed authors not CCed: davem@davemloft.net; 3 maintainers not CCed: davem@davemloft.net kuba@kernel.org pabeni@redhat.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 88 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Aaron Conole March 18, 2022, 12:43 p.m. UTC
During NAT, a tuple collision may occur.  When this happens, openvswitch
will make a second pass through NAT which will perform additional packet
modification.  This will update the skb data, but not the flow key that
OVS uses.  This means that future flow lookups, and packet matches will
have incorrect data.  This has been supported since
5d50aa83e2c8 ("openvswitch: support asymmetric conntrack").

That commit failed to properly update the sw_flow_key attributes, since
it only called the ovs_ct_nat_update_key once, rather than each time
ovs_ct_nat_execute was called.  As these two operations are linked, the
ovs_ct_nat_execute() function should always make sure that the
sw_flow_key is updated after a successful call through NAT infrastructure.

Fixes: 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack")
Cc: Dumitru Ceara <dceara@redhat.com>
Cc: Numan Siddique <nusiddiq@redhat.com>
Signed-off-by: Aaron Conole <aconole@redhat.com>
---
v1->v2: removed forward decl., moved the ovs_nat_update_key function
        made sure it compiles with NF_NAT disabled and enabled

 net/openvswitch/conntrack.c | 118 ++++++++++++++++++------------------
 1 file changed, 59 insertions(+), 59 deletions(-)

Comments

Eelco Chaudron March 18, 2022, 1:47 p.m. UTC | #1
On 18 Mar 2022, at 13:43, Aaron Conole wrote:

> During NAT, a tuple collision may occur.  When this happens, openvswitch
> will make a second pass through NAT which will perform additional packet
> modification.  This will update the skb data, but not the flow key that
> OVS uses.  This means that future flow lookups, and packet matches will
> have incorrect data.  This has been supported since
> 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack").
>
> That commit failed to properly update the sw_flow_key attributes, since
> it only called the ovs_ct_nat_update_key once, rather than each time
> ovs_ct_nat_execute was called.  As these two operations are linked, the
> ovs_ct_nat_execute() function should always make sure that the
> sw_flow_key is updated after a successful call through NAT infrastructure.
>
> Fixes: 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack")
> Cc: Dumitru Ceara <dceara@redhat.com>
> Cc: Numan Siddique <nusiddiq@redhat.com>
> Signed-off-by: Aaron Conole <aconole@redhat.com>

You were right about the diff, it really looks messy and I had to apply it to review it :)

The patch looks fine to me!!

Acked-by: Eelco Chaudron <echaudro@redhat.com>

> ---
> v1->v2: removed forward decl., moved the ovs_nat_update_key function
>         made sure it compiles with NF_NAT disabled and enabled
>
>  net/openvswitch/conntrack.c | 118 ++++++++++++++++++------------------
>  1 file changed, 59 insertions(+), 59 deletions(-)
>
> diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
> index c07afff57dd3..4a947c13c813 100644
> --- a/net/openvswitch/conntrack.c
> +++ b/net/openvswitch/conntrack.c
> @@ -734,6 +734,57 @@ static bool skb_nfct_cached(struct net *net,
>  }
>
>  #if IS_ENABLED(CONFIG_NF_NAT)
> +static void ovs_nat_update_key(struct sw_flow_key *key,
> +			       const struct sk_buff *skb,
> +			       enum nf_nat_manip_type maniptype)
> +{
> +	if (maniptype == NF_NAT_MANIP_SRC) {
> +		__be16 src;
> +
> +		key->ct_state |= OVS_CS_F_SRC_NAT;
> +		if (key->eth.type == htons(ETH_P_IP))
> +			key->ipv4.addr.src = ip_hdr(skb)->saddr;
> +		else if (key->eth.type == htons(ETH_P_IPV6))
> +			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
> +			       sizeof(key->ipv6.addr.src));
> +		else
> +			return;
> +
> +		if (key->ip.proto == IPPROTO_UDP)
> +			src = udp_hdr(skb)->source;
> +		else if (key->ip.proto == IPPROTO_TCP)
> +			src = tcp_hdr(skb)->source;
> +		else if (key->ip.proto == IPPROTO_SCTP)
> +			src = sctp_hdr(skb)->source;
> +		else
> +			return;
> +
> +		key->tp.src = src;
> +	} else {
> +		__be16 dst;
> +
> +		key->ct_state |= OVS_CS_F_DST_NAT;
> +		if (key->eth.type == htons(ETH_P_IP))
> +			key->ipv4.addr.dst = ip_hdr(skb)->daddr;
> +		else if (key->eth.type == htons(ETH_P_IPV6))
> +			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
> +			       sizeof(key->ipv6.addr.dst));
> +		else
> +			return;
> +
> +		if (key->ip.proto == IPPROTO_UDP)
> +			dst = udp_hdr(skb)->dest;
> +		else if (key->ip.proto == IPPROTO_TCP)
> +			dst = tcp_hdr(skb)->dest;
> +		else if (key->ip.proto == IPPROTO_SCTP)
> +			dst = sctp_hdr(skb)->dest;
> +		else
> +			return;
> +
> +		key->tp.dst = dst;
> +	}
> +}
> +
>  /* Modelled after nf_nat_ipv[46]_fn().
>   * range is only used for new, uninitialized NAT state.
>   * Returns either NF_ACCEPT or NF_DROP.
> @@ -741,7 +792,7 @@ static bool skb_nfct_cached(struct net *net,
>  static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
>  			      enum ip_conntrack_info ctinfo,
>  			      const struct nf_nat_range2 *range,
> -			      enum nf_nat_manip_type maniptype)
> +			      enum nf_nat_manip_type maniptype, struct sw_flow_key *key)
>  {
>  	int hooknum, nh_off, err = NF_ACCEPT;
>
> @@ -813,58 +864,11 @@ static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
>  push:
>  	skb_push_rcsum(skb, nh_off);
>
> -	return err;
> -}
> -
> -static void ovs_nat_update_key(struct sw_flow_key *key,
> -			       const struct sk_buff *skb,
> -			       enum nf_nat_manip_type maniptype)
> -{
> -	if (maniptype == NF_NAT_MANIP_SRC) {
> -		__be16 src;
> -
> -		key->ct_state |= OVS_CS_F_SRC_NAT;
> -		if (key->eth.type == htons(ETH_P_IP))
> -			key->ipv4.addr.src = ip_hdr(skb)->saddr;
> -		else if (key->eth.type == htons(ETH_P_IPV6))
> -			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
> -			       sizeof(key->ipv6.addr.src));
> -		else
> -			return;
> -
> -		if (key->ip.proto == IPPROTO_UDP)
> -			src = udp_hdr(skb)->source;
> -		else if (key->ip.proto == IPPROTO_TCP)
> -			src = tcp_hdr(skb)->source;
> -		else if (key->ip.proto == IPPROTO_SCTP)
> -			src = sctp_hdr(skb)->source;
> -		else
> -			return;
> -
> -		key->tp.src = src;
> -	} else {
> -		__be16 dst;
> -
> -		key->ct_state |= OVS_CS_F_DST_NAT;
> -		if (key->eth.type == htons(ETH_P_IP))
> -			key->ipv4.addr.dst = ip_hdr(skb)->daddr;
> -		else if (key->eth.type == htons(ETH_P_IPV6))
> -			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
> -			       sizeof(key->ipv6.addr.dst));
> -		else
> -			return;
> -
> -		if (key->ip.proto == IPPROTO_UDP)
> -			dst = udp_hdr(skb)->dest;
> -		else if (key->ip.proto == IPPROTO_TCP)
> -			dst = tcp_hdr(skb)->dest;
> -		else if (key->ip.proto == IPPROTO_SCTP)
> -			dst = sctp_hdr(skb)->dest;
> -		else
> -			return;
> +	/* Update the flow key if NAT successful. */
> +	if (err == NF_ACCEPT)
> +		ovs_nat_update_key(key, skb, maniptype);
>
> -		key->tp.dst = dst;
> -	}
> +	return err;
>  }
>
>  /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */
> @@ -906,7 +910,7 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
>  	} else {
>  		return NF_ACCEPT; /* Connection is not NATed. */
>  	}
> -	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype);
> +	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key);
>
>  	if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) {
>  		if (ct->status & IPS_SRC_NAT) {
> @@ -916,17 +920,13 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
>  				maniptype = NF_NAT_MANIP_SRC;
>
>  			err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range,
> -						 maniptype);
> +						 maniptype, key);
>  		} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
>  			err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL,
> -						 NF_NAT_MANIP_SRC);
> +						 NF_NAT_MANIP_SRC, key);
>  		}
>  	}
>
> -	/* Mark NAT done if successful and update the flow key. */
> -	if (err == NF_ACCEPT)
> -		ovs_nat_update_key(key, skb, maniptype);
> -
>  	return err;
>  }
>  #else /* !CONFIG_NF_NAT */
> -- 
> 2.31.1
patchwork-bot+netdevbpf@kernel.org March 22, 2022, 5:40 a.m. UTC | #2
Hello:

This patch was applied to netdev/net.git (master)
by Jakub Kicinski <kuba@kernel.org>:

On Fri, 18 Mar 2022 08:43:19 -0400 you wrote:
> During NAT, a tuple collision may occur.  When this happens, openvswitch
> will make a second pass through NAT which will perform additional packet
> modification.  This will update the skb data, but not the flow key that
> OVS uses.  This means that future flow lookups, and packet matches will
> have incorrect data.  This has been supported since
> 5d50aa83e2c8 ("openvswitch: support asymmetric conntrack").
> 
> [...]

Here is the summary with links:
  - [net,v2] openvswitch: always update flow key after nat
    https://git.kernel.org/netdev/net/c/60b44ca6bd75

You are awesome, thank you!
diff mbox series

Patch

diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index c07afff57dd3..4a947c13c813 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -734,6 +734,57 @@  static bool skb_nfct_cached(struct net *net,
 }
 
 #if IS_ENABLED(CONFIG_NF_NAT)
+static void ovs_nat_update_key(struct sw_flow_key *key,
+			       const struct sk_buff *skb,
+			       enum nf_nat_manip_type maniptype)
+{
+	if (maniptype == NF_NAT_MANIP_SRC) {
+		__be16 src;
+
+		key->ct_state |= OVS_CS_F_SRC_NAT;
+		if (key->eth.type == htons(ETH_P_IP))
+			key->ipv4.addr.src = ip_hdr(skb)->saddr;
+		else if (key->eth.type == htons(ETH_P_IPV6))
+			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
+			       sizeof(key->ipv6.addr.src));
+		else
+			return;
+
+		if (key->ip.proto == IPPROTO_UDP)
+			src = udp_hdr(skb)->source;
+		else if (key->ip.proto == IPPROTO_TCP)
+			src = tcp_hdr(skb)->source;
+		else if (key->ip.proto == IPPROTO_SCTP)
+			src = sctp_hdr(skb)->source;
+		else
+			return;
+
+		key->tp.src = src;
+	} else {
+		__be16 dst;
+
+		key->ct_state |= OVS_CS_F_DST_NAT;
+		if (key->eth.type == htons(ETH_P_IP))
+			key->ipv4.addr.dst = ip_hdr(skb)->daddr;
+		else if (key->eth.type == htons(ETH_P_IPV6))
+			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
+			       sizeof(key->ipv6.addr.dst));
+		else
+			return;
+
+		if (key->ip.proto == IPPROTO_UDP)
+			dst = udp_hdr(skb)->dest;
+		else if (key->ip.proto == IPPROTO_TCP)
+			dst = tcp_hdr(skb)->dest;
+		else if (key->ip.proto == IPPROTO_SCTP)
+			dst = sctp_hdr(skb)->dest;
+		else
+			return;
+
+		key->tp.dst = dst;
+	}
+}
+
 /* Modelled after nf_nat_ipv[46]_fn().
  * range is only used for new, uninitialized NAT state.
  * Returns either NF_ACCEPT or NF_DROP.
@@ -741,7 +792,7 @@  static bool skb_nfct_cached(struct net *net,
 static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
 			      enum ip_conntrack_info ctinfo,
 			      const struct nf_nat_range2 *range,
-			      enum nf_nat_manip_type maniptype)
+			      enum nf_nat_manip_type maniptype, struct sw_flow_key *key)
 {
 	int hooknum, nh_off, err = NF_ACCEPT;
 
@@ -813,58 +864,11 @@  static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
 push:
 	skb_push_rcsum(skb, nh_off);
 
-	return err;
-}
-
-static void ovs_nat_update_key(struct sw_flow_key *key,
-			       const struct sk_buff *skb,
-			       enum nf_nat_manip_type maniptype)
-{
-	if (maniptype == NF_NAT_MANIP_SRC) {
-		__be16 src;
-
-		key->ct_state |= OVS_CS_F_SRC_NAT;
-		if (key->eth.type == htons(ETH_P_IP))
-			key->ipv4.addr.src = ip_hdr(skb)->saddr;
-		else if (key->eth.type == htons(ETH_P_IPV6))
-			memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
-			       sizeof(key->ipv6.addr.src));
-		else
-			return;
-
-		if (key->ip.proto == IPPROTO_UDP)
-			src = udp_hdr(skb)->source;
-		else if (key->ip.proto == IPPROTO_TCP)
-			src = tcp_hdr(skb)->source;
-		else if (key->ip.proto == IPPROTO_SCTP)
-			src = sctp_hdr(skb)->source;
-		else
-			return;
-
-		key->tp.src = src;
-	} else {
-		__be16 dst;
-
-		key->ct_state |= OVS_CS_F_DST_NAT;
-		if (key->eth.type == htons(ETH_P_IP))
-			key->ipv4.addr.dst = ip_hdr(skb)->daddr;
-		else if (key->eth.type == htons(ETH_P_IPV6))
-			memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
-			       sizeof(key->ipv6.addr.dst));
-		else
-			return;
-
-		if (key->ip.proto == IPPROTO_UDP)
-			dst = udp_hdr(skb)->dest;
-		else if (key->ip.proto == IPPROTO_TCP)
-			dst = tcp_hdr(skb)->dest;
-		else if (key->ip.proto == IPPROTO_SCTP)
-			dst = sctp_hdr(skb)->dest;
-		else
-			return;
+	/* Update the flow key if NAT successful. */
+	if (err == NF_ACCEPT)
+		ovs_nat_update_key(key, skb, maniptype);
 
-		key->tp.dst = dst;
-	}
+	return err;
 }
 
 /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */
@@ -906,7 +910,7 @@  static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
 	} else {
 		return NF_ACCEPT; /* Connection is not NATed. */
 	}
-	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype);
+	err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key);
 
 	if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) {
 		if (ct->status & IPS_SRC_NAT) {
@@ -916,17 +920,13 @@  static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
 				maniptype = NF_NAT_MANIP_SRC;
 
 			err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range,
-						 maniptype);
+						 maniptype, key);
 		} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
 			err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL,
-						 NF_NAT_MANIP_SRC);
+						 NF_NAT_MANIP_SRC, key);
 		}
 	}
 
-	/* Mark NAT done if successful and update the flow key. */
-	if (err == NF_ACCEPT)
-		ovs_nat_update_key(key, skb, maniptype);
-
 	return err;
 }
 #else /* !CONFIG_NF_NAT */