diff mbox series

[ovs-dev] ofproto-dpif-xlate: Fix recirculation with patch port and controller.

Message ID 168416414918.7273.15419064875718310114.stgit@rawp
State Changes Requested
Headers show
Series [ovs-dev] ofproto-dpif-xlate: Fix recirculation with patch port and controller. | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Paolo Valerio May 15, 2023, 3:22 p.m. UTC
If a packet originating from the controller recirculates after going
through a patch port, it gets dropped with the following message:

ofproto_dpif_upcall(handler8)|INFO|received packet on unassociated
  datapath port 4294967295

This happens because there's no xport_uuid in the recirculation node
and at the same type in_port refers to the patch port.

The patch, in the case of zeroed uuid, retrieves the xport starting
from the ofproto_uuid stored in the recirc node.

Signed-off-by: Paolo Valerio <pvalerio@redhat.com>
---
 ofproto/ofproto-dpif-xlate.c |   11 +++++++++--
 tests/ofproto-dpif.at        |   34 ++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

Comments

Ilya Maximets May 19, 2023, 6:33 p.m. UTC | #1
On 5/15/23 17:22, Paolo Valerio wrote:
> If a packet originating from the controller recirculates after going
> through a patch port, it gets dropped with the following message:
> 
> ofproto_dpif_upcall(handler8)|INFO|received packet on unassociated
>   datapath port 4294967295
> 
> This happens because there's no xport_uuid in the recirculation node
> and at the same type in_port refers to the patch port.
> 
> The patch, in the case of zeroed uuid, retrieves the xport starting
> from the ofproto_uuid stored in the recirc node.
> 
> Signed-off-by: Paolo Valerio <pvalerio@redhat.com>
> ---
>  ofproto/ofproto-dpif-xlate.c |   11 +++++++++--
>  tests/ofproto-dpif.at        |   34 ++++++++++++++++++++++++++++++++++
>  2 files changed, 43 insertions(+), 2 deletions(-)
> 
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index c01177718..3509cc73c 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -1533,8 +1533,15 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
>  
>          ofp_port_t in_port = recirc_id_node->state.metadata.in_port;
>          if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) {
> -            struct uuid xport_uuid = recirc_id_node->state.xport_uuid;
> -            xport = xport_lookup_by_uuid(xcfg, &xport_uuid);
> +            if (uuid_is_zero(&recirc_id_node->state.xport_uuid)) {
> +                const struct xbridge *bridge =
> +                    xbridge_lookup_by_uuid(xcfg, &recirc_id_node->state.ofproto_uuid);
> +                xport = bridge ? get_ofp_port(bridge, in_port) : NULL;

IIUC, xport_uuid is designed to not be uuid of the patch port.
But the in_port here is a patch port, right?  So, we will find
a different xport, right?

Shouldn't we just fall into the else condition that handles
NONE and CONTROLLER and not look for xport?

Best regards, Ilya Maximets.
Paolo Valerio May 22, 2023, 10:16 p.m. UTC | #2
Ilya Maximets <i.maximets@ovn.org> writes:

> On 5/15/23 17:22, Paolo Valerio wrote:
>> If a packet originating from the controller recirculates after going
>> through a patch port, it gets dropped with the following message:
>> 
>> ofproto_dpif_upcall(handler8)|INFO|received packet on unassociated
>>   datapath port 4294967295
>> 
>> This happens because there's no xport_uuid in the recirculation node
>> and at the same type in_port refers to the patch port.
>> 
>> The patch, in the case of zeroed uuid, retrieves the xport starting
>> from the ofproto_uuid stored in the recirc node.
>> 
>> Signed-off-by: Paolo Valerio <pvalerio@redhat.com>
>> ---
>>  ofproto/ofproto-dpif-xlate.c |   11 +++++++++--
>>  tests/ofproto-dpif.at        |   34 ++++++++++++++++++++++++++++++++++
>>  2 files changed, 43 insertions(+), 2 deletions(-)
>> 
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index c01177718..3509cc73c 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -1533,8 +1533,15 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
>>  
>>          ofp_port_t in_port = recirc_id_node->state.metadata.in_port;
>>          if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) {
>> -            struct uuid xport_uuid = recirc_id_node->state.xport_uuid;
>> -            xport = xport_lookup_by_uuid(xcfg, &xport_uuid);
>> +            if (uuid_is_zero(&recirc_id_node->state.xport_uuid)) {
>> +                const struct xbridge *bridge =
>> +                    xbridge_lookup_by_uuid(xcfg, &recirc_id_node->state.ofproto_uuid);
>> +                xport = bridge ? get_ofp_port(bridge, in_port) : NULL;
>
> IIUC, xport_uuid is designed to not be uuid of the patch port.
> But the in_port here is a patch port, right?  So, we will find
> a different xport, right?
>
> Shouldn't we just fall into the else condition that handles
> NONE and CONTROLLER and not look for xport?
>

I guess it's ok to fall in the else in this case.
The only problem is that we'd return the ofproto even if the in_port is
invalid.
This would make in turn fail "conntrack - fragment reassembly with L3 L4
protocol information". This test was fixed in the past after it already
broke once 323ae1e808e6 ("ofproto-dpif-xlate: Fix recirculation when
in_port is OFPP_CONTROLLER.") fixed the use case involving packet-out
and recirculation.

One possibility is to just retrieve the xport for that case in order to
verify the in_port belongs to the bridge, without returning it (so
honoring the xport_uuid logic). Maybe this could be done in the else
branch so to make clear we're handling the special case related to
OFPP_{NONE,CONTROLLER}.

WDYT?

> Best regards, Ilya Maximets.
Ilya Maximets May 23, 2023, 7:29 p.m. UTC | #3
On 5/23/23 00:16, Paolo Valerio wrote:
> Ilya Maximets <i.maximets@ovn.org> writes:
> 
>> On 5/15/23 17:22, Paolo Valerio wrote:
>>> If a packet originating from the controller recirculates after going
>>> through a patch port, it gets dropped with the following message:
>>>
>>> ofproto_dpif_upcall(handler8)|INFO|received packet on unassociated
>>>   datapath port 4294967295
>>>
>>> This happens because there's no xport_uuid in the recirculation node
>>> and at the same type in_port refers to the patch port.
>>>
>>> The patch, in the case of zeroed uuid, retrieves the xport starting
>>> from the ofproto_uuid stored in the recirc node.
>>>
>>> Signed-off-by: Paolo Valerio <pvalerio@redhat.com>
>>> ---
>>>  ofproto/ofproto-dpif-xlate.c |   11 +++++++++--
>>>  tests/ofproto-dpif.at        |   34 ++++++++++++++++++++++++++++++++++
>>>  2 files changed, 43 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>>> index c01177718..3509cc73c 100644
>>> --- a/ofproto/ofproto-dpif-xlate.c
>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>> @@ -1533,8 +1533,15 @@ xlate_lookup_ofproto_(const struct dpif_backer *backer,
>>>  
>>>          ofp_port_t in_port = recirc_id_node->state.metadata.in_port;
>>>          if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) {
>>> -            struct uuid xport_uuid = recirc_id_node->state.xport_uuid;
>>> -            xport = xport_lookup_by_uuid(xcfg, &xport_uuid);
>>> +            if (uuid_is_zero(&recirc_id_node->state.xport_uuid)) {
>>> +                const struct xbridge *bridge =
>>> +                    xbridge_lookup_by_uuid(xcfg, &recirc_id_node->state.ofproto_uuid);
>>> +                xport = bridge ? get_ofp_port(bridge, in_port) : NULL;
>>
>> IIUC, xport_uuid is designed to not be uuid of the patch port.
>> But the in_port here is a patch port, right?  So, we will find
>> a different xport, right?
>>
>> Shouldn't we just fall into the else condition that handles
>> NONE and CONTROLLER and not look for xport?
>>
> 
> I guess it's ok to fall in the else in this case.
> The only problem is that we'd return the ofproto even if the in_port is
> invalid.
> This would make in turn fail "conntrack - fragment reassembly with L3 L4
> protocol information". This test was fixed in the past after it already
> broke once 323ae1e808e6 ("ofproto-dpif-xlate: Fix recirculation when
> in_port is OFPP_CONTROLLER.") fixed the use case involving packet-out
> and recirculation.
> 
> One possibility is to just retrieve the xport for that case in order to
> verify the in_port belongs to the bridge, without returning it (so
> honoring the xport_uuid logic). Maybe this could be done in the else
> branch so to make clear we're handling the special case related to
> OFPP_{NONE,CONTROLLER}.
> 
> WDYT?

It seems like there should be a better generic solution for all this,
but sounds OK.

> 
>> Best regards, Ilya Maximets.
>
diff mbox series

Patch

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index c01177718..3509cc73c 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1533,8 +1533,15 @@  xlate_lookup_ofproto_(const struct dpif_backer *backer,
 
         ofp_port_t in_port = recirc_id_node->state.metadata.in_port;
         if (in_port != OFPP_NONE && in_port != OFPP_CONTROLLER) {
-            struct uuid xport_uuid = recirc_id_node->state.xport_uuid;
-            xport = xport_lookup_by_uuid(xcfg, &xport_uuid);
+            if (uuid_is_zero(&recirc_id_node->state.xport_uuid)) {
+                const struct xbridge *bridge =
+                    xbridge_lookup_by_uuid(xcfg, &recirc_id_node->state.ofproto_uuid);
+                xport = bridge ? get_ofp_port(bridge, in_port) : NULL;
+            } else {
+                struct uuid xport_uuid = recirc_id_node->state.xport_uuid;
+                xport = xport_lookup_by_uuid(xcfg, &xport_uuid);
+            }
+
             if (xport && xport->xbridge && xport->xbridge->ofproto) {
                 goto out;
             }
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 6824ce0bb..8b9447c74 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -5854,6 +5854,40 @@  OVS_WAIT_UNTIL([check_flows], [ovs-ofctl dump-flows br0])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+# Checks for regression against a bug in which OVS dropped packets
+# originating from the the controller passing through a patch port
+AT_SETUP([ofproto-dpif - packet-out recirculation OFPP_CONTROLLER and patch port])
+OVS_VSWITCHD_START(
+    [add-port br0 patch-br1 -- \
+     set interface patch-br1 type=patch options:peer=patch-br0 -- \
+     add-br br1 -- set bridge br1 datapath-type=dummy fail-mode=secure -- \
+     add-port br1 patch-br0 -- set interface patch-br0 type=patch options:peer=patch-br1
+])
+
+add_of_ports --pcap br1 1
+
+AT_DATA([flows-br0.txt], [dnl
+table=0 icmp actions=output:patch-br1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows-br0.txt])
+
+AT_DATA([flows-br1.txt], [dnl
+table=0, icmp actions=ct(table=1,zone=1)
+table=1, ct_state=+trk, icmp actions=p1
+])
+AT_CHECK([ovs-ofctl add-flows br1 flows-br1.txt])
+
+packet=50540000000750540000000508004500005c000000008001b94dc0a80001c0a80002080013fc00000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
+AT_CHECK([ovs-ofctl packet-out br0 "in_port=controller packet=$packet actions=table"])
+
+OVS_WAIT_UNTIL_EQUAL([ovs-ofctl dump-flows -m br1 | grep "ct_state" | ofctl_strip], [dnl
+ table=1, n_packets=1, n_bytes=106, ct_state=+trk,icmp actions=output:2])
+
+OVS_WAIT_UNTIL([ovs-pcap p1-tx.pcap | grep -q "$packet"])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - debug_slow action])
 OVS_VSWITCHD_START
 add_of_ports br0 1 2 3