On 13.05.2026 12:51, Dumitru Ceara wrote:
> Hi Alexandra,
>
> Thanks for the fix!

Hi Dumitru! Thanks for the review.  I'll answer below.


>
> On 5/8/26 10:00 PM, Alexandra Rukomoinikova via dev wrote:
>> ARP requests sent through localnet port from physical network are
>> processed only on chassis hosting the HA group, while requests for
>> distributed NAT are also distributed across chassis.
>>
>> Previously, the case where a VIF port exists on the same logical switch
>> connected to the physical network was not handled. Now, for each VIF port
>> on such an external switch, ARP requests are processed on the chassis that
>> hosts that port.
>>
>> Code simply iterates over all ports within the logical switch, which is
>> considered acceptable since there are typically not a lot other non-VIF
>> ports on an external switch.
>>
>> Fixed I-P: vif ports are processed incrementally, so if switch with 
>> connection
>> to the physical network was created and then a vif port was added,
>> ls_arp processing node would not work process this update.
>>
> Maybe "would not process this update" instead of "would not work..."?
>> Fixes: 1b4058b ("northd: Process external arps on ha chassis.")
> The Fixes format is not exactly correct, the correct way to format it is:
>
> git log -1 --pretty=format:"CC: %an <%ae>%nFixes: %h (\"%s\")"
> --abbrev=12 COMMIT_REF
>
> As documented in the submitting-patches.rst file.
I'll fix it) I always miss this point...🙁
>> Reported-at: https://redhat.atlassian.net/browse/FDP-3767
> Our internal automation is "special".  This should actually be:
> https://redhat.atlassian.net/browse/FDP-3774
ack
>> Signed-off-by: Alexandra Rukomoinikova <[email protected]>
>> ---
>>   northd/northd.c     | 25 ++++++++++++++++++
>>   northd/northd.h     |  6 +++++
>>   tests/ovn-northd.at | 17 +++++++-----
>>   tests/system-ovn.at | 64 +++++++++++++++++++++++++++++++++++++++++++++
>>   4 files changed, 106 insertions(+), 6 deletions(-)
>>
>> diff --git a/northd/northd.c b/northd/northd.c
>> index 6ff505ca1..f55dbc1d5 100644
>> --- a/northd/northd.c
>> +++ b/northd/northd.c
>> @@ -4812,6 +4812,10 @@ ls_handle_lsp_changes(struct ovsdb_idl_txn 
>> *ovnsb_idl_txn,
>>               struct nbrec_logical_switch_port *new_nbsp = 
>> changed_ls->ports[j];
>>               op = ovn_port_find_in_datapath(od, new_nbsp);
>>   
>> +            if (!hmapx_is_empty(&od->phys_ports)) {
>> +                goto fail;
>> +            }
>> +
> This could go outside the loop, it doesn't depend on ports[j[.
>
> However, this is a bit worrying, it will fail incremental processing for
> any LSP addition if it's part of a localnet logical switch which will
> have performance implications at scale I guess.
>
> Can't we handle the case of "changed lsp" (or new/deleted lsp) in
> ls_arp_northd_handler() too?  And then incrementally in
> lflow_handle_ls_arp_changes().
yeah, sure. I wanted to do that, but I just thought that the case where 
vif ports are connected to switch with a physical network doesn't really 
require that vif many ports, so I thought I might miss it. My mistake =)
>
>>               if (!op) {
>>                   if (!lsp_can_be_inc_processed(new_nbsp)) {
>>                       goto fail;
>> @@ -10066,6 +10070,8 @@ 
>> build_drop_arp_nd_flows_for_unbound_router_ports(struct ovn_port *op,
>>    * router connection ports that requires chassis residence.
>>    * ARP requests coming from localnet/l2gateway ports
>>    * allowed for processing on resident chassis only.
>> + * If logical switch has VIF port, ARP requests are allowed
>> + * to be processed on the chassis hosting this VIF port.
>>    */
>>   static void
>>   build_lswitch_arp_chassis_resident(const struct ovn_datapath *od,
>> @@ -10133,6 +10139,25 @@ build_lswitch_arp_chassis_resident(const struct 
>> ovn_datapath *od,
>>               }
>>           }
>>   
>> +        HMAP_FOR_EACH (op, dp_node, &od->ports) {
>> +            if (!port_is_vif(op)) {
>> +                continue;
>> +            }
>> +
>> +            for (size_t i = 0; i < op->n_lsp_addrs; i++) {
>> +                for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
>> +                    ds_clear(&match);
>> +                    ds_put_format(&match,
>> +                                  REGBIT_EXT_ARP " == 1 && arp.tpa == %s "
>> +                                  "&& is_chassis_resident(%s)",
>> +                                  op->lsp_addrs[i].ipv4_addrs[j].addr_s,
>> +                                  op->json_key);
>> +                    ovn_lflow_add(lflows, od, S_SWITCH_IN_APPLY_PORT_SEC, 
>> 85,
>> +                                  ds_cstr(&match), "next;", ar->lflow_ref);
>> +                }
>> +            }
> I'm a bit worried about these new flows.  Also about the flows we added
> for NATs in 16b79a66d2c3 ("northd: Restrict external ARP request to
> logical_ip for dnat_and_snat.") (CC Ales).
>
> We already add ARP responder flows for VIFs in table S_SWITCH_IN_ARP_ND_RSP.
>
> Shouldn't we rework the original 1b4058b ("northd: Process external arps
> on ha chassis.") and do the drop for ARPs with REGBIT_EXT_ARP == 1 in
> the switch arp responder stage?
>
> Then we wouldn't need to add another set of logical flows for each LSP
> IP.  There are a few cases we need to carefully account for then but it
> would be more scalable in my opinion.
>
> What do you think?


I like this idea, I will do it. I will try to take all cases into account.

>
>> +        }
>> +
>>           ovn_lflow_add(lflows, od, S_SWITCH_IN_APPLY_PORT_SEC, 70,
>>                         REGBIT_EXT_ARP" == 1", "drop;", ar->lflow_ref);
>>       }
>> diff --git a/northd/northd.h b/northd/northd.h
>> index 81ab07600..7092f6001 100644
>> --- a/northd/northd.h
>> +++ b/northd/northd.h
>> @@ -1178,6 +1178,12 @@ od_is_centralized(const struct ovn_datapath *od)
>>       return !od->is_distributed;
>>   }
>>   
>> +static inline bool
>> +port_is_vif(const struct ovn_port *op)
>> +{
>> +    return op->sb ? !strcmp(op->sb->type, "") : 0;
> Should be ": false" instead of ": 0".
>
> Also, I'd rely on the NB type instead of the port_binding type here.
>
> We could use this helper in a bunch of other places in northd.c where we
> check LSP type.
>
>> +}
>> +
>>   struct ovn_port *ovn_port_find(const struct hmap *ports, const char *name);
>>   
>>   void build_igmp_lflows(struct hmap *igmp_groups,
>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>> index 3f237b076..9d1d00380 100644
>> --- a/tests/ovn-northd.at
>> +++ b/tests/ovn-northd.at
>> @@ -19702,15 +19702,10 @@ check ovn-nbctl lr-add lr1
>>   check ovn-nbctl lrp-add lr1 down_link f0:00:00:00:00:f1 192.168.1.1/24
>>   
>>   check ovn-nbctl ls-add ls1
>> -check ovn-nbctl lsp-add ls1 up_link
>>   check ovn-nbctl lsp-add ls1 down_vif1
>>   check ovn-nbctl lsp-add ls1 down_vif2
>>   check ovn-nbctl lsp-add ls1 down_ext
>> -
>> -check ovn-nbctl set Logical_Switch_Port up_link \
>> -    type=router \
>> -    options:router-port=down_link \
>> -    addresses=router
>> +check ovn-nbctl lsp-add-router-port ls1 up_link down_link
>>   
>>   check ovn-nbctl lsp-set-addresses down_vif1 'f0:00:00:00:00:01 
>> 192.168.1.101'
>>   check ovn-nbctl lsp-set-addresses down_vif2 'f0:00:00:00:00:02 
>> 192.168.1.102'
>> @@ -19728,6 +19723,8 @@ AT_CHECK([ovn-sbctl lflow-list ls1 | grep 
>> ls_in_apply_port_sec | ovn_strip_lflow
>>     table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=70   , match=(reg0[[22]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=75   , match=(reg0[[22]] == 1 
>> && is_chassis_resident("cr-down_link")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.101 && is_chassis_resident("down_vif1")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.102 && is_chassis_resident("down_vif2")), action=(next;)
>>   ])
>>   
>>   # Check nat adding to dgr attached to logical switch trigger ls-arp flow.
>> @@ -19740,6 +19737,8 @@ AT_CHECK([ovn-sbctl lflow-list ls1 | grep 
>> ls_in_apply_port_sec | ovn_strip_lflow
>>     table=??(ls_in_apply_port_sec), priority=70   , match=(reg0[[22]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=75   , match=(reg0[[22]] == 1 
>> && is_chassis_resident("cr-down_link")), action=(next;)
>>     table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 
>> && arp.tpa == 192.168.0.3 && is_chassis_resident("down_vif1")), 
>> action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.101 && is_chassis_resident("down_vif1")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.102 && is_chassis_resident("down_vif2")), action=(next;)
>>   ])
>>   
>>   check ovn-nbctl --wait=sb lr-nat-del lr1 dnat_and_snat 192.168.0.3
>> @@ -19748,6 +19747,8 @@ AT_CHECK([ovn-sbctl lflow-list ls1 | grep 
>> ls_in_apply_port_sec | ovn_strip_lflow
>>     table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=70   , match=(reg0[[22]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=75   , match=(reg0[[22]] == 1 
>> && is_chassis_resident("cr-down_link")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.101 && is_chassis_resident("down_vif1")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.102 && is_chassis_resident("down_vif2")), action=(next;)
>>   ])
>>   
>>   # Check changing logical port type to l2gateway.
>> @@ -19757,6 +19758,8 @@ AT_CHECK([ovn-sbctl lflow-list ls1 | grep 
>> ls_in_apply_port_sec | ovn_strip_lflow
>>     table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=70   , match=(reg0[[22]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=75   , match=(reg0[[22]] == 1 
>> && is_chassis_resident("cr-down_link")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.101 && is_chassis_resident("down_vif1")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.102 && is_chassis_resident("down_vif2")), action=(next;)
>>   ])
>>   
>>   # Check changing logical port type to vif.
>> @@ -19773,6 +19776,8 @@ AT_CHECK([ovn-sbctl lflow-list ls1 | grep 
>> ls_in_apply_port_sec | ovn_strip_lflow
>>     table=??(ls_in_apply_port_sec), priority=50   , match=(reg0[[15]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=70   , match=(reg0[[22]] == 1), 
>> action=(drop;)
>>     table=??(ls_in_apply_port_sec), priority=75   , match=(reg0[[22]] == 1 
>> && is_chassis_resident("cr-down_link")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.101 && is_chassis_resident("down_vif1")), action=(next;)
>> +  table=??(ls_in_apply_port_sec), priority=85   , match=(reg0[[22]] == 1 && 
>> arp.tpa == 192.168.1.102 && is_chassis_resident("down_vif2")), action=(next;)
>>   ])
>>   
>>   # Check changing removing logical port.
>> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>> index 582ed194b..0648c44b6 100644
>> --- a/tests/system-ovn.at
>> +++ b/tests/system-ovn.at
>> @@ -21814,3 +21814,67 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port 
>> patch-.*/d
>>   
>>   AT_CLEANUP
>>   ])
>> +
>> +OVN_FOR_EACH_NORTHD([
>> +AT_SETUP([VIF port connected to localnet network])
>> +#
>> +# Topology:
>> +#    (fabric) -- localnet-port -- LS --- DGP(chassis2) -- LR
>> +#                                 |
>> +#                                 |
>> +#                               VM (chassis1)
>> +#
>> +It is expected that ARP requests to this port are allowed on the chesis 
>> that hosts this port.
> Missing "#".
>
> Typo: chesis
>
>> +
>> +ovn_start
>> +OVS_TRAFFIC_VSWITCHD_START()
>> +ADD_BR([br-int])
>> +ADD_BR([br-ext])
>> +
>> +ovs-ofctl add-flow br-ext action=normal
>> +# Set external-ids in br-int needed for ovn-controller
>> +ovs-vsctl \
> Missing "check".
>
>> +        -- set Open_vSwitch . external-ids:system-id=hv1 \
>> +        -- set Open_vSwitch . 
>> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
>> +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
>> +        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
>> +        -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext
> Missing "\" at end of line which means we essentially won't configure
> the line below.
>
>> +        -- set bridge br-int fail-mode=secure 
>> other-config:disable-in-band=true
>> +
>> +# Start ovn-controller
>> +start_daemon ovn-controller
>> +
>> +check ovn-nbctl lr-add lr1
>> +check ovn-nbctl ls-add public
>> +
>> +check ovn-nbctl lrp-add lr1 rp-public 00:00:02:01:02:03 172.31.1.1/24
>> +check ovn-nbctl lsp-add-router-port public public-rp rp-public
>> +check ovn-nbctl lsp-add-localnet-port public localnet phynet
>> +check ovn-nbctl lrp-set-gateway-chassis rp-public hv2
>> +check ovn-nbctl --wait=hv sync
>> +
>> +ADD_NAMESPACES(ext)
>> +ADD_VETH(ext, ext, br-ext, "172.31.1.3/24", "f0:00:00:01:02:04", \
>> +         "172.31.1.1")
>> +
>> +ADD_NAMESPACES(lsp)
>> +ADD_VETH(lsp, lsp, br-int, "172.31.1.2/24", "f0:00:00:01:02:03", \
>> +         "172.31.1.1")
>> +
>> +check ovn-nbctl lsp-add public lsp
>> +check ovn-nbctl lsp-set-addresses lsp "f0:00:00:01:02:03 172.31.1.2"
>> +check ovn-nbctl --wait=hv sync
>> +
>> +NS_CHECK_EXEC([ext], [ping -q -c 3 -i 0.3 -w 2 172.31.1.2 | FORMAT_PING], \
>> +[0], [dnl
>> +3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> +])
>> +
>> +OVN_CLEANUP_CONTROLLER([hv1])
>> +OVN_CLEANUP_NORTHD
>> +as
>> +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
>> +/connection dropped.*/d"])
>> +
>> +AT_CLEANUP
>> +])
> Regards,
> Dumitru
>

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to