Collect vif and router ports IPs/MAC bindings in SB Advertised_Mac_Binding 
table in
order to announce them as EVPN Type2 Route messages.

Signed-off-by: Lorenzo Bianconi <[email protected]>
---
 NEWS                              |   3 +
 northd/en-advertised-route-sync.c | 250 ++++++++++++++++++++++++++++++
 northd/en-advertised-route-sync.h |   7 +
 northd/inc-proc-northd.c          |  10 +-
 ovn-nb.xml                        |   6 +
 ovn-sb.ovsschema                  |  22 ++-
 ovn-sb.xml                        |  29 ++++
 tests/ovn-northd.at               |  48 +++++-
 8 files changed, 366 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index fb4d63194..77a329669 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,9 @@ Post v25.09.0
        Switches. If set to "true" ovn-controller will give preference to SB
        (Static_)MAC_Bindings of adjacent logical routers over ARPs learned
        through EVPN on the switch.
+     * Add the "other_config:dynamic-routing-redistribute=ip" to Logical
+       Switches to announce local IPs/MAC bindings belonging to the same
+       EVPN domain.
    - Add support for Network Function insertion in OVN with stateful traffic
      redirection capability in Logical Switch datapath. The feature introduces
      three new NB database tables:
diff --git a/northd/en-advertised-route-sync.c 
b/northd/en-advertised-route-sync.c
index 423baaacf..1bcf1ebf9 100644
--- a/northd/en-advertised-route-sync.c
+++ b/northd/en-advertised-route-sync.c
@@ -834,3 +834,253 @@ advertised_route_table_sync(
     hmap_destroy(&sync_routes);
 }
 
