[ovs-dev] [RFC PATCH v1 4/6] L3 N-S support in ovn, advertise router port mac from gateway chassis

Ankur Sharma ankur.sharma at nutanix.com
Wed Feb 20 22:34:57 UTC 2019


Background:
[1] https://mail.openvswitch.org/pipermail/ovs-dev/2018-October/353066.html
[2] https://docs.google.com/document/d/1uoQH478wM1OZ16HrxzbOUvk5LvFnfNEWbkPT6Zmm9OU/edit?usp=sharing

This Series:
Layer 2, Layer 3 E-W and Layer 3 N-S (NO NAT) changes for vlan
backed distributed logical router.

This Patch:
a. For N-S (No NAT) case, we will rely on gateway-chassis construct.
   i.e on a logical router, one router port will be assigned gateway
   chassis(s) and this router port becomes the gateway for north to south
   traffic.

b. This patch adds changes to advertise router port (cr-lrp-*) mac on its
   active gateway chassis, by sending GARP.

c. Additionally, patch adds support for sending the GARP series periodically.
   i.e router port mac will be advertised periodically
   (not just when it is realized on a chassis). This is needed because when
   we add NATing support, then south to north traffic will also go via
   gateway chassis.

Signed-off-by: Ankur Sharma <ankur.sharma at nutanix.com>
---
 ovn/controller/pinctrl.c | 173 ++++++++++++++++++++++++++++++++++++++++++++---
 ovn/lib/ovn-util.c       |  31 +++++++++
 ovn/lib/ovn-util.h       |   6 ++
 3 files changed, 201 insertions(+), 9 deletions(-)

diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 5aebbfc..77d28ac 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -67,6 +67,13 @@ static void destroy_buffered_packets_map(void);
 static void pinctrl_handle_put_mac_binding(const struct flow *md,
                                            const struct flow *headers,
                                            bool is_arp);
