> Extend physical_consider_evpn_arp() to also install flows in
> the dedicated EVPN ARP side table (OFTABLE_EVPN_ARP_LOOKUP,
> table 113) for the switch datapath.  These flows are matched
> by the chk_evpn_arp() action added in the previous commit.
> 
> Each flow matches on metadata (switch dp_key) and IP address
> (reg0 for IPv4, xxreg0 for IPv6).  On a hit, it loads the
> resolved MAC into eth.dst and sets MLF_EVPN_LOOKUP_BIT.
> 
> The flows use the same UUID as the router-side neighbor flows,
> so ofctrl_remove_flows() in physical_handle_evpn_arp_changes()
> automatically removes both router-side and switch-side flows
> when an EVPN ARP entry is deleted.
> 
> Assisted-by: Claude Opus 4.6, Claude Code
> Signed-off-by: Ales Musil <[email protected]>

Acked-by: Lorenzo Bianconi <[email protected]>

> ---
>  controller/physical.c | 44 +++++++++++++++++++++++++++++--
>  tests/system-ovn.at   | 60 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 102 insertions(+), 2 deletions(-)
> 
> diff --git a/controller/physical.c b/controller/physical.c
> index 7584d6065..89ff2785e 100644
> --- a/controller/physical.c
> +++ b/controller/physical.c
> @@ -3689,6 +3689,8 @@ physical_consider_evpn_fdb(const struct evpn_fdb *fdb,
>  static void
>  physical_consider_evpn_arp(const struct hmap *local_datapaths,
>                             const struct evpn_arp *arp,
> +                           struct ofpbuf *ofpacts,
> +                           struct match *match,
>                             struct ovn_desired_flow_table *flow_table)
>  {
>      /* Walk connected OVN routers and install neighbor flows for the ARPs
> @@ -3706,6 +3708,38 @@ physical_consider_evpn_arp(const struct hmap 
> *local_datapaths,
>          consider_neighbor_flow(remote_pb, &arp->flow_uuid, &arp->ip, 
> arp->mac,
>                                 flow_table, arp->priority, false);
>      }
> +
> +    /* Install EVPN ARP lookup flows in the dedicated side table for the
> +     * switch datapath.  These flows are matched by the chk_evpn_arp()
> +     * action.  On a hit they load the resolved MAC into eth.dst
> +     * and set MLF_EVPN_LOOKUP_BIT. */
> +    const struct in6_addr *ip = &arp->ip;
> +    struct eth_addr mac = arp->mac;
> +    const struct local_datapath *ldp = arp->ldp;
> +
> +    match_init_catchall(match);
> +    match_set_metadata(match, htonll(ldp->datapath->tunnel_key));
> +
> +    if (IN6_IS_ADDR_V4MAPPED(ip)) {
> +        ovs_be32 ip4 = in6_addr_get_mapped_ipv4(ip);
> +        match_set_reg(match, 0, ntohl(ip4));
> +    } else {
> +        ovs_be128 value;
> +        memcpy(&value, ip, sizeof(value));
> +        match_set_xxreg(match, 0, ntoh128(value));
> +    }
> +
> +    ofpbuf_clear(ofpacts);
> +
> +    /* Load resolved MAC into eth.dst. */
> +    put_load_bytes(mac.ea, sizeof mac.ea,
> +                   MFF_ETH_DST, 0, 48, ofpacts);
> +    /* Set the EVPN lookup result bit. */
> +    put_load(1, MFF_LOG_FLAGS, MLF_EVPN_LOOKUP_BIT, 1, ofpacts);
> +
> +    ofctrl_add_flow(flow_table, OFTABLE_EVPN_ARP_LOOKUP, arp->priority,
> +                    arp->flow_uuid.parts[0], match, ofpacts,
> +                    &arp->flow_uuid);
>  }
>  
>  static void
> @@ -3753,7 +3787,8 @@ physical_eval_evpn_flows(const struct physical_ctx *ctx,
>  
>      const struct evpn_arp *arp;
>      HMAP_FOR_EACH (arp, hmap_node, ctx->evpn_arps) {
> -        physical_consider_evpn_arp(ctx->local_datapaths, arp, flow_table);
> +        physical_consider_evpn_arp(ctx->local_datapaths, arp, ofpacts,
> +                                   &match, flow_table);
>      }
>  }
>  
> @@ -3967,14 +4002,19 @@ physical_handle_evpn_arp_changes(const struct hmap 
> *local_datapaths,
>                                   const struct hmapx *updated_arps,
>                                   const struct uuidset *removed_arps)
>  {
> +    struct ofpbuf ofpacts;
> +    ofpbuf_init(&ofpacts, 0);
> +    struct match match = MATCH_CATCHALL_INITIALIZER;
>  
>      const struct hmapx_node *node;
>      HMAPX_FOR_EACH (node, updated_arps) {
>          const struct evpn_arp *arp = node->data;
>  
>          ofctrl_remove_flows(flow_table, &arp->flow_uuid);
> -        physical_consider_evpn_arp(local_datapaths, arp, flow_table);
> +        physical_consider_evpn_arp(local_datapaths, arp, &ofpacts,
> +                                   &match, flow_table);
>      }
> +    ofpbuf_uninit(&ofpacts);
>  
>      const struct uuidset_node *uuidset_node;
>      UUIDSET_FOR_EACH (uuidset_node, removed_arps) {
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 35df0ec2f..1f0f17cb7 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -18528,6 +18528,66 @@ ovn-appctl evpn/vtep-arp-list > arp_after
>  
>  check diff -q arp_before arp_after
>  
> +AS_BOX([EVPN ARP/ND side table flows])
> +dnl Verify that EVPN ARP entries also produce flows in the dedicated
> +dnl side table (OFTABLE_EVPN_ARP_LOOKUP) for the switch datapath.
> +dnl These flows match on metadata+IP and load the resolved MAC into
> +dnl eth.dst while setting the EVPN lookup bit (reg10[25]).
> +dnl Verify we have 6 flows: 3 IPv4 + 3 IPv6.
> +AT_CHECK([test "$(ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP 
> | \
> +                  grep -c priority)" = "6"])
> +
> +dnl Verify each IPv4 EVPN entry has a side table flow.
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0xac100132" | grep -q "metadata=0x$dp_key"])
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0xac10013c" | grep -q "metadata=0x$dp_key"])
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0xac100146" | grep -q "metadata=0x$dp_key"])
> +
> +dnl Verify each IPv6 EVPN entry has a side table flow.
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0x1720016" | grep "reg3=0x50" | grep -q 
> "metadata=0x$dp_key"])
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0x1720016" | grep "reg3=0x60" | grep -q 
> "metadata=0x$dp_key"])
> +AT_CHECK([ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +          grep "reg0=0x1720016" | grep "reg3=0x70" | grep -q 
> "metadata=0x$dp_key"])
> +
> +dnl Verify recompute stability for side table flows.
> +ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +    grep priority | awk '{print $[7], $[8]}' | sort > evpn_side_before
> +
> +check ovn-appctl inc-engine/recompute
> +check ovn-nbctl --wait=hv sync
> +
> +ovs-ofctl dump-flows br-int table=OFTABLE_EVPN_ARP_LOOKUP | \
> +    grep priority | awk '{print $[7], $[8]}' | sort > evpn_side_after
> +AT_CHECK([diff -q evpn_side_before evpn_side_after])
> +
> +dnl Verify side table flows are removed when EVPN entries are removed.
> +check ip neigh del dev $BR_NAME 172.16.1.50
> +check ip neigh del dev $BR_NAME 172.16.1.60
> +check ip neigh del dev $BR_NAME 172.16.1.70
> +
> +check ip -6 neigh del dev $BR_NAME 172:16::50
> +check ip -6 neigh del dev $BR_NAME 172:16::60
> +check ip -6 neigh del dev $BR_NAME 172:16::70
> +
> +OVS_WAIT_UNTIL([test "$(ovs-ofctl dump-flows br-int 
> table=OFTABLE_EVPN_ARP_LOOKUP | \
> +                         grep -c priority)" = "0"])
> +
> +dnl Re-add the EVPN entries for subsequent tests.
> +check ip neigh add dev $BR_NAME 172.16.1.50 lladdr f0:00:0f:16:10:50 nud 
> noarp extern_learn
> +check ip neigh add dev $BR_NAME 172.16.1.60 lladdr f0:00:0f:16:10:60 nud 
> noarp extern_learn
> +check ip neigh add dev $BR_NAME 172.16.1.70 lladdr f0:00:0f:16:10:70 nud 
> noarp extern_learn
> +
> +check ip -6 neigh add dev $BR_NAME 172:16::50 lladdr f0:00:0f:16:10:50 nud 
> noarp extern_learn
> +check ip -6 neigh add dev $BR_NAME 172:16::60 lladdr f0:00:0f:16:10:60 nud 
> noarp extern_learn
> +check ip -6 neigh add dev $BR_NAME 172:16::70 lladdr f0:00:0f:16:10:70 nud 
> noarp extern_learn
> +
> +OVS_WAIT_UNTIL([test "$(ovs-ofctl dump-flows br-int 
> table=OFTABLE_EVPN_ARP_LOOKUP | \
> +                         grep -c priority)" = "6"])
> +
>  # Remove remote workload ARP entries and check ovn-controller's state.
>  check ip neigh del dev $BR_NAME 172.16.1.50
>  check ip neigh del dev $BR_NAME 172.16.1.60
> -- 
> 2.54.0
> 
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> 
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to