+struct evpn_type2 {
+    struct hmap_node hmap_node;
+
+    const struct sbrec_port_binding *sb;
+    char *ip;
+    char *mac;
+};
+
+static bool
+evpn_is_ip_redistribute_running(const struct ovn_datapath *od)
+{
+    int64_t vni = ovn_smap_get_llong(&od->nbs->other_config,
+                                     "dynamic-routing-vni", -1);
+    if (!ovn_is_valid_vni(vni)) {
+        return false;
+    }
+
+    const char *redistribute = smap_get(&od->nbs->other_config,
+                                        "dynamic-routing-redistribute");
+    return redistribute && !strcmp(redistribute, "ip");
+}
+
+static struct evpn_type2 *
+evpn_type2_entry_find(struct hmap *map,
+                      const char *ip, const char *mac)
+{
+    uint32_t hash = hash_string(ip, 0);
+    hash = hash_string(mac, hash);
+
+    struct evpn_type2 *e;
+    HMAP_FOR_EACH_WITH_HASH (e, hmap_node, hash, map) {
+        if (!strcmp(e->ip, ip) && !strcmp(e->mac, mac)) {
+            return e;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+evpn_type2_entry_add(struct hmap *map,
+                     const struct sbrec_port_binding *sb,
+                     const char *ip, const char *mac)
+{
+    struct evpn_type2 *e = xmalloc(sizeof *e);
+    e->ip = xstrdup(ip);
+    e->mac = xstrdup(mac);
+    e->sb = sb;
+
+    uint32_t hash = hash_string(ip, 0);
+    hash = hash_string(mac, hash);
+    hmap_insert(map, &e->hmap_node, hash);
+}
+
+static void
+evpn_type2_entry_destroy(struct evpn_type2 *e)
+{
+    free(e->ip);
+    free(e->mac);
+    free(e);
+}
+
+static void
+evpn_add_type2(struct hmap *map, const struct sbrec_port_binding *sb,
+               struct lport_addresses *addr)
+{
+    struct ds ip = DS_EMPTY_INITIALIZER;
+
+    if (!addr) {
+        return;
+    }
+
+    for (size_t i = 0; i < addr->n_ipv4_addrs; i++) {
+        ds_clear(&ip);
+        ds_put_format(&ip, "%s/32", addr->ipv4_addrs[i].addr_s);
+        if (!evpn_type2_entry_find(map, ds_cstr(&ip), addr->ea_s)) {
+            evpn_type2_entry_add(map, sb, ds_cstr(&ip), addr->ea_s);
+        }
+    }
+
+    for (size_t i = 0; i < addr->n_ipv6_addrs; i++) {
+        if (prefix_is_link_local(&addr->ipv6_addrs[i].addr, 128)) {
+            continue;
+        }
+
+        ds_clear(&ip);
+        ds_put_format(&ip, "%s/128", addr->ipv6_addrs[i].addr_s);
+        if (!evpn_type2_entry_find(map, ds_cstr(&ip), addr->ea_s)) {
+            evpn_type2_entry_add(map, sb, ds_cstr(&ip), addr->ea_s);
+        }
+    }
+
+    ds_destroy(&ip);
+}
+
+static void
+build_evpn_type2_for_ls(const struct ovn_datapath *od, struct hmap *map)
+{
+    ovs_assert(od->nbs);
+
+    if (!evpn_is_ip_redistribute_running(od)) {
+        return;
+    }
+
+    struct ovn_port *op;
+    HMAP_FOR_EACH (op, dp_node, &od->ports) {
+        if (!op->sb) {
+            continue;
+        }
+
+        if (lsp_is_router(op->nbsp) && op->peer) {
+            evpn_add_type2(map, op->sb, &op->peer->lrp_networks);
+        }
+
+        if (!strcmp(op->nbsp->type, "")) { /* LSP */
+            evpn_add_type2(map, op->sb, op->lsp_addrs);
+        }
+    }
+}
+
+static void
+build_evpn_type2_for_lr(const struct ovn_datapath *od, struct hmap *map)
+{
+    ovs_assert(od->nbr);
+
+    struct ovn_port *op;
+    HMAP_FOR_EACH (op, dp_node, &od->ports) {
+        struct ovn_port *peer = op->peer;
+        if (!peer || !peer->od || !peer->od->nbs || !peer->sb) {
+            continue;
+        }
+
+        struct ovn_datapath *peer_od = peer->od;
+        if (evpn_is_ip_redistribute_running(peer_od)) {
+            evpn_add_type2(map, peer->sb, &op->lrp_networks);
+        }
+    }
+}
+
+void *
+en_evpn_type2_sync_init(struct engine_node *node OVS_UNUSED,
+                        struct engine_arg *arg OVS_UNUSED)
+{
+    return NULL;
+}
+
+enum engine_node_state
+en_evpn_type2_sync_run(struct engine_node *node, void *data OVS_UNUSED)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    const struct sbrec_advertised_mac_binding_table *sbrec_adv_mb_table =
+        EN_OVSDB_GET(engine_get_input("SB_advertised_mac_binding", node));
+    const struct engine_context *eng_ctx = engine_get_context();
+
+    struct hmap evpn_type2_map = HMAP_INITIALIZER(&evpn_type2_map);
+
+    struct ovn_datapath *od;
+    HMAP_FOR_EACH (od, key_node, &northd_data->lr_datapaths.datapaths) {
+        build_evpn_type2_for_lr(od, &evpn_type2_map);
+    }
+
+    HMAP_FOR_EACH (od, key_node, &northd_data->ls_datapaths.datapaths) {
+        build_evpn_type2_for_ls(od, &evpn_type2_map);
+    }
+
+    struct evpn_type2 *e;
+    const struct sbrec_advertised_mac_binding *sb_adv_mb;
+    SBREC_ADVERTISED_MAC_BINDING_TABLE_FOR_EACH_SAFE (sb_adv_mb,
+                                          sbrec_adv_mb_table) {
+        e = evpn_type2_entry_find(&evpn_type2_map, sb_adv_mb->ip,
+                                  sb_adv_mb->mac);
+        if (!e) {
+            sbrec_advertised_mac_binding_delete(sb_adv_mb);
+        } else {
+            hmap_remove(&evpn_type2_map, &e->hmap_node);
+            evpn_type2_entry_destroy(e);
+        }
+    }
+
+    HMAP_FOR_EACH_POP (e, hmap_node, &evpn_type2_map) {
+        sb_adv_mb =
+            sbrec_advertised_mac_binding_insert(eng_ctx->ovnsb_idl_txn);
+        sbrec_advertised_mac_binding_set_datapath(sb_adv_mb, e->sb->datapath);
+        sbrec_advertised_mac_binding_set_logical_port(sb_adv_mb, e->sb);
+        sbrec_advertised_mac_binding_set_ip(sb_adv_mb, e->ip);
+        sbrec_advertised_mac_binding_set_mac(sb_adv_mb, e->mac);
+        evpn_type2_entry_destroy(e);
+    }
+
+    hmap_destroy(&evpn_type2_map);
+
+    return EN_UPDATED;
+}
+
+void
+en_evpn_type2_sync_cleanup(void *data OVS_UNUSED)
+{
+}
+
+enum engine_input_handler_result
+evpn_type2_sync_northd_change_handler(struct engine_node *node,
+                                      void *data OVS_UNUSED)
+{
+    struct northd_data *northd_data = engine_get_input_data("northd", node);
+    if (!northd_has_tracked_data(&northd_data->trk_data)) {
+        return EN_UNHANDLED;
+    }
+
+    struct northd_tracked_data *nd_changes = &northd_data->trk_data;
+
+    struct hmapx_node *n;
+    HMAPX_FOR_EACH (n, &nd_changes->trk_switches.crupdated) {
+        const struct ovn_datapath *od = n->data;
+        if (evpn_is_ip_redistribute_running(od)) {
+            return EN_UNHANDLED;
+        }
+    }
+    HMAPX_FOR_EACH (n, &nd_changes->trk_switches.deleted) {
+        const struct ovn_datapath *od = n->data;
+        if (evpn_is_ip_redistribute_running(od)) {
+            return EN_UNHANDLED;
+        }
+    }
+
+    HMAPX_FOR_EACH (n, &nd_changes->trk_routers.crupdated) {
+        const struct ovn_datapath *od = n->data;
+        struct ovn_port *op;
+        HMAP_FOR_EACH (op, dp_node, &od->ports) {
+            struct ovn_port *peer = op->peer;
+            if (peer && peer->od && peer->od->nbs &&
+                evpn_is_ip_redistribute_running(peer->od)) {
+                return EN_UNHANDLED;
+            }
+        }
+    }
+    HMAPX_FOR_EACH (n, &nd_changes->trk_routers.deleted) {
+        const struct ovn_datapath *od = n->data;
+        struct ovn_port *op;
+        HMAP_FOR_EACH (op, dp_node, &od->ports) {
+            struct ovn_port *peer = op->peer;
+            if (peer && peer->od && peer->od->nbs &&
+                evpn_is_ip_redistribute_running(peer->od)) {
+                return EN_UNHANDLED;
+            }
+        }
+    }
+
+    return EN_HANDLED_UNCHANGED;
+}
+
diff --git a/northd/en-advertised-route-sync.h 
b/northd/en-advertised-route-sync.h
index 7a2927000..2557a6c7f 100644
--- a/northd/en-advertised-route-sync.h
+++ b/northd/en-advertised-route-sync.h
@@ -42,4 +42,11 @@ enum engine_node_state en_advertised_route_sync_run(struct 
engine_node *,
 void *en_dynamic_routes_init(struct engine_node *, struct engine_arg *);
 void en_dynamic_routes_cleanup(void *data);
 enum engine_node_state en_dynamic_routes_run(struct engine_node *, void *data);
+
+void *en_evpn_type2_sync_init(struct engine_node *, struct engine_arg *);
+void en_evpn_type2_sync_cleanup(void *data);
+enum engine_node_state en_evpn_type2_sync_run(struct engine_node *,
+                                              void *data);
+enum engine_input_handler_result
+evpn_type2_sync_northd_change_handler(struct engine_node *, void *data);
 #endif /* EN_ADVERTISED_ROUTE_SYNC_H */
diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index fdea550d7..762c72f2d 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -118,7 +118,8 @@ static unixctl_cb_func chassis_features_list;
     SB_NODE(ecmp_nexthop) \
     SB_NODE(acl_id) \
     SB_NODE(advertised_route) \
-    SB_NODE(learned_route)
+    SB_NODE(learned_route) \
+    SB_NODE(advertised_mac_binding)
 
 enum sb_engine_node {
 #define SB_NODE(NAME) SB_##NAME,
@@ -181,6 +182,7 @@ static ENGINE_NODE(ecmp_nexthop);
 static ENGINE_NODE(multicast_igmp);
 static ENGINE_NODE(acl_id);
 static ENGINE_NODE(advertised_route_sync);
+static ENGINE_NODE(evpn_type2_sync);
 static ENGINE_NODE(learned_route_sync, CLEAR_TRACKED_DATA);
 static ENGINE_NODE(dynamic_routes);
 static ENGINE_NODE(group_ecmp_route, CLEAR_TRACKED_DATA);
@@ -355,6 +357,11 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_advertised_route_sync, &en_northd,
                      advertised_route_sync_northd_change_handler);
 
+    engine_add_input(&en_evpn_type2_sync, &en_sb_advertised_mac_binding, NULL);
+    engine_add_input(&en_evpn_type2_sync, &en_sb_port_binding, NULL);
+    engine_add_input(&en_evpn_type2_sync, &en_northd,
+                     evpn_type2_sync_northd_change_handler);
+
     engine_add_input(&en_learned_route_sync, &en_sb_learned_route,
                      learned_route_sync_sb_learned_route_change_handler);
     engine_add_input(&en_learned_route_sync, &en_northd,
@@ -452,6 +459,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_sync_from_sb, &en_sb_ha_chassis_group, NULL);
 
     engine_add_input(&en_northd_output, &en_acl_id, NULL);
+    engine_add_input(&en_northd_output, &en_evpn_type2_sync, NULL);
     engine_add_input(&en_northd_output, &en_sync_from_sb, NULL);
     engine_add_input(&en_northd_output, &en_sync_to_sb,
                      northd_output_sync_to_sb_handler);
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 73b5f213f..c2d16f5eb 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -977,6 +977,12 @@
           routers.
         </p>
 
+        <p>
+          If <code>ip</code> is specified then ovn-controller will advertise
+          all IPs/MACs bindings that are local to the chassis. The applies to
+          VIFs and router ports.
+        </p>
+
         <p>
           NOTE: this feature is experimental and may be subject to
           removal/change in the future.
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index f22141489..9fddf042d 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "21.6.0",
-    "cksum": "1200327755 35814",
+    "version": "21.7.0",
+    "cksum": "1565005821 36671",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -695,6 +695,22 @@
                "id": {"type": {"key": {"type": "integer",
                                               "minInteger": 0,
                                               "maxInteger": 32767}}}},
-           "isRoot": true}
+           "isRoot": true},
+        "Advertised_Mac_Binding": {
+            "columns": {
+                "datapath": {"type": {"key": {"type": "uuid",
+                                              "refTable": "Datapath_Binding",
+                                              "refType": "strong"}}},
+                "logical_port": {"type": {"key": {"type": "uuid",
+                                                  "refTable": "Port_Binding",
+                                                  "refType": "strong"}}},
+                "ip": {"type": "string"},
+                "mac": {"type": "string"},
+                "external_ids": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}}},
+            "indexes": [["datapath", "logical_port",
+                         "ip", "mac"]],
+            "isRoot": true}
     }
 }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index a781129b7..3eab5f998 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -5457,4 +5457,33 @@ tcp.flags = RST;
       <code>allow-established</code> ACL.
     </column>
   </table>