+static bool
+pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_chassis_by_name,
+                            struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                            const struct sbrec_chassis *chassis,
+                            const struct sset *active_tunnels,
+                            const char *port_name);
+
 static void init_put_mac_bindings(void);
 static void destroy_put_mac_bindings(void);
 static void run_put_mac_bindings(
@@ -114,6 +121,8 @@ static void send_ipv6_ras(
 COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
 COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
 
+#define GARP_DEF_REPEAT_INTERVAL_MS   (3 * 60 * 1000) // 3 mins
+
 void
 pinctrl_init(void)
 {
@@ -2089,6 +2098,8 @@ struct garp_data {
     int backoff;                 /* Backoff for the next announcement. */
     uint32_t dp_key;             /* Datapath used to output this GARP. */
     uint32_t port_key;           /* Port to inject the GARP into. */
+    bool is_repeat;              /* Send GARPs continously */
+    long long int repeat_interval; /* Interval between GARP bursts in ms */
 };
 
 /* Contains GARPs to be sent. */
@@ -2112,7 +2123,8 @@ destroy_send_garps(void)
 
 static void
 add_garp(const char *name, const struct eth_addr ea, ovs_be32 ip,
-         uint32_t dp_key, uint32_t port_key)
+         uint32_t dp_key, uint32_t port_key, bool is_repeat,
+         long long int repeat_interval)
 {
     struct garp_data *garp = xmalloc(sizeof *garp);
     garp->ea = ea;
@@ -2121,15 +2133,19 @@ add_garp(const char *name, const struct eth_addr ea, ovs_be32 ip,
     garp->backoff = 1;
     garp->dp_key = dp_key;
     garp->port_key = port_key;
+    garp->is_repeat = is_repeat;
+    garp->repeat_interval = repeat_interval;
     shash_add(&send_garp_data, name, garp);
 }
 
 /* Add or update a vif for which GARPs need to be announced. */
 static void
-send_garp_update(const struct sbrec_port_binding *binding_rec,
+send_garp_update(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                 const struct sbrec_port_binding *binding_rec,
                  struct shash *nat_addresses)
 {
     volatile struct garp_data *garp = NULL;
+
     /* Update GARP for NAT IP if it exists.  Consider port bindings with type
      * "l3gateway" for logical switch ports attached to gateway routers, and
      * port bindings with type "patch" for logical switch ports attached to
@@ -2151,7 +2167,7 @@ send_garp_update(const struct sbrec_port_binding *binding_rec,
                     add_garp(name, laddrs->ea,
                              laddrs->ipv4_addrs[i].addr,
                              binding_rec->datapath->tunnel_key,
-                             binding_rec->tunnel_key);
+                             binding_rec->tunnel_key, false, 0);
                 }
                 free(name);
             }
@@ -2161,6 +2177,60 @@ send_garp_update(const struct sbrec_port_binding *binding_rec,
         return;
     }
 
+    /* Update GARPs for local chassisredirect port, if the peer
+     * layer 2 switch is of type vlan.
+     */
+    if (!strcmp(binding_rec->type, "chassisredirect")) {
+       struct eth_addr mac;
+       ovs_be32 ip, mask;
+       uint32_t dp_key = 0;
+       uint32_t port_key = 0;
+       const struct sbrec_port_binding *peer_port = NULL;
+       const struct sbrec_port_binding *distributed_port = NULL;
+
+       if (!ovn_sbrec_get_port_binding_ip_mac(binding_rec, &mac,
+                                              &ip, &mask)) {
+          // Router Port binding without ip and mac configured.
+          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+          VLOG_WARN_RL(&rl, "cannot send garp, router port binding: %s, "
+                       "does not have proper ip,mac values: %s",
+                       binding_rec->logical_port, *binding_rec->mac);
+          return;
+       }
+
+       const char *lrp_name = smap_get(&binding_rec->options, "distributed-port");
+       ovs_assert(lrp_name);
+
+       distributed_port = lport_lookup_by_name(sbrec_port_binding_by_name, lrp_name);
+       ovs_assert(distributed_port);
+
+       const char *peer_name = smap_get(&distributed_port->options, "peer");
+       ovs_assert(peer_name);
+
+       peer_port = lport_lookup_by_name(sbrec_port_binding_by_name, peer_name);
+       ovs_assert(peer_port);
+
+       const char *network_type = smap_get(&peer_port->datapath->external_ids, "network-type");
+
+       // Advertise GARP only of logical switch is of type vlan.
+       if (!network_type || strcmp(network_type, "vlan")) {
+          return;
+       }
+
+       dp_key = peer_port->datapath->tunnel_key;
+       port_key = peer_port->tunnel_key;
+
+       garp = shash_find_data(&send_garp_data, binding_rec->logical_port);
+       if (garp) {
+          garp->dp_key = dp_key;
+          garp->port_key = port_key;
+       } else {
+          add_garp(binding_rec->logical_port, mac, ip,
+                   dp_key, port_key, true, GARP_DEF_REPEAT_INTERVAL_MS);
+       }
+       return;
+    }
+
     /* Update GARP for vif if it exists. */
     garp = shash_find_data(&send_garp_data, binding_rec->logical_port);
     if (garp) {
@@ -2180,7 +2250,8 @@ send_garp_update(const struct sbrec_port_binding *binding_rec,
 
         add_garp(binding_rec->logical_port,
                  laddrs.ea, laddrs.ipv4_addrs[0].addr,
-                 binding_rec->datapath->tunnel_key, binding_rec->tunnel_key);
+                 binding_rec->datapath->tunnel_key, binding_rec->tunnel_key,
+                 false, 0);
 
         destroy_lport_addresses(&laddrs);
         break;
@@ -2238,7 +2309,13 @@ send_garp(struct garp_data *garp, long long int current_time)
         garp->backoff *= 2;
         garp->announce_time = current_time + garp->backoff * 1000;
     } else {
-        garp->announce_time = LLONG_MAX;
+       if (garp->is_repeat) {
+          garp->backoff = 1;
+          garp->announce_time = current_time + garp->repeat_interval;
+
+       } else {
+          garp->announce_time = LLONG_MAX;
+       }
     }
     return garp->announce_time;
 }
@@ -2492,6 +2569,65 @@ get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_chassis_by_name,
 }
 
 static void
+get_local_cr_ports(struct ovsdb_idl_index *sbrec_chassis_by_name,
+                   struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                   struct sset *local_cr_ports,
+                   struct sset *local_l3gw_ports,
+                   const struct sbrec_chassis *chassis,
+                   const struct sset *active_tunnels)
+{
+   const char *gw_port;
+   SSET_FOR_EACH(gw_port, local_l3gw_ports) {
+      const struct sbrec_port_binding *binding_rec;
+
+      binding_rec = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
+      if (!binding_rec) {
+         continue;
+      }
+
+      /* For the patch port we will add send garp for peer's ip and mac. */
+      if (!strcmp(binding_rec->type, "patch")) {
+         const struct sbrec_port_binding *cr_port = NULL;
+
+         bool is_cr_resident;
+         struct eth_addr mac;
+         ovs_be32 ip, mask;
+
+         const char *peer_name = smap_get(&binding_rec->options, "peer");
+         ovs_assert(peer_name);
+
+         char *cr_peer_name = xasprintf("cr-%s", peer_name);
+         cr_port = lport_lookup_by_name(sbrec_port_binding_by_name, cr_peer_name);
+         free(cr_peer_name);
+
+         if (!cr_port) {
+            continue;
+         }
+
+         is_cr_resident = pinctrl_is_chassis_resident(sbrec_chassis_by_name,
+                                                      sbrec_port_binding_by_name,
+                                                      chassis,
+                                                      active_tunnels,
+                                                      cr_port->logical_port);
+         if (!is_cr_resident) {
+            continue;
+         }
+
+         if (!ovn_sbrec_get_port_binding_ip_mac(cr_port, &mac, &ip, &mask)) {
+            // Router Port binding without ip and mac configured.
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "cannot send garp, router port binding: %s, "
+                         "does not have proper ip,mac values: %s",
+                         cr_port->logical_port, *cr_port->mac);
+            return;
+         }
+
+         sset_add(local_cr_ports, cr_port->logical_port);
+      }
+   }
+}
+
+static void
 send_garp_wait(void)
 {
     poll_timer_wait_until(send_garp_time);
@@ -2508,6 +2644,8 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
 {
     struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs);
     struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports);
+    struct sset local_cr_ports = SSET_INITIALIZER(&local_cr_ports);
+
     struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys);
     struct shash nat_addresses;
 
@@ -2523,12 +2661,19 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
                                &nat_ip_keys, &local_l3gw_ports,
                                chassis, active_tunnels,
                                &nat_addresses);
