On Wed, Feb 18, 2026 at 10:49 AM Dumitru Ceara via dev < [email protected]> wrote:
> The default values didn't allow dual-stack deployments. Moreover, they > made very specific assumptions and would likely be overridden by the CMS > in all deployments. > > Instead make them mandatory if dynamic-routing-vni is configured for an > EVPN enabled logical switch. > > Also: > - made sure dynamic-routing-*-ifname are ignored if VNI not set (this is > what our docs said) > - moved the man page description of the options in the right place, in a > group listing all EVPN options > > Reported-at: https://issues.redhat.com/browse/FDP-3136 > Signed-off-by: Dumitru Ceara <[email protected]> > --- > NOTE: As discussed on-list [0] this patch targets 26.03 (main branch > before hard freeze). > > [0] > https://mail.openvswitch.org/pipermail/ovs-dev/2026-February/430230.html > --- > controller/neighbor.c | 67 ++++++++++++----------- > northd/en-datapath-logical-switch.c | 85 +++++++++++++++++------------ > ovn-nb.xml | 62 +++++++++++++-------- > tests/multinode.at | 14 +++-- > tests/ovn.at | 82 ++++++++++++++++++++++++++++ > tests/system-common-macros.at | 9 +-- > 6 files changed, 217 insertions(+), 102 deletions(-) > > diff --git a/controller/neighbor.c b/controller/neighbor.c > index eae5eadb24..57df1e90c9 100644 > --- a/controller/neighbor.c > +++ b/controller/neighbor.c > @@ -28,12 +28,6 @@ > > VLOG_DEFINE_THIS_MODULE(neighbor); > > -static const char *neighbor_interface_prefixes[] = { > - [NEIGH_IFACE_BRIDGE] = "br-", > - [NEIGH_IFACE_VXLAN] = "vxlan-", > - [NEIGH_IFACE_LOOPBACK] = "lo-", > -}; > - > static const char *neighbor_opt_name[] = { > [NEIGH_IFACE_BRIDGE] = "dynamic-routing-bridge-ifname", > [NEIGH_IFACE_VXLAN] = "dynamic-routing-vxlan-ifname", > @@ -87,17 +81,16 @@ advertise_neigh_find(const struct hmap *neighbors, > struct eth_addr mac, > > static void > neigh_parse_device_name(struct sset *device_names, struct local_datapath > *ld, > - enum neighbor_interface_type type, uint32_t vni) > + enum neighbor_interface_type type) > { > const char *names = smap_get_def(&ld->datapath->external_ids, > neighbor_opt_name[type], ""); > sset_from_delimited_string(device_names, names, ","); > if (sset_is_empty(device_names)) { > - /* Default device name if not specified. */ > - char if_name[IFNAMSIZ + 1]; > - snprintf(if_name, sizeof if_name, "%s%"PRIu32, > - neighbor_interface_prefixes[type], vni); > - sset_add(device_names, if_name); > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > + VLOG_WARN_RL(&rl, "Datapath "UUID_FMT" misses %s", > + UUID_ARGS(&ld->datapath->header_.uuid), > + neighbor_opt_name[type]); > } > } > > @@ -123,7 +116,7 @@ neighbor_run(struct neighbor_ctx_in *n_ctx_in, > } > > struct sset device_names; > - neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_VXLAN, > vni); > + neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_VXLAN); > const char *name; > SSET_FOR_EACH (name, &device_names) { > struct neighbor_interface_monitor *vxlan = > @@ -133,49 +126,57 @@ neighbor_run(struct neighbor_ctx_in *n_ctx_in, > } > sset_destroy(&device_names); > > - neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_LOOPBACK, > vni); > + struct neighbor_interface_monitor *lo = NULL; > + neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_LOOPBACK); > if (sset_count(&device_names) > 1) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > VLOG_WARN_RL(&rl, "Datapath "UUID_FMT" too many names > provided " > "for loopback device", > UUID_ARGS(&ld->datapath->header_.uuid)); > } > - struct neighbor_interface_monitor *lo = > - neighbor_interface_monitor_alloc(NEIGH_AF_BRIDGE, > - NEIGH_IFACE_LOOPBACK, vni, > - SSET_FIRST(&device_names)); > - vector_push(n_ctx_out->monitored_interfaces, &lo); > + > + if (!sset_is_empty(&device_names)) { > + lo = neighbor_interface_monitor_alloc(NEIGH_AF_BRIDGE, > + NEIGH_IFACE_LOOPBACK, > vni, > + > SSET_FIRST(&device_names)); > + vector_push(n_ctx_out->monitored_interfaces, &lo); > + } > sset_destroy(&device_names); > > - neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_BRIDGE, > vni); > + struct neighbor_interface_monitor *br_v4 = NULL; > + struct neighbor_interface_monitor *br_v6 = NULL; > + neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_BRIDGE); > if (sset_count(&device_names) > 1) { > static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > VLOG_WARN_RL(&rl, "Datapath "UUID_FMT" too many names > provided " > "for bridge device", > UUID_ARGS(&ld->datapath->header_.uuid)); > } > - struct neighbor_interface_monitor *br_v4 = > - neighbor_interface_monitor_alloc(NEIGH_AF_INET, > - NEIGH_IFACE_BRIDGE, vni, > - SSET_FIRST(&device_names)); > - vector_push(n_ctx_out->monitored_interfaces, &br_v4); > - > - struct neighbor_interface_monitor *br_v6 = > - neighbor_interface_monitor_alloc(NEIGH_AF_INET6, > - NEIGH_IFACE_BRIDGE, vni, > - SSET_FIRST(&device_names)); > - vector_push(n_ctx_out->monitored_interfaces, &br_v6); > + > + if (!sset_is_empty(&device_names)) { > + br_v4 = > + neighbor_interface_monitor_alloc(NEIGH_AF_INET, > + NEIGH_IFACE_BRIDGE, vni, > + > SSET_FIRST(&device_names)); > + vector_push(n_ctx_out->monitored_interfaces, &br_v4); > + > + br_v6 = > + neighbor_interface_monitor_alloc(NEIGH_AF_INET6, > + NEIGH_IFACE_BRIDGE, vni, > + > SSET_FIRST(&device_names)); > + vector_push(n_ctx_out->monitored_interfaces, &br_v6); > + } > sset_destroy(&device_names); > > enum neigh_redistribute_mode mode = > parse_neigh_dynamic_redistribute(&ld->datapath->external_ids); > - if (nrm_mode_FDB_is_set(mode)) { > + if (nrm_mode_FDB_is_set(mode) && lo) { > neighbor_collect_mac_to_advertise(n_ctx_in, > &lo->announced_neighbors, > n_ctx_out->advertised_pbs, > ld->datapath); > } > - if (nrm_mode_IP_is_set(mode)) { > + if (nrm_mode_IP_is_set(mode) && br_v4 && br_v6) { > neighbor_collect_ip_mac_to_advertise(n_ctx_in, > > &br_v4->announced_neighbors, > > &br_v6->announced_neighbors, > diff --git a/northd/en-datapath-logical-switch.c > b/northd/en-datapath-logical-switch.c > index d37dfa2bd1..ef4c95d28c 100644 > --- a/northd/en-datapath-logical-switch.c > +++ b/northd/en-datapath-logical-switch.c > @@ -59,6 +59,20 @@ get_requested_tunnel_key(const struct > nbrec_logical_switch *nbs, > return requested_tunnel_key; > } > > +static const char * > +get_dynamic_device_name(const struct nbrec_logical_switch *nbs, > + const char *dynamic_routing_option) > +{ > + const char *ifname = smap_get(&nbs->other_config, > dynamic_routing_option); > + > + if (!ifname) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); > + VLOG_WARN_RL(&rl, "Missing %s for datapath %s", > + dynamic_routing_option, nbs->name); > + } > + return ifname; > +} > + > static bool > check_dynamic_device_name(const char *dynamic_routing_option, > const char *if_name) > @@ -125,46 +139,47 @@ gather_external_ids(const struct > nbrec_logical_switch *nbs, > const char *vni = smap_get(&nbs->other_config, "dynamic-routing-vni"); > if (vni) { > smap_add(external_ids, "dynamic-routing-vni", vni); > - } > > - const char *bridge_ifname = smap_get(&nbs->other_config, > - "dynamic-routing-bridge-ifname"); > - if (bridge_ifname && > - check_dynamic_device_name("dynamic-routing-bridge-ifname", > - bridge_ifname)) { > - smap_add(external_ids, "dynamic-routing-bridge-ifname", > - bridge_ifname); > - } > + const char *bridge_ifname = > + get_dynamic_device_name(nbs, "dynamic-routing-bridge-ifname"); > + if (bridge_ifname && > + check_dynamic_device_name("dynamic-routing-bridge-ifname", > + bridge_ifname)) { > + smap_add(external_ids, "dynamic-routing-bridge-ifname", > + bridge_ifname); > + } > > - const char *vxlan_ifnames = smap_get(&nbs->other_config, > - "dynamic-routing-vxlan-ifname"); > - if (vxlan_ifnames && > - check_dynamic_device_names("dynamic-routing-vxlan-ifname", > - vxlan_ifnames)) { > - smap_add(external_ids, "dynamic-routing-vxlan-ifname", > - vxlan_ifnames); > - } > + const char *vxlan_ifnames = > + get_dynamic_device_name(nbs, "dynamic-routing-vxlan-ifname"); > + if (vxlan_ifnames && > + check_dynamic_device_names("dynamic-routing-vxlan-ifname", > + vxlan_ifnames)) { > + smap_add(external_ids, "dynamic-routing-vxlan-ifname", > + vxlan_ifnames); > + } > > - const char *adv_ifname = smap_get(&nbs->other_config, > - "dynamic-routing-advertise-ifname"); > - if (adv_ifname && > - check_dynamic_device_name("dynamic-routing-advertise-ifname", > - adv_ifname)) { > - smap_add(external_ids, "dynamic-routing-advertise-ifname", > - adv_ifname); > - } > + const char *adv_ifname = > + get_dynamic_device_name(nbs, > "dynamic-routing-advertise-ifname"); > + if (adv_ifname && > + check_dynamic_device_name("dynamic-routing-advertise-ifname", > + adv_ifname)) { > + smap_add(external_ids, "dynamic-routing-advertise-ifname", > + adv_ifname); > + } > > - const char *redistribute = > - smap_get(&nbs->other_config, "dynamic-routing-redistribute"); > - if (redistribute) { > - smap_add(external_ids, "dynamic-routing-redistribute", > redistribute); > - } > + const char *redistribute = > + smap_get(&nbs->other_config, "dynamic-routing-redistribute"); > + if (redistribute) { > + smap_add(external_ids, "dynamic-routing-redistribute", > + redistribute); > + } > > - const char *prefer_evpn_arp_local = > - smap_get(&nbs->other_config, "dynamic-routing-arp-prefer-local"); > - if (prefer_evpn_arp_local) { > - smap_add(external_ids, "dynamic-routing-arp-prefer-local", > - prefer_evpn_arp_local); > + const char *prefer_evpn_arp_local = > + smap_get(&nbs->other_config, > "dynamic-routing-arp-prefer-local"); > + if (prefer_evpn_arp_local) { > + smap_add(external_ids, "dynamic-routing-arp-prefer-local", > + prefer_evpn_arp_local); > + } > } > > /* For backwards-compatibility, also store the NB UUID in > diff --git a/ovn-nb.xml b/ovn-nb.xml > index ae37d03d15..d4cef12e04 100644 > --- a/ovn-nb.xml > +++ b/ovn-nb.xml > @@ -795,27 +795,6 @@ > <column name="external_ids" key="neutron:network_name"> > Another name for the logical switch. > </column> > - > - <column name="other_config" key="dynamic-routing-bridge-ifname"> > - Set the interface name for the bridge used for EVPN integration. > - The default name is <code>br-$vni</code>. > - Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > - table="Logical_switch"/> is set to valid VNI. > - </column> > - > - <column name="other_config" key="dynamic-routing-vxlan-ifname"> > - List of interface names used for the vxlan devices used for EVPN > - integration. The default name is <code>vxlan-$vni</code>. > - Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > - table="Logical_switch"/> is set to valid VNI. > - </column> > - > - <column name="other_config" key="dynamic-routing-advertise-ifname"> > - Set the interface name for the advertise device used for EVPN > - integration. The default name is <code>lo-$vni</code>. > - Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > - table="Logical_switch"/> is set to valid VNI. > - </column> > </group> > > <group title="IP Address Assignment"> > @@ -919,6 +898,16 @@ > dropped, even if use_ct_inv_match is set to true. > Default: <code>false</code>. > </column> > + </group> > + > + <group title="EVPN Options"> > + <p> > + These options control the EVPN configuration of the logical > switch. > + To enable EVPN integration set > + <ref column="other_config" key="dynamic-routing-vni"/> to a valid > + VNI number to represent the EVPN L2 domain extended by the > + logical switch. > + </p> > > <column name="other_config" key="dynamic-routing-vni" > type='{"type": "integer", "minInteger": 0, > @@ -929,9 +918,14 @@ > </p> > > <p> > - The ovn-controller expects three interfaces to exist within the > - BGP vrf: <code>br-$vni</code>, <code>lo-$vni</code> and > - <code>vxlan-$vni</code>. > + All of the following configuration options must also be provided > + in order for the configuration to be valid: > + <ref column="other_config" key="dynamic-routing-bridge-ifname" > + table="Logical_switch"/>, > + <ref column="other_config" key="dynamic-routing-vxlan-ifname" > + table="Logical_switch"/>, > + <ref column="other_config" > key="dynamic-routing-advertise-ifname" > + table="Logical_switch"/>, > </p> > > <p> > @@ -940,6 +934,26 @@ > </p> > </column> > > + <column name="other_config" key="dynamic-routing-bridge-ifname"> > + Set the interface name for the bridge used for EVPN integration. > + Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > + table="Logical_switch"/> is set to valid VNI. > + </column> > + > + <column name="other_config" key="dynamic-routing-vxlan-ifname"> > + List of interface names used for the vxlan devices used for EVPN > + integration. > + Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > + table="Logical_switch"/> is set to valid VNI. > + </column> > + > + <column name="other_config" key="dynamic-routing-advertise-ifname"> > + Set the interface name for the advertise device used for EVPN > + integration. > + Only relevant if <ref column="other_config" > key="dynamic-routing-vni" > + table="Logical_switch"/> is set to valid VNI. > + </column> > + > <column name="other_config" key="dynamic-routing-fdb-prefer-local" > type='{"type": "boolean"}'> > <p> > diff --git a/tests/multinode.at b/tests/multinode.at > index b5331af247..e02bd6f074 100644 > --- a/tests/multinode.at > +++ b/tests/multinode.at > @@ -3515,8 +3515,11 @@ m_wait_for_ports_up > > # Enable EVPN support for the distributed logical switch and redistribute > # local FDBs. > -check multinode_nbctl set logical_switch ls \ > - other_config:dynamic-routing-vni=$vni \ > +check multinode_nbctl set logical_switch ls \ > + other_config:dynamic-routing-vni=$vni \ > + other_config:dynamic-routing-bridge-ifname=br-$vni \ > + other_config:dynamic-routing-vxlan-ifname=vxlan-$vni \ > + other_config:dynamic-routing-advertise-ifname=lo-$vni \ > other_config:dynamic-routing-redistribute=fdb,ip > check multinode_nbctl --wait=hv sync > dp_key=$(m_fetch_column Datapath_Binding tunnel_key external_ids:name=ls) > @@ -4336,8 +4339,11 @@ check m_as ovn-gw-2 /data/create_fake_vm.sh w2 w2 \ > m_wait_for_ports_up > > # Enable EVPN support for the distributed logical switch. > -check multinode_nbctl set logical_switch ls \ > - other_config:dynamic-routing-vni=$vni \ > +check multinode_nbctl set logical_switch ls \ > + other_config:dynamic-routing-vni=$vni \ > + other_config:dynamic-routing-bridge-ifname=br-$vni \ > + other_config:dynamic-routing-vxlan-ifname=vxlan-$vni \ > + other_config:dynamic-routing-advertise-ifname=lo-$vni \ > other_config:dynamic-routing-redistribute=fdb,ip > > check multinode_nbctl --wait=hv sync > diff --git a/tests/ovn.at b/tests/ovn.at > index 3a3b05ab7f..17a59eaebc 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -45700,3 +45700,85 @@ AT_CHECK([grep -q 'name="eth-unknown"' > hv1/ovn-controller.log], [1]) > OVN_CLEANUP([hv1]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([EVPN ifname configurations]) > +ovn_start > + > +net_add n1 > +sim_add hv > +ovs-vsctl add-br br-phys > +ovn_attach n1 br-phys 192.168.0.1 > + > +as northd > +check ovn-appctl -t ovn-northd vlog/set jsonrpc:info > +check ovn-appctl -t ovn-northd vlog/disable-rate-limit > +as hv > +check ovn-appctl -t ovn-controller vlog/disable-rate-limit > + > +clear_logs() { > + as northd > + check ovn-appctl -t ovn-northd vlog/close > + check rm northd/ovn-northd.log > + check ovn-appctl -t ovn-northd vlog/reopen > + > + as hv > + check ovn-appctl -t ovn-controller vlog/close > + check rm hv/ovn-controller.log > + check ovn-appctl -t ovn-controller vlog/reopen > +} > + > +check ovn-nbctl ls-add ls -- lsp-add ls lsp > +check ovs-vsctl add-port br-int lsp -- \ > + set Interface lsp external_ids:iface-id=lsp > + > +wait_for_ports_up > +check ovn-nbctl --wait=hv sync > + > +AS_BOX([Missing bridge ifname]) > +clear_logs > +check ovn-nbctl --wait=hv > \ > + -- set logical_switch ls other_config:dynamic-routing-vni=42 > \ > + -- set logical_switch ls > other_config:dynamic-routing-vxlan-ifname=foo \ > + -- set logical_switch ls > other_config:dynamic-routing-advertise-ifname=bar > + > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-bridge-ifname" > northd/ovn-northd.log]) > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-bridge-ifname" > hv/ovn-controller.log]) > +check ovn-nbctl --wait=hv clear logical_switch ls other_config > + > +AS_BOX([Missing vxlan ifname]) > +clear_logs > +check ovn-nbctl --wait=hv > \ > + -- set logical_switch ls other_config:dynamic-routing-vni=42 > \ > + -- set logical_switch ls > other_config:dynamic-routing-bridge-ifname=foo \ > + -- set logical_switch ls > other_config:dynamic-routing-advertise-ifname=bar > + > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-vxlan-ifname" > northd/ovn-northd.log]) > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-vxlan-ifname" > hv/ovn-controller.log]) > +check ovn-nbctl --wait=hv clear logical_switch ls other_config > + > +AS_BOX([Missing advertise ifname]) > +clear_logs > +check ovn-nbctl --wait=hv > \ > + -- set logical_switch ls other_config:dynamic-routing-vni=42 > \ > + -- set logical_switch ls > other_config:dynamic-routing-bridge-ifname=foo \ > + -- set logical_switch ls other_config:dynamic-routing-vxlan-ifname=bar > + > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-advertise-ifname" > northd/ovn-northd.log]) > +OVS_WAIT_UNTIL([grep -q "WARN.*dynamic-routing-advertise-ifname" > hv/ovn-controller.log]) > +check ovn-nbctl --wait=hv clear logical_switch ls other_config > + > +AS_BOX([Valid config]) > +clear_logs > +check ovn-nbctl --wait=hv > \ > + -- set logical_switch ls other_config:dynamic-routing-vni=42 > \ > + -- set logical_switch ls > other_config:dynamic-routing-bridge-ifname=foo \ > + -- set logical_switch ls > other_config:dynamic-routing-vxlan-ifname=bar \ > + -- set logical_switch ls > other_config:dynamic-routing-advertise-ifname=xyz > + > +AT_CHECK([grep -q "WARN.*dynamic-routing" northd/ovn-northd.log], [1]) > +AT_CHECK([grep -q "WARN.*dynamic-routing" hv/ovn-controller.log], [1]) > + > +OVN_CLEANUP([hv]) > +AT_CLEANUP > +]) > diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at > index 0f4f8952c6..5062f02b0e 100644 > --- a/tests/system-common-macros.at > +++ b/tests/system-common-macros.at > @@ -213,12 +213,9 @@ m4_define([SET_EVPN_IFACE_NAMES], > [[ $ifname = "default" ]] && VXLAN_V6_NAME=vxlan-v6-$vni || > VXLAN_V6_NAME=vxlan-v6-$ifname > [[ $ifname = "default" ]] && LO_NAME=lo-$vni || LO_NAME=lo-$ifname > > - if [[ $ifname != "default" ]]; then > - check ovn-nbctl set logical_switch $switch \ > - other_config:dynamic-routing-bridge-ifname=$BR_NAME \ > - other_config:dynamic-routing-advertise-ifname=$LO_NAME > - fi > - check ovn-nbctl set logical_switch $switch \ > + check ovn-nbctl set logical_switch $switch \ > + other_config:dynamic-routing-bridge-ifname=$BR_NAME \ > + other_config:dynamic-routing-advertise-ifname=$LO_NAME \ > > other_config:dynamic-routing-vxlan-ifname=$VXLAN_NAME","$VXLAN_V6_NAME > > export BR_NAME VXLAN_NAME VXLAN_V6_NAME LO_NAME > -- > 2.52.0 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > Looks good to me, thanks. Acked-by: Ales Musil <[email protected]> _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
