Hi Lorenzo, Thanks for the new revision!
On 12/19/25 3:35 PM, Lorenzo Bianconi wrote: > Introduce the capability to specify multiple ips for ovn-evpn-local-ip > option in order to enable dual-stack for EVPN vxlan tunnels. > The IPs used for vxlan tunnel can be specified using the following > syntax in the external_ids column of Open_vSwitch table: > > external_ids:ovn-evpn-local-ip=10:1.1.1.1,20:[2::2],30:3.3.3.3,40:[4::4],30:[3::3] This is not accurate anymore, it should be ovn-evpn-local-ip-mapping but please see below. > > Reported-at: https://issues.redhat.com/browse/FDP-2768 > Signed-off-by: Lorenzo Bianconi <[email protected]> > --- > Changes in v4: > - Do not use linear lookup in evpn_local_ip_lookup_by_vni() > - Introduce ovn-evpn-local-ip-mapping config option I know we had discussed it on the v3 patch but, probably because I was off for a few weeks :), I changed my mind, I'm sorry. It's a bit annoying that we had to add a new option for the mapping. What if we go back to a single option, ovn-evpn-local-ip but use a different separator, e.g.: ovn-evpn-local-ip=<VNI1>-<IP4_1>,<VNI1>-<IP6_1>,<VNI2>-<IP4_1>,<DEF_IP4>,<DEF_IP6> Some examples: 10-1.1.1.1,20-2.2.2.2,30-3.3.3.3,10-1::1,20-2::2,5::5 yields: - VNI 10: 1.1.1.1 and 1::1 - VNI 20: 2.2.2.2 and 2::2 - VNI 30: 3.3.3.3 (no IPv6 local IP) - default-v4: not present - default-v6: 5::5 10-1.1.1.1,5.5.5.5,20-2.2.2.2,5::5,10-1::1,20-2::2 yields: - VNI 10: 1.1.1.1 and 1::1 - VNI 20: 2.2.2.2 and 2::2 - default-v4: 5.5.5.5 - default-v6: 5::5 So the parsing would be: - split by ',' - for each token: - split by '-': - if only one token results then this is a default IP (v4 or v6) - if two token result then the first token should be parsed as VNI and the second one as an IP (v4 or v6) - if more tokens result then report an error What do you think? > Changes in v3: > - Add default IP configuration > - Fix possible parsing crashes in evpn_vni_local_ip_map_alloc() > - Use hashmap for vni_local_ip mapping > Changes in v2: > - Add NEWS entry > - Update documentation > - Add ip_address_from_str utility routine > - Fix IPv4 vs IPv6 openflow management in physical_consider_evpn_multicast() > - Add Dual-Stack entry in EVPN_SWITCH_TESTS function > --- > NEWS | 2 + > TODO.rst | 4 - > controller/chassis.c | 21 ++ > controller/neighbor.c | 5 +- > controller/ovn-controller.8.xml | 20 +- > controller/physical.c | 232 ++++++++++--- > lib/ovn-util.c | 23 ++ > lib/ovn-util.h | 2 + > tests/system-common-macros.at | 14 +- > tests/system-ovn.at | 596 +++++++++++++++++++++++++++++++- > 10 files changed, 847 insertions(+), 72 deletions(-) > > diff --git a/NEWS b/NEWS > index 87500de03..9883fb81d 100644 > --- a/NEWS > +++ b/NEWS > @@ -80,6 +80,8 @@ Post v25.09.0 > - Add fallback support for Network Function. > - Introduce the capability to specify EVPN device names using > Logical_Switch > other_config column. > + - Introduce the capability to specify multiple ips for ovn-evpn-local-ip > + option. Same here, this is now inaccurate. > > OVN v25.09.0 - xxx xx xxxx > -------------------------- > diff --git a/TODO.rst b/TODO.rst > index 0c9b9598b..9f5e0976d 100644 > --- a/TODO.rst > +++ b/TODO.rst > @@ -156,10 +156,6 @@ OVN To-do List > Otherwise we could try to add duplicated Learned_Routes and the ovnsb > commit would fail. > > - * Allow ovn-evpn-local-ip to accept list of > - $VNI1:$LOCAL_IP1,$VNI2:$LOCAL_IP2 combinations which will be properly > - reflected in physical flows for given LS with VNI. > - > * Add support for EVPN L3, that involves MAC Binding learning and > advertisement. > > diff --git a/controller/chassis.c b/controller/chassis.c > index 4afb6da93..b152fabc5 100644 > --- a/controller/chassis.c > +++ b/controller/chassis.c > @@ -60,6 +60,7 @@ struct ovs_chassis_cfg { > const char *trim_timeout_ms; > const char *evpn_vxlan_port; > const char *evpn_local_ip; > + const char *evpn_local_ip_mapping; > > /* Set of encap types parsed from the 'ovn-encap-type' external-id. */ > struct sset encap_type_set; > @@ -220,6 +221,13 @@ get_evpn_local_ip(const struct smap *ext_ids, const char > *chassis_id) > "ovn-evpn-local-ip", ""); > } > > +static const char * > +get_evpn_local_ip_mapping(const struct smap *ext_ids, const char *chassis_id) > +{ > + return get_chassis_external_id_value(ext_ids, chassis_id, > + "ovn-evpn-local-ip-mapping", ""); > +} > + > static const char * > get_datapath_type(const struct ovsrec_bridge *br_int) > { > @@ -358,6 +366,8 @@ chassis_parse_ovs_config(const struct > ovsrec_open_vswitch_table *ovs_table, > get_evpn_vxlan_port(&cfg->external_ids, chassis_id); > ovs_cfg->evpn_local_ip = > get_evpn_local_ip(&cfg->external_ids, chassis_id); > + ovs_cfg->evpn_local_ip_mapping = > + get_evpn_local_ip_mapping(&cfg->external_ids, chassis_id); > > chassis_parse_ovs_encap_type(encap_type, &ovs_cfg->encap_type_set); > > @@ -412,6 +422,8 @@ chassis_build_other_config(const struct ovs_chassis_cfg > *ovs_cfg, > smap_replace(config, "ovn-chassis-mac-mappings", ovs_cfg->chassis_macs); > smap_replace(config, "ovn-evpn-vxlan-ports", ovs_cfg->evpn_vxlan_port); > smap_replace(config, "ovn-evpn-local-ip", ovs_cfg->evpn_local_ip); > + smap_replace(config, "ovn-evpn-local-ip-mapping", > + ovs_cfg->evpn_local_ip_mapping); > smap_replace(config, "is-interconn", > ovs_cfg->is_interconn ? "true" : "false"); > smap_replace(config, OVN_FEATURE_PORT_UP_NOTIF, "true"); > @@ -544,6 +556,14 @@ chassis_other_config_changed(const struct > ovs_chassis_cfg *ovs_cfg, > return true; > } > > + const char *chassis_evpn_local_ip_mapping = > + get_evpn_local_ip_mapping(&chassis_rec->other_config, > + chassis_rec->name); > + if (strcmp(ovs_cfg->evpn_local_ip_mapping, > + chassis_evpn_local_ip_mapping)) { > + return true; > + } > + > if (!smap_get_bool(&chassis_rec->other_config, OVN_FEATURE_PORT_UP_NOTIF, > false)) { > return true; > @@ -787,6 +807,7 @@ update_supported_sset(struct sset *supported) > sset_add(supported, "is-interconn"); > sset_add(supported, "ovn-evpn-vxlan-ports"); > sset_add(supported, "ovn-evpn-local-ip"); > + sset_add(supported, "ovn-evpn-local-ip-mapping"); > > /* Internal options. */ > sset_add(supported, "is-vtep"); > diff --git a/controller/neighbor.c b/controller/neighbor.c > index 9aeb1e36b..e77b41d29 100644 > --- a/controller/neighbor.c > +++ b/controller/neighbor.c > @@ -91,7 +91,6 @@ neigh_parse_device_name(struct sset *device_names, struct > local_datapath *ld, > { > const char *names = smap_get_def(&ld->datapath->external_ids, > neighbor_opt_name[type], ""); > - sset_clear(device_names); > sset_from_delimited_string(device_names, names, ","); > if (sset_is_empty(device_names)) { > /* Default device name if not specified. */ > @@ -123,7 +122,7 @@ neighbor_run(struct neighbor_ctx_in *n_ctx_in, > continue; > } > > - struct sset device_names = SSET_INITIALIZER(&device_names); > + struct sset device_names; > neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_VXLAN, vni); > const char *name; > SSET_FOR_EACH (name, &device_names) { > @@ -132,6 +131,7 @@ neighbor_run(struct neighbor_ctx_in *n_ctx_in, > NEIGH_IFACE_VXLAN, vni, > name); > vector_push(n_ctx_out->monitored_interfaces, &vxlan); > } > + sset_destroy(&device_names); > > neigh_parse_device_name(&device_names, ld, NEIGH_IFACE_LOOPBACK, > vni); > if (sset_count(&device_names) > 1) { > @@ -145,6 +145,7 @@ neighbor_run(struct neighbor_ctx_in *n_ctx_in, > 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); > if (sset_count(&device_names) > 1) { > diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml > index dfc7cc217..1c394c6a0 100644 > --- a/controller/ovn-controller.8.xml > +++ b/controller/ovn-controller.8.xml > @@ -432,10 +432,22 @@ > > <dt><code>external_ids:ovn-evpn-local-ip</code></dt> > <dd> > - IP address used as a source address for the EVPN traffic leaving this > - OVN setup. There is currently support only for single IP address > - being specified. NOTE: this feature is experimental and may be > subject > - to removal/change in the future. > + Default IP address list used as a source addresses for the EVPN > traffic > + leaving this OVN setup. The <code>ovn-evpn-local-ip</code> can be > + specified by the CMS according to the following syntax: <code> > + external_ids:ovn-evpn-local-ip=IPv4,IPv6</code>. > + NOTE: this feature is experimental and may be subject to > removal/change > + in the future. > + </dd> > + > + <dt><code>external_ids:ovn-evpn-local-ip-mapping</code></dt> > + <dd> > + IP address list used as a source addresses for the EVPN traffic > + leaving this OVN setup. The <code>ovn-evpn-local-ip-mapping</code> > + can be specified by the CMS according to the following syntax: > + <code>external_ids:ovn-evpn-local-ip-mapping=vni0:IPv4,vni1:IPv4, > + vni1:[IPv6]</code>. NOTE: this feature is experimental and may be > + ubject to removal/change in the future. Typo: ubject > </dd> > </dl> > > diff --git a/controller/physical.c b/controller/physical.c > index b9c60c8ab..760eda423 100644 > --- a/controller/physical.c > +++ b/controller/physical.c > @@ -3178,13 +3178,132 @@ physical_eval_remote_chassis_flows(const struct > physical_ctx *ctx, > ofpbuf_uninit(&ingress_ofpacts); > } > > +struct vni_local_ip { > + struct hmap_node hmap_node; > + struct in6_addr ip; > + uint32_t vni; > +}; > + > +/* Default local ip mapping */ > +static struct in6_addr def_local_ip4, def_local_ip6; > + It's unfortunate we have to set up global variables for defaults. I think a better way to do this is to use a proper type for EVPN VNI mappings instead of a raw hmap initialized by evpn_vni_local_ip_map_alloc(). I.e., we could have a new type: struct evpn_local_ip_map { struct hmap vni_ip_v4; /* Per VNI local IPv4 vni_local_ips. */ struct hmap vni_ip_v6; /* Per VNI local IPv4 vni_local_ips. */ struct in6_addr default_ip4; /* Default local IPv4. */ struct in6_addr default_ip6; /* Default local IPv6. */ }; And have the evpn_local_ip_lookup_by_vni() and evpn_vni_local_ip_map_alloc() functions operate on a 'struct evpn_local_ip_map` instead of a raw hmap. > +static struct in6_addr * > +evpn_local_ip_lookup_by_vni(const struct hmap *map, uint32_t vni, bool ipv4) Nit: evpn_local_ip_find() is explicit enough in my opinion. > +{ > + struct vni_local_ip *e; > + HMAP_FOR_EACH_WITH_HASH (e, hmap_node, hash_add(vni, ipv4), map) { > + if (IN6_IS_ADDR_V4MAPPED(&e->ip) != ipv4) { > + continue; > + } > + if (e->vni == vni) { > + return &e->ip; > + } > + } > + > + if (ipv4 && ipv6_addr_is_set(&def_local_ip4)) { > + return &def_local_ip4; > + } > + > + if (!ipv4 && ipv6_addr_is_set(&def_local_ip6)) { > + return &def_local_ip6; > + } > + > + return NULL; > +} > + > +static void > +evpn_vni_local_ip_map_alloc(struct hmap *map, const struct smap *config) Nit: s/evpn_vni_local_ip_map_alloc/evpn_local_ip_map_init/ > +{ > + char *tokstr, *token, *ptr0 = NULL; > + struct in6_addr ip; > + int addr_family; > + > + const char *local_ip_str = smap_get_def(config, "ovn-evpn-local-ip", ""); > + if (strlen(local_ip_str)) { > + tokstr = xstrdup(local_ip_str); > + token = strtok_r(tokstr, ",", &ptr0); > + /* Primary default IP */ > + if (!ip_address_from_str(token, &ip, &addr_family)) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "Invalid IP: %s", token); We leak 'tokstr' here. > + return; > + } > + > + struct in6_addr *ip_ptr = addr_family == AF_INET ? &def_local_ip4 > + : &def_local_ip6; > + *ip_ptr = ip; > + char *ip_str = strtok_r(NULL, "", &ptr0); > + if (ip_str && ip_address_from_str(ip_str, &ip, &addr_family)) { If we use the new ovn-evpn-local-ip scheme I suggested in the beggining of the email we can just use ip46_parse(ip_str, &ip) here without having to add a new helper. > + /* Secondary default IP */ > + ip_ptr = addr_family == AF_INET ? &def_local_ip4 : > &def_local_ip6; > + *ip_ptr = ip; We don't warn if the user incorrectly specified more than one local IP. Even with the unified ovn-evpn-local-ip suggestion above we should validate that: - there's at most one default IPv4 - there's at most one default IPv6 - we don't have duplicate IPs for a given family for any VNI > + } > + > + free(tokstr); > + return; > + } > + > + local_ip_str = smap_get_def(config, "ovn-evpn-local-ip-mapping", ""); > + tokstr = xstrdup(local_ip_str); > + for (token = strtok_r(tokstr, ",", &ptr0); token; > + token = strtok_r(NULL, ",", &ptr0)) { > + char *ptr1 = NULL, *vni_str = strtok_r(token, ":", &ptr1); > + char *ip_str = strtok_r(NULL, "", &ptr1); > + uint32_t vni; > + > + if (!ip_address_from_str(ip_str, &ip, &addr_family)) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "EVPN enabled, but required 'evpn-local-ip' is > " > + "missing or invalid %s ", local_ip_str); > + continue; > + } > + > + if (!vni_str) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "Required VNI not configured"); > + continue; > + } > + > + if (!ovs_scan(vni_str, "%u", &vni) || !ovn_is_valid_vni(vni)) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "Invalid VNI: %s", vni_str); > + continue; > + } > + > + struct vni_local_ip *e = xmalloc(sizeof *e); > + e->vni = vni; > + e->ip = ip; Nit: please use designated initializers, we've been trying to encourage that lately, that is: *e = (struct vni_local_ip) { .vni = vni, .ip = ip, }; > + hmap_insert(map, &e->hmap_node, hash_add(vni, addr_family == > AF_INET)); We should first do a lookup here to avoid inserting duplicates. And warn if a duplicate is detected. > + } > + free(tokstr); > +} > + > +static void > +evpn_vni_local_ip_map_destroy(struct hmap *map) Nit: s/evpn_vni_local_ip_map_destroy/evpn_local_ip_map_destroy/ > +{ > + struct vni_local_ip *e; > + HMAP_FOR_EACH_POP (e, hmap_node, map) { > + free(e); > + } > + hmap_destroy(map); > +} > + > static void > physical_consider_evpn_binding(const struct evpn_binding *binding, > - const struct in6_addr *local_ip, > + const struct hmap *vni_ip_map, > struct ofpbuf *ofpacts, struct match *match, > - struct ovn_desired_flow_table *flow_table, > - bool ipv4) > + struct ovn_desired_flow_table *flow_table) > { > + bool ipv4 = IN6_IS_ADDR_V4MAPPED(&binding->remote_ip); > + const struct in6_addr *local_ip = evpn_local_ip_lookup_by_vni(vni_ip_map, > + > binding->vni, > + ipv4); > + if (!local_ip) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + VLOG_WARN_RL(&rl, "failed to get local tunnel ip"); This warning message is not very helpful if we don't log the binding->vni here. > + return; > + } > + > /* Ingress flows. */ > ofpbuf_clear(ofpacts); > match_init_catchall(match); > @@ -3308,39 +3427,66 @@ physical_consider_evpn_binding(const struct > evpn_binding *binding, > > static void > physical_consider_evpn_multicast(const struct evpn_multicast_group *mc_group, > - const struct in6_addr *local_ip, > + const struct hmap *vni_ip_map, > struct ofpbuf *ofpacts, struct match *match, > - struct ovn_desired_flow_table *flow_table, > - bool ipv4) > + struct ovn_desired_flow_table *flow_table) > { > - const struct evpn_binding *binding = NULL; > + struct in6_addr *local_ip4 = > + evpn_local_ip_lookup_by_vni(vni_ip_map, mc_group->vni, true); Maybe we should have a evpn_local_ip_find_v4() that returns the IPv4 address as ovs_be32 (or 0 if not found). > + struct in6_addr *local_ip6 = > + evpn_local_ip_lookup_by_vni(vni_ip_map, mc_group->vni, false); > Same here, I think a evpn_local_ip_find_v6() that returns the IPv6 address would be nice. > ofpbuf_clear(ofpacts); > uint32_t multicast_tunnel_keys[] = {OVN_MCAST_FLOOD_TUNNEL_KEY, > OVN_MCAST_UNKNOWN_TUNNEL_KEY, > OVN_MCAST_FLOOD_L2_TUNNEL_KEY}; > - if (ipv4) { > - ovs_be32 ip4 = in6_addr_get_mapped_ipv4(local_ip); > + > + put_load(mc_group->vni, MFF_TUN_ID, 0, 24, ofpacts); > + > + ovs_be32 ip4; > + const struct evpn_binding *binding = NULL; > + if (local_ip4) { > + ip4 = in6_addr_get_mapped_ipv4(local_ip4); > put_load_bytes(&ip4, sizeof ip4, MFF_TUN_SRC, 0, 32, ofpacts); If you take into account the previous comments this could become: if (ip4) { put_load_bytes(...) } > - } else { > - put_load_bytes(local_ip, sizeof *local_ip, MFF_TUN_IPV6_SRC, > - 0, 128, ofpacts); > + > + const struct hmapx_node *node; > + HMAPX_FOR_EACH (node, &mc_group->bindings) { > + binding = node->data; > + if (!IN6_IS_ADDR_V4MAPPED(&binding->remote_ip)) { > + continue; > + } > + > + ovs_be32 remote_ip4 = > + in6_addr_get_mapped_ipv4(&binding->remote_ip); > + put_load_bytes(&remote_ip4, sizeof remote_ip4, MFF_TUN_DST, 0, > 32, > + ofpacts); > + ofpact_put_OUTPUT(ofpacts)->port = binding->tunnel_ofport; > + } > + put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts); > } > - put_load(mc_group->vni, MFF_TUN_ID, 0, 24, ofpacts); > > - const struct hmapx_node *node; > - HMAPX_FOR_EACH (node, &mc_group->bindings) { > - binding = node->data; > - if (ipv4) { > - ovs_be32 ip4 = in6_addr_get_mapped_ipv4(&binding->remote_ip); > - put_load_bytes(&ip4, sizeof ip4, MFF_TUN_DST, 0, 32, ofpacts); > - } else { > + if (local_ip4 && local_ip6) { We should add a comment here explaining why we need to zero the tunnel v4 fields before moving on to the IPv6 remotes. It might not be too obvious that we: - first walk all the v4 remotes, generate tun encap actions for them - zero out the tun_v4 OVS fields to avoid having inconsistent OVS flow actions (set non-zero tunnel IPv4 src/dst followed by set non-zero tunnel IPv6 src/dst) - walk all the v6 remotes, generate tun encap actions for them > + ip4 = 0; > + put_load_bytes(&ip4, sizeof ip4, MFF_TUN_SRC, 0, 32, ofpacts); > + put_load_bytes(&ip4, sizeof ip4, MFF_TUN_DST, 0, 32, ofpacts); > + } > + > + if (local_ip6) { > + put_load_bytes(local_ip6, sizeof *local_ip6, MFF_TUN_IPV6_SRC, > + 0, 128, ofpacts); > + const struct hmapx_node *node; > + HMAPX_FOR_EACH (node, &mc_group->bindings) { > + binding = node->data; > + if (IN6_IS_ADDR_V4MAPPED(&binding->remote_ip)) { > + continue; > + } > + > put_load_bytes(&binding->remote_ip, sizeof binding->remote_ip, > MFF_TUN_IPV6_DST, 0, 128, ofpacts); > + ofpact_put_OUTPUT(ofpacts)->port = binding->tunnel_ofport; > } > - ofpact_put_OUTPUT(ofpacts)->port = binding->tunnel_ofport; > + put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts); > } > - put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts); > > ovs_assert(!hmapx_is_empty(&mc_group->bindings)); > for (size_t i = 0; i < ARRAY_SIZE(multicast_tunnel_keys); i++) { > @@ -3418,30 +3564,23 @@ physical_eval_evpn_flows(const struct physical_ctx > *ctx, > return; > } > > - const char *local_ip_str = smap_get_def(&ctx->chassis->other_config, > - "ovn-evpn-local-ip", ""); > - struct in6_addr local_ip; > - if (!ip46_parse(local_ip_str, &local_ip)) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > - VLOG_WARN_RL(&rl, "EVPN enabled, but required 'evpn-local-ip' is " > - "missing or invalid %s ", local_ip_str); > - return; > - } > + struct hmap vni_ip_map = HMAP_INITIALIZER(&vni_ip_map); > + evpn_vni_local_ip_map_alloc(&vni_ip_map, &ctx->chassis->other_config); > > struct match match = MATCH_CATCHALL_INITIALIZER; > - bool ipv4 = IN6_IS_ADDR_V4MAPPED(&local_ip); > - > const struct evpn_binding *binding; > + > HMAP_FOR_EACH (binding, hmap_node, ctx->evpn_bindings) { > - physical_consider_evpn_binding(binding, &local_ip, ofpacts, > - &match, flow_table, ipv4); > + physical_consider_evpn_binding(binding, &vni_ip_map, ofpacts, > + &match, flow_table); > } > > const struct evpn_multicast_group *mc_group; > HMAP_FOR_EACH (mc_group, hmap_node, ctx->evpn_multicast_groups) { > - physical_consider_evpn_multicast(mc_group, &local_ip, ofpacts, > - &match, flow_table, ipv4); > + physical_consider_evpn_multicast(mc_group, &vni_ip_map, ofpacts, > + &match, flow_table); > } > + evpn_vni_local_ip_map_destroy(&vni_ip_map); > > const struct evpn_fdb *fdb; > HMAP_FOR_EACH (fdb, hmap_node, ctx->evpn_fdbs) { > @@ -3569,34 +3708,29 @@ physical_handle_evpn_binding_changes( > const struct uuidset *removed_bindings, > const struct uuidset *removed_multicast_groups) > { > - const char *local_ip_str = smap_get_def(&ctx->chassis->other_config, > - "ovn-evpn-local-ip", ""); > - struct in6_addr local_ip; > - if (!ip46_parse(local_ip_str, &local_ip)) { > - return; > - } > + struct hmap vni_ip_map = HMAP_INITIALIZER(&vni_ip_map); > + evpn_vni_local_ip_map_alloc(&vni_ip_map, &ctx->chassis->other_config); > > struct ofpbuf ofpacts; > ofpbuf_init(&ofpacts, 0); > struct match match = MATCH_CATCHALL_INITIALIZER; > - bool ipv4 = IN6_IS_ADDR_V4MAPPED(&local_ip); > > const struct hmapx_node *node; > HMAPX_FOR_EACH (node, updated_bindings) { > const struct evpn_binding *binding = node->data; > - Nit: unrelated change. > ofctrl_remove_flows(flow_table, &binding->flow_uuid); > - physical_consider_evpn_binding(binding, &local_ip, &ofpacts, > - &match, flow_table, ipv4); > + physical_consider_evpn_binding(binding, &vni_ip_map, &ofpacts, > + &match, flow_table); > } > > HMAPX_FOR_EACH (node, updated_multicast_groups) { > const struct evpn_multicast_group *mc_group = node->data; > > ofctrl_remove_flows(flow_table, &mc_group->flow_uuid); > - physical_consider_evpn_multicast(mc_group, &local_ip, &ofpacts, > - &match, flow_table, ipv4); > + physical_consider_evpn_multicast(mc_group, &vni_ip_map, &ofpacts, > + &match, flow_table); > } > + evpn_vni_local_ip_map_destroy(&vni_ip_map); > > ofpbuf_uninit(&ofpacts); > > diff --git a/lib/ovn-util.c b/lib/ovn-util.c > index cec029e42..e60f73522 100644 > --- a/lib/ovn-util.c > +++ b/lib/ovn-util.c > @@ -926,6 +926,29 @@ ip_address_and_port_from_lb_key(const char *key, char > **ip_address, > return true; > } > > +bool > +ip_address_from_str(const char *ip_str, struct in6_addr *ip, int > *addr_family) We won't need this anymore if we move the per vni config back into ovn-evpn-local-ip as suggested above. > +{ > + memset(ip, 0, sizeof(*ip)); > + *addr_family = 0; > + > + struct sockaddr_storage ss; > + if (!inet_parse_active(ip_str, 0, &ss, false, NULL)) { > + return false; > + } > + > + if (ss_get_port(&ss)) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_WARN_RL(&rl, "port must not be specified"); This warning message is not that useful if we don't specify for which ip_str we failed parsing. > + return false; > + } > + > + *ip = ss_get_address(&ss); > + *addr_family = ss.ss_family; > + > + return true; > +} > + > /* Increment this for any logical flow changes, if an existing OVN action is > * modified or a stage is added to a logical pipeline. > * > diff --git a/lib/ovn-util.h b/lib/ovn-util.h > index daff01995..3f486e736 100644 > --- a/lib/ovn-util.h > +++ b/lib/ovn-util.h > @@ -291,6 +291,8 @@ bool ovn_str_to_ushort(const char *, int base, unsigned > short *); > bool ip_address_and_port_from_lb_key(const char *key, char **ip_address, > struct in6_addr *ip, uint16_t *port, > int *addr_family); > +bool ip_address_from_str(const char *ip_str, struct in6_addr *ip, > + int *addr_family); > > /* Returns the internal OVN version. The caller must free the returned > * value. */ > diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at > index 9c5a124a0..e1a880bd2 100644 > --- a/tests/system-common-macros.at > +++ b/tests/system-common-macros.at > @@ -209,18 +209,20 @@ m4_define([SET_EVPN_IFACE_NAMES], > ifname=$1 switch=$2 vni=$3 > > [[ $ifname = "default" ]] && BR_NAME=br-$vni || BR_NAME=br-$ifname > - [[ $ifname = "default" ]] && VXLAN_NAME=vxlan-$vni || > VXLAN_NAME=vxlan-$ifname > + [[ $ifname = "default" ]] && VXLAN_NAME=vxlan-$vni || > VXLAN_NAME=vxlan-v4-$ifname > + [[ $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-vxlan-ifname=$VXLAN_NAME \ > + check ovn-nbctl set logical_switch $switch > \ > + other_config:dynamic-routing-bridge-ifname=$BR_NAME > \ Unrelated change. > other_config:dynamic-routing-advertise-ifname=$LO_NAME > fi > + check ovn-nbctl set logical_switch $switch \ > + > other_config:dynamic-routing-vxlan-ifname=$VXLAN_NAME","$VXLAN_V6_NAME > > - export BR_NAME VXLAN_NAME LO_NAME > - on_exit 'unset BR_NAME VXLAN_NAME LO_NAME' > + export BR_NAME VXLAN_NAME VXLAN_V6_NAME LO_NAME > + on_exit 'unset BR_NAME VXLAN_NAME VXLAN_V6_NAME LO_NAME' > ] > ) > > diff --git a/tests/system-ovn.at b/tests/system-ovn.at > index ec3b3735f..482908de6 100644 > --- a/tests/system-ovn.at > +++ b/tests/system-ovn.at > @@ -17731,10 +17731,10 @@ > priority=1050,tun_id=0xa,tun_src=169.0.0.10,tun_dst=169.0.0.1,in_port=$ofport > ac > > AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_REMOTE_VTEP_OUTPUT | grep output | \ > awk '{print $[7], $[8]}' | sort], [0], [dnl > -priority=50,reg15=0x8000,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8000,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > priority=50,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > -priority=50,reg15=0x8001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > -priority=50,reg15=0x8004,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8001,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8004,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > ]) > > @@ -18170,7 +18170,7 @@ check ovs-vsctl \ > -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ > -- set Open_vSwitch . external-ids:ovn-encap-ip=169::1 \ > -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext \ > - -- set Open_vSwitch . external-ids:ovn-evpn-local-ip=169::1 \ > + -- set Open_vSwitch . external-ids:ovn-evpn-local-ip=169.0.0.1,169::1 \ > -- set Open_vSwitch . external-ids:ovn-evpn-vxlan-ports=4789 \ > -- set bridge br-int fail-mode=secure other-config:disable-in-band=true > > @@ -18268,10 +18268,10 @@ > priority=1050,tun_id=0xa,tun_ipv6_src=169::10,tun_ipv6_dst=169::1,in_port=$ofpor > > AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_REMOTE_VTEP_OUTPUT | grep output | \ > awk '{print $[7], $[8]}' | sort], [0], [dnl > -priority=50,reg15=0x8000,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8000,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > priority=50,reg15=0x80000001,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > -priority=50,reg15=0x8001,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > -priority=50,reg15=0x8004,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8001,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8004,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > ]) > > @@ -18684,6 +18684,588 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port > patch-.*/d > AT_CLEANUP > ]) > > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([dynamic-routing - EVPN $1 naming - Dual Stack]) > +AT_KEYWORDS([dynamic-routing]) > + > +CHECK_VRF() > +CHECK_CONNTRACK() > +CHECK_CONNTRACK_NAT() > + > +IFNAME=$1 > +vni=10 > +VRF_RESERVE([$vni]) > +ovn_start > +OVS_TRAFFIC_VSWITCHD_START() > +ADD_BR([br-int]) > +ADD_BR([br-ext], [set Bridge br-ext fail-mode=standalone]) > + > +# Set external-ids in br-int needed for ovn-controller. > +check ovs-vsctl \ > + -- 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 \ > + -- set Open_vSwitch . > external-ids:ovn-evpn-local-ip-mapping=10:169.0.0.1,10:[[169::1]] \ Nit: we could use $vni instead of hardcoded 10. > + -- set Open_vSwitch . external-ids:ovn-evpn-vxlan-ports=4789 \ > + -- set bridge br-int fail-mode=secure other-config:disable-in-band=true > + > +# Start ovn-controller. > +start_daemon ovn-controller > + > +OVS_WAIT_WHILE([ip link | grep -q ovnvrf$vni:.*UP]) > + > +check ovn-nbctl > \ > + -- ls-add ls-evpn > \ > + -- lsp-add ls-evpn workload1 > \ > + -- lsp-set-addresses workload1 "f0:00:0f:16:01:10 172.16.1.10 > 172:16::10" \ > + -- lsp-add ls-evpn workload2 > \ > + -- lsp-set-addresses workload2 "f0:00:0f:16:01:20 172.16.1.20 > 172:16::20" \ > + -- lsp-add-localnet-port ls-evpn ln_port phynet > + > +SET_EVPN_IFACE_NAMES([$IFNAME], [ls-evpn], [$vni]) > + > +ADD_NAMESPACES(workload1) > +ADD_VETH(workload1, workload1, br-int, "172:16::10/64", "f0:00:0f:16:01:10", > \ > + "172:16::1", "nodad", "172.16.1.10/24", "172.16.1.1") > + > +ADD_NAMESPACES(workload2) > +ADD_VETH(workload2, workload2, br-int, "172:16::20/64", "f0:00:0f:16:01:20", > \ > + "172:16::1", "nodad", "172.16.1.20/24", "172.16.1.1") > + > +OVN_POPULATE_ARP > +check ovn-nbctl --wait=hv sync > +wait_for_ports_up > + > +# Setup a VRF for the VNI. > +check ip link add vrf-$vni type vrf table $vni > +on_exit "ip link del vrf-$vni" > +check ip link set vrf-$vni up > + > +# Add VNI bridge. > +check ip link add $BR_NAME type bridge > +on_exit "ip link del $BR_NAME" > +check ip link set $BR_NAME master vrf-$vni addrgenmode none > +check ip link set dev $BR_NAME up > + > +# Add VXLAN VTEP for the VNI (linked to the OVS vxlan_sys_<port> interface). > +# Use a dstport different than the one used by OVS. > +# This is fine because we don't actually want traffic to pass through > +# the $vxlan interface. FRR should read the dstport from the linked > +# vxlan_sys_${vxlan_port} device. > +dstport=$((60000 + $vni)) > +check ip link add $VXLAN_NAME type vxlan \ > + id $vni dstport $dstport local 169.0.0.1 nolearning > +check ip link add $VXLAN_V6_NAME type vxlan \ > + id $vni dstport $dstport local 169::1 nolearning > +on_exit "ip link del $VXLAN_NAME; ip link del $VXLAN_V6_NAME" If the "ip link" fails for the $VXLAN_V6_NAME device we don't cleanup the v4 one. We should split this on_exit into two separate ones, one for v4 one for v6. > +check ip link set dev $VXLAN_NAME up > +check ip link set dev $VXLAN_V6_NAME up > +check ip link set $VXLAN_NAME master $BR_NAME > +check ip link set $VXLAN_V6_NAME master $BR_NAME > + > +# Add a dummy loopback to the VNI bridge to be used for advertising local > +# MACs. > +check ip link add name $LO_NAME type dummy > +on_exit "ip link del $LO_NAME" > +check ip link set $LO_NAME master $BR_NAME > +check ip link set $LO_NAME up > + > +AS_BOX([L2 EVPN VTEP and FDB learning]) > + > +check ovn-nbctl --wait=hv set logical_switch ls-evpn > other_config:dynamic-routing-vni=$vni > +ofport=$(ovs-vsctl --bare --columns ofport find Interface > name="ovn-evpn-4789") > +dp_key=$(fetch_column Datapath tunnel_key external_ids:name=ls-evpn) > + > +AT_CHECK([ovn-appctl evpn/remote-vtep-list], [0], [dnl > +]) > + > +AT_CHECK([ovn-appctl evpn/vtep-binding-list], [0], [dnl > +]) > + > +AT_CHECK([ovn-appctl evpn/vtep-multicast-group-list], [0], [dnl > +]) > + > +# Simulate remote VTEP. > +check bridge fdb append 00:00:00:00:00:00 dev $VXLAN_NAME dst 169.0.0.10 > static permanent > +check bridge fdb append 00:00:00:00:00:00 dev $VXLAN_V6_NAME dst 169::10 > static permanent > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/remote-vtep-list], [0], [dnl > +IP: 169::10, port: 4789, vni: $vni > +IP: 169.0.0.10, port: 4789, vni: $vni > +]) > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-binding-list | cut -d',' > -f2-], [0], [dnl > + Remote IP: 169::10, vni: $vni, binding_key: 0x80000002, tunnel_ofport: > $ofport, dp_key: $dp_key > + Remote IP: 169.0.0.10, vni: $vni, binding_key: 0x80000001, tunnel_ofport: > $ofport, dp_key: $dp_key > +]) > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-multicast-group-list | > grep -q 169.0.0.10]) > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-multicast-group-list | > grep -q 169::10]) > +AT_CHECK([ovn-appctl evpn/vtep-multicast-group-list | wc -l], [0], [1 > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG | > grep priority=1050 | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=1050,tun_id=0xa,tun_ipv6_src=169::10,tun_ipv6_dst=169::1,in_port=$ofport > > actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000002->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +priority=1050,tun_id=0xa,tun_src=169.0.0.10,tun_dst=169.0.0.1,in_port=$ofport > > actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000001->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_REMOTE_VTEP_OUTPUT | grep output | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=50,reg15=0x8000,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x80000002,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x8001,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=50,reg15=0x8004,metadata=0x$dp_key > actions=load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT),load:0->NXM_NX_TUN_IPV4_SRC[[]],load:0->NXM_NX_TUN_IPV4_DST[[]],load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],output:$ofport,resubmit(,OFTABLE_LOCAL_OUTPUT) > +priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000002,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_LEARN_REMOTE_FDB | grep priority | \ > + awk '{print $[7], $[8]}' | strip_cookie | sort], [0], [dnl > +priority=100,reg14=0x80000001,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000002,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +]) > + > +# Simulate more remote VTEPs. > +check bridge fdb append 00:00:00:00:00:00 dev $VXLAN_NAME dst 169.0.0.20 > static permanent > +check bridge fdb append 00:00:00:00:00:01 dev $VXLAN_NAME dst 169.0.0.30 > extern_learn > +check bridge fdb append 00:00:00:00:00:10 dev $VXLAN_V6_NAME dst 169::30 > extern_learn > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/remote-vtep-list | sort], [0], > [dnl > +IP: 169.0.0.10, port: 4789, vni: $vni > +IP: 169.0.0.20, port: 4789, vni: $vni > +IP: 169.0.0.30, port: 4789, vni: $vni > +IP: 169::10, port: 4789, vni: 10 > +IP: 169::30, port: 4789, vni: 10 > +]) > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-binding-list | cut -d',' > -f2- | sort], [0], [dnl > + Remote IP: 169.0.0.10, vni: $vni, binding_key: 0x80000001, tunnel_ofport: > $ofport, dp_key: $dp_key > + Remote IP: 169.0.0.20, vni: $vni, binding_key: 0x80000003, tunnel_ofport: > $ofport, dp_key: $dp_key > + Remote IP: 169.0.0.30, vni: $vni, binding_key: 0x80000004, tunnel_ofport: > $ofport, dp_key: $dp_key > + Remote IP: 169::10, vni: $vni, binding_key: 0x80000002, tunnel_ofport: > $ofport, dp_key: $dp_key > + Remote IP: 169::30, vni: $vni, binding_key: 0x80000005, tunnel_ofport: > $ofport, dp_key: $dp_key > +]) > + > +# We cannot check the output directly because the order might change. > +OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q > "169.0.0.10"]) > +OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q > "169.0.0.20"]) > +OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q > "169.0.0.30"]) > +OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q > "169::10"]) > +OVS_WAIT_UNTIL([ovn-appctl evpn/vtep-multicast-group-list | grep -q > "169::30"]) > +AT_CHECK([ovn-appctl evpn/vtep-multicast-group-list | wc -l], [0], [1 > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_PHY_TO_LOG | > grep priority=1050 | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=1050,tun_id=0xa,tun_ipv6_src=169::10,tun_ipv6_dst=169::1,in_port=$ofport > > actions=load:0x1->OXM_OF_METADATA[[0..31]],load:0x80000002->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +priority=1050,tun_id=0xa,tun_ipv6_src=169::30,tun_ipv6_dst=169::1,in_port=$ofport > > actions=load:0x1->OXM_OF_METADATA[[0..31]],load:0x80000005->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +priority=1050,tun_id=0xa,tun_src=169.0.0.10,tun_dst=169.0.0.1,in_port=$ofport > > actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000001->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +priority=1050,tun_id=0xa,tun_src=169.0.0.20,tun_dst=169.0.0.1,in_port=$ofport > > actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000003->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +priority=1050,tun_id=0xa,tun_src=169.0.0.30,tun_dst=169.0.0.1,in_port=$ofport > > actions=load:0x$dp_key->OXM_OF_METADATA[[0..31]],load:0x80000004->NXM_NX_REG14[[]],resubmit(,OFTABLE_LEARN_REMOTE_FDB),resubmit(,OFTABLE_LOG_INGRESS_PIPELINE) > +]) > + > +ovs-ofctl dump-flows br-int table=OFTABLE_REMOTE_VTEP_OUTPUT > > oftable_remote_vtep_output > +AT_CHECK_UNQUOTED([grep "output" oftable_remote_vtep_output | grep -v > resubmit | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=50,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x80000002,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x80000003,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa9000014->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x80000004,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900001e->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=50,reg15=0x80000005,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x30->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000001,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900000a->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000002,metadata=0x$dp_key > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x10->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000003,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa9000014->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000004,metadata=0x$dp_key > actions=load:0xa9000001->NXM_NX_TUN_IPV4_SRC[[]],load:0xa900001e->NXM_NX_TUN_IPV4_DST[[]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +priority=55,reg10=0x1/0x1,reg15=0x80000005,metadata=0x1 > actions=load:0x1->NXM_NX_TUN_IPV6_SRC[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_SRC[[64..127]],load:0x30->NXM_NX_TUN_IPV6_DST[[0..63]],load:0x169000000000000->NXM_NX_TUN_IPV6_DST[[64..127]],load:0xa->NXM_NX_TUN_ID[[0..23]],load:0xffff->NXM_OF_IN_PORT[[]],output:$ofport > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_LEARN_REMOTE_FDB | grep priority | \ > + awk '{print $[7], $[8]}' | strip_cookie | sort], [0], [dnl > +priority=100,reg14=0x80000001,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000002,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000003,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000004,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000005,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=00:00:00:00:00:01 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=00:00:00:00:00:10 > actions=drop > +]) > + > +AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c > "load:0xa900000a"], [0], [3 > +]) > +AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c > "load:0xa9000014"], [0], [3 > +]) > +AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c > "load:0xa900001e"], [0], [3 > +]) > + > +# Simulate remote workload. > +check bridge fdb add f0:00:0f:16:10:50 dev $VXLAN_NAME dst 169.0.0.10 static > extern_learn > +check bridge fdb add f0:00:0f:16:10:60 dev $VXLAN_NAME dst 169.0.0.20 static > extern_learn > +check bridge fdb add f0:00:0f:16:10:70 dev $VXLAN_NAME dst 169.0.0.30 static > extern_learn > +check bridge fdb add f0:00:0f:16:10:80 dev $VXLAN_V6_NAME dst 169::30 static > extern_learn > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-fdb-list | cut -d',' -f2- > | sort], [0], [dnl > + MAC: 00:00:00:00:00:01, vni: $vni, binding_key: 0x80000004, dp_key: $dp_key > + MAC: 00:00:00:00:00:10, vni: $vni, binding_key: 0x80000005, dp_key: $dp_key > + MAC: f0:00:0f:16:10:50, vni: $vni, binding_key: 0x80000001, dp_key: $dp_key > + MAC: f0:00:0f:16:10:60, vni: $vni, binding_key: 0x80000003, dp_key: $dp_key > + MAC: f0:00:0f:16:10:70, vni: $vni, binding_key: 0x80000004, dp_key: $dp_key > + MAC: f0:00:0f:16:10:80, vni: $vni, binding_key: 0x80000005, dp_key: $dp_key > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_GET_REMOTE_FDB > | grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=0 actions=load:0->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=00:00:00:00:00:01 > actions=load:0x80000004->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=00:00:00:00:00:10 > actions=load:0x80000005->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:50 > actions=load:0x80000001->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:60 > actions=load:0x80000003->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:70 > actions=load:0x80000004->NXM_NX_REG1[[]] > +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:80 > actions=load:0x80000005->NXM_NX_REG1[[]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_LEARN_REMOTE_FDB | grep priority | \ > + awk '{print $[7], $[8]}' | strip_cookie | sort], [0], [dnl > +priority=100,reg14=0x80000001,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000002,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000003,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000004,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000005,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=150,reg14=0x80000001,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:50 > actions=drop > +priority=150,reg14=0x80000003,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:60 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=00:00:00:00:00:01 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:70 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=00:00:00:00:00:10 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:80 > actions=drop > +]) > + > +# Check that the recompute won't change the UUIDs and tunnel keys. > +ovn-appctl evpn/vtep-binding-list > bindings_before > +ovn-appctl evpn/vtep-multicast-group-list > mc_groups_before > +ovn-appctl evpn/vtep-fdb-list > fdb_before > + > +check ovn-appctl inc-engine/recompute > +check ovn-nbctl --wait=hv sync > + > +ovn-appctl evpn/vtep-binding-list > bindings_after > +ovn-appctl evpn/vtep-multicast-group-list > mc_groups_after > +ovn-appctl evpn/vtep-fdb-list > fdb_after > + > +check diff -q bindings_before bindings_after > +check diff -q mc_groups_before mc_groups_after > +check diff -q fdb_before fdb_after > + > +AS_BOX([L2 EVPN FDB advertising]) > + > +check ovn-nbctl --wait=hv set logical_switch ls-evpn > other_config:dynamic-routing-redistribute=fdb > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([bridge fdb show | grep $LO_NAME | grep > "f0:00:0f:16:01" | sort], [0], [dnl > +f0:00:0f:16:01:10 dev $LO_NAME master $BR_NAME static > +f0:00:0f:16:01:10 dev $LO_NAME vlan 1 master $BR_NAME static > +f0:00:0f:16:01:20 dev $LO_NAME master $BR_NAME static > +f0:00:0f:16:01:20 dev $LO_NAME vlan 1 master $BR_NAME static > +]) > + > +check ovn-nbctl --wait=hv lsp-del workload2 > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([bridge fdb show | grep $LO_NAME | grep > "f0:00:0f:16:01" | sort], [0], [dnl > +f0:00:0f:16:01:10 dev $LO_NAME master $BR_NAME static > +f0:00:0f:16:01:10 dev $LO_NAME vlan 1 master $BR_NAME static > +]) > + > +check ovn-nbctl --wait=hv remove logical_switch ls-evpn other_config > dynamic-routing-redistribute > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([bridge fdb show | grep $LO_NAME | grep > "f0:00:0f:16:01" | sort], [0], [dnl > +]) > + > +check ovn-nbctl --wait=hv set logical_switch ls-evpn > other_config:fdb_age_threshold=300 > +ovs-ofctl dump-flows br-int table=OFTABLE_LEARN_REMOTE_FDB > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_LEARN_REMOTE_FDB | grep priority | \ > + awk '{print $[7], $[8]}' | strip_cookie | sort], [0], [dnl > +priority=100,reg14=0x80000001,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,hard_timeout=300,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000002,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,hard_timeout=300,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000003,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,hard_timeout=300,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000004,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,hard_timeout=300,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000005,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,hard_timeout=300,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=150,reg14=0x80000001,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:50 > actions=drop > +priority=150,reg14=0x80000003,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:60 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=00:00:00:00:00:01 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:70 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=00:00:00:00:00:10 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:80 > actions=drop > +]) > + > +check ovn-nbctl --wait=hv remove logical_switch ls-evpn other_config > fdb_age_threshold > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int > table=OFTABLE_LEARN_REMOTE_FDB | grep priority | \ > + awk '{print $[7], $[8]}' | strip_cookie | sort], [0], [dnl > +priority=100,reg14=0x80000001,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000002,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000003,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000004,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=100,reg14=0x80000005,metadata=0x$dp_key > actions=learn(table=OFTABLE_GET_REMOTE_FDB,priority=150,delete_learned,OXM_OF_METADATA[[]],NXM_OF_ETH_DST[[]]=NXM_OF_ETH_SRC[[]],load:NXM_NX_REG14[[]]->NXM_NX_REG1[[]]) > +priority=150,reg14=0x80000001,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:50 > actions=drop > +priority=150,reg14=0x80000003,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:60 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=00:00:00:00:00:01 > actions=drop > +priority=150,reg14=0x80000004,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:70 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=00:00:00:00:00:10 > actions=drop > +priority=150,reg14=0x80000005,metadata=0x$dp_key,dl_src=f0:00:0f:16:10:80 > actions=drop > +]) > + > +AS_BOX([L2 EVPN ARP learning]) > +# Add a router connected to the EVPN logical switch. > +check ovn-nbctl --wait=hv \ > + -- lr-add lr \ > + -- lrp-add lr lr-ls-evpn f0:00:0f:16:01:01 172.16.1.1/24 172:16::1/64 \ > + -- lsp-add-router-port ls-evpn ls-evpn-lr lr-ls-evpn > + > +rtr_dp_key=$(fetch_column Datapath tunnel_key external_ids:name=lr) > +rtr_port_key=$(fetch_column Port_Binding tunnel_key logical_port=lr-ls-evpn) > + > +# Simulate remote workload ARPs (type-2 MAC+IP EVPN route). > +# ovn-controller needs to add OF rules for ARP lookup but no rules for > +# MAC_CACHE use. These entries do not age out automatically, their lifetime > +# is controlled by the BGP-EVPN control plane. > +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_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-arp-list | cut -d',' -f2- > | sort], [0], [dnl > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172.16.1.50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172:16::50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172.16.1.60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172:16::60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172.16.1.70, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172:16::70, dp_key: $dp_key > +]) > + > +AS_BOX([Check dynamic-routing-arp-prefer-local=true]) > +check ovn-nbctl --wait=hv set Logical_Switch ls-evpn > other_config:dynamic-routing-arp-prefer-local=true > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,reg0=0xac10010a,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:01:10,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg0=0xac100132,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg0=0xac10013c,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg0=0xac100146,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg4=0x1720016,reg5=0,reg6=0,reg7=0x50,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg4=0x1720016,reg5=0,reg6=0,reg7=0x60,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=20,reg4=0x1720016,reg5=0,reg6=0,reg7=0x70,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_LOOKUP | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg0=0xac10010a,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,arp,reg0=0xac100132,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,arp,reg0=0xac10013c,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,arp,reg0=0xac100146,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x50,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x60,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=20,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x70,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,arp_spa=172.16.1.10,arp_op=2 > actions=drop > +priority=100,ip,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,nw_src=172.16.1.10 > actions=drop > +]) > + > +AS_BOX([Check dynamic-routing-arp-prefer-local=false]) > +check ovn-nbctl --wait=hv set Logical_Switch ls-evpn > other_config:dynamic-routing-arp-prefer-local=false > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,reg0=0xac10010a,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:01:10,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac100132,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac10013c,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac100146,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x50,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x60,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x70,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_LOOKUP | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg0=0xac10010a,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac100132,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac10013c,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac100146,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x50,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x60,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x70,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,arp_spa=172.16.1.10,arp_op=2 > actions=drop > +priority=100,ip,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,nw_src=172.16.1.10 > actions=drop > +]) > + > +# Check that the recompute won't change the UUIDs and flows. > +ovn-appctl evpn/vtep-arp-list > arp_before > + > +check ovn-appctl inc-engine/recompute > +check ovn-nbctl --wait=hv sync > + > +ovn-appctl evpn/vtep-arp-list > arp_after > + > +check diff -q arp_before arp_after > + > +# 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 > +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_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-arp-list | cut -d',' -f2- > | sort], [0], [dnl > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,reg0=0xac10010a,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:01:10,load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_LOOKUP | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg0=0xac10010a,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10 > actions=load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_CACHE_USE | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,arp_spa=172.16.1.10,arp_op=2 > actions=drop > +priority=100,ip,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10,nw_src=172.16.1.10 > actions=drop > +]) > + > +# Re-add the remote workload ARPs, remove the router, check that flows are > +# removed (vtep-arp-list should still list the ARPs as they're learned on > +# the logical switch that still exists). > +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_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-arp-list | cut -d',' -f2- > | sort], [0], [dnl > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172.16.1.50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172:16::50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172.16.1.60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172:16::60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172.16.1.70, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172:16::70, dp_key: $dp_key > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,reg0=0xac10010a,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:01:10,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac100132,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac10013c,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg0=0xac100146,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x50,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:50,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x60,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:60,load:0x1->NXM_NX_REG10[[6]] > +priority=200,reg4=0x1720016,reg5=0,reg6=0,reg7=0x70,reg15=0x$rtr_port_key,metadata=0x$rtr_dp_key > actions=mod_dl_dst:f0:00:0f:16:10:70,load:0x1->NXM_NX_REG10[[6]] > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_LOOKUP | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +priority=100,arp,reg0=0xac10010a,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:01:10 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac100132,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac10013c,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,arp,reg0=0xac100146,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x50,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:50,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x60,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:60,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +priority=200,icmp6,reg0=0x1720016,reg1=0,reg2=0,reg3=0x70,reg14=0x$rtr_port_key,metadata=0x$rtr_dp_key,dl_src=f0:00:0f:16:10:70,icmp_code=0 > actions=load:0x1->NXM_NX_REG10[[6]] > +]) > + > +check ovn-nbctl --wait=hv lr-del lr > +AT_CHECK_UNQUOTED([ovn-appctl evpn/vtep-arp-list | cut -d',' -f2- | sort], > [0], [dnl > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172.16.1.50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:50, IP: 172:16::50, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172.16.1.60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:60, IP: 172:16::60, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172.16.1.70, dp_key: $dp_key > + VNI: 10, MAC: f0:00:0f:16:10:70, IP: 172:16::70, dp_key: $dp_key > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_BINDING | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +]) > + > +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int table=OFTABLE_MAC_LOOKUP | > grep priority | \ > + awk '{print $[7], $[8]}' | sort], [0], [dnl > +]) > + > +AS_BOX([L2 EVPN ARP advertising]) > +# Add a router connected to the EVPN logical switch. > +check ovn-nbctl --wait=hv \ > + -- lr-add lr \ > + -- set Logical_Router lr options:chassis=hv1 \ > + -- lrp-add lr lr-ls-evpn f0:00:0f:16:01:01 172.16.1.1/24 172:16::1/64 > + > +ls_evpn_uuid=$(fetch_column Datapath_Binding _uuid external_ids:name=ls-evpn) > +check ovn-nbctl --wait=hv set logical_switch ls-evpn > other_config:dynamic-routing-redistribute=fdb,ip > +check_row_count Advertised_MAC_Binding 1 ip='172.16.1.10' > mac='f0\:00\:0f\:16\:01\:10' datapath=$ls_evpn_uuid > +check_row_count Advertised_MAC_Binding 1 ip='172.16.1.1' > mac='f0\:00\:0f\:16\:01\:01' datapath=$ls_evpn_uuid > +check_row_count Advertised_MAC_Binding 1 ip='172\:16\:\:10' > mac='f0\:00\:0f\:16\:01\:10' datapath=$ls_evpn_uuid > +check_row_count Advertised_MAC_Binding 1 ip='172\:16\:\:1' > mac='f0\:00\:0f\:16\:01\:01' datapath=$ls_evpn_uuid > +check_row_count Advertised_MAC_Binding 4 > + > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.10 lladdr > f0:00:0f:16:01:10 NOARP']) > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.1 lladdr > f0:00:0f:16:01:01 NOARP']) > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::10 > lladdr f0:00:0f:16:01:10 NOARP']) > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::1 > lladdr f0:00:0f:16:01:01 NOARP']) > + > +OVS_WAIT_FOR_OUTPUT_UNQUOTED([bridge fdb show | grep $LO_NAME | grep > "f0:00:0f:16:01" | sort], [0], [dnl > +f0:00:0f:16:01:01 dev $LO_NAME master $BR_NAME static > +f0:00:0f:16:01:01 dev $LO_NAME vlan 1 master $BR_NAME static > +f0:00:0f:16:01:10 dev $LO_NAME master $BR_NAME static > +f0:00:0f:16:01:10 dev $LO_NAME vlan 1 master $BR_NAME static > +]) > + > +check ovn-nbctl --wait=hv lsp-add ls-evpn workload2 \ > + -- lsp-set-addresses workload2 "f0:00:0f:16:01:20 172.16.1.20 172:16::20" > + > +check_row_count Advertised_MAC_Binding 1 ip='172.16.1.20' > mac='f0\:00\:0f\:16\:01\:20' datapath=$ls_evpn_uuid > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.20 lladdr > f0:00:0f:16:01:20 NOARP']) > + > +check_row_count Advertised_MAC_Binding 1 ip='172\:16\:\:20' > mac='f0\:00\:0f\:16\:01\:20' datapath=$ls_evpn_uuid > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::20 > lladdr f0:00:0f:16:01:20 NOARP']) > + > +check ovn-nbctl --wait=hv lsp-del workload2 > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.20 lladdr > f0:00:0f:16:01:20 NOARP'], [1]) > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::20 > lladdr f0:00:0f:16:01:20 NOARP'], [1]) > + > +check ovn-nbctl --wait=hv lsp-del workload1 > +check_row_count Advertised_MAC_Binding 2 > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.10 lladdr > f0:00:0f:16:01:10 NOARP'], [1]) > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::10 > lladdr f0:00:0f:16:01:10 NOARP'], [1]) > + > +check ovn-nbctl --wait=hv lrp-del lr-ls-evpn > +check_row_count Advertised_MAC_Binding 0 > +AT_CHECK([ip neigh show dev $BR_NAME nud noarp | grep -q '172.16.1.1 lladdr > f0:00:0f:16:01:01 NOARP'], [1]) > +AT_CHECK([ip -6 neigh show dev $BR_NAME nud noarp | grep -q '172:16::1 > lladdr f0:00:0f:16:01:01 NOARP'], [1]) > + > +check ovn-nbctl --wait=hv ls-del ls-evpn > +check ovn-nbctl --wait=hv lr-del lr > + > +OVN_CLEANUP_CONTROLLER([hv1]) > + > +OVN_CLEANUP_NORTHD > + > +as > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d > +/Failed to acquire.*/d > +/connection dropped.*/d"]) > +AT_CLEANUP > +]) > + > ]) dnl EVPN_SWITCH_TESTS > > EVPN_SWITCH_TESTS([default]) Regards, Dumitru _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
