Blob Blame History Raw
From 853c8bfd101109fce7b7422486ddaff4e2e6aa03 Mon Sep 17 00:00:00 2001
From: Numan Siddique <numans@ovn.org>
Date: Fri, 29 Jan 2021 14:46:30 +0530
Subject: [PATCH 07/16] MAC learning: Add a new FDB table in southbound db.

This patch
  - Adds a new table 'FDB' in Southbound database.
  - Adds a new pinctrl_handler action - ACTION_OPCODE_PUT_FDB
    which learns the port-to-mac bindings and stores it in the
    'FDB' table. This handler will be used later.
  - Upcoming patch adds necessary OVN actions to learn the port-to-mac
    bindings.

Unlike MAC_Binding Southbound table, FDB table stores the datapath
tunnel key and port key.  This makes it easier for ovn-controller to
program the OF rules and it doesn't need to do any logical port
lookup in the Port_Binding table.

Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Numan Siddique <numans@ovn.org>
(cherry picked from upstream commit 6ec3b12590f9193659d549e30d96b1a22bbb1288)

Change-Id: Iaff80667e71d20fbab434fb3031b2222e22425aa
---
 controller/mac-learn.c      |  79 +++++++++++++++++++++++++++++
 controller/mac-learn.h      |  20 ++++++++
 controller/ovn-controller.c |   5 ++
 controller/pinctrl.c        | 118 ++++++++++++++++++++++++++++++++++++++++++++
 controller/pinctrl.h        |   1 +
 include/ovn/actions.h       |   4 ++
 ovn-sb.ovsschema            |  17 ++++++-
 ovn-sb.xml                  |  24 +++++++++
 8 files changed, 266 insertions(+), 2 deletions(-)

diff --git a/controller/mac-learn.c b/controller/mac-learn.c
index 36a6d6e..27634dc 100644
--- a/controller/mac-learn.c
+++ b/controller/mac-learn.c
@@ -19,11 +19,13 @@
 
 /* OpenvSwitch lib includes. */
 #include "openvswitch/vlog.h"
+#include "lib/packets.h"
 #include "lib/smap.h"
 
 VLOG_DEFINE_THIS_MODULE(mac_learn);
 
 #define MAX_MAC_BINDINGS 1000
+#define MAX_FDB_ENTRIES  1000
 
 static size_t mac_binding_hash(uint32_t dp_key, uint32_t port_key,
                                struct in6_addr *);
@@ -31,7 +33,12 @@ static struct mac_binding *mac_binding_find(struct hmap *mac_bindings,
                                             uint32_t dp_key,
                                             uint32_t port_key,
                                             struct in6_addr *ip, size_t hash);
+static size_t fdb_entry_hash(uint32_t dp_key, struct eth_addr *);
 
