[ovs-dev] [PATCH v2] ofproto-dpif: APIs and CLI option to add/delete static fdb entry

Vasu Dasari vdasari at gmail.com
Sat May 8 22:18:41 UTC 2021


Currently there is an option to add/flush/show ARP/ND neighbor. This covers L3
side.  For L2 side, there is only fdb show command. This patch gives an option
to add/del an fdb entry via CLI.

CLI command looks like:

To add:
    ovs-appctl fdb/add <bridge> <port> <vlan> <Mac>
    ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05

To del:
    ovs-appctl fdb/del <bridge> <port> <vlan> <Mac>
    ovs-appctl fdb/del br0 p1 0 50:54:00:00:00:05

Static entry should not age. To indicate that entry being programmed is a static entry,
'expires' field in 'struct mac_entry' will be set to a INT_MAX. A check for this value
is made while deleting mac entry as part of regular aging process. Another check as part
of mac-update process, when a packet with same source mac as this entry arrives on the
configured port will not modify the expires field

Added two new APIs to provide convinient interfacde to add and delete static-macs
void xlate_add_static_mac_entry(const struct ofproto_dpif *, ofp_port_t in_port,
                               struct eth_addr dl_src, int vlan);
void xlate_delete_static_mac_entry(const struct ofproto_dpif *,
                                  struct eth_addr dl_src, int vlan);

Signed-off-by: Vasu Dasari <vdasari at gmail.com>
Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2019-June/048894.html
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1597752

---
v1:
 - Fixed 0-day robot warnings
 v2:
 - Fix valgrind error in the modified code in mac_learning_insert() where a read is
   is performed on e->expires which is not initialized
---
 lib/mac-learning.c           | 27 ++++++++++++++++++---
 lib/mac-learning.h           |  3 +++
 ofproto/ofproto-dpif-xlate.c | 26 ++++++++++++++++++++
 ofproto/ofproto-dpif-xlate.h |  5 ++++
 ofproto/ofproto-dpif.c       | 47 ++++++++++++++++++++++++++++++++++++
 tests/ofproto-dpif.at        | 40 ++++++++++++++++++++++++++++++
 6 files changed, 145 insertions(+), 3 deletions(-)

diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index 9442858d9..437db894c 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -39,8 +39,13 @@ COVERAGE_DEFINE(mac_learning_moved);
 int
 mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e)
 {
-    time_t remaining = e->expires - time_now();
-    return ml->idle_time - remaining;
+    /* For static fdb entries, expires would be initialized with INT_MAX */
+    if (INT_MAX == e->expires) {
+        return -1;
+    } else {
+        time_t remaining = e->expires - time_now();
+        return ml->idle_time - remaining;
+    }
 }
 
 static uint32_t
@@ -282,6 +287,18 @@ mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time)
     }
 }
 
+/* Changes the MAC aging timeout of a mac_entry to 'idle_time' seconds. */
+void
+mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr mac,
+        int vlan, unsigned int idle_time)
+{
+    struct mac_entry *e;
+    e = mac_entry_lookup(ml, mac, vlan);
+    if (e) {
+        e->expires = idle_time;
+    }
+}
+
 /* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it
  * to be within a reasonable range. */
 void
@@ -336,6 +353,7 @@ mac_learning_insert(struct mac_learning *ml,
         e->vlan = vlan;
         e->grat_arp_lock = TIME_MIN;
         e->mlport = NULL;
+        e->expires = 0;
         COVERAGE_INC(mac_learning_learned);
         ml->total_learned++;
     } else {
@@ -348,7 +366,10 @@ mac_learning_insert(struct mac_learning *ml,
         ovs_list_remove(&e->port_lru_node);
         ovs_list_push_back(&e->mlport->port_lrus, &e->port_lru_node);
     }
-    e->expires = time_now() + ml->idle_time;
+    /* Do not update 'expires' for static mac entry */
+    if (e->expires != INT_MAX) {
+        e->expires = time_now() + ml->idle_time;
+    }
 
     return e;
 }
diff --git a/lib/mac-learning.h b/lib/mac-learning.h
index 0ddab06cb..1137aae63 100644
--- a/lib/mac-learning.h
+++ b/lib/mac-learning.h
@@ -202,6 +202,9 @@ bool mac_learning_set_flood_vlans(struct mac_learning *ml,
 void mac_learning_set_idle_time(struct mac_learning *ml,
                                 unsigned int idle_time)
     OVS_REQ_WRLOCK(ml->rwlock);
+void mac_entry_set_idle_time(struct mac_learning *ml, struct eth_addr src,
+                             int vlan, unsigned int idle_time)
+    OVS_REQ_WRLOCK(ml->rwlock);
 void mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries)
     OVS_REQ_WRLOCK(ml->rwlock);
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 7108c8a30..8580b39f4 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -8011,6 +8011,32 @@ xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
     update_learning_table__(xbridge, xbundle, dl_src, vlan, is_grat_arp);
 }
 
