summary refs log tree commit diff
path: root/src/core/devices/ovs
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices/ovs')
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c95
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c53
2 files changed, 114 insertions, 34 deletions
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 74707d24..6b9d3d7e 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -13,6 +13,7 @@
 #include "devices/nm-device-private.h"
 #include "nm-active-connection.h"
 #include "nm-setting-connection.h"
+#include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-port.h"
 
@@ -23,7 +24,10 @@
 
 typedef struct {
     NMOvsdb *ovsdb;
-    bool     waiting_for_interface : 1;
+    GSource *wait_link_idle_source;
+    gulong   wait_link_signal_id;
+    int      wait_link_ifindex;
+    bool     wait_link_is_waiting : 1;
 } NMDeviceOvsInterfacePrivate;
 
 struct _NMDeviceOvsInterface {
@@ -115,10 +119,10 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink)
 {
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(device);
 
-    if (!pllink || !priv->waiting_for_interface)
+    if (!pllink || !priv->wait_link_is_waiting)
         return;
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
 
     if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG) {
         if (!nm_device_hw_addr_set_cloned(device,
@@ -199,17 +203,83 @@ ready_for_ip_config(NMDevice *device)
     return nm_device_get_ip_ifindex(device) > 0;
 }
 
+static gboolean
+_set_ip_ifindex_tun(gpointer user_data)
+{
+    NMDevice                    *device = user_data;
+    NMDeviceOvsInterface        *self   = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv   = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    priv->wait_link_is_waiting = FALSE;
+    nm_device_set_ip_ifindex(device, priv->wait_link_ifindex);
+
+    nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_activate_schedule_stage3_ip_config(device, FALSE);
+
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_netdev_tun_link_cb(NMPlatform     *platform,
+                    int             obj_type_i,
+                    int             ifindex,
+                    NMPlatformLink *pllink,
+                    int             change_type_i,
+                    NMDevice       *device)
+{
+    const NMPlatformSignalChangeType change_type = change_type_i;
+    NMDeviceOvsInterface            *self        = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate     *priv        = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    if (change_type == NM_PLATFORM_SIGNAL_ADDED) {
+        if (pllink->type == NM_LINK_TYPE_TUN
+            && nm_streq0(pllink->name, nm_device_get_iface(device))) {
+            nm_clear_g_signal_handler(platform, &priv->wait_link_signal_id);
+
+            priv->wait_link_ifindex = ifindex;
+
+            priv->wait_link_idle_source = nm_g_idle_add_source(_set_ip_ifindex_tun, device);
+        }
+    }
+}
+
 static void
 act_stage3_ip_config(NMDevice *device, int addr_family)
 {
-    NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
-    NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+    NMActiveConnection          *controller_act = NULL;
+    NMSettingOvsBridge          *s_ovs_bridge   = NULL;
+    NMDeviceOvsInterface        *self           = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv           = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
     if (!_is_internal_interface(device)) {
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL);
         return;
     }
 
+    /* When the ovs-bridge controller is using netdev datapath, the interface
+     * link created is a tun device instead of a ovs-interface. NetworkManager must
+     * detect the creation of the tun link and attach the ifindex to the
+     * ovs-interface device. */
+    controller_act = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device));
+    if (controller_act && nm_device_get_ip_ifindex(device) <= 0 && priv->wait_link_signal_id == 0) {
+        controller_act = nm_active_connection_get_master(controller_act);
+        if (controller_act) {
+            controller_act = nm_active_connection_get_master(controller_act);
+            if (controller_act)
+                s_ovs_bridge = nm_connection_get_setting_ovs_bridge(
+                    nm_active_connection_get_applied_connection(controller_act));
+            if (s_ovs_bridge
+                && nm_streq0(nm_setting_ovs_bridge_get_datapath_type(s_ovs_bridge), "netdev"))
+                priv->wait_link_signal_id = g_signal_connect(nm_device_get_platform(device),
+                                                             NM_PLATFORM_SIGNAL_LINK_CHANGED,
+                                                             G_CALLBACK(_netdev_tun_link_cb),
+                                                             self);
+        }
+    }
+
     /* FIXME(l3cfg): we should create the IP ifindex before stage3 start.
      *
      * For now it's here because when the ovs-interface enters stage3, then it's added to the
@@ -219,11 +289,15 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
      * This should change. */
     if (nm_device_get_ip_ifindex(device) <= 0) {
         _LOGT(LOGD_DEVICE, "waiting for link to appear");
-        priv->waiting_for_interface = TRUE;
+        priv->wait_link_is_waiting = TRUE;
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL);
         return;
     }
 
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+    nm_clear_g_signal_handler(nm_device_get_platform(device), &priv->wait_link_signal_id);
+
     if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) {
         nm_device_devip_set_failed(device, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
         return;
@@ -244,7 +318,8 @@ deactivate(NMDevice *device)
     NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
 }
 
 typedef struct {
@@ -351,7 +426,7 @@ deactivate_async(NMDevice                  *device,
         .callback_user_data = callback_user_data,
     };
 
-    if (!priv->waiting_for_interface
+    if (!priv->wait_link_is_waiting
         && !nm_platform_link_get_by_ifname(nm_device_get_platform(device),
                                            nm_device_get_iface(device))) {
         _LOGT(LOGD_CORE, "deactivate: link not present, proceeding");
@@ -360,7 +435,9 @@ deactivate_async(NMDevice                  *device,
         return;
     }
 
-    if (priv->waiting_for_interface) {
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    if (priv->wait_link_is_waiting) {
         /* At this point we have issued an INSERT and a DELETE
          * command for the interface to ovsdb. We don't know if
          * vswitchd will see the two updates or only one. We
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 7c45e0e4..44e16cb7 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -104,6 +104,7 @@ typedef struct {
     OvsdbMethodCallback callback;
     gpointer            user_data;
     OvsdbMethodPayload  payload;
+    GObject            *shutdown_wait_obj;
 } OvsdbMethodCall;
 
 /*****************************************************************************/
@@ -245,6 +246,8 @@ static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table,
 static void
 _call_complete(OvsdbMethodCall *call, json_t *response, GError *error)
 {
+    g_clear_object(&call->shutdown_wait_obj);
+
     if (response) {
         gs_free char *str = NULL;
 
@@ -378,12 +381,14 @@ ovsdb_call_method(NMOvsdb                  *self,
 
     call  = g_slice_new(OvsdbMethodCall);
     *call = (OvsdbMethodCall){
-        .self      = self,
-        .call_id   = CALL_ID_UNSPEC,
-        .command   = command,
-        .callback  = callback,
-        .user_data = user_data,
+        .self              = self,
+        .call_id           = CALL_ID_UNSPEC,
+        .command           = command,
+        .callback          = callback,
+        .user_data         = user_data,
+        .shutdown_wait_obj = g_object_new(G_TYPE_OBJECT, NULL),
     };
+    nm_shutdown_wait_obj_register_object(call->shutdown_wait_obj, "ovsdb-call");
 
     if (add_first)
         c_list_link_front(&priv->calls_lst_head, &call->calls_lst);
@@ -2481,29 +2486,27 @@ typedef struct {
 static void
 _transact_cb(NMOvsdb *self, json_t *result, GError *error, gpointer user_data)
 {
-    OvsdbCall  *call = user_data;
-    const char *err;
-    const char *err_details;
-    size_t      index;
-    json_t     *value;
-
-    if (error)
-        goto out;
-
-    json_array_foreach (result, index, value) {
-        if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
-            g_set_error(&error,
-                        G_IO_ERROR,
-                        G_IO_ERROR_FAILED,
-                        "Error running the transaction: %s: %s",
-                        err,
-                        err_details);
-            goto out;
+    OvsdbCall            *call  = user_data;
+    gs_free_error GError *local = NULL;
+    const char           *err;
+    const char           *err_details;
+    size_t                index;
+    json_t               *value;
+
+    if (!error) {
+        json_array_foreach (result, index, value) {
+            if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
+                local = g_error_new(G_IO_ERROR,
+                                    G_IO_ERROR_FAILED,
+                                    "Error running the transaction: %s: %s",
+                                    err,
+                                    err_details);
+                break;
+            }
         }
     }
 
-out:
-    call->callback(error, call->user_data);
+    call->callback(local ?: error, call->user_data);
     nm_g_slice_free(call);
 }