]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/netdev-vport.c
ovsdb-idl: Fix memleak when reinserting tracked orphan rows.
[mirror_ovs.git] / lib / netdev-vport.c
index 1dae7e0316a2fb559a3f81408f908713ff6868f9..0252b61dea2b81199a27ec1601e864b0a3ed15e0 100644 (file)
 #include "packets.h"
 #include "openvswitch/poll-loop.h"
 #include "route-table.h"
+#include "simap.h"
 #include "smap.h"
 #include "socket-util.h"
 #include "unaligned.h"
 #include "unixctl.h"
 #include "openvswitch/vlog.h"
-#include "netdev-tc-offloads.h"
 #ifdef __linux__
 #include "netdev-linux.h"
 #endif
@@ -66,10 +66,14 @@ static uint64_t rt_change_seqno;
 static int get_patch_config(const struct netdev *netdev, struct smap *args);
 static int get_tunnel_config(const struct netdev *, struct smap *args);
 static bool tunnel_check_status_change__(struct netdev_vport *);
+static void update_vxlan_global_cfg(struct netdev *,
+                                    struct netdev_tunnel_config *,
+                                    struct netdev_tunnel_config *);
 
 struct vport_class {
     const char *dpif_port;
     struct netdev_class netdev_class;
+    struct simap global_cfg_tracker;
 };
 
 bool
@@ -78,7 +82,7 @@ netdev_vport_is_vport_class(const struct netdev_class *class)
     return is_vport_class(class);
 }
 
-static const struct vport_class *
+static struct vport_class *
 vport_class_cast(const struct netdev_class *class)
 {
     ovs_assert(is_vport_class(class));
@@ -107,7 +111,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
 
     return (class->get_config == get_tunnel_config &&
             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
-             !strcmp("lisp", type) || !strcmp("stt", type)) );
+             !strcmp("lisp", type) || !strcmp("stt", type) ||
+             !strcmp("gtpu", type)));
 }
 
 const char *
