> 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