+
+  <table name="Advertised_Mac_Binding">
+    <p>
+      Each record represents an ip/mac binding the
+      <code>OVN_Northbound.Logical_Switch</code> is announcing outside
+      the network fabric if <code>EVPN</code> is enabled on the datapath.
+    </p>
+
+    <column name="datapath">
+      The datapath belonging to the
+      <code>OVN_Northbound.Logical_Switch</code> this type2 route belongs to.
+    </column>
+
+    <column name="logical_port">
+      This is the <ref table="Port_Binding"/> this type2 route belongs to.
+    </column>
+
+    <column name="ip">
+      Announced host route (e.g. 192.168.100.0/32).
+    </column>
+
+    <column name="mac">
+      Announced mac address.
+    </column>
+
+    <column name="external_ids">
+      See <em>External IDs</em> at the beginning of this document.
+    </column>
+  </table>
 </database>
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 07fb57bd6..66b36ae10 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -4077,7 +4077,7 @@ check_engine_stats northd recompute nocompute
 check_engine_stats bfd recompute nocompute
 check_engine_stats routes recompute nocompute
 check_engine_stats lflow recompute nocompute
-check_engine_stats northd_output norecompute compute
+check_engine_stats northd_output recompute nocompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 
@@ -4093,7 +4093,7 @@ check_engine_stats northd recompute nocompute
 check_engine_stats bfd recompute nocompute
 check_engine_stats routes recompute nocompute
 check_engine_stats lflow recompute nocompute