+
+    get_local_cr_ports(sbrec_chassis_by_name,
+                       sbrec_port_binding_by_name,
+                       &local_cr_ports, &local_l3gw_ports,
+                       chassis, active_tunnels);
+
     /* For deleted ports and deleted nat ips, remove from send_garp_data. */
     struct shash_node *iter, *next;
     SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) {
         if (!sset_contains(&localnet_vifs, iter->name) &&
-            !sset_contains(&nat_ip_keys, iter->name)) {
-            send_garp_delete(iter->name);
+            !sset_contains(&nat_ip_keys, iter->name) &&
+            !sset_contains(&local_cr_ports, iter->name)) {
+           send_garp_delete(iter->name);
         }
     }
 
@@ -2538,7 +2683,7 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
         const struct sbrec_port_binding *pb = lport_lookup_by_name(
             sbrec_port_binding_by_name, iface_id);
         if (pb) {
-            send_garp_update(pb, &nat_addresses);
+            send_garp_update(sbrec_port_binding_by_name, pb, &nat_addresses);
         }
     }
 
@@ -2548,7 +2693,17 @@ send_garp_run(struct ovsdb_idl_index *sbrec_chassis_by_name,
         const struct sbrec_port_binding *pb
             = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
         if (pb) {
-            send_garp_update(pb, &nat_addresses);
+            send_garp_update(sbrec_port_binding_by_name, pb, &nat_addresses);
+        }
+    }
+
+    /* Update send_garp_data for nat-addresses. */
+    const char *cr_port;
+    SSET_FOR_EACH (cr_port, &local_cr_ports) {
+        const struct sbrec_port_binding *pb
+            = lport_lookup_by_name(sbrec_port_binding_by_name, cr_port);
+        if (pb) {
+            send_garp_update(sbrec_port_binding_by_name, pb, &nat_addresses);
         }
     }
 
diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
index aa03919..ae08261 100644
--- a/ovn/lib/ovn-util.c
+++ b/ovn/lib/ovn-util.c
@@ -16,6 +16,7 @@
 #include "ovn-util.h"
 #include "dirs.h"
 #include "openvswitch/vlog.h"
+#include "openvswitch/ofp-parse.h"
 #include "ovn/lib/ovn-nb-idl.h"
 #include "ovn/lib/ovn-sb-idl.h"
 
@@ -364,3 +365,33 @@ ovn_logical_flow_hash(const struct uuid *logical_datapath,
     hash = hash_string(match, hash);
     return hash_string(actions, hash);
 }
+
+/*  Extracts the mac, ip and mask for a sbrec_port_binding.
+ *
+ *  Expects following format:
+ *  "MAC_ADDRESS IP/MASK"
+ *
+ *  Return true if MAC, IP and MASK are found, false otherwise.
+ */
+bool
+ovn_sbrec_get_port_binding_ip_mac(const struct sbrec_port_binding *binding,
+                                  struct eth_addr *mac,
+                                  ovs_be32 *ip, ovs_be32 *mask)
+{
+    char *err_str = NULL;
+
+    err_str = str_to_mac(binding->mac[0], mac);
+    if (err_str) {
+        free(err_str);
+        return false;
+    }
+
+    err_str = ip_parse_masked(binding->mac[0] + ETH_ADDR_STRLEN + 1,
+                              ip, mask);
+    if (err_str) {
+        free(err_str);
+        return false;
+    }
+
+    return true;
+}
diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
index 6d5e1df..c01595a 100644
--- a/ovn/lib/ovn-util.h
+++ b/ovn/lib/ovn-util.h
@@ -19,6 +19,7 @@
 #include "lib/packets.h"
 
 struct nbrec_logical_router_port;
+struct sbrec_port_binding;
 struct sbrec_logical_flow;
 struct uuid;
 
@@ -81,4 +82,9 @@ uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
                                uint16_t priority,
                                const char *match, const char *actions);
 
+bool
+ovn_sbrec_get_port_binding_ip_mac(const struct sbrec_port_binding *binding,
+                                  struct eth_addr *mac, ovs_be32 *ip,
+                                  ovs_be32 *mask);
+
 #endif
-- 
1.8.3.1



More information about the dev mailing list