diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/dns/nm-dns-dnsconfd.c | 124 | ||||
| -rw-r--r-- | src/core/nm-l3cfg.c | 69 | ||||
| -rw-r--r-- | src/core/nm-manager.c | 164 | ||||
| -rw-r--r-- | src/core/nm-netns.c | 19 | ||||
| -rw-r--r-- | src/core/nm-netns.h | 4 | ||||
| -rw-r--r-- | src/core/nm-policy.c | 35 | ||||
| -rw-r--r-- | src/core/settings/plugins/keyfile/nms-keyfile-plugin.c | 13 | ||||
| -rw-r--r-- | src/nm-cloud-setup/main.c | 388 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-oci.c | 77 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider.c | 9 | ||||
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider.h | 6 | ||||
| -rw-r--r-- | src/nmtui/nmt-page-ip6.c | 1 | ||||
| -rwxr-xr-x | src/tests/client/test-client.py | 117 |
13 files changed, 754 insertions, 272 deletions
diff --git a/src/core/dns/nm-dns-dnsconfd.c b/src/core/dns/nm-dns-dnsconfd.c index b356c2f9..63b3060f 100644 --- a/src/core/dns/nm-dns-dnsconfd.c +++ b/src/core/dns/nm-dns-dnsconfd.c @@ -32,6 +32,8 @@ typedef struct { char *name_owner; guint name_owner_changed_id; GCancellable *name_owner_cancellable; + bool was_start_tried; + GCancellable *start_cancellable; GVariant *latest_update_args; guint awaited_configuration_serial; @@ -61,7 +63,11 @@ G_DEFINE_TYPE(NMDnsDnsconfd, nm_dns_dnsconfd, NM_TYPE_DNS_PLUGIN) #define DNSCONFD_DBUS_SERVICE "com.redhat.dnsconfd" -typedef enum { CONNECTION_FAIL, CONNECTION_SUCCESS, CONNECTION_WAIT } ConnectionState; +typedef enum { + CONNECTION_FAIL, + CONNECTION_SUCCESS, + CONNECTION_WAIT, +} ConnectionState; /*****************************************************************************/ @@ -329,29 +335,8 @@ get_networks(NMDnsConfigIPData *ip_data, char ***networks) static void server_builder_append_interface_info(GVariantBuilder *argument_builder, const char *interface, - char **networks, - const char *connection_id, - const char *connection_uuid, - const char *dbus_path) + char **networks) { - if (connection_id) { - g_variant_builder_add(argument_builder, - "{sv}", - "connection-id", - g_variant_new("s", connection_id)); - } - if (connection_uuid) { - g_variant_builder_add(argument_builder, - "{sv}", - "connection-uuid", - g_variant_new("s", connection_uuid)); - } - if (dbus_path) { - g_variant_builder_add(argument_builder, - "{sv}", - "connection-object", - g_variant_new("s", dbus_path)); - } if (interface) { g_variant_builder_add(argument_builder, "{sv}", "interface", g_variant_new("s", interface)); } @@ -500,6 +485,7 @@ name_owner_changed(NMDnsDnsconfd *self, const char *name_owner) } _LOGT("D-Bus name for dnsconfd got owner %s", name_owner); + priv->was_start_tried = FALSE; if (!subscribe_serial(self)) { /* This means that in time between new name and subscribe serial call @@ -544,6 +530,33 @@ get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) name_owner_changed(user_data, name_owner); } +static void +dnsconfd_start_done(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + NMDnsDnsconfd *self; + NMDnsDnsconfdPrivate *priv; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *response = NULL; + + response = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source_object), res, &error); + if (nm_utils_error_is_cancelled(error)) + return; + + self = user_data; + priv = NM_DNS_DNSCONFD_GET_PRIVATE(self); + nm_clear_g_cancellable(&priv->start_cancellable); + + if (!res) { + g_dbus_error_strip_remote_error(error); + _LOGW("failed to start Dnsconfd %s", error->message); + } else { + _LOGT("succesfully started Dnsconfd"); + } + + /* No update maybe changed or state change, as this is handled by the name owner callbacks + * which should be triggered right after this */ +} + static ConnectionState ensure_all_connected(NMDnsDnsconfd *self) { @@ -581,6 +594,19 @@ ensure_all_connected(NMDnsDnsconfd *self) self); } + if (!priv->was_start_tried) { + _LOGT("attempting to start Dnsconfd via DBus"); + priv->was_start_tried = TRUE; + nm_clear_g_cancellable(&priv->start_cancellable); + priv->start_cancellable = g_cancellable_new(); + nm_dbus_connection_call_start_service_by_name(priv->dbus_connection, + DNSCONFD_DBUS_SERVICE, + -1, + priv->start_cancellable, + dnsconfd_start_done, + self); + } + return CONNECTION_WAIT; } @@ -589,18 +615,11 @@ parse_all_interface_config(GVariantBuilder *argument_builder, const CList *ip_data_lst_head, const char *ca) { - NMDnsConfigIPData *ip_data; - const char *const *dns_server_strings; - guint nameserver_count; - const char *ifname; - NMDevice *device; - NMActiveConnection *active_connection; - NMSettingsConnection *settings_connection; - NMActRequest *act_request; - const char *connection_id; - const char *connection_uuid; - const char *dbus_path; - gboolean explicit_default = is_default_interface_explicit(ip_data_lst_head); + NMDnsConfigIPData *ip_data; + const char *const *dns_server_strings; + guint nameserver_count; + const char *ifname; + gboolean explicit_default = is_default_interface_explicit(ip_data_lst_head); c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) { /* No need to free insides of routing and search domains, as they point to data @@ -615,23 +634,7 @@ parse_all_interface_config(GVariantBuilder *argument_builder, &nameserver_count); if (!nameserver_count) continue; - ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); - device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, ip_data->data->ifindex); - act_request = nm_device_get_act_request(device); - active_connection = NM_ACTIVE_CONNECTION(act_request); - - /* Presume that when we have server of this interface then the interface has to have - * an active connection */ - nm_assert(active_connection); - - settings_connection = nm_active_connection_get_settings_connection(active_connection); - connection_id = nm_settings_connection_get_id(settings_connection); - connection_uuid = nm_settings_connection_get_uuid(settings_connection); - dbus_path = nm_dbus_object_get_path_still_exported(NM_DBUS_OBJECT(act_request)); - - /* dbus_path also should be set, because if we are parsing this connection then we - * expect it to be active and exported on dbus */ - nm_assert(dbus_path && dbus_path[0] != 0); + ifname = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex); gather_interface_domains(ip_data, explicit_default, &routing_domains, &search_domains); get_networks(ip_data, &networks); @@ -643,12 +646,7 @@ parse_all_interface_config(GVariantBuilder *argument_builder, routing_domains, search_domains, ca)) { - server_builder_append_interface_info(argument_builder, - ifname, - networks, - connection_id, - connection_uuid, - dbus_path); + server_builder_append_interface_info(argument_builder, ifname, networks); } } } @@ -696,7 +694,10 @@ update(NMDnsPlugin *plugin, /* We need to consider only whether we are connected, because newer update call * overrides the old one */ - if (all_connected != CONNECTION_SUCCESS) { + if (all_connected == CONNECTION_FAIL) { + priv->plugin_state = DNSCONFD_PLUGIN_IDLE; + _LOGT("failed to connect"); + } else if (all_connected == CONNECTION_WAIT) { priv->plugin_state = DNSCONFD_PLUGIN_WAIT_CONNECT; _LOGT("not connected, waiting to connect"); } else { @@ -704,6 +705,8 @@ update(NMDnsPlugin *plugin, _LOGT("connected, waiting for update to finish"); } + _nm_dns_plugin_update_pending_maybe_changed(plugin); + if (all_connected == CONNECTION_FAIL) { nm_utils_error_set(error, NM_UTILS_ERROR_UNKNOWN, @@ -717,8 +720,6 @@ update(NMDnsPlugin *plugin, send_dnsconfd_update(self); - _nm_dns_plugin_update_pending_maybe_changed(NM_DNS_PLUGIN(self)); - return TRUE; } @@ -731,6 +732,7 @@ stop(NMDnsPlugin *plugin) nm_clear_g_cancellable(&priv->update_cancellable); nm_clear_g_cancellable(&priv->name_owner_cancellable); nm_clear_g_cancellable(&priv->serial_cancellable); + nm_clear_g_cancellable(&priv->start_cancellable); nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->properties_changed_id); } diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c index fb48b860..a9aa506d 100644 --- a/src/core/nm-l3cfg.c +++ b/src/core/nm-l3cfg.c @@ -372,6 +372,8 @@ G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT) #define _NETNS_WATCHER_IP_ADDR_TAG(self, addr_family) \ ((gconstpointer) & (((char *) self)[1 + NM_IS_IPv4(addr_family)])) +#define _NETNS_WATCHER_MPTCP_IPV6_TAG(self) ((gconstpointer) & (((char *) self)[3])) + /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_CORE @@ -4960,7 +4962,7 @@ next: } out: - nm_netns_watcher_remove_all(self->priv.netns, TAG, FALSE); + nm_netns_watcher_remove_dirty(self->priv.netns, TAG); } /*****************************************************************************/ @@ -4973,6 +4975,30 @@ _global_tracker_mptcp_untrack(NML3Cfg *self, int addr_family) TRUE); } +static void +mptcp_ipv6_addr_cb(NMNetns *netns, + NMNetnsWatcherType watcher_type, + const NMNetnsWatcherData *watcher_data, + gconstpointer tag, + const NMNetnsWatcherEventData *event_data, + gpointer user_data) +{ + NML3Cfg *self = user_data; + + if (event_data->ip_addr.change_type == NM_PLATFORM_SIGNAL_REMOVED) + return; + + nm_assert(NMP_OBJECT_GET_TYPE(event_data->ip_addr.obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); + + if (event_data->ip_addr.obj->ip6_address.n_ifa_flags & IFA_F_TENTATIVE) + return; + + /* We are inside the handler for a platform event, we should not + * perform other operations on platform synchronously. Schedule a + * commit in a idle handler. */ + nm_l3cfg_commit_on_idle_schedule(self, NM_L3_CFG_COMMIT_TYPE_AUTO); +} + static gboolean _l3_commit_mptcp_af(NML3Cfg *self, NML3CfgCommitType commit_type, @@ -5051,6 +5077,8 @@ _l3_commit_mptcp_af(NML3Cfg *self, self->priv.p->combined_l3cd_commited, addr_family, (const NMPlatformIPAddress **) &addr) { + const NMPObject *obj; + /* We want to evaluate the with-{loopback,link_local}-{4,6} flags based on the actual * ifa_scope that the address will have once we configure it. * "addr" is an address we want to configure, we expect that it will @@ -5077,6 +5105,34 @@ _l3_commit_mptcp_af(NML3Cfg *self, break; } + obj = nm_platform_ip_address_get(self->priv.platform, + addr_family, + self->priv.ifindex, + addr); + if (!obj) { + /* The address is not yet configured in platform, typically due to + * IPv4 DAD; skip it for now otherwise the kernel will try to use + * the endpoint, it will fail, and it will never try it again. */ + goto skip_addr; + } + if (!IS_IPv4 && (obj->ip6_address.n_ifa_flags & IFA_F_TENTATIVE)) { + NMNetnsWatcherData watcher_data = {}; + + /* The endpoint is not usable when the address is tentative. + * Watch the address until it becomes non-tentative and then + * schedule a new commit. */ + watcher_data.ip_addr.addr.addr_family = AF_INET6; + watcher_data.ip_addr.addr.addr.addr6 = addr->a6.address; + + nm_netns_watcher_add(self->priv.netns, + NM_NETNS_WATCHER_TYPE_IP_ADDR, + &watcher_data, + _NETNS_WATCHER_MPTCP_IPV6_TAG(self), + mptcp_ipv6_addr_cb, + self); + goto skip_addr; + } + a.addr = nm_ip_addr_init(addr_family, addr->ax.address_ptr); /* We track the address with different priorities, that depends @@ -5105,6 +5161,8 @@ skip_addr: } } + nm_netns_watcher_remove_dirty(self->priv.netns, _NETNS_WATCHER_MPTCP_IPV6_TAG(self)); + if (!any_tracked) { /* We need to make it known that this ifindex is used. Track a dummy object. */ if (nmp_global_tracker_track( @@ -5841,12 +5899,9 @@ finalize(GObject *object) gboolean changed; if (self->priv.netns) { - nm_netns_watcher_remove_all(self->priv.netns, - _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET), - TRUE); - nm_netns_watcher_remove_all(self->priv.netns, - _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET6), - TRUE); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET)); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_IP_ADDR_TAG(self, AF_INET6)); + nm_netns_watcher_remove_all(self->priv.netns, _NETNS_WATCHER_MPTCP_IPV6_TAG(self)); } nm_prioq_destroy(&self->priv.p->failedobj_prioq); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index c9bcbd12..a861b2fe 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -2594,7 +2594,7 @@ nm_manager_remove_device(NMManager *self, const char *ifname, NMDeviceType devic * Returns: A #NMDevice that was just realized; %NULL if none */ static NMDevice * -system_create_virtual_device(NMManager *self, NMConnection *connection) +system_create_virtual_device(NMManager *self, NMConnection *connection, GError **error) { NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self); NMDeviceFactory *factory; @@ -2605,20 +2605,20 @@ system_create_virtual_device(NMManager *self, NMConnection *connection) NMDevice *device = NULL; NMDevice *parent = NULL; NMDevice *dev_candidate; - gs_free_error GError *error = NULL; - NMLogLevel log_level; g_return_val_if_fail(NM_IS_MANAGER(self), NULL); g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); - iface = nm_manager_get_connection_iface(self, connection, &parent, &parent_spec, &error); - if (!iface) { - _LOG3D(LOGD_DEVICE, connection, "can't get a name of a virtual device: %s", error->message); + iface = nm_manager_get_connection_iface(self, connection, &parent, &parent_spec, error); + if (!iface) return NULL; - } if (parent_spec && !parent) { - /* parent is not ready, wait */ + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_DEPENDENCY_FAILED, + "Parent device for '%s' not available", + iface); return NULL; } @@ -2626,7 +2626,11 @@ system_create_virtual_device(NMManager *self, NMConnection *connection) c_list_for_each_entry (dev_candidate, &priv->devices_lst_head, devices_lst) { if (nm_device_check_connection_compatible(dev_candidate, connection, FALSE, NULL)) { if (nm_device_is_real(dev_candidate)) { - _LOG3D(LOGD_DEVICE, connection, "already created virtual interface name %s", iface); + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Device named '%s' already exists", + iface); return NULL; } @@ -2640,27 +2644,22 @@ system_create_virtual_device(NMManager *self, NMConnection *connection) factory = nm_device_factory_manager_find_factory_for_connection(connection); if (!factory) { - _LOG3E(LOGD_DEVICE, - connection, - "(%s) NetworkManager plugin for '%s' unavailable", - iface, - nm_connection_get_connection_type(connection)); + g_set_error(error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "'%s' plugin for '%s' unavailable", + nm_connection_get_connection_type(connection), + iface); return NULL; } - device = nm_device_factory_create_device(factory, iface, NULL, connection, NULL, &error); - if (!device) { - _LOG3W(LOGD_DEVICE, connection, "factory can't create the device: %s", error->message); + device = nm_device_factory_create_device(factory, iface, NULL, connection, NULL, error); + if (!device) return NULL; - } _LOG3D(LOGD_DEVICE, connection, "create virtual device %s", nm_device_get_iface(device)); - if (!add_device(self, device, &error)) { - _LOG3W(LOGD_DEVICE, - connection, - "can't register the device with manager: %s", - error->message); + if (!add_device(self, device, error)) { g_object_unref(device); return NULL; } @@ -2679,10 +2678,8 @@ system_create_virtual_device(NMManager *self, NMConnection *connection) return device; } - if (!find_controller(self, connection, device, NULL, NULL, NULL, &error)) { - _LOG3D(LOGD_DEVICE, connection, "skip activation: %s", error->message); + if (!find_controller(self, connection, device, NULL, NULL, NULL, error)) return device; - } /* Create backing resources if the device has any autoconnect connections */ connections = nm_settings_get_connections_sorted_by_autoconnect_priority(priv->settings, NULL); @@ -2699,18 +2696,8 @@ system_create_virtual_device(NMManager *self, NMConnection *connection) continue; /* Create any backing resources the device needs */ - if (!nm_device_create_and_realize(device, connection, parent, &error)) { - log_level = - g_error_matches(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES) - ? LOGL_DEBUG - : LOGL_ERR; - _NMLOG3(log_level, - LOGD_DEVICE, - connection, - "couldn't create the device: %s", - error->message); + if (!nm_device_create_and_realize(device, connection, parent, error)) return NULL; - } retry_connections_for_parent_device(self, device); break; @@ -2751,8 +2738,9 @@ retry_connections_for_parent_device(NMManager *self, NMDevice *device) static void connection_changed(NMManager *self, NMSettingsConnection *sett_conn) { - NMConnection *connection; - NMDevice *device; + NMConnection *connection; + NMDevice *device; + gs_free_error GError *error = NULL; if (NM_FLAGS_ANY(nm_settings_connection_get_flags(sett_conn), NM_SETTINGS_CONNECTION_INT_FLAGS_VOLATILE @@ -2764,9 +2752,11 @@ connection_changed(NMManager *self, NMSettingsConnection *sett_conn) if (!nm_connection_is_virtual(connection)) return; - device = system_create_virtual_device(self, connection); - if (!device) + device = system_create_virtual_device(self, connection, &error); + if (!device) { + _LOG3D(LOGD_DEVICE, connection, "Can't create a virtual device: %s", error->message); return; + } /* Maybe the device that was created was needed by some other * connection's device (parent of a VLAN). Let the connections @@ -4699,10 +4689,16 @@ found_better: if (nm_g_hash_table_contains(exclude_devices, device)) continue; - if (!nm_device_is_available(device, - for_user_request - ? NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST - : NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) + /* During startup, NM performs a cleanup of the ovsdb to remove previous entries. + * Before the device is suitable for the connection, it must have ovsdb->ready set + * to TRUE. Performing this check in all kind of interfaces is too agressive and leads + * to race conditions, e.g when a non-virtual bond port gets a carrier, preventing the + * device to be a good candidate for the connection. */ + if (nm_device_get_device_type(device) == NM_DEVICE_TYPE_OVS_INTERFACE + && !nm_device_is_available(device, + for_user_request + ? NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST + : NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) continue; /* determine the priority of this device. Currently, this priority is independent @@ -6542,18 +6538,9 @@ find_device_for_activation(NMManager *self, return FALSE; device = find_device_by_iface(self, iface, connection, NULL, NULL); - if (!device) { - g_set_error_literal(error, - NM_MANAGER_ERROR, - NM_MANAGER_ERROR_UNKNOWN_DEVICE, - "Failed to find a compatible device for this connection"); - return FALSE; - } } } - nm_assert(is_vpn || NM_IS_DEVICE(device)); - *out_device = device; *out_is_vpn = is_vpn; @@ -6678,7 +6665,14 @@ impl_manager_activate_connection(NMDBusObject *obj, if (!subject) goto error; - if (!find_device_for_activation(self, sett_conn, NULL, device_path, &device, &is_vpn, &error)) { + if (!find_device_for_activation(self, sett_conn, NULL, device_path, &device, &is_vpn, &error)) + goto error; + + if (!device && !is_vpn) { + g_set_error_literal(&error, + NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Failed to find a compatible device for this connection"); goto error; } @@ -6801,6 +6795,7 @@ _add_and_activate_auth_done(NMManager *self, const char *error_desc) { NMManagerPrivate *priv; + NMDevice *device; GError *error = NULL; if (!success) { @@ -6813,6 +6808,14 @@ _add_and_activate_auth_done(NMManager *self, nm_active_connection_get_subject(active), error->message); g_dbus_method_invocation_take_error(invocation, error); + + device = nm_active_connection_get_device(active); + if (device && nm_device_is_software(device) + && !nm_device_managed_type_is_external_or_assume(device) + && _check_remove_dev_on_link_deleted(self, device)) { + remove_device(self, device, FALSE); + } + return; } @@ -6968,32 +6971,11 @@ impl_manager_add_and_activate_connection(NMDBusObject *obj, goto error; } - if (is_vpn) { - /* Try to fill the VPN's connection setting and name at least */ - if (!nm_connection_get_setting_vpn(incompl_conn)) { - error = g_error_new_literal(NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_SETTING, - "VPN connections require a 'vpn' setting"); - g_prefix_error(&error, "%s: ", NM_SETTING_VPN_SETTING_NAME); - goto error; - } - - conns = nm_settings_connections_array_to_connections( - nm_settings_get_connections(priv->settings, NULL), - -1); + conns = nm_settings_connections_array_to_connections( + nm_settings_get_connections(priv->settings, NULL), + -1); - nm_utils_complete_generic(priv->platform, - incompl_conn, - NM_SETTING_VPN_SETTING_NAME, - conns, - NULL, - _("VPN connection"), - NULL, - NULL); - } else { - conns = nm_settings_connections_array_to_connections( - nm_settings_get_connections(priv->settings, NULL), - -1); + if (device) { /* Let each device subclass complete the connection */ if (!nm_device_complete_connection(device, incompl_conn, @@ -7001,9 +6983,27 @@ impl_manager_add_and_activate_connection(NMDBusObject *obj, conns, &error)) goto error; - } + } else { + nm_utils_complete_generic(priv->platform, + incompl_conn, + nm_connection_get_connection_type(incompl_conn), + conns, + is_vpn ? _("VPN connection") : NULL, + nm_connection_get_connection_type(incompl_conn), + NULL, + NULL); + + if (!nm_connection_verify(incompl_conn, &error)) + goto error; + + if (!is_vpn && !device) { + nm_assert(nm_connection_is_virtual(incompl_conn)); - nm_assert(_nm_connection_verify(incompl_conn, NULL) == NM_SETTING_VERIFY_SUCCESS); + device = system_create_virtual_device(self, incompl_conn, &error); + if (!device) + goto error; + } + } active = _new_active_connection(self, is_vpn, diff --git a/src/core/nm-netns.c b/src/core/nm-netns.c index 420d01e4..57dbe9c5 100644 --- a/src/core/nm-netns.c +++ b/src/core/nm-netns.c @@ -1369,8 +1369,8 @@ nm_netns_watcher_remove_handle(NMNetns *self, NMNetnsWatcherHandle *handle) g_object_unref(self); } -void -nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all) +static void +watcher_remove(NMNetns *self, gconstpointer tag, gboolean all) { NMNetnsPrivate *priv; WatcherByTag *watcher_by_tag; @@ -1420,6 +1420,21 @@ nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all) } } +void +nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag) +{ + watcher_remove(self, tag, TRUE); +} + +/* Similar to nm_netns_watcher_remove_all(), but removes only watchers + * that were marked as "dirty" in a previous call of this function and were + * not added back via nm_netns_watcher_add() in the meantime. */ +void +nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag) +{ + watcher_remove(self, tag, FALSE); +} + /*****************************************************************************/ static void diff --git a/src/core/nm-netns.h b/src/core/nm-netns.h index 7725ae79..43e9c781 100644 --- a/src/core/nm-netns.h +++ b/src/core/nm-netns.h @@ -98,7 +98,7 @@ void nm_netns_watcher_add(NMNetns *self, NMNetnsWatcherCallback callback, gpointer user_data); -void -nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag, gboolean all /* or only dirty */); +void nm_netns_watcher_remove_all(NMNetns *self, gconstpointer tag); +void nm_netns_watcher_remove_dirty(NMNetns *self, gconstpointer tag); #endif /* __NM_NETNS_H__ */ diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c index f86d8115..33073e4a 100644 --- a/src/core/nm-policy.c +++ b/src/core/nm-policy.c @@ -1873,8 +1873,7 @@ unblock_autoconnect_for_children(NMPolicy *self, const char *parent_device, const char *parent_uuid_settings, const char *parent_uuid_applied, - const char *parent_mac_addr, - gboolean reset_devcon_autoconnect) + const char *parent_mac_addr) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); NMSettingsConnection *const *connections; @@ -1915,10 +1914,8 @@ unblock_autoconnect_for_children(NMPolicy *self, parent_mac_addr)) continue; - if (reset_devcon_autoconnect) { - if (nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn)) - changed = TRUE; - } + if (nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn)) + changed = TRUE; /* unblock the devices associated with that connection */ if (nm_manager_devcon_autoconnect_blocked_reason_set( @@ -1940,12 +1937,11 @@ static void unblock_autoconnect_for_ports(NMPolicy *self, const char *controller_device, const char *controller_uuid_settings, - const char *controller_uuid_applied, - gboolean reset_devcon_autoconnect) + const char *controller_uuid_applied) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self); NMSettingsConnection *const *connections; - gboolean changed; + gboolean changed = FALSE; guint i; _LOGT(LOGD_CORE, @@ -1959,7 +1955,6 @@ unblock_autoconnect_for_ports(NMPolicy *self, "\"", "")); - changed = FALSE; connections = nm_settings_get_connections(priv->settings, NULL); for (i = 0; connections[i]; i++) { NMSettingsConnection *sett_conn = connections[i]; @@ -1977,10 +1972,8 @@ unblock_autoconnect_for_ports(NMPolicy *self, controller_uuid_settings)) continue; - if (reset_devcon_autoconnect) { - if (nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn)) - changed = TRUE; - } + if (nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn)) + changed = TRUE; /* unblock the devices associated with that connection */ if (nm_manager_devcon_autoconnect_blocked_reason_set( @@ -2015,7 +2008,7 @@ unblock_autoconnect_for_ports_for_sett_conn(NMPolicy *self, NMSettingsConnection controller_uuid_settings = nm_setting_connection_get_uuid(s_con); controller_device = nm_setting_connection_get_interface_name(s_con); - unblock_autoconnect_for_ports(self, controller_device, controller_uuid_settings, NULL, TRUE); + unblock_autoconnect_for_ports(self, controller_device, controller_uuid_settings, NULL); } static void @@ -2028,7 +2021,6 @@ activate_port_or_children_connections(NMPolicy *self, const char *controller_uuid_applied = NULL; const char *parent_mac_addr = NULL; NMActRequest *req; - gboolean internal_activation = FALSE; controller_device = nm_device_get_iface(device); nm_assert(controller_device); @@ -2039,7 +2031,6 @@ activate_port_or_children_connections(NMPolicy *self, if (req) { NMConnection *connection; NMSettingsConnection *sett_conn; - NMAuthSubject *subject; sett_conn = nm_active_connection_get_settings_connection(NM_ACTIVE_CONNECTION(req)); if (sett_conn) @@ -2051,25 +2042,19 @@ activate_port_or_children_connections(NMPolicy *self, if (nm_streq0(controller_uuid_settings, controller_uuid_applied)) controller_uuid_applied = NULL; - - subject = nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(req)); - internal_activation = - subject && (nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL); } if (!activate_children_connections_only) { unblock_autoconnect_for_ports(self, controller_device, controller_uuid_settings, - controller_uuid_applied, - !internal_activation); + controller_uuid_applied); } unblock_autoconnect_for_children(self, controller_device, controller_uuid_settings, controller_uuid_applied, - parent_mac_addr, - !internal_activation); + parent_mac_addr); } static gboolean diff --git a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c index 1679cab6..681eef85 100644 --- a/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c +++ b/src/core/settings/plugins/keyfile/nms-keyfile-plugin.c @@ -880,12 +880,11 @@ nms_keyfile_plugin_update_connection(NMSKeyfilePlugin *self, NMConnection **out_connection, GError **error) { - NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE(self); - NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE(storage_x); - gs_unref_object NMConnection *connection_clone = NULL; - gs_unref_object NMConnection *reread = NULL; - gs_free char *full_filename = NULL; - gs_free_error GError *local = NULL; + NMSKeyfilePluginPrivate *priv = NMS_KEYFILE_PLUGIN_GET_PRIVATE(self); + NMSKeyfileStorage *storage = NMS_KEYFILE_STORAGE(storage_x); + gs_unref_object NMConnection *reread = NULL; + gs_free char *full_filename = NULL; + gs_free_error GError *local = NULL; struct timespec mtime; const char *previous_filename; gboolean reread_same; @@ -946,7 +945,7 @@ nms_keyfile_plugin_update_connection(NMSKeyfilePlugin *self, &local)) { _LOGW("commit: failure to write %s (%s) to \"%s\": %s", uuid, - nm_connection_get_id(connection_clone), + nm_connection_get_id(connection), previous_filename, local->message); g_propagate_error(error, g_steal_pointer(&local)); diff --git a/src/nm-cloud-setup/main.c b/src/nm-cloud-setup/main.c index 44500ffa..9de93418 100644 --- a/src/nm-cloud-setup/main.c +++ b/src/nm-cloud-setup/main.c @@ -140,6 +140,8 @@ out: /*****************************************************************************/ +static const NMUtilsNamedValue *gl_interfaces_map = NULL; + static NMUtilsNamedValue * _map_interfaces_parse(void) { @@ -200,46 +202,38 @@ _map_interfaces_parse(void) static const char * _device_get_hwaddr(NMDevice *device) { - static const NMUtilsNamedValue *gl_map_interfaces_map = NULL; - static gsize gl_initialized = 0; - const NMUtilsNamedValue *map = NULL; + const NMUtilsNamedValue *map = NULL; 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. - * - * For testing, we can set NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES - * to a ';' separate list of "$INTERFACE=$HWADDR", which means that we - * pretend that device with ip-interface "$INTERFACE" has the specified permanent - * MAC address. */ - - if (g_once_init_enter(&gl_initialized)) { - gl_map_interfaces_map = _map_interfaces_parse(); - g_once_init_leave(&gl_initialized, 1); - } - - map = gl_map_interfaces_map; - if (G_UNLIKELY(map)) { - const char *const iface = nm_device_get_iface(NM_DEVICE(device)); - - /* For testing, the device<->hwaddr is remapped and the actual permanent - * MAC address of the device ignored. This mapping is configured via - * NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES environment variable. */ + if (NM_IS_DEVICE_ETHERNET(device)) { + /* Ethernet interfaces in cloud environments are identified by their permanent + * MAC address. + * + * For testing, we can set NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES + * to a ';' separate list of "$INTERFACE=$HWADDR", which means that we + * pretend that device with ip-interface "$INTERFACE" has the specified permanent + * MAC address. */ + + map = gl_interfaces_map; + if (G_UNLIKELY(map)) { + const char *const iface = nm_device_get_iface(NM_DEVICE(device)); + + /* For testing, the device<->hwaddr is remapped and the actual permanent + * MAC address of the device ignored. This mapping is configured via + * NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES environment variable. */ + if (!iface) + return NULL; + + for (; map->name; map++) { + if (nm_streq(map->name, iface)) + return map->value_str; + } - if (!iface) return NULL; - - for (; map->name; map++) { - if (nm_streq(map->name, iface)) - return map->value_str; } - return NULL; - } - - 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); @@ -295,7 +289,7 @@ _nmc_get_ethernet_hwaddrs(NMClient *nmc) } static NMDevice * -_nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr) +_nmc_get_device_by_hwaddr(NMClient *nmc, const GType type_device, const char *hwaddr) { const GPtrArray *devices; guint i; @@ -307,7 +301,7 @@ _nmc_get_device_by_hwaddr(NMClient *nmc, const char *hwaddr) const char *hwaddr_dev; gs_free char *s = NULL; - if (!NM_IS_DEVICE_ETHERNET(device)) + if (!G_TYPE_CHECK_INSTANCE_TYPE(device, type_device)) continue; hwaddr_dev = _device_get_hwaddr(device); @@ -427,13 +421,22 @@ _nmc_mangle_connection(NMDevice *device, NM_SET_OUT(out_skipped_single_addr, FALSE); NM_SET_OUT(out_changed, FALSE); + if (nm_streq(nm_connection_get_connection_type(connection), NM_SETTING_MACVLAN_SETTING_NAME)) { + /* The MACVLAN just sits in between, no L3 configuration on it */ + return; + } else if (!nm_streq(nm_connection_get_connection_type(connection), + NM_SETTING_VLAN_SETTING_NAME)) { + /* Preserve existing L3 configuration if not a VLAN */ + if (device) { + if ((ac = nm_device_get_active_connection(device)) + && (remote_connection = NM_CONNECTION(nm_active_connection_get_connection(ac)))) + remote_s_ip = nm_connection_get_setting_ip4_config(remote_connection); + } + } + s_ip = nm_connection_get_setting_ip4_config(connection); nm_assert(NM_IS_SETTING_IP4_CONFIG(s_ip)); - if ((ac = nm_device_get_active_connection(device)) - && (remote_connection = NM_CONNECTION(nm_active_connection_get_connection(ac)))) - remote_s_ip = nm_connection_get_setting_ip4_config(remote_connection); - addrs_new = g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_address_unref); rules_new = g_ptr_array_new_full(config_data->ipv4s_len, (GDestroyNotify) nm_ip_routing_rule_unref); @@ -566,54 +569,31 @@ _nmc_mangle_connection(NMDevice *device, /*****************************************************************************/ static gboolean -_config_one(SigTermData *sigterm_data, - NMClient *nmc, - const NMCSProviderGetConfigResult *result, - guint idx) +_config_existing(SigTermData *sigterm_data, + const NMCSProviderGetConfigIfaceData *config_data, + NMClient *nmc, + const NMCSProviderGetConfigResult *result, + const char *connection_type, + NMDevice *device) { - const NMCSProviderGetConfigIfaceData *config_data = result->iface_datas_arr[idx]; - const char *hwaddr = config_data->hwaddr; - gs_unref_object NMDevice *device = NULL; - gs_unref_object NMConnection *applied_connection = NULL; - guint64 applied_version_id; - gs_free_error GError *error = NULL; - gboolean changed; - gboolean skipped_single_addr; - gboolean version_id_changed; - guint try_count; - gboolean any_changes = FALSE; - gboolean maybe_no_preserved_external_ip; - - g_main_context_iteration(NULL, FALSE); - - if (g_cancellable_is_cancelled(sigterm_data->cancellable)) - return FALSE; - - device = nm_g_object_ref(_nmc_get_device_by_hwaddr(nmc, hwaddr)); - if (!device) { - _LOGD("config device %s: skip because device not found", hwaddr); - return FALSE; - } - - if (!nmcs_provider_get_config_iface_data_is_valid(config_data)) { - _LOGD("config device %s: skip because meta data not successfully fetched", hwaddr); - return FALSE; - } - - if (config_data->iface_idx >= 100) { - /* since we use the iface_idx to select a table number, the range is limited from - * 0 to 99. Note that the providers are required to provide increasing numbers, - * so this means we bail out after the first 100 devices. */ - _LOGD("config device %s: skip because number of supported interfaces reached", hwaddr); - return FALSE; - } + const char *hwaddr = config_data->hwaddr; + gs_unref_object NMConnection *applied_connection = NULL; + guint64 applied_version_id; + gs_free_error GError *error = NULL; + gboolean changed; + gboolean skipped_single_addr; + gboolean version_id_changed; + guint try_count; + gboolean any_changes; + gboolean maybe_no_preserved_external_ip; _LOGD("config device %s: configuring \"%s\" (%s)...", hwaddr, nm_device_get_iface(device) ?: "/unknown/", nm_object_get_path(NM_OBJECT(device))); - try_count = 0; + try_count = 0; + any_changes = FALSE; try_again: g_clear_object(&applied_connection); @@ -638,7 +618,7 @@ try_again: return any_changes; } - if (_nmc_skip_connection_by_type(applied_connection, NM_SETTING_WIRED_SETTING_NAME)) { + if (_nmc_skip_connection_by_type(applied_connection, connection_type)) { _LOGD("config device %s: device has no suitable applied connection. Skip", hwaddr); return any_changes; } @@ -705,7 +685,7 @@ try_again: nm_connection_get_uuid(applied_connection), error->message); } - return any_changes; + return TRUE; } _LOGD("config device %s: connection \"%s\" (%s) reapplied", @@ -713,17 +693,254 @@ try_again: nm_connection_get_id(applied_connection), nm_connection_get_uuid(applied_connection)); + return TRUE; +} + +static gboolean +_config_ethernet(SigTermData *sigterm_data, + const NMCSProviderGetConfigIfaceData *config_data, + NMClient *nmc, + const NMCSProviderGetConfigResult *result) +{ + gs_unref_object NMDevice *device = NULL; + + device = nm_g_object_ref( + _nmc_get_device_by_hwaddr(nmc, NM_TYPE_DEVICE_ETHERNET, config_data->hwaddr)); + if (!device) { + _LOGD("config device %s: skip because device not found", config_data->hwaddr); + return FALSE; + } + + return _config_existing(sigterm_data, + config_data, + nmc, + result, + NM_SETTING_WIRED_SETTING_NAME, + device); +} + +static gboolean +_oci_new_vlan_dev(SigTermData *sigterm_data, + const NMCSProviderGetConfigIfaceData *config_data, + NMClient *nmc, + const NMCSProviderGetConfigResult *result, + const char *connection_type, + const char *parent_hwaddr) +{ + const char *hwaddr = config_data->hwaddr; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMActiveConnection *active_connection = NULL; + gs_free_error GError *error = NULL; + gs_free char *macvlan_name = NULL; + gs_free char *connection_id = NULL; + char *ifname = NULL; + const char *macvlan_parent = NULL; + const char *wired_mac_addr = NULL; + const NMUtilsNamedValue *map = NULL; + const char *ip4_config_method; + NMSetting *s_user; + + connection = nm_simple_connection_new(); + + macvlan_name = g_strdup_printf("macvlan%ld", config_data->iface_idx); + connection_id = g_strdup_printf("%s%ld", connection_type, config_data->iface_idx); + + wired_mac_addr = parent_hwaddr; + if (nm_streq(connection_type, NM_SETTING_MACVLAN_SETTING_NAME)) { + /* In NM-ci, use macvlan.parent instead of wired.mac-address for parent matching + * because we are faking the MAC addresses via NM_CLOUD_SETUP_MAP_INTERFACES. + * The daemon still needs the real MAC, not the mapped one, so it won't work. */ + map = gl_interfaces_map; + if (G_UNLIKELY(map)) { + for (; map->name; map++) { + if (nm_streq(map->value_str, parent_hwaddr)) { + macvlan_parent = map->name; + wired_mac_addr = NULL; + break; + } + } + } + + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_MACVLAN, + NM_SETTING_MACVLAN_MODE, + NM_SETTING_MACVLAN_MODE_VEPA, + NM_SETTING_MACVLAN_PARENT, + macvlan_parent, + NULL)); + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_IP6_CONFIG, + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_DISABLED, + NULL)); + ip4_config_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; + ifname = macvlan_name; + } else if (nm_streq(connection_type, NM_SETTING_VLAN_SETTING_NAME)) { + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_VLAN, + NM_SETTING_VLAN_PARENT, + macvlan_name, + NM_SETTING_VLAN_ID, + config_data->priv.oci.vlan_tag, + NULL)); + ip4_config_method = NM_SETTING_IP4_CONFIG_METHOD_MANUAL; + } else { + g_return_val_if_reached(FALSE); + } + + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_CONNECTION, + NM_SETTING_CONNECTION_ID, + connection_id, + NM_SETTING_CONNECTION_TYPE, + connection_type, + NM_SETTING_CONNECTION_INTERFACE_NAME, + ifname, + NULL)); + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_IP4_CONFIG, + NM_SETTING_IP_CONFIG_METHOD, + ip4_config_method, + NULL)); + + nm_connection_add_setting(connection, + g_object_new(NM_TYPE_SETTING_WIRED, + NM_SETTING_WIRED_MAC_ADDRESS, + wired_mac_addr, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + hwaddr, + NULL)); + + s_user = nm_setting_user_new(); + nm_connection_add_setting(connection, s_user); + nm_setting_user_set_data(NM_SETTING_USER(s_user), + "org.freedesktop.NetworkManager.origin", + "nm-cloud-setup", + NULL); + + _nmc_mangle_connection(NULL, connection, result, config_data, NULL, NULL); + + _LOGD("config device %s: creating %s connection for VLAN %d on %s...", + hwaddr, + ifname ?: connection_type, + config_data->priv.oci.vlan_tag, + parent_hwaddr); + + active_connection = nmcs_add_and_activate(nmc, NULL, connection, &error); + if (active_connection == NULL) { + if (!nm_utils_error_is_cancelled(error)) { + _LOGD("config device %s: failure to activate connection: %s", hwaddr, error->message); + } + return FALSE; + } + + _LOGD("config device %s: connection \"%s\" (%s) created", + hwaddr, + nm_active_connection_get_id(active_connection), + nm_active_connection_get_uuid(active_connection)); + + return TRUE; +} + +static gboolean +_oci_config_vnic_dev(SigTermData *sigterm_data, + const NMCSProviderGetConfigIfaceData *config_data, + NMClient *nmc, + const NMCSProviderGetConfigResult *result, + const GType device_type, + const char *connection_type, + const char *parent_hwaddr) +{ + gs_unref_object NMDevice *device = NULL; + + device = nm_g_object_ref(_nmc_get_device_by_hwaddr(nmc, device_type, config_data->hwaddr)); + if (device) { + /* There is a device. Modify and reapply the currently applied connection. */ + return _config_existing(sigterm_data, config_data, nmc, result, connection_type, device); + } else { + /* There is no device, but we're configuring a VLAN. + * We can just go ahead and create one with a new connection. */ + return _oci_new_vlan_dev(sigterm_data, + config_data, + nmc, + result, + connection_type, + parent_hwaddr); + } +} + +static gboolean +_config_one(SigTermData *sigterm_data, + NMCSProvider *provider, + NMClient *nmc, + const NMCSProviderGetConfigResult *result, + guint idx) +{ + const NMCSProviderGetConfigIfaceData *config_data = result->iface_datas_arr[idx]; + gboolean any_changes; + + g_main_context_iteration(NULL, FALSE); + + if (g_cancellable_is_cancelled(sigterm_data->cancellable)) + return FALSE; + + if (!nmcs_provider_get_config_iface_data_is_valid(config_data)) { + _LOGD("config device %s: skip because meta data not successfully fetched", + config_data->hwaddr); + return FALSE; + } + + if (config_data->iface_idx >= 100) { + /* since we use the iface_idx to select a table number, the range is limited from + * 0 to 99. Note that the providers are required to provide increasing numbers, + * so this means we bail out after the first 100 devices. */ + _LOGD("config device %s: skip because number of supported interfaces reached", + config_data->hwaddr); + return FALSE; + } + + if (NMCS_IS_PROVIDER_OCI(provider) && config_data->priv.oci.vlan_tag != 0) { + if (config_data->priv.oci.parent_hwaddr == NULL) { + _LOGW("config device %s: has vlan id %d but no parent device", + config_data->hwaddr, + config_data->priv.oci.vlan_tag); + return FALSE; + } + + /* MACVLAN first, because VLAN is on top of it. */ + any_changes = _oci_config_vnic_dev(sigterm_data, + config_data, + nmc, + result, + NM_TYPE_DEVICE_MACVLAN, + NM_SETTING_MACVLAN_SETTING_NAME, + config_data->priv.oci.parent_hwaddr); + any_changes += _oci_config_vnic_dev(sigterm_data, + config_data, + nmc, + result, + NM_TYPE_DEVICE_VLAN, + NM_SETTING_VLAN_SETTING_NAME, + config_data->hwaddr); + + } else { + any_changes = _config_ethernet(sigterm_data, config_data, nmc, result); + } + return any_changes; } static gboolean -_config_all(SigTermData *sigterm_data, NMClient *nmc, const NMCSProviderGetConfigResult *result) +_config_all(SigTermData *sigterm_data, + NMCSProvider *provider, + NMClient *nmc, + const NMCSProviderGetConfigResult *result) { gboolean any_changes = FALSE; guint i; for (i = 0; i < result->n_iface_datas; i++) { - if (_config_one(sigterm_data, nmc, result, i)) + if (_config_one(sigterm_data, provider, nmc, result, i)) any_changes = TRUE; } @@ -804,11 +1021,14 @@ main(int argc, const char *const *argv) goto done; } + /* Initialize map used in test scenarios. */ + gl_interfaces_map = _map_interfaces_parse(); + result = _get_config(sigterm_cancellable, provider, nmc); if (!result) goto done; - if (_config_all(&sigterm_data, nmc, result)) + if (_config_all(&sigterm_data, provider, nmc, result)) _LOGI("some changes were applied for provider %s", nmcs_provider_get_name(provider)); else _LOGD("no changes were applied for provider %s", nmcs_provider_get_name(provider)); diff --git a/src/nm-cloud-setup/nmcs-provider-oci.c b/src/nm-cloud-setup/nmcs-provider-oci.c index b0b0edf9..17cd997e 100644 --- a/src/nm-cloud-setup/nmcs-provider-oci.c +++ b/src/nm-cloud-setup/nmcs-provider-oci.c @@ -92,6 +92,9 @@ _get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) gs_unref_bytes GBytes *response = NULL; gs_free_error GError *error = NULL; nm_auto_decref_json json_t *vnics = NULL; + gboolean is_baremetal; + gs_unref_ptrarray GPtrArray *phys_nic_macs = NULL; + GHashTableIter h_iter; size_t i; nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error); @@ -112,12 +115,24 @@ _get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) goto out; } + if (json_array_size(vnics) > 0) { + is_baremetal = NULL != json_object_get(json_array_get(vnics, 0), "nicIndex"); + _LOGI("get-config: detected %s instance", is_baremetal ? "baremetal" : "VM"); + } else { + is_baremetal = FALSE; + _LOGI("get-config: empty VNICs metadata, cannot detect instance type"); + } + + if (is_baremetal) + phys_nic_macs = g_ptr_array_sized_new(16); + 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; + json_int_t nic_index = -1, vlan_tag = -1; vnic = json_array_get(vnics, i); if (!json_is_object(vnic)) { @@ -130,12 +145,28 @@ _get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) field = json_object_get(vnic, "macAddr"); val = field && json_is_string(field) ? json_string_value(field) : NULL; - if (!val) { + mac = val ? nmcs_utils_hwaddr_normalize(val, json_string_length(field)) : NULL; + if (!mac) { _VNIC_WARN("missing or invalid 'macAddr', ignoring VNIC"); continue; } - mac = nmcs_utils_hwaddr_normalize(val, json_string_length(field)); + if (is_baremetal) { + field = json_object_get(vnic, "nicIndex"); + nic_index = field && json_is_integer(field) ? json_integer_value(field) : -1; + if (nic_index < 0 || nic_index >= 1024) { /* 1024 = random limit to prevent abuse*/ + _VNIC_WARN("missing or invalid 'nicIndex', ignoring VNIC"); + continue; + } + + field = json_object_get(vnic, "vlanTag"); + vlan_tag = field && json_is_integer(field) ? json_integer_value(field) : -1; + if (vlan_tag < 0) { + _VNIC_WARN("missing or invalid 'vlanTag', ignoring VNIC"); + continue; + } + } + config_iface_data = nmcs_provider_get_config_iface_data_create(get_config_data, FALSE, mac); config_iface_data->iface_idx = i; @@ -168,6 +199,48 @@ _get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) } else { _VNIC_WARN("missing or invalid 'subnetCidrBlock'"); } + + if (is_baremetal) { + gboolean is_phys_nic = vlan_tag == 0; + + /* In baremetal instances, configure VNICs' VLAN (physical NICs don't need it) */ + if (is_phys_nic) { + config_iface_data->priv.oci.vlan_tag = 0; + config_iface_data->priv.oci.parent_hwaddr = NULL; + if (nic_index >= phys_nic_macs->len) + g_ptr_array_set_size(phys_nic_macs, + NM_MAX((guint) (nic_index + 1), phys_nic_macs->len * 2)); + phys_nic_macs->pdata[nic_index] = (gpointer) config_iface_data->hwaddr; + } else { + /* We might not have all the physical NICs' MACs yet, save nicIndex for later */ + config_iface_data->priv.oci.parent_hwaddr = GINT_TO_POINTER((int) nic_index); + config_iface_data->priv.oci.vlan_tag = vlan_tag; + } + } + } + + if (is_baremetal) { + g_hash_table_iter_init(&h_iter, get_config_data->result_dict); + + /* Now that all the metadata is processed we should have all the physical NICs' MACs */ + while (g_hash_table_iter_next(&h_iter, NULL, (gpointer *) &config_iface_data)) { + bool is_phys_nic = config_iface_data->priv.oci.vlan_tag == 0; + int nic_index = GPOINTER_TO_INT(config_iface_data->priv.oci.parent_hwaddr); + + if (is_phys_nic) + continue; + + if (nic_index >= phys_nic_macs->len || phys_nic_macs->pdata[nic_index] == NULL) { + _LOGW("get-config: physical NIC for nicIndex=%d not found, ignoring VNIC " + "(VNIC macAddr=%s)", + nic_index, + config_iface_data->hwaddr); + g_hash_table_iter_remove(&h_iter); + continue; + } + + config_iface_data->priv.oci.parent_hwaddr = g_strdup(phys_nic_macs->pdata[nic_index]); + } } out: diff --git a/src/nm-cloud-setup/nmcs-provider.c b/src/nm-cloud-setup/nmcs-provider.c index 0f06c4e2..251749ba 100644 --- a/src/nm-cloud-setup/nmcs-provider.c +++ b/src/nm-cloud-setup/nmcs-provider.c @@ -188,6 +188,7 @@ nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_co iface_data = g_slice_new(NMCSProviderGetConfigIfaceData); *iface_data = (NMCSProviderGetConfigIfaceData) { + .provider = g_object_ref(get_config_data->self), .get_config_data = get_config_data, .hwaddr = g_strdup(hwaddr), .iface_idx = -1, @@ -203,6 +204,11 @@ nmcs_provider_get_config_iface_data_create(NMCSProviderGetConfigTaskData *get_co iface_data->priv.aliyun = (typeof(iface_data->priv.aliyun)) { .has_primary_ip_address = FALSE, }; + } else if (G_OBJECT_TYPE(get_config_data->self) == nmcs_provider_oci_get_type()) { + iface_data->priv.oci = (typeof(iface_data->priv.oci)) { + .vlan_tag = 0, + .parent_hwaddr = NULL, + }; } /* the has does not own the key (iface_datta->hwaddr), the lifetime of the @@ -220,6 +226,9 @@ _iface_data_free(gpointer data) g_free(iface_data->ipv4s_arr); nm_g_ptr_array_unref(iface_data->iproutes); g_free((char *) iface_data->hwaddr); + if (G_OBJECT_TYPE(iface_data->provider) == nmcs_provider_oci_get_type()) + g_free((char *) iface_data->priv.oci.parent_hwaddr); + g_clear_object(&iface_data->provider); nm_g_slice_free(iface_data); } diff --git a/src/nm-cloud-setup/nmcs-provider.h b/src/nm-cloud-setup/nmcs-provider.h index 98e50ea3..95a591c6 100644 --- a/src/nm-cloud-setup/nmcs-provider.h +++ b/src/nm-cloud-setup/nmcs-provider.h @@ -17,6 +17,8 @@ typedef struct { * dictionary. */ const char *hwaddr; + struct _NMCSProvider *provider; + struct _NMCSProviderGetConfigTaskData *get_config_data; in_addr_t *ipv4s_arr; @@ -51,6 +53,10 @@ typedef struct { bool has_primary_ip_address : 1; bool ipv4s_arr_ordered : 1; } aliyun; + struct { + guint32 vlan_tag; /* 0 if no VLAN is needed */ + const char *parent_hwaddr; + } oci; } priv; } NMCSProviderGetConfigIfaceData; diff --git a/src/nmtui/nmt-page-ip6.c b/src/nmtui/nmt-page-ip6.c index 7e7a48c5..bd29a3a7 100644 --- a/src/nmtui/nmt-page-ip6.c +++ b/src/nmtui/nmt-page-ip6.c @@ -29,6 +29,7 @@ static NmtNewtPopupEntry ip6methods[] = { {N_("Automatic (DHCP-only)"), NM_SETTING_IP6_CONFIG_METHOD_DHCP}, {N_("Link-Local"), NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL}, {N_("Manual"), NM_SETTING_IP6_CONFIG_METHOD_MANUAL}, + {N_("Shared"), NM_SETTING_IP6_CONFIG_METHOD_SHARED}, {N_("Disabled"), NM_SETTING_IP6_CONFIG_METHOD_DISABLED}, {NULL, NULL}}; diff --git a/src/tests/client/test-client.py b/src/tests/client/test-client.py index d01b3bce..ff734d1a 100755 --- a/src/tests/client/test-client.py +++ b/src/tests/client/test-client.py @@ -2907,6 +2907,123 @@ class TestNmCloudSetup(unittest.TestCase): ) self.assertEqual(exitstatus, 0, "Unexpectedly returned a non-zero status") + @cloud_setup_test + def test_oci_vlans(self): + self._mock_devices() + + oci_meta = "/opc/v2/" + self._mock_path(oci_meta + "instance", "{}") + self._mock_path( + oci_meta + "vnics", + """ + [ + { + "macAddr": "%s", + "privateIp": "%s", + "subnetCidrBlock": "172.31.16.0/20", + "virtualRouterIp": "172.31.16.1", + "vlanTag": 0, + "nicIndex": 0, + "vnicId": "ocid1.vnic.oc1.cz-adamov1.foobarbaz" + }, + { + "macAddr": "%s", + "privateIp": "%s", + "subnetCidrBlock": "172.31.166.0/20", + "virtualRouterIp": "172.31.166.1", + "vlanTag": 0, + "nicIndex": 1, + "vnicId": "ocid1.vnic.oc1.uk-hogwarts.expelliarmus" + }, + { + "macAddr": "C0:00:00:00:00:10", + "privateIp": "172.31.10.10", + "subnetCidrBlock": "172.31.10.0/20", + "virtualRouterIp": "172.31.10.1", + "vlanTag": 700, + "nicIndex": 0, + "vnicId": "ocid1.vnic.oc1.uk-hogwarts.keka" + } + ] + """ + % ( + TestNmCloudSetup._mac1, + TestNmCloudSetup._ip1, + TestNmCloudSetup._mac2, + TestNmCloudSetup._ip2, + ), + ) + + # Run nm-cloud-setup for the first time + pexp = self.ctx.cmd_call_pexpect( + ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH, + [], + { + "NM_CLOUD_SETUP_OCI_HOST": self.md_url, + "NM_CLOUD_SETUP_LOG": "trace", + "NM_CLOUD_SETUP_OCI": "yes", + }, + ) + + pexp.expect("provider oci detected") + pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02") + pexp.expect("get-config: starting") + pexp.expect("get-config: success") + pexp.expect("meta data received") + + # No configuration for the ethernets + pexp.expect('configuring "eth0"') + pexp.expect("device has no suitable applied connection. Skip") + + # Setting up the VLAN + pexp.expect("creating macvlan2 connection for VLAN 700 on CC:00:00:00:00:01...") + pexp.expect("creating vlan connection for VLAN 700 on C0:00:00:00:00:10...") + pexp.expect("some changes were applied for provider oci") + + (exitstatus, signalstatus, valgrind_log) = self.ctx.cmd_close_pexpect(pexp) + Util.valgrind_check_log(valgrind_log, "test_oci_vlans") + self.assertIsNone( + signalstatus, + "Unexpectedly got " + Util.signal_no_to_str(signalstatus or 0), + ) + self.assertEqual(exitstatus, 0, "Unexpectedly returned a non-zero status") + + # TODO: Actually check the contents of the connection + # Probably needs changes to the mock service API + conn_macvlan = self.ctx.srv.findConnections(con_id="connection-3") + assert conn_macvlan is not None + conn_vlan = self.ctx.srv.findConnections(con_id="connection-4") + assert conn_vlan is not None + + # Run nm-cloud-setup for the second time + pexp = self.ctx.cmd_call_pexpect( + ENV_NM_TEST_CLIENT_CLOUD_SETUP_PATH, + [], + { + "NM_CLOUD_SETUP_OCI_HOST": self.md_url, + "NM_CLOUD_SETUP_LOG": "trace", + "NM_CLOUD_SETUP_OCI": "yes", + }, + ) + + # Just the same ol' thing, just no changes this time + pexp.expect("provider oci detected") + pexp.expect("found interfaces: CC:00:00:00:00:01, CC:00:00:00:00:02") + pexp.expect("get-config: starting") + pexp.expect("get-config: success") + pexp.expect("meta data received") + pexp.expect('configuring "eth0"') + pexp.expect("device has no suitable applied connection. Skip") + pexp.expect("no changes were applied for provider oci") + + (exitstatus, signalstatus, valgrind_log) = self.ctx.cmd_close_pexpect(pexp) + Util.valgrind_check_log(valgrind_log, "test_oci_vlans") + self.assertIsNone( + signalstatus, + "Unexpectedly got " + Util.signal_no_to_str(signalstatus or 0), + ) + self.assertEqual(exitstatus, 0, "Unexpectedly returned a non-zero status") + ############################################################################### |