summary refs log tree commit diff
path: root/src/nm-cloud-setup
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-cloud-setup')
-rw-r--r--src/nm-cloud-setup/main.c34
-rw-r--r--src/nm-cloud-setup/meson.build8
-rw-r--r--src/nm-cloud-setup/nm-cloud-setup-utils.c45
-rw-r--r--src/nm-cloud-setup/nm-cloud-setup-utils.h9
-rw-r--r--src/nm-cloud-setup/nm-cloud-setup.service.in1
-rw-r--r--src/nm-cloud-setup/nm-http-client.c6
-rw-r--r--src/nm-cloud-setup/nmcs-provider-azure.c2
-rw-r--r--src/nm-cloud-setup/nmcs-provider-gcp.c2
-rw-r--r--src/nm-cloud-setup/nmcs-provider-oci.c221
-rw-r--r--src/nm-cloud-setup/nmcs-provider-oci.h27
-rw-r--r--src/nm-cloud-setup/nmcs-provider.c10
-rw-r--r--src/nm-cloud-setup/nmcs-provider.h7
12 files changed, 340 insertions, 32 deletions
diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c
index 084b5f62..44500ffa 100644
--- a/src/nm-cloud-setup/main.c
+++ b/src/nm-cloud-setup/main.c
@@ -11,6 +11,7 @@
 #include "nmcs-provider-gcp.h"
 #include "nmcs-provider-azure.h"
 #include "nmcs-provider-aliyun.h"
+#include "nmcs-provider-oci.h"
 #include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 
 /*****************************************************************************/
@@ -104,6 +105,7 @@ _provider_detect(SigTermData *sigterm_data)
         NMCS_TYPE_PROVIDER_GCP,
         NMCS_TYPE_PROVIDER_AZURE,
         NMCS_TYPE_PROVIDER_ALIYUN,
+        NMCS_TYPE_PROVIDER_OCI,
     };
     int    i;
     gulong cancellable_signal_id;
@@ -178,7 +180,7 @@ _map_interfaces_parse(void)
         nm_assert(j < alloc_len);
         m = &map_interfaces[j++];
 
-        *m = (NMUtilsNamedValue){
+        *m = (NMUtilsNamedValue) {
             .name      = g_strndup(str, s - str),
             .value_str = hwaddr,
         };
@@ -187,7 +189,7 @@ _map_interfaces_parse(void)
     }
 
     nm_assert(j < alloc_len);
-    map_interfaces[j++] = (NMUtilsNamedValue){
+    map_interfaces[j++] = (NMUtilsNamedValue) {
         .name      = NULL,
         .value_str = NULL,
     };
@@ -196,13 +198,14 @@ _map_interfaces_parse(void)
 }
 
 static const char *
