| From 8aa7b526dc0b5dbf40c1b834d76a667ad672a410 Mon Sep 17 00:00:00 2001 |
| From: Dumitru Ceara <dceara@redhat.com> |
| Date: Wed, 7 Oct 2020 17:48:03 +0200 |
| Subject: openvswitch: handle DNAT tuple collision |
| |
| From: Dumitru Ceara <dceara@redhat.com> |
| |
| commit 8aa7b526dc0b5dbf40c1b834d76a667ad672a410 upstream. |
| |
| With multiple DNAT rules it's possible that after destination |
| translation the resulting tuples collide. |
| |
| For example, two openvswitch flows: |
| nw_dst=10.0.0.10,tp_dst=10, actions=ct(commit,table=2,nat(dst=20.0.0.1:20)) |
| nw_dst=10.0.0.20,tp_dst=10, actions=ct(commit,table=2,nat(dst=20.0.0.1:20)) |
| |
| Assuming two TCP clients initiating the following connections: |
| 10.0.0.10:5000->10.0.0.10:10 |
| 10.0.0.10:5000->10.0.0.20:10 |
| |
| Both tuples would translate to 10.0.0.10:5000->20.0.0.1:20 causing |
| nf_conntrack_confirm() to fail because of tuple collision. |
| |
| Netfilter handles this case by allocating a null binding for SNAT at |
| egress by default. Perform the same operation in openvswitch for DNAT |
| if no explicit SNAT is requested by the user and allocate a null binding |
| for SNAT for packets in the "original" direction. |
| |
| Reported-at: https://bugzilla.redhat.com/1877128 |
| Suggested-by: Florian Westphal <fw@strlen.de> |
| Fixes: 05752523e565 ("openvswitch: Interface with NAT.") |
| Signed-off-by: Dumitru Ceara <dceara@redhat.com> |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/openvswitch/conntrack.c | 20 ++++++++++++-------- |
| 1 file changed, 12 insertions(+), 8 deletions(-) |
| |
| --- a/net/openvswitch/conntrack.c |
| +++ b/net/openvswitch/conntrack.c |
| @@ -709,15 +709,19 @@ static int ovs_ct_nat(struct net *net, s |
| } |
| err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype); |
| |
| - if (err == NF_ACCEPT && |
| - ct->status & IPS_SRC_NAT && ct->status & IPS_DST_NAT) { |
| - if (maniptype == NF_NAT_MANIP_SRC) |
| - maniptype = NF_NAT_MANIP_DST; |
| - else |
| - maniptype = NF_NAT_MANIP_SRC; |
| + if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { |
| + if (ct->status & IPS_SRC_NAT) { |
| + if (maniptype == NF_NAT_MANIP_SRC) |
| + maniptype = NF_NAT_MANIP_DST; |
| + else |
| + maniptype = NF_NAT_MANIP_SRC; |
| |
| - err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, |
| - maniptype); |
| + err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, |
| + maniptype); |
| + } else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { |
| + err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, |
| + NF_NAT_MANIP_SRC); |
| + } |
| } |
| |
| /* Mark NAT done if successful and update the flow key. */ |