+static struct fdb_entry *fdb_entry_find(struct hmap *fdbs, uint32_t dp_key,
+                                        struct eth_addr *mac, size_t hash);
+
+/* mac_binding functions. */
 void
 ovn_mac_bindings_init(struct hmap *mac_bindings)
 {
@@ -79,6 +86,55 @@ ovn_mac_binding_add(struct hmap *mac_bindings, uint32_t dp_key,
     return mb;
 }
 
+/* fdb functions. */
+void
+ovn_fdb_init(struct hmap *fdbs)
+{
+    hmap_init(fdbs);
+}
+
+void
+ovn_fdbs_flush(struct hmap *fdbs)
+{
+    struct fdb_entry *fdb_e;
+    HMAP_FOR_EACH_POP (fdb_e, hmap_node, fdbs) {
+        free(fdb_e);
+    }
+}
+
+void
+ovn_fdbs_destroy(struct hmap *fdbs)
+{
+   ovn_fdbs_flush(fdbs);
+   hmap_destroy(fdbs);
+}
+
+struct fdb_entry *
+ovn_fdb_add(struct hmap *fdbs, uint32_t dp_key, struct eth_addr mac,
+            uint32_t port_key)
+{
+    uint32_t hash = fdb_entry_hash(dp_key, &mac);
+
+    struct fdb_entry *fdb_e =
+        fdb_entry_find(fdbs, dp_key, &mac, hash);
+    if (!fdb_e) {
+        if (hmap_count(fdbs) >= MAX_FDB_ENTRIES) {
+            return NULL;
+        }
+
+        fdb_e = xzalloc(sizeof *fdb_e);
+        fdb_e->dp_key = dp_key;
+        fdb_e->mac = mac;
+        hmap_insert(fdbs, &fdb_e->hmap_node, hash);
+    }
+    fdb_e->port_key = port_key;
+
+    return fdb_e;
+
+}
+
+/* mac_binding related static functions. */
+
 static size_t
 mac_binding_hash(uint32_t dp_key, uint32_t port_key, struct in6_addr *ip)
 {
@@ -99,3 +155,26 @@ mac_binding_find(struct hmap *mac_bindings, uint32_t dp_key,
 
     return NULL;
 }
+
+/* fdb related static functions. */
+
+static size_t
+fdb_entry_hash(uint32_t dp_key, struct eth_addr *mac)
+{
+    uint64_t mac64 = eth_addr_to_uint64(*mac);
+    return hash_2words(dp_key, hash_uint64(mac64));
+}
+
+static struct fdb_entry *
+fdb_entry_find(struct hmap *fdbs, uint32_t dp_key,
+               struct eth_addr *mac, size_t hash)
+{
+    struct fdb_entry *fdb_e;
+    HMAP_FOR_EACH_WITH_HASH (fdb_e, hmap_node, hash, fdbs) {
+        if (fdb_e->dp_key == dp_key && eth_addr_equals(fdb_e->mac, *mac)) {
+            return fdb_e;
+        }
+    }
+
+    return NULL;
+}
diff --git a/controller/mac-learn.h b/controller/mac-learn.h
index 3a8520c..7a7897e 100644
--- a/controller/mac-learn.h
+++ b/controller/mac-learn.h
@@ -43,4 +43,24 @@ struct mac_binding *ovn_mac_binding_add(struct hmap *mac_bindings,
                                         struct eth_addr mac);
 
 
+
+struct fdb_entry {
+    struct hmap_node hmap_node; /* In a hmap. */
+
+    /* Key. */
+    uint32_t dp_key;
+    struct eth_addr mac;
+
+    /* value. */
+    uint32_t port_key;
+};
+
+void ovn_fdb_init(struct hmap *fdbs);
+void ovn_fdbs_flush(struct hmap *fdbs);
+void ovn_fdbs_destroy(struct hmap *fdbs);
+
+struct fdb_entry *ovn_fdb_add(struct hmap *fdbs,
+                                uint32_t dp_key, struct eth_addr mac,
+                                uint32_t port_key);
+
 #endif /* OVN_MAC_LEARN_H */
\ No newline at end of file
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index fc2a767..cc002db 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -2471,6 +2471,10 @@ main(int argc, char *argv[])
         = ip_mcast_index_create(ovnsb_idl_loop.idl);
     struct ovsdb_idl_index *sbrec_igmp_group
         = igmp_group_index_create(ovnsb_idl_loop.idl);
+    struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac
+        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
+                                  &sbrec_fdb_col_mac,
+                                  &sbrec_fdb_col_dp_key);
 
     ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
     ovsdb_idl_omit_alert(ovnsb_idl_loop.idl,
@@ -2864,6 +2868,7 @@ main(int argc, char *argv[])
                                     sbrec_mac_binding_by_lport_ip,
                                     sbrec_igmp_group,
                                     sbrec_ip_multicast,
+                                    sbrec_fdb_by_dp_key_mac,
                                     sbrec_dns_table_get(ovnsb_idl_loop.idl),
                                     sbrec_controller_event_table_get(
                                         ovnsb_idl_loop.idl),
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 0f258ba..3dc1038 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -340,6 +340,22 @@ static void bfd_monitor_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                             const struct sbrec_chassis *chassis,
                             const struct sset *active_tunnels)
                             OVS_REQUIRES(pinctrl_mutex);
+static void init_fdb_entries(void);
+static void destroy_fdb_entries(void);
+static const struct sbrec_fdb *fdb_lookup(
+    struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
+    uint32_t dp_key, const char *mac);
+static void run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                        struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
+                        const struct fdb_entry *fdb_e)
+                        OVS_REQUIRES(pinctrl_mutex);
+static void run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                        struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac)
+                        OVS_REQUIRES(pinctrl_mutex);
+static void wait_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn);
+static void pinctrl_handle_put_fdb(const struct flow *md,
+                                   const struct flow *headers)
+                                   OVS_REQUIRES(pinctrl_mutex);
 
 COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
 COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
@@ -506,6 +522,7 @@ pinctrl_init(void)
     init_put_vport_bindings();
     init_svc_monitors();
     bfd_monitor_init();