+void
+xlate_add_static_mac_entry(const struct ofproto_dpif *ofproto,
+                           ofp_port_t in_port,
+                           struct eth_addr dl_src, int vlan)
+{
+    xlate_mac_learning_update(ofproto, in_port, dl_src, vlan, false);
+
+    ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+    mac_entry_set_idle_time(ofproto->ml, dl_src, vlan, INT_MAX);
+    ovs_rwlock_unlock(&ofproto->ml->rwlock);
+}
+
+void
+xlate_delete_static_mac_entry(const struct ofproto_dpif *ofproto,
+                              struct eth_addr dl_src, int vlan)
+{
+    struct mac_entry *e = NULL;
+
+    ovs_rwlock_wrlock(&ofproto->ml->rwlock);
+    e = mac_learning_lookup(ofproto->ml, dl_src, vlan);
+    if (e) {
+        mac_learning_expire(ofproto->ml, e);
+    }
+    ovs_rwlock_unlock(&ofproto->ml->rwlock);
+}
+
 void
 xlate_set_support(const struct ofproto_dpif *ofproto,
                     const struct dpif_backer_support *support)
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 3426a27b2..9e6e95756 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -225,6 +225,11 @@ int xlate_send_packet(const struct ofport_dpif *, bool oam, struct dp_packet *);
 void xlate_mac_learning_update(const struct ofproto_dpif *ofproto,
                                ofp_port_t in_port, struct eth_addr dl_src,
                                int vlan, bool is_grat_arp);
+void xlate_add_static_mac_entry(const struct ofproto_dpif *,
+                                ofp_port_t in_port,
+                                struct eth_addr dl_src, int vlan);
+void xlate_delete_static_mac_entry(const struct ofproto_dpif *,
+                                  struct eth_addr dl_src, int vlan);
 
 void xlate_set_support(const struct ofproto_dpif *,
                        const struct dpif_backer_support *);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index fd0b2fdea..2d2c60c0b 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5864,6 +5864,49 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
     ds_destroy(&ds);
 }
 
+static void
+ofproto_unixctl_fdb_update(struct unixctl_conn *conn, int argc OVS_UNUSED,
+                         const char *argv[], void *aux OVS_UNUSED)
+{
+    const char *br_name = argv[1];
+    const char *name = argv[2];
+    struct eth_addr mac;
+    uint16_t vlan = atoi(argv[3]);
+    const char *op = (const char *) aux;
+    const struct ofproto_dpif *ofproto;
+    ofp_port_t    in_port = OFPP_NONE;
+    struct ofproto_port ofproto_port;
+
+    ofproto = ofproto_dpif_lookup_by_name(br_name);
+    if (!ofproto) {
+        unixctl_command_reply_error(conn, "no such bridge");
+        return;
+    }
+
+    if (!eth_addr_from_string(argv[4], &mac)) {
+        unixctl_command_reply_error(conn, "bad MAC address");
+        return;
+    }
+
+    if (ofproto_port_query_by_name(&ofproto->up, name, &ofproto_port)) {
+        unixctl_command_reply_error(conn,
+                "software error, odp port is present but no ofp port");
+        return;
+    }
+    in_port = ofproto_port.ofp_port;
+    ofproto_port_destroy(&ofproto_port);
+
+    if (!strcmp(op, "add")) {
+        xlate_add_static_mac_entry(ofproto, in_port, mac, vlan);
+        unixctl_command_reply(conn, "OK");
+    } else if (!strcmp(op, "del")) {
+        xlate_delete_static_mac_entry(ofproto, mac, vlan);
+        unixctl_command_reply(conn, "OK");
+    } else {
+        unixctl_command_reply_error(conn, "software error, unknown op");
+    }
+}
+
 static void
 ofproto_unixctl_fdb_stats_clear(struct unixctl_conn *conn, int argc,
                                 const char *argv[], void *aux OVS_UNUSED)
@@ -6415,6 +6458,10 @@ ofproto_unixctl_init(void)
     }
     registered = true;
 
+    unixctl_command_register("fdb/add", "[bridge port vlan mac]", 4, 4,
+                             ofproto_unixctl_fdb_update, "add");
+    unixctl_command_register("fdb/del", "[bridge port vlan mac]", 4, 4,
+                             ofproto_unixctl_fdb_update, "del");
     unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
                              ofproto_unixctl_fdb_flush, NULL);
     unixctl_command_register("fdb/show", "bridge", 1, 1,
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 24bbd884c..95b8dd7ce 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -6753,6 +6753,46 @@ PORTNAME
         portName=p2
 ])])
 
+AT_SETUP([ofproto-dpif - static MAC programming])
+OVS_VSWITCHD_START([set bridge br0 fail-mode=standalone])
+add_of_ports br0 1 2
+
+arp='eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+
+AT_CHECK([ovs-appctl fdb/add br0 p1 0 50:54:00:00:00:05], [0], [OK
+])
+
+dnl Check for the static MAC entry is programmed
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+    1     0  50:54:00:00:00:05   -1
+])
+
+dnl Trace an ARP packet arriving on p1
+OFPROTO_TRACE(
+  [ovs-dummy],
+  [in_port(1),eth(src=50:54:00:00:00:05,dst=ff:ff:ff:ff:ff:ff),$arp],
+  [-generate],
+  [2,100])
+
+dnl Check that age on static MAC entry is not altered because of above learning event
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+    1     0  50:54:00:00:00:05   -1
+])
+
+dnl Remove static mac entry
+AT_CHECK([ovs-appctl fdb/del br0 p1 0 50:54:00:00:00:05], [0], [OK
+])
+
+dnl Check that entry is removed
+AT_CHECK([ovs-appctl fdb/show br0], [0], [dnl
+ port  VLAN  MAC                Age
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - basic truncate action])
 OVS_VSWITCHD_START
 add_of_ports br0 1 2 3 4 5
-- 
2.29.2



More information about the dev mailing list