-check_engine_stats northd_output norecompute compute
+check_engine_stats northd_output recompute nocompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 
@@ -4105,7 +4105,7 @@ check_engine_stats northd recompute nocompute
 check_engine_stats bfd recompute nocompute
 check_engine_stats route_policies recompute nocompute
 check_engine_stats lflow recompute nocompute
-check_engine_stats northd_output norecompute compute
+check_engine_stats northd_output recompute nocompute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 
@@ -4140,7 +4140,7 @@ check_engine_stats northd recompute nocompute
 check_engine_stats bfd recompute nocompute
 check_engine_stats routes recompute nocompute
 check_engine_stats lflow recompute nocompute
-check_engine_stats northd_output norecompute compute
+check_engine_stats northd_output recompute compute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 
@@ -4155,7 +4155,7 @@ check_engine_stats northd recompute nocompute
 check_engine_stats bfd recompute nocompute
 check_engine_stats route_policies recompute nocompute
 check_engine_stats lflow recompute nocompute
-check_engine_stats northd_output norecompute compute
+check_engine_stats northd_output recompute compute
 CHECK_NO_CHANGE_AFTER_RECOMPUTE
 check as northd ovn-appctl -t ovn-northd inc-engine/clear-stats
 
@@ -18716,3 +18716,41 @@ AT_CHECK(
 
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD_NO_HV([
+AT_SETUP([LS EVPN Route Type2 sync])
+ovn_start ovn-northd
+
+AS_BOX([Create logical switch.])
+check ovn-nbctl ls-add ls-evpn                                          \
+    -- set logical_switch ls-evpn other_config:dynamic-routing-vni=10   \
+    -- lsp-add ls-evpn vm1                                              \
+    -- lsp-set-addresses vm1 "00:00:00:00:01:00 10.0.0.2"
+check ovn-nbctl --wait=sb sync
+check_row_count Advertised_Mac_Binding 0
+ls_evpn_uuid=$(fetch_column Datapath_Binding _uuid external_ids:name=ls-evpn)
+
+check ovn-nbctl --wait=sb set logical_switch ls-evpn 
other_config:dynamic-routing-redistribute=ip
+wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.2/32 datapath=$ls_evpn_uuid 
mac='00\:00\:00\:00\:01\:00'
+
+check ovn-nbctl lr-add lr                                       \
+    -- lrp-add lr lrp 00:00:00:00:01:01 10.0.0.1/24             \
+    -- lsp-add ls-evpn ls-evpn-lrp                              \
+    -- lsp-set-type ls-evpn-lrp router                          \
+    -- lsp-set-addresses ls-evpn-lrp 00:00:00:00:01:01          \
+    -- lsp-set-options ls-evpn-lrp router-port=lrp
+check ovn-nbctl --wait=sb sync
+
+wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.2/32 datapath=$ls_evpn_uuid 
mac='00\:00\:00\:00\:01\:00'
+wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.1/32 datapath=$ls_evpn_uuid 
mac='00\:00\:00\:00\:01\:01'
+check_row_count Advertised_Mac_Binding 2
+
+check ovn-nbctl --wait=sb lsp-del vm1
+wait_row_count Advertised_Mac_Binding 1 ip=10.0.0.1/32 datapath=$ls_evpn_uuid 
mac='00\:00\:00\:00\:01\:01'
+check_row_count Advertised_Mac_Binding 1
+
+check ovn-nbctl --wait=sb clear logical_switch ls-evpn other_config
+wait_row_count Advertised_Mac_Binding 0
+
+AT_CLEANUP
+])
-- 
2.51.0

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

Reply via email to