-_device_get_hwaddr(NMDeviceEthernet *device)
+_device_get_hwaddr(NMDevice *device)
 {
     static const NMUtilsNamedValue *gl_map_interfaces_map = NULL;
     static gsize                    gl_initialized        = 0;
     const NMUtilsNamedValue        *map                   = NULL;
 
-    nm_assert(NM_IS_DEVICE_ETHERNET(device));
+    nm_assert(NM_IS_DEVICE_ETHERNET(device) || NM_IS_DEVICE_MACVLAN(device)
+              || NM_IS_DEVICE_VLAN(device));
 
     /* Network interfaces in cloud environments are identified by their permanent
      * MAC address.
@@ -236,11 +239,15 @@ _device_get_hwaddr(NMDeviceEthernet *device)
         return NULL;
     }
 
-    return nm_device_ethernet_get_permanent_hw_address(device);
+    if (NM_IS_DEVICE_ETHERNET(device)) {
+        return nm_device_ethernet_get_permanent_hw_address(NM_DEVICE_ETHERNET(device));
+    } else {
+        return nm_device_get_hw_address(device);
+    }
 }
 
 static char **
-_nmc_get_hwaddrs(NMClient *nmc)
+_nmc_get_ethernet_hwaddrs(NMClient *nmc)
 {
     gs_unref_ptrarray GPtrArray *hwaddrs = NULL;
     const GPtrArray             *devices;
@@ -261,7 +268,7 @@ _nmc_get_hwaddrs(NMClient *nmc)
         if (nm_device_get_state(device) < NM_DEVICE_STATE_UNAVAILABLE)
             continue;
 
-        hwaddr = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
+        hwaddr = _device_get_hwaddr(device);
         if (!hwaddr)
             continue;
 
@@ -303,7 +310,7 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr)
         if (!NM_IS_DEVICE_ETHERNET(device))
             continue;
 
-        hwaddr_dev = _device_get_hwaddr(NM_DEVICE_ETHERNET(device));
+        hwaddr_dev = _device_get_hwaddr(device);
         if (!hwaddr_dev)
             continue;
 
@@ -350,7 +357,7 @@ _get_config(GCancellable *sigterm_cancellable, NMCSProvider *provider, NMClient
     };
     gs_strfreev char **hwaddrs = NULL;
 
-    hwaddrs = _nmc_get_hwaddrs(nmc);
+    hwaddrs = _nmc_get_ethernet_hwaddrs(nmc);
 
     nmcs_provider_get_config(provider,
                              TRUE,
@@ -387,11 +394,10 @@ _nmc_skip_connection_by_user_data(NMConnection *connection)
 }
 
 static gboolean
-_nmc_skip_connection_by_type(NMConnection *connection)
+_nmc_skip_connection_by_type(NMConnection *connection, const char *connection_type)
 {
-    if (!nm_streq0(nm_connection_get_connection_type(connection), NM_SETTING_WIRED_SETTING_NAME))
+    if (!nm_streq0(nm_connection_get_connection_type(connection), connection_type))
         return TRUE;
-
     if (!nm_connection_get_setting_ip4_config(connection))
         return TRUE;
 
@@ -632,7 +638,7 @@ try_again:
         return any_changes;
     }
 
-    if (_nmc_skip_connection_by_type(applied_connection)) {
+    if (_nmc_skip_connection_by_type(applied_connection, NM_SETTING_WIRED_SETTING_NAME)) {
         _LOGD("config device %s: device has no suitable applied connection. Skip", hwaddr);
         return any_changes;
     }
@@ -766,7 +772,7 @@ main(int argc, const char *const *argv)
 
     sigterm_cancellable = g_cancellable_new();
 
-    sigterm_data = (SigTermData){
+    sigterm_data = (SigTermData) {
         .cancellable     = sigterm_cancellable,
         .enabled         = TRUE,
         .signal_received = FALSE,
diff --git a/src/nm-cloud-setup/meson.build b/src/nm-cloud-setup/meson.build
index b1269ec6..adb425ec 100644
--- a/src/nm-cloud-setup/meson.build
+++ b/src/nm-cloud-setup/meson.build
@@ -9,6 +9,12 @@ if install_systemdunitdir
     configuration: data_conf,
   )
 
+  test(
+    'check-nm-cloud-setup.service',
+    find_program(join_paths(source_root, 'src/tests/check-systemd-unit.sh')),
+    args: [ join_paths(meson.current_build_dir(), 'nm-cloud-setup.service') ],
+  )
+
   install_data(
     'nm-cloud-setup.timer',
     install_dir: systemd_systemdsystemunitdir,
@@ -30,12 +36,14 @@ libnm_cloud_setup_core = static_library(
     'nmcs-provider-gcp.c',
     'nmcs-provider-azure.c',
     'nmcs-provider-aliyun.c',
+    'nmcs-provider-oci.c',
     'nmcs-provider.c',
   ),
   dependencies: [
     libnm_dep,
     glib_dep,
     libcurl_dep,
+    jansson_dep,
   ],
 )
 
diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.c b/src/nm-cloud-setup/nm-cloud-setup-utils.c
index 75739c7c..314e4011 100644
--- a/src/nm-cloud-setup/nm-cloud-setup-utils.c
+++ b/src/nm-cloud-setup/nm-cloud-setup-utils.c
@@ -126,7 +126,7 @@ nmcs_wait_for_objects_iterate_until_done(GMainContext *context, int timeout_msec
     _wait_for_objects_iterate_loops = g_slist_prepend(_wait_for_objects_iterate_loops, loop);
     G_UNLOCK(_wait_for_objects_lock);
 
-    data = (WaitForObjectsData){
+    data = (WaitForObjectsData) {
         .loop        = loop,
         .got_timeout = FALSE,
     };
@@ -615,3 +615,46 @@ again:
     NM_SET_OUT(out_version_id_changed, FALSE);
     return TRUE;
 }
+
+/*****************************************************************************/
+
+typedef struct {
+    GMainLoop          *main_loop;
+    GError            **error;
+    NMActiveConnection *active_connection;
+} AddAndActivateData;
+
+static void
+_nmcs_add_and_activate_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    AddAndActivateData *data = user_data;
+
+    data->active_connection =
+        nm_client_add_and_activate_connection_finish(NM_CLIENT(source), result, data->error);
+    g_main_loop_quit(data->main_loop);
+}
+
+NMActiveConnection *
+nmcs_add_and_activate(NMClient     *client,
+                      GCancellable *sigterm_cancellable,
+                      NMConnection *connection,
+                      GError      **error)
+{
+    nm_auto_unref_gmainloop GMainLoop *main_loop = g_main_loop_new(NULL, FALSE);
+    AddAndActivateData                 data      = {
+                             .main_loop = main_loop,
+                             .error     = error,
+    };
+
+    nm_client_add_and_activate_connection_async(client,
+                                                connection,
+                                                NULL,
+                                                NULL,
+                                                sigterm_cancellable,
+                                                _nmcs_add_and_activate_cb,
+                                                &data);
+
+    g_main_loop_run(main_loop);
+
+    return data.active_connection;
+}
diff --git a/src/nm-cloud-setup/nm-cloud-setup-utils.h b/src/nm-cloud-setup/nm-cloud-setup-utils.h
index 4ca4634c..96205369 100644
--- a/src/nm-cloud-setup/nm-cloud-setup-utils.h
+++ b/src/nm-cloud-setup/nm-cloud-setup-utils.h
@@ -12,6 +12,7 @@
 #define NMCS_ENV_NM_CLOUD_SETUP_AZURE  "NM_CLOUD_SETUP_AZURE"
 #define NMCS_ENV_NM_CLOUD_SETUP_EC2    "NM_CLOUD_SETUP_EC2"
 #define NMCS_ENV_NM_CLOUD_SETUP_GCP    "NM_CLOUD_SETUP_GCP"