@@ -184,21 +189,36 @@ netdev_vport_alloc(void)
 int
 netdev_vport_construct(struct netdev *netdev_)
 {
+    const struct netdev_class *class = netdev_get_class(netdev_);
+    const char *dpif_port = netdev_vport_class_get_dpif_port(class);
     struct netdev_vport *dev = netdev_vport_cast(netdev_);
+    const char *p, *name = netdev_get_name(netdev_);
     const char *type = netdev_get_type(netdev_);
+    uint16_t port = 0;
 
     ovs_mutex_init(&dev->mutex);
     eth_addr_random(&dev->etheraddr);
 
-    /* Add a default destination port for tunnel ports if none specified. */
+    if (name && dpif_port && (strlen(name) > strlen(dpif_port) + 1) &&
+        (!strncmp(name, dpif_port, strlen(dpif_port)))) {
+        p = name + strlen(dpif_port) + 1;
+        port = atoi(p);
+    }
+
+    /* If a destination port for tunnel ports is specified in the netdev
+     * name, use it instead of the default one. Otherwise, use the default
+     * destination port */
     if (!strcmp(type, "geneve")) {
-        dev->tnl_cfg.dst_port = htons(GENEVE_DST_PORT);
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(GENEVE_DST_PORT);
     } else if (!strcmp(type, "vxlan")) {
-        dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(VXLAN_DST_PORT);
+        update_vxlan_global_cfg(netdev_, NULL, &dev->tnl_cfg);
     } else if (!strcmp(type, "lisp")) {
-        dev->tnl_cfg.dst_port = htons(LISP_DST_PORT);
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
     } else if (!strcmp(type, "stt")) {
-        dev->tnl_cfg.dst_port = htons(STT_DST_PORT);
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
+    } else if (!strcmp(type, "gtpu")) {
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
     }
 
     dev->tnl_cfg.dont_fragment = true;
@@ -210,6 +230,11 @@ static void
 netdev_vport_destruct(struct netdev *netdev_)
 {
     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
+    const char *type = netdev_get_type(netdev_);
+
+    if (!strcmp(type, "vxlan")) {
+        update_vxlan_global_cfg(netdev_, &netdev->tnl_cfg, NULL);
+    }
 
     free(netdev->peer);
     ovs_mutex_destroy(&netdev->mutex);
@@ -411,6 +436,8 @@ tunnel_supported_layers(const char *type,
     } else if (!strcmp(type, "vxlan")
                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
         return TNL_L2 | TNL_L3;
+    } else if (!strcmp(type, "gtpu")) {
+        return TNL_L3;
     } else {
         return TNL_L2;
     }
@@ -421,6 +448,117 @@ default_pt_mode(enum tunnel_layers layers)
     return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2;
 }
 
+static char *
+vxlan_get_port_ext_gbp_str(uint16_t port, bool gbp,
+                           char namebuf[], size_t bufsize)
+{
+    snprintf(namebuf, bufsize, "dst_port_%d%s",
+             port, gbp ? "_gbp" : "");
+
+    return namebuf;
+}
+
+static void
+update_vxlan_global_cfg(struct netdev *netdev,
+                        struct netdev_tunnel_config *old_cfg,
+                        struct netdev_tunnel_config *new_cfg)
+{
+    unsigned int count;
+    char namebuf[20];
+    const char *type = netdev_get_type(netdev);
+    struct vport_class *vclass = vport_class_cast(netdev_get_class(netdev));
+
+    if (strcmp(type, "vxlan") ||
+        (old_cfg != NULL && new_cfg != NULL &&
+         old_cfg->dst_port == new_cfg->dst_port &&
+         old_cfg->exts == new_cfg->exts)) {
+        return;
+    }
+
+    if (old_cfg != NULL) {
+        vxlan_get_port_ext_gbp_str(ntohs(old_cfg->dst_port),
+                                   old_cfg->exts &
+                                   (1 << OVS_VXLAN_EXT_GBP),
+                                   namebuf, sizeof(namebuf));
+
+        count = simap_get(&vclass->global_cfg_tracker, namebuf);
+        if (count != 0) {
+            if (--count) {
+                simap_put(&vclass->global_cfg_tracker, namebuf, count);
+            } else {
+                simap_find_and_delete(&vclass->global_cfg_tracker, namebuf);
+           }
+        }
+    }
+
+    if (new_cfg != NULL) {
+        vxlan_get_port_ext_gbp_str(ntohs(new_cfg->dst_port),
+                                   new_cfg->exts &
+                                   (1 << OVS_VXLAN_EXT_GBP),
+                                   namebuf, sizeof(namebuf));
+
+        simap_increase(&vclass->global_cfg_tracker, namebuf, 1);
+    }
+}
+
+static bool
+is_concomitant_vxlan_tunnel_present(struct netdev_vport *dev,
+                                    const struct netdev_tunnel_config *tnl_cfg)
+{
+    char namebuf[20];
+    const char *type = netdev_get_type(&dev->up);
+    struct vport_class *vclass = vport_class_cast(netdev_get_class(&dev->up));
+
+    if (strcmp(type, "vxlan")) {
+        return false;
+    }
+
+    if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port &&
+        (dev->tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GBP)) ==
+        (tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GBP))) {
+
+        if (ntohs(dev->tnl_cfg.dst_port) == VXLAN_DST_PORT) {
+            /* Special case where we kept the default port/gbp, only ok if
+               the opposite of the default does not exits */
+            vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port),
+                                       !(tnl_cfg->exts &
+                                         (1 << OVS_VXLAN_EXT_GBP)),
+                                       namebuf, sizeof(namebuf));
+
+            if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* Same port: ok if no one is left with the previous configuration */
+    if (dev->tnl_cfg.dst_port == tnl_cfg->dst_port) {
+        vxlan_get_port_ext_gbp_str(ntohs(dev->tnl_cfg.dst_port),
+                                   dev->tnl_cfg.exts &
+                                   (1 << OVS_VXLAN_EXT_GBP),
+                                   namebuf, sizeof(namebuf));
+
+        if (simap_get(&vclass->global_cfg_tracker, namebuf) > 1) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /* Different port: ok if the opposite gbp option does not yet exists */
+    vxlan_get_port_ext_gbp_str(ntohs(tnl_cfg->dst_port),
+                               !(tnl_cfg->exts &
+                                 (1 << OVS_VXLAN_EXT_GBP)),
+                               namebuf, sizeof(namebuf));
+
+    if (simap_get(&vclass->global_cfg_tracker, namebuf) > 0) {
+        return true;
+    }
+
+    return false;
+}
+
 static int
 set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
 {
@@ -456,6 +594,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
         tnl_cfg.dst_port = htons(STT_DST_PORT);
     }
 