+    init_fdb_entries();
     pinctrl.br_int_name = NULL;
     pinctrl_handler_seq = seq_create();
     pinctrl_main_seq = seq_create();
@@ -3020,6 +3037,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
         ovs_mutex_unlock(&pinctrl_mutex);
         break;
 
+    case ACTION_OPCODE_PUT_FDB:
+        ovs_mutex_lock(&pinctrl_mutex);
+        pinctrl_handle_put_fdb(&pin.flow_metadata.flow, &headers);
+        ovs_mutex_unlock(&pinctrl_mutex);
+        break;
+
     case ACTION_OPCODE_PUT_DHCPV6_OPTS:
         pinctrl_handle_put_dhcpv6_opts(swconn, &packet, &pin, &userdata,
                                        &continuation);
@@ -3297,6 +3320,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
             struct ovsdb_idl_index *sbrec_igmp_groups,
             struct ovsdb_idl_index *sbrec_ip_multicast_opts,
+            struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
             const struct sbrec_dns_table *dns_table,
             const struct sbrec_controller_event_table *ce_table,
             const struct sbrec_service_monitor_table *svc_mon_table,
@@ -3333,6 +3357,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                       chassis);
     bfd_monitor_run(ovnsb_idl_txn, bfd_table, sbrec_port_binding_by_name,
                     chassis, active_tunnels);
+    run_put_fdbs(ovnsb_idl_txn, sbrec_fdb_by_dp_key_mac);
     ovs_mutex_unlock(&pinctrl_mutex);
 }
 
@@ -3856,6 +3881,7 @@ pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn)
     wait_put_vport_bindings(ovnsb_idl_txn);
     int64_t new_seq = seq_read(pinctrl_main_seq);
     seq_wait(pinctrl_main_seq, new_seq);
+    wait_put_fdbs(ovnsb_idl_txn);
 }
 
 /* Called by ovn-controller. */
@@ -3877,6 +3903,7 @@ pinctrl_destroy(void)
     ip_mcast_snoop_destroy();
     destroy_svc_monitors();
     bfd_monitor_destroy();
+    destroy_fdb_entries();
     seq_destroy(pinctrl_main_seq);
     seq_destroy(pinctrl_handler_seq);
 }
@@ -7495,3 +7522,94 @@ pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
         svc_mon->next_send_time = time_msec() + svc_mon->interval;
     }
 }
+
+static struct hmap put_fdbs;
+
+/* MAC learning (fdb) related functions.  Runs within the main
+ * ovn-controller thread context. */
+
+static void
+init_fdb_entries(void)
+{
+    ovn_fdb_init(&put_fdbs);
+}
+
+static void
+destroy_fdb_entries(void)
+{
+    ovn_fdbs_destroy(&put_fdbs);
+}
+
+static const struct sbrec_fdb *
+fdb_lookup(struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac, uint32_t dp_key,
+           const char *mac)
+{
+    struct sbrec_fdb *fdb = sbrec_fdb_index_init_row(sbrec_fdb_by_dp_key_mac);
+    sbrec_fdb_index_set_dp_key(fdb, dp_key);
+    sbrec_fdb_index_set_mac(fdb, mac);
+
+    const struct sbrec_fdb *retval
+        = sbrec_fdb_index_find(sbrec_fdb_by_dp_key_mac, fdb);
+
+    sbrec_fdb_index_destroy_row(fdb);
+
+    return retval;
+}
+
+static void
+run_put_fdb(struct ovsdb_idl_txn *ovnsb_idl_txn,
+            struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
+            const struct fdb_entry *fdb_e)
+{
+    /* Convert ethernet argument to string form for database. */
+    char mac_string[ETH_ADDR_STRLEN + 1];
+    snprintf(mac_string, sizeof mac_string,
+             ETH_ADDR_FMT, ETH_ADDR_ARGS(fdb_e->mac));
+
+    /* Update or add an FDB entry. */
+    const struct sbrec_fdb *sb_fdb =
+        fdb_lookup(sbrec_fdb_by_dp_key_mac, fdb_e->dp_key, mac_string);
+    if (!sb_fdb) {
+        sb_fdb = sbrec_fdb_insert(ovnsb_idl_txn);
+        sbrec_fdb_set_dp_key(sb_fdb, fdb_e->dp_key);
+        sbrec_fdb_set_mac(sb_fdb, mac_string);
+    }
+    sbrec_fdb_set_port_key(sb_fdb, fdb_e->port_key);
+}
+
+static void
+run_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn,
+             struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac)
+             OVS_REQUIRES(pinctrl_mutex)
+{
+    if (!ovnsb_idl_txn) {
+        return;
+    }
+
+    const struct fdb_entry *fdb_e;
+    HMAP_FOR_EACH (fdb_e, hmap_node, &put_fdbs) {
+        run_put_fdb(ovnsb_idl_txn, sbrec_fdb_by_dp_key_mac, fdb_e);
+    }
+    ovn_fdbs_flush(&put_fdbs);
+}
+
+
+static void
+wait_put_fdbs(struct ovsdb_idl_txn *ovnsb_idl_txn)
+{
+    if (ovnsb_idl_txn && !hmap_is_empty(&put_fdbs)) {
+        poll_immediate_wake();
+    }
+}
+
+/* Called with in the pinctrl_handler thread context. */
+static void
+pinctrl_handle_put_fdb(const struct flow *md, const struct flow *headers)
+                       OVS_REQUIRES(pinctrl_mutex)
+{
+    uint32_t dp_key = ntohll(md->metadata);
+    uint32_t port_key = md->regs[MFF_LOG_INPORT - MFF_REG0];
+
+    ovn_fdb_add(&put_fdbs, dp_key, headers->dl_src, port_key);
+    notify_pinctrl_main();
+}
diff --git a/controller/pinctrl.h b/controller/pinctrl.h
index 8555d98..cc0a519 100644
--- a/controller/pinctrl.h
+++ b/controller/pinctrl.h
@@ -42,6 +42,7 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
                  struct ovsdb_idl_index *sbrec_igmp_groups,
                  struct ovsdb_idl_index *sbrec_ip_multicast_opts,
