Add node that will hold data about recently added
MAC bindings. This node will be used later on to prevent
race condition in packet buffering. the MAC binding is
kept in the node longer than the timeout of the buffered
packet to make sure that we don't miss it.

Signed-off-by: Ales Musil <amu...@redhat.com>
---
v4: Rebase on top of current main.
    Split into three patches.
---
 controller/ovn-controller.c | 201 ++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 76be2426e..d465f00e9 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -45,6 +45,7 @@
 #include "lib/vswitch-idl.h"
 #include "local_data.h"
 #include "lport.h"
+#include "mac-learn.h"
 #include "memory.h"
 #include "ofctrl.h"
 #include "ofctrl-seqno.h"
@@ -4303,6 +4304,185 @@ pflow_output_debug_handler(struct engine_node *node, 
void *data)
     return true;
 }
 
+static void
+recent_mac_bindings_add(struct hmap *recent_mbs,
+                        const struct sbrec_mac_binding *smb,
+                        const struct hmap *local_datapaths,
+                        struct ovsdb_idl_index *sbrec_port_binding_by_name)
+{
+    ovs_be32 ip4;
+    struct in6_addr ip;
+    struct eth_addr mac;
+    const struct sbrec_port_binding *pb;
+
+    if (!get_local_datapath(local_datapaths, smb->datapath->tunnel_key)) {
+        return;
+    }
+
+    pb = lport_lookup_by_name(sbrec_port_binding_by_name, smb->logical_port);
+    if (!pb) {
+        return;
+    }
+
+    if (!eth_addr_from_string(smb->mac, &mac)) {
+        return;
+    }
+
+    if (strchr(smb->ip, '.')) {
+        if (!ip_parse(smb->ip, &ip4)) {
+            return;
+        }
+        ip = in6_addr_mapped_ipv4(ip4);
+    } else {
+        if (!ipv6_parse(smb->ip, &ip)) {
+            return;
+        }
+    }
+
+    /* Give the recent mac_binding entry bigger timeout than the buffered
+     * packet in case the MAC binding is created earlier. */
+    ovn_mac_binding_add(recent_mbs, smb->datapath->tunnel_key, pb->tunnel_key,
+                        &ip, mac, 2 * OVN_BUFFERED_PACKETS_TIMEOUT, false);
+
+    const char *redirect_port =
+        smap_get(&pb->options, "chassis-redirect-port");
+    if (!redirect_port) {
+        return;
+    }
+
+    pb = lport_lookup_by_name(sbrec_port_binding_by_name, redirect_port);
+    if (!pb || pb->datapath->tunnel_key != smb->datapath->tunnel_key ||
+        strcmp(pb->type, "chassisredirect")) {
+        return;
+    }
+
+    /* Add the same entry also for chassisredirect port as the buffered traffic
+     * might be buffered on the cr port. */
+    ovn_mac_binding_add(recent_mbs, smb->datapath->tunnel_key, pb->tunnel_key,
+                        &ip, mac, 2 * OVN_BUFFERED_PACKETS_TIMEOUT, false);
+}
+
+static void *
+en_recent_mac_bindings_init(struct engine_node *node OVS_UNUSED,
+                            struct engine_arg *arg OVS_UNUSED)
+{
+    struct hmap *recent_mbs = xmalloc(sizeof *recent_mbs);
+    hmap_init(recent_mbs);
+
+    return recent_mbs;
+}
+
+static void
+en_recent_mac_bindings_cleanup(void *data)
+{
+    struct hmap *recent_mbs = data;
+    ovn_mac_bindings_destroy(recent_mbs);
+}
+
+static void
+en_recent_mac_bindings_clear_tracked_data(void *data)
+{
+    long long now = time_msec();
+    struct hmap *recent_mbs = data;
+
+    struct mac_binding *mb;
+    HMAP_FOR_EACH_SAFE (mb, hmap_node, recent_mbs) {
+        if (ovn_mac_binding_is_expired(mb, now)) {
+            ovn_mac_binding_remove(mb, recent_mbs);
+        }
+    }
+}
+
+static void
+en_recent_mac_bindings_run(struct engine_node *node, void *data)
+{
+    struct hmap *recent_mbs = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    const struct sbrec_mac_binding_table *mac_binding_table =
+        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
+    struct ovsdb_idl_index *sbrec_port_binding_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_port_binding", node),
+            "name");
+
+    const struct sbrec_mac_binding *smb;
+    SBREC_MAC_BINDING_TABLE_FOR_EACH (smb, mac_binding_table) {
+        recent_mac_bindings_add(recent_mbs, smb, &rt_data->local_datapaths,
+                                sbrec_port_binding_by_name);
+    }
+}
+
+static bool
+recent_mac_bindings_mac_binding_handler(struct engine_node *node, void *data)
+{
+    struct hmap *recent_mbs = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    const struct sbrec_mac_binding_table *mac_binding_table =
+        EN_OVSDB_GET(engine_get_input("SB_mac_binding", node));
+    struct ovsdb_idl_index *sbrec_port_binding_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_port_binding", node),
+            "name");
+
+    const struct sbrec_mac_binding *smb;
+    SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (smb, mac_binding_table) {
+        /* The deleted entry will expire eventually. */
+        if (sbrec_mac_binding_is_deleted(smb)) {
+            continue;
+        }
+
+        recent_mac_bindings_add(recent_mbs, smb, &rt_data->local_datapaths,
+                                sbrec_port_binding_by_name);
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
+static bool
+recent_mac_bindings_runtime_data_handler(struct engine_node *node, void *data)
+{
+    struct hmap *recent_mbs = data;
+    struct ed_type_runtime_data *rt_data =
+        engine_get_input_data("runtime_data", node);
+    struct ovsdb_idl_index *smb_by_datapath =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_mac_binding", node),
+            "datapath");
+    struct ovsdb_idl_index *sbrec_port_binding_by_name =
+        engine_ovsdb_node_get_index(
+            engine_get_input("SB_port_binding", node),
+            "name");
+
+    if (!rt_data->tracked) {
+        return false;
+    }
+
+    struct tracked_datapath *tdp;
+    HMAP_FOR_EACH (tdp, node, &rt_data->tracked_dp_bindings) {
+        if (tdp->tracked_type != TRACKED_RESOURCE_NEW) {
+            continue;
+        }
+
+        struct sbrec_mac_binding *mb_index_row =
+            sbrec_mac_binding_index_init_row(smb_by_datapath);
+        sbrec_mac_binding_index_set_datapath(mb_index_row, tdp->dp);
+
+        const struct sbrec_mac_binding *smb;
+        SBREC_MAC_BINDING_FOR_EACH_EQUAL (smb, mb_index_row, smb_by_datapath) {
+            recent_mac_bindings_add(recent_mbs, smb, &rt_data->local_datapaths,
+                                    sbrec_port_binding_by_name);
+        }
+
+        sbrec_mac_binding_index_destroy_row(mb_index_row);
+    }
+
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 static void *
 en_controller_output_init(struct engine_node *node OVS_UNUSED,
                           struct engine_arg *arg OVS_UNUSED)
@@ -4339,6 +4519,14 @@ controller_output_lflow_output_handler(struct 
engine_node *node,
     return true;
 }
 
+static bool
+flow_output_recent_mac_bindings_handler(struct engine_node *node,
+                                        void *data OVS_UNUSED)
+{
+    engine_set_node_state(node, EN_UPDATED);
+    return true;
+}
+
 struct ovn_controller_exit_args {
     bool *exiting;
     bool *restart;
@@ -4611,6 +4799,8 @@ main(int argc, char *argv[])
     ENGINE_NODE(northd_options, "northd_options");
     ENGINE_NODE(dhcp_options, "dhcp_options");
     ENGINE_NODE_WITH_CLEAR_TRACK_DATA(lb_data, "lb_data");
+    ENGINE_NODE_WITH_CLEAR_TRACK_DATA(recent_mac_bindings,
+                                      "recent_mac_bindings");
 
 #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
     SB_NODES
@@ -4783,10 +4973,21 @@ main(int argc, char *argv[])
     engine_add_input(&en_runtime_data, &en_ovs_interface_shadow,
                      runtime_data_ovs_interface_shadow_handler);
 
+    engine_add_input(&en_recent_mac_bindings, &en_sb_mac_binding,
+                     recent_mac_bindings_mac_binding_handler);
+    engine_add_input(&en_recent_mac_bindings, &en_runtime_data,
+                     recent_mac_bindings_runtime_data_handler);
+    /* Using a noop handler since we really need just access to the index
+     * which allows us to filter port binding by name. */
+    engine_add_input(&en_recent_mac_bindings, &en_sb_port_binding,
+                     engine_noop_handler);
+
     engine_add_input(&en_controller_output, &en_lflow_output,
                      controller_output_lflow_output_handler);
     engine_add_input(&en_controller_output, &en_pflow_output,
                      controller_output_pflow_output_handler);
+    engine_add_input(&en_controller_output, &en_recent_mac_bindings,
+                     flow_output_recent_mac_bindings_handler);
 
     struct engine_arg engine_arg = {
         .sb_idl = ovnsb_idl_loop.idl,
-- 
2.39.2

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to