+    if (!strcmp(type, "gtpu")) {
+        tnl_cfg.dst_port = htons(GTPU_DST_PORT);
+    }
+
     needs_dst_port = netdev_vport_needs_dst_port(dev_);
     tnl_cfg.dont_fragment = true;
 
@@ -612,7 +754,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
     enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg);
     const char *full_type = (strcmp(type, "vxlan") ? type
                              : (tnl_cfg.exts & (1 << OVS_VXLAN_EXT_GPE)
-                                ? "VXLAN-GPE" : "VXLAN (without GPE"));
+                                ? "VXLAN-GPE" : "VXLAN (without GPE)"));
     const char *packet_type = smap_get(args, "packet_type");
     if (!packet_type) {
         tnl_cfg.pt_mode = default_pt_mode(layers);
@@ -678,6 +820,16 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
                                &tnl_cfg.out_key_present,
                                &tnl_cfg.out_key_flow);
 
+    if (is_concomitant_vxlan_tunnel_present(dev, &tnl_cfg)) {
+        ds_put_format(&errors, "%s: VXLAN-GBP, and non-VXLAN-GBP "
+                      "tunnels can't be configured on the same "
+                      "dst_port\n",
+                      name);
+        err = EEXIST;
+        goto out;
+    }
+    update_vxlan_global_cfg(dev_, &dev->tnl_cfg, &tnl_cfg);
+
     ovs_mutex_lock(&dev->mutex);
     if (memcmp(&dev->tnl_cfg, &tnl_cfg, sizeof tnl_cfg)) {
         dev->tnl_cfg = tnl_cfg;
@@ -764,7 +916,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
-            (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
+            (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
+            (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT)) {
             smap_add_format(args, "dst_port", "%d", dst_port);
         }
     }
@@ -936,7 +1089,7 @@ set_patch_config(struct netdev *dev_, const struct smap *args, char **errp)
 }
 
 static int
-get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+netdev_vport_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
     struct netdev_vport *dev = netdev_vport_cast(netdev);
 
@@ -952,7 +1105,7 @@ get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 }
 
 static enum netdev_pt_mode
-get_pt_mode(const struct netdev *netdev)
+netdev_vport_get_pt_mode(const struct netdev *netdev)
 {
     struct netdev_vport *dev = netdev_vport_cast(netdev);
 
@@ -972,130 +1125,125 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
 }
 
 #define NETDEV_VPORT_GET_IFINDEX netdev_vport_get_ifindex
-#define NETDEV_FLOW_OFFLOAD_API LINUX_FLOW_OFFLOAD_API
 #else /* !__linux__ */
 #define NETDEV_VPORT_GET_IFINDEX NULL
-#define NETDEV_FLOW_OFFLOAD_API NO_OFFLOAD_API
 #endif /* __linux__ */
 