+#define NMCS_ENV_NM_CLOUD_SETUP_OCI    "NM_CLOUD_SETUP_OCI"
 #define NMCS_ENV_NM_CLOUD_SETUP_LOG    "NM_CLOUD_SETUP_LOG"
 
 /* Undocumented/internal environment variables for configuring nm-cloud-setup.
@@ -20,6 +21,7 @@
 #define NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST     "NM_CLOUD_SETUP_AZURE_HOST"
 #define NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST       "NM_CLOUD_SETUP_EC2_HOST"
 #define NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST       "NM_CLOUD_SETUP_GCP_HOST"
+#define NMCS_ENV_NM_CLOUD_SETUP_OCI_HOST       "NM_CLOUD_SETUP_OCI_HOST"
 #define NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES "NM_CLOUD_SETUP_MAP_INTERFACES"
 
 /*****************************************************************************/
@@ -126,7 +128,7 @@ again:
 char *nmcs_utils_uri_build_concat_v(const char *base, const char **components, gsize n_components);
 
 #define nmcs_utils_uri_build_concat(base, ...) \
-    nmcs_utils_uri_build_concat_v(base, ((const char *[]){__VA_ARGS__}), NM_NARG(__VA_ARGS__))
+    nmcs_utils_uri_build_concat_v(base, ((const char *[]) {__VA_ARGS__}), NM_NARG(__VA_ARGS__))
 
 const char *nmcs_utils_uri_complete_interned(const char *uri);
 
@@ -151,6 +153,11 @@ NMConnection *nmcs_device_get_applied_connection(NMDevice     *device,
                                                  guint64      *version_id,
                                                  GError      **error);
 