+                 struct ovsdb_idl_index *sbrec_fdb_by_dp_key_mac,
                  const struct sbrec_dns_table *,
                  const struct sbrec_controller_event_table *,
                  const struct sbrec_service_monitor_table *,
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index be87e61..a2d28c6 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -641,6 +641,10 @@ enum action_opcode {
      * The actions, in OpenFlow 1.3 format, follow the action_header.
      */
     ACTION_OPCODE_SCTP_ABORT,
+
+    /* put_fdb(inport, eth.src).
+     */
+    ACTION_OPCODE_PUT_FDB,
 };
 
 /* Header. */
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
index 0d20f08..246e390 100644
--- a/ovn-sb.ovsschema
+++ b/ovn-sb.ovsschema
@@ -1,7 +1,7 @@
 {
     "name": "OVN_Southbound",
-    "version": "20.15.0",
-    "cksum": "539683023 25965",
+    "version": "20.16.0",
+    "cksum": "1219580357 26536",
     "tables": {
         "SB_Global": {
             "columns": {
@@ -513,6 +513,19 @@
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
             "indexes": [["logical_port", "dst_ip", "src_port", "disc"]],
+            "isRoot": true},
+        "FDB": {
+            "columns": {
+                "mac": {"type": "string"},
+                "dp_key": {
+                     "type": {"key": {"type": "integer",
+                                      "minInteger": 1,
+                                      "maxInteger": 16777215}}},
+                "port_key": {
+                     "type": {"key": {"type": "integer",
+                                      "minInteger": 1,
+                                      "maxInteger": 16777215}}}},
+            "indexes": [["mac", "dp_key"]],
             "isRoot": true}
     }
 }
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 2f251bd..b8912b3 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -4330,4 +4330,28 @@ tcp.flags = RST;
       </column>
     </group>
   </table>
+
+  <table name="FDB" title="Port to MAC bindings">
+    <p>
+      This table is primarily used to learn the MACs observed on a VIF
+      which belongs to a <code>Logical_Switch_Port</code> record in
+      <code>OVN_Northbound</code> whose port security is disabled
+      and 'unknown' address set.  If port security is disabled on a
+      <code>Logical_Switch_Port</code> record, OVN should allow traffic
+      with any source mac from the VIF.  This table will be used to deliver
+      a packet to the VIF, If a packet's <code>eth.dst</code> is learnt.
+    </p>
+
+    <column name="mac">
+      The learnt mac address.
+    </column>
+
+    <column name="dp_key">
+      The key of the datapath on which this FDB was learnt.
+    </column>
+
+    <column name="port_key">
+      The key of the port binding on which this FDB was learnt.
+    </column>
+  </table>
 </database>
-- 
1.8.3.1