-#define VPORT_FUNCTIONS(GET_CONFIG, SET_CONFIG,             \
-                        GET_TUNNEL_CONFIG, GET_STATUS,      \
-                        BUILD_HEADER,                       \
-                        PUSH_HEADER, POP_HEADER,            \
-                        GET_IFINDEX)                        \
-    NULL,                                                   \
-    netdev_vport_run,                                       \
-    netdev_vport_wait,                                      \
-                                                            \
-    netdev_vport_alloc,                                     \
-    netdev_vport_construct,                                 \
-    netdev_vport_destruct,                                  \
-    netdev_vport_dealloc,                                   \
-    GET_CONFIG,                                             \
-    SET_CONFIG,                                             \
-    GET_TUNNEL_CONFIG,                                      \
-    BUILD_HEADER,                                           \
-    PUSH_HEADER,                                            \
-    POP_HEADER,                                             \
-    NULL,                       /* get_numa_id */           \
-    NULL,                       /* set_tx_multiq */         \
-                                                            \
-    NULL,                       /* send */                  \
-    NULL,                       /* send_wait */             \
-                                                            \
-    netdev_vport_set_etheraddr,                             \
-    netdev_vport_get_etheraddr,                             \
-    NULL,                       /* get_mtu */               \
-    NULL,                       /* set_mtu */               \
-    GET_IFINDEX,                                            \
-    NULL,                       /* get_carrier */           \
-    NULL,                       /* get_carrier_resets */    \
-    NULL,                       /* get_miimon */            \
-    get_stats,                                              \
-    NULL,                       /* get_custom_stats */      \
-                                                            \
-    NULL,                       /* get_features */          \
-    NULL,                       /* set_advertisements */    \
-    get_pt_mode,                                            \
-                                                            \
-    NULL,                       /* set_policing */          \
-    NULL,                       /* get_qos_types */         \
-    NULL,                       /* get_qos_capabilities */  \
-    NULL,                       /* get_qos */               \
-    NULL,                       /* set_qos */               \
-    NULL,                       /* get_queue */             \
-    NULL,                       /* set_queue */             \
-    NULL,                       /* delete_queue */          \
-    NULL,                       /* get_queue_stats */       \
-    NULL,                       /* queue_dump_start */      \
-    NULL,                       /* queue_dump_next */       \
-    NULL,                       /* queue_dump_done */       \
-    NULL,                       /* dump_queue_stats */      \
-                                                            \
-    NULL,                       /* set_in4 */               \
-    NULL,                       /* get_addr_list */         \
-    NULL,                       /* add_router */            \
-    NULL,                       /* get_next_hop */          \
-    GET_STATUS,                                             \
-    NULL,                       /* arp_lookup */            \
-                                                            \
-    netdev_vport_update_flags,                              \
-    NULL,                       /* reconfigure */           \
-                                                            \
-    NULL,                   /* rx_alloc */                  \
-    NULL,                   /* rx_construct */              \
-    NULL,                   /* rx_destruct */               \
-    NULL,                   /* rx_dealloc */                \
-    NULL,                   /* rx_recv */                   \
-    NULL,                   /* rx_wait */                   \
-    NULL,                   /* rx_drain */                  \
-                                                            \
-    NETDEV_FLOW_OFFLOAD_API
-
-
-#define TUNNEL_CLASS(NAME, DPIF_PORT, BUILD_HEADER, PUSH_HEADER, POP_HEADER,   \
-                     GET_IFINDEX)                                              \
-    { DPIF_PORT,                                                               \
-        { NAME, false,                                                         \
-          VPORT_FUNCTIONS(get_tunnel_config,                                   \
-                          set_tunnel_config,                                   \
-                          get_netdev_tunnel_config,                            \
-                          tunnel_get_status,                                   \
-                          BUILD_HEADER, PUSH_HEADER, POP_HEADER,               \
-                          GET_IFINDEX) }}
+#define VPORT_FUNCTIONS_COMMON                      \
+    .run = netdev_vport_run,                        \
+    .wait = netdev_vport_wait,                      \
+    .alloc = netdev_vport_alloc,                    \
+    .construct = netdev_vport_construct,            \
+    .destruct = netdev_vport_destruct,              \
+    .dealloc = netdev_vport_dealloc,                \
+    .set_etheraddr = netdev_vport_set_etheraddr,    \
+    .get_etheraddr = netdev_vport_get_etheraddr,    \
+    .get_stats = netdev_vport_get_stats,            \
+    .get_pt_mode = netdev_vport_get_pt_mode,        \
+    .update_flags = netdev_vport_update_flags
+
+#define TUNNEL_FUNCTIONS_COMMON                     \
+    VPORT_FUNCTIONS_COMMON,                         \
+    .get_config = get_tunnel_config,                \
+    .set_config = set_tunnel_config,                \
+    .get_tunnel_config = get_netdev_tunnel_config,  \
+    .get_status = tunnel_get_status
 
 void
 netdev_vport_tunnel_register(void)
 {
     /* The name of the dpif_port should be short enough to accomodate adding
      * a port number to the end if one is necessary. */
-    static const struct vport_class vport_classes[] = {
-        TUNNEL_CLASS("geneve", "genev_sys", netdev_geneve_build_header,
-                                            netdev_tnl_push_udp_header,
-                                            netdev_geneve_pop_header,
-                                            NETDEV_VPORT_GET_IFINDEX),
-        TUNNEL_CLASS("gre", "gre_sys", netdev_gre_build_header,
-                                       netdev_gre_push_header,
-                                       netdev_gre_pop_header,
-                                       NULL),
-        TUNNEL_CLASS("vxlan", "vxlan_sys", netdev_vxlan_build_header,
-                                           netdev_tnl_push_udp_header,
-                                           netdev_vxlan_pop_header,
-                                           NETDEV_VPORT_GET_IFINDEX),
-        TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL),
-        TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL),
-        TUNNEL_CLASS("erspan", "erspan_sys", netdev_erspan_build_header,
-                                             netdev_erspan_push_header,
-                                             netdev_erspan_pop_header,
-                                             NULL),
-        TUNNEL_CLASS("ip6erspan", "ip6erspan_sys", netdev_erspan_build_header,
-                                                   netdev_erspan_push_header,
-                                                   netdev_erspan_pop_header,
-                                                   NULL),
-        TUNNEL_CLASS("ip6gre", "ip6gre_sys", netdev_gre_build_header,
-                                             netdev_gre_push_header,
-                                             netdev_gre_pop_header,
-                                             NULL),
+    static struct vport_class vport_classes[] = {
+        { "genev_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "geneve",
+              .build_header = netdev_geneve_build_header,
+              .push_header = netdev_tnl_push_udp_header,
+              .pop_header = netdev_geneve_pop_header,
+              .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "gre_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "gre",
+              .build_header = netdev_gre_build_header,
+              .push_header = netdev_gre_push_header,
+              .pop_header = netdev_gre_pop_header,
+              .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "vxlan_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "vxlan",
+              .build_header = netdev_vxlan_build_header,
+              .push_header = netdev_tnl_push_udp_header,
+              .pop_header = netdev_vxlan_pop_header,
+              .get_ifindex = NETDEV_VPORT_GET_IFINDEX
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "lisp_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "lisp"
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "stt_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "stt"
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "erspan_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "erspan",
+              .build_header = netdev_erspan_build_header,
+              .push_header = netdev_erspan_push_header,
+              .pop_header = netdev_erspan_pop_header
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "ip6erspan_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "ip6erspan",
+              .build_header = netdev_erspan_build_header,
+              .push_header = netdev_erspan_push_header,
+              .pop_header = netdev_erspan_pop_header
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "ip6gre_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "ip6gre",
+              .build_header = netdev_gre_build_header,
+              .push_header = netdev_gre_push_header,
+              .pop_header = netdev_gre_pop_header,
+              .get_ifindex = NETDEV_VPORT_GET_IFINDEX,
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+        { "gtpu_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "gtpu",
+              .build_header = netdev_gtpu_build_header,
+              .push_header = netdev_gtpu_push_header,
+              .pop_header = netdev_gtpu_pop_header,
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+
     };
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
 
@@ -1103,6 +1251,7 @@ netdev_vport_tunnel_register(void)
         int i;
 
         for (i = 0; i < ARRAY_SIZE(vport_classes); i++) {
+            simap_init(&vport_classes[i].global_cfg_tracker);
             netdev_register_provider(&vport_classes[i].netdev_class);
         }
 
@@ -1116,12 +1265,15 @@ netdev_vport_tunnel_register(void)
 void
 netdev_vport_patch_register(void)
 {
-    static const struct vport_class patch_class =
-        { NULL,
-            { "patch", false,
-              VPORT_FUNCTIONS(get_patch_config,
-                              set_patch_config,
-                              NULL,
-                              NULL, NULL, NULL, NULL, NULL) }};
+    static struct vport_class patch_class = {
+        NULL,
+        { VPORT_FUNCTIONS_COMMON,
+          .type = "patch",
+          .get_config = get_patch_config,
+          .set_config = set_patch_config,
+        },
+        {{NULL, NULL, 0, 0}}
+    };
+    simap_init(&patch_class.global_cfg_tracker);
     netdev_register_provider(&patch_class.netdev_class);
 }