+NMActiveConnection *nmcs_add_and_activate(NMClient     *client,
+                                          GCancellable *sigterm_cancellable,
+                                          NMConnection *connection,
+                                          GError      **error);
+
 gboolean nmcs_device_reapply(NMDevice     *device,
                              GCancellable *sigterm_cancellable,
                              NMConnection *connection,
diff --git a/src/nm-cloud-setup/nm-cloud-setup.service.in b/src/nm-cloud-setup/nm-cloud-setup.service.in
index cb782b22..455f0ee0 100644
--- a/src/nm-cloud-setup/nm-cloud-setup.service.in
+++ b/src/nm-cloud-setup/nm-cloud-setup.service.in
@@ -31,6 +31,7 @@ ExecStart=@libexecdir@/nm-cloud-setup
 #Environment=NM_CLOUD_SETUP_GCP=yes
 #Environment=NM_CLOUD_SETUP_AZURE=yes
 #Environment=NM_CLOUD_SETUP_ALIYUN=yes
+#Environment=NM_CLOUD_SETUP_OCI=yes
 
 CapabilityBoundingSet=
 KeyringMode=private
diff --git a/src/nm-cloud-setup/nm-http-client.c b/src/nm-cloud-setup/nm-http-client.c
index 20ef6473..0731ba25 100644
--- a/src/nm-cloud-setup/nm-http-client.c
+++ b/src/nm-cloud-setup/nm-http-client.c
@@ -201,7 +201,7 @@ _ehandle_complete(EHandleData *edata, GError *error_take)
     _ehandle_free_ehandle(edata);
 
     req_result  = g_slice_new(GetResult);
-    *req_result = (GetResult){
+    *req_result = (GetResult) {
         .response_code = response_code,
         /* This ensures that response_data is always NUL terminated. This is an important guarantee
          * that NMHttpClient makes. */
@@ -280,7 +280,7 @@ nm_http_client_req(NMHttpClient       *self,
     priv = NM_HTTP_CLIENT_GET_PRIVATE(self);
 
     edata  = g_slice_new(EHandleData);
-    *edata = (EHandleData){
+    *edata = (EHandleData) {
         .task      = nm_g_task_new(self, cancellable, nm_http_client_req, callback, user_data),
         .recv_data = NM_STR_BUF_INIT(0, FALSE),
         .max_data  = max_data,
@@ -558,7 +558,7 @@ nm_http_client_poll_req(NMHttpClient               *self,
     g_return_if_fail(!cancellable || G_CANCELLABLE(cancellable));
 
     poll_req_data  = g_slice_new(PollReqData);
-    *poll_req_data = (PollReqData){
+    *poll_req_data = (PollReqData) {
         .task = nm_g_task_new(self, cancellable, nm_http_client_poll_req, callback, user_data),
         .uri  = g_strdup(uri),
         .request_timeout_ms = request_timeout_ms,
diff --git a/src/nm-cloud-setup/nmcs-provider-azure.c b/src/nm-cloud-setup/nmcs-provider-azure.c
index 78eda16c..d4999b5f 100644
--- a/src/nm-cloud-setup/nmcs-provider-azure.c
+++ b/src/nm-cloud-setup/nmcs-provider-azure.c
@@ -500,7 +500,7 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
             continue;
 
         iface_data  = g_slice_new(AzureIfaceData);
-        *iface_data = (AzureIfaceData){
+        *iface_data = (AzureIfaceData) {
             .get_config_data      = get_config_data,
             .iface_get_config     = NULL,
             .intern_iface_idx     = intern_iface_idx,
diff --git a/src/nm-cloud-setup/nmcs-provider-gcp.c b/src/nm-cloud-setup/nmcs-provider-gcp.c
index 4d9ef965..ce932c1d 100644
--- a/src/nm-cloud-setup/nmcs-provider-gcp.c
+++ b/src/nm-cloud-setup/nmcs-provider-gcp.c
@@ -381,7 +381,7 @@ _get_net_ifaces_list_cb(GObject *source, GAsyncResult *result, gpointer user_dat
             continue;
 
         iface_data  = g_slice_new(GCPIfaceData);
-        *iface_data = (GCPIfaceData){
+        *iface_data = (GCPIfaceData) {
             .get_config_data  = get_config_data,
             .iface_get_config = NULL,
             .intern_iface_idx = intern_iface_idx,
diff --git a/src/nm-cloud-setup/nmcs-provider-oci.c b/src/nm-cloud-setup/nmcs-provider-oci.c
new file mode 100644
index 00000000..b0b0edf9
--- /dev/null
+++ b/src/nm-cloud-setup/nmcs-provider-oci.c
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+#include "nmcs-provider-oci.h"
+#include "nm-cloud-setup-utils.h"
+#include "libnm-glib-aux/nm-jansson.h"
+
+/*****************************************************************************/
+
+#define HTTP_TIMEOUT_MS 3000
+
+#define NM_OCI_HEADER "Authorization:Bearer Oracle"
+#define NM_OCI_HOST   "169.254.169.254"
+#define NM_OCI_BASE   "http://" NM_OCI_HOST
+
+NMCS_DEFINE_HOST_BASE(_oci_base, NMCS_ENV_NM_CLOUD_SETUP_OCI_HOST, NM_OCI_BASE);
+
+#define _oci_uri_concat(...) nmcs_utils_uri_build_concat(_oci_base(), "opc/v2/", __VA_ARGS__)
+
+/*****************************************************************************/
+
+struct _NMCSProviderOCI {
+    NMCSProvider parent;
+};
+
+struct _NMCSProviderOCIClass {
+    NMCSProviderClass parent;
+};
+
+G_DEFINE_TYPE(NMCSProviderOCI, nmcs_provider_oci, NMCS_TYPE_PROVIDER);
+
+/*****************************************************************************/
+
+static void
+_detect_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    gs_unref_object GTask *task      = user_data;
+    gs_free_error GError  *get_error = NULL;
+    gs_free_error GError  *error     = NULL;
+
+    nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error);
+
+    if (nm_utils_error_is_cancelled(get_error)) {
+        g_task_return_error(task, g_steal_pointer(&get_error));
+        return;
+    }
+
+    if (get_error) {
+        nm_utils_error_set(&error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "failure to get OCI instance data: %s",
+                           get_error->message);
+        g_task_return_error(task, g_steal_pointer(&error));
+        return;
+    }
+
+    g_task_return_boolean(task, TRUE);
+}
+
+static void
+detect(NMCSProvider *provider, GTask *task)
+{
+    NMHttpClient *http_client;
+    gs_free char *uri = NULL;
+
+    http_client = nmcs_provider_get_http_client(provider);
+
+    nm_http_client_poll_req(http_client,
+                            (uri = _oci_uri_concat("instance")),
+                            HTTP_TIMEOUT_MS,
+                            256 * 1024,
+                            7000,
+                            1000,
+                            NM_MAKE_STRV(NM_OCI_HEADER),
+                            NULL,
+                            g_task_get_cancellable(task),
+                            NULL,
+                            NULL,
+                            _detect_done_cb,
+                            task);
+}
+
+/*****************************************************************************/
+
+#define _VNIC_WARN(msg) _LOGW("get-config: " msg "(VNIC %s idx=%zu)", vnic_id, i)
+
+static void
+_get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    NMCSProviderGetConfigTaskData  *get_config_data;
+    NMCSProviderGetConfigIfaceData *config_iface_data;
+    gs_unref_bytes GBytes          *response = NULL;
+    gs_free_error GError           *error    = NULL;
+    nm_auto_decref_json json_t     *vnics    = NULL;
+    size_t                          i;
+
+    nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error);
+
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    get_config_data = user_data;
+
+    if (error)
+        goto out;
+
+    vnics = json_loads(g_bytes_get_data(response, NULL), JSON_REJECT_DUPLICATES, NULL);
+    if (!vnics || !json_is_array(vnics)) {
+        nm_utils_error_set(&error,
+                           NM_UTILS_ERROR_UNKNOWN,
+                           "get-config: JSON parse failure, can't configure VNICs");
+        goto out;
+    }
+
+    for (i = 0; i < json_array_size(vnics); i++) {
+        json_t       *vnic, *field;
+        const char   *vnic_id = "", *val;
+        gs_free char *mac     = NULL;
+        in_addr_t     addr;
+        int           prefix;
+
+        vnic = json_array_get(vnics, i);
+        if (!json_is_object(vnic)) {
+            _VNIC_WARN("JSON parse failure, ignoring VNIC");
+            continue;
+        }
+
+        field   = json_object_get(vnic, "vnicId");
+        vnic_id = field && json_is_string(field) ? json_string_value(field) : "";
+
+        field = json_object_get(vnic, "macAddr");
+        val   = field && json_is_string(field) ? json_string_value(field) : NULL;
+        if (!val) {
+            _VNIC_WARN("missing or invalid 'macAddr', ignoring VNIC");
+            continue;
+        }
+
+        mac               = nmcs_utils_hwaddr_normalize(val, json_string_length(field));
+        config_iface_data = nmcs_provider_get_config_iface_data_create(get_config_data, FALSE, mac);
+        config_iface_data->iface_idx = i;
+
+        field = json_object_get(vnic, "privateIp");
+        val   = field && json_is_string(field) ? json_string_value(field) : NULL;
+        if (val && nm_inet_parse_bin(AF_INET, val, NULL, &addr)) {
+            config_iface_data->has_ipv4s    = TRUE;
+            config_iface_data->ipv4s_len    = 1;
+            config_iface_data->ipv4s_arr    = g_new(in_addr_t, 1);
+            config_iface_data->ipv4s_arr[0] = addr;
+        } else {
+            _VNIC_WARN("missing or invalid 'privateIp'");
+        }
+
+        field = json_object_get(vnic, "virtualRouterIp");
+        val   = field && json_is_string(field) ? json_string_value(field) : NULL;
+        if (val && nm_inet_parse_bin(AF_INET, val, NULL, &addr)) {
+            config_iface_data->has_gateway = TRUE;
+            config_iface_data->gateway     = addr;
+        } else {
+            _VNIC_WARN("missing or invalid 'virtualRouterIp'");
+        }
+
+        field = json_object_get(vnic, "subnetCidrBlock");
+        val   = field && json_is_string(field) ? json_string_value(field) : NULL;
+        if (val && nm_inet_parse_with_prefix_bin(AF_INET, val, NULL, &addr, &prefix)) {
+            config_iface_data->has_cidr    = TRUE;
+            config_iface_data->cidr_addr   = addr;
+            config_iface_data->cidr_prefix = prefix;
+        } else {
+            _VNIC_WARN("missing or invalid 'subnetCidrBlock'");
+        }
+    }
+
+out:
+    _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error));
+}
+
+static void
+get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data)
+{
+    gs_free const char *uri = NULL;
+
+    nm_http_client_poll_req(nmcs_provider_get_http_client(provider),
+                            (uri = _oci_uri_concat("vnics")),
+                            HTTP_TIMEOUT_MS,
+                            256 * 1024,
+                            15000,
+                            1000,
+                            NM_MAKE_STRV(NM_OCI_HEADER),
+                            NULL,
+                            get_config_data->intern_cancellable,
+                            NULL,
+                            NULL,
+                            _get_config_done_cb,
+                            get_config_data);
+}
+
+/*****************************************************************************/
+
+static void
+nmcs_provider_oci_init(NMCSProviderOCI *self)
+{}
+
+static void
+dispose(GObject *object)
+{
+    G_OBJECT_CLASS(nmcs_provider_oci_parent_class)->dispose(object);
+}
+
+static void
+nmcs_provider_oci_class_init(NMCSProviderOCIClass *klass)
+{
+    GObjectClass      *object_class   = G_OBJECT_CLASS(klass);
+    NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass);
+
+    object_class->dispose = dispose;
+
+    provider_class->_name                 = "oci";
+    provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_OCI;
+    provider_class->detect                = detect;
+    provider_class->get_config            = get_config;
+}
diff --git a/src/nm-cloud-setup/nmcs-provider-oci.h b/src/nm-cloud-setup/nmcs-provider-oci.h
new file mode 100644
index 00000000..8447bc1c
--- /dev/null
+++ b/src/nm-cloud-setup/nmcs-provider-oci.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef __NMCS_PROVIDER_OCI_H__
+#define __NMCS_PROVIDER_OCI_H__
+
+#include "nmcs-provider.h"
+
+/*****************************************************************************/
+
+typedef struct _NMCSProviderOCI      NMCSProviderOCI;
+typedef struct _NMCSProviderOCIClass NMCSProviderOCIClass;
+
+#define NMCS_TYPE_PROVIDER_OCI (nmcs_provider_oci_get_type())
+#define NMCS_PROVIDER_OCI(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCI))
+#define NMCS_PROVIDER_OCI_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCIClass))
+#define NMCS_IS_PROVIDER_OCI(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_OCI))
+#define NMCS_IS_PROVIDER_OCI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMCS_TYPE_PROVIDER_OCI))
+#define NMCS_PROVIDER_OCI_GET_CLASS(obj) \
+    (G_TYPE_INSTANCE_GET_CLASS((obj), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCIClass))
+
+GType nmcs_provider_oci_get_type(void);
+
+/*****************************************************************************/
+
+#endif /* __NMCS_PROVIDER_OCI_H__ */
diff --git a/src/nm-cloud-setup/nmcs-provider.c b/src/nm-cloud-setup/nmcs-provider.c
index 5b4412b3..0f06c4e2 100644
--- a/src/nm-cloud-setup/nmcs-provider.c
+++ b/src/nm-cloud-setup/nmcs-provider.c
@@ -3,6 +3,8 @@
 #include "libnm-client-aux-extern/nm-default-client.h"
 
 #include "nmcs-provider.h"
+#include "nmcs-provider-aliyun.h"
+#include "nmcs-provider-oci.h"
 
 #include "nm-cloud-setup-utils.h"
 
@@ -95,7 +97,7 @@ nmcs_provider_get_config_result_new(GHashTable *iface_datas)
     g_ptr_array_add(ptrarr, NULL);
 
     result  = g_new(NMCSProviderGetConfigResult, 1);
-    *result = (NMCSProviderGetConfigResult){
+    *result = (NMCSProviderGetConfigResult) {
         .iface_datas   = g_hash_table_ref(iface_datas),
         .n_iface_datas = n_iface_datas,
         .iface_datas_arr =
@@ -185,7 +187,7 @@ nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_co
     nm_assert(NMCS_IS_PROVIDER(get_config_data->self));
 
     iface_data  = g_slice_new(NMCSProviderGetConfigIfaceData);
-    *iface_data = (NMCSProviderGetConfigIfaceData){
+    *iface_data = (NMCSProviderGetConfigIfaceData) {
         .get_config_data = get_config_data,
         .hwaddr          = g_strdup(hwaddr),
         .iface_idx       = -1,
@@ -198,7 +200,7 @@ nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_co
      * Also, knowing the type would allow us to initialize to something other than
      * false/0/NULL/0.0. */
     if (G_OBJECT_TYPE(get_config_data->self) == nmcs_provider_aliyun_get_type()) {
-        iface_data->priv.aliyun = (typeof(iface_data->priv.aliyun)){
+        iface_data->priv.aliyun = (typeof(iface_data->priv.aliyun)) {
             .has_primary_ip_address = FALSE,
         };
     }
@@ -293,7 +295,7 @@ nmcs_provider_get_config(NMCSProvider       *self,
     _LOGD("get-config: starting");
 
     get_config_data  = g_slice_new(NMCSProviderGetConfigTaskData);
-    *get_config_data = (NMCSProviderGetConfigTaskData){
+    *get_config_data = (NMCSProviderGetConfigTaskData) {
         /* "self" is kept alive by "task". */
         .self = self,
         .task = nm_g_task_new(self, cancellable, nmcs_provider_get_config, callback, user_data),
diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h
index 9e5eeebe..98e50ea3 100644
--- a/src/nm-cloud-setup/nmcs-provider.h
+++ b/src/nm-cloud-setup/nmcs-provider.h
@@ -220,11 +220,4 @@ void nmcs_provider_get_config(NMCSProvider       *provider,
 NMCSProviderGetConfigResult *
 nmcs_provider_get_config_finish(NMCSProvider *provider, GAsyncResult *result, GError **error);
 
-/*****************************************************************************/
-
-/* Forward declare the implemented gtype getters so we can use it at a few places without requiring
- * to include the full header. The other parts of those headers should not be used aside where they
- * are necessary. */
-GType nmcs_provider_aliyun_get_type(void);
-
 #endif /* __NMCS_PROVIDER_H__ */