summaryrefslogtreecommitdiff
path: root/src/nm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm.c')
-rw-r--r--src/nm.c296
1 files changed, 188 insertions, 108 deletions
diff --git a/src/nm.c b/src/nm.c
index 319a80b..5a2e62b 100644
--- a/src/nm.c
+++ b/src/nm.c
@@ -26,58 +26,15 @@
#include <glib/gprintf.h>
#include <uuid.h>
+#include "names.h"
#include "netplan.h"
#include "nm.h"
#include "parse.h"
#include "parse-globals.h"
+#include "parse-nm.h"
#include "util.h"
#include "util-internal.h"
#include "validation.h"
-#include "parse-nm.h"
-
-GString* udev_rules;
-
-/**
- * Append NM device specifier of @def to @s.
- */
-static void
-g_string_append_netdef_match(GString* s, const NetplanNetDefinition* def)
-{
- g_assert(!def->match.driver || def->set_name);
- if (def->match.mac || def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
- if (def->match.mac) {
- g_string_append_printf(s, "mac:%s,", def->match.mac);
- }
- /* MAC could change, e.g. for bond slaves. Ignore by interface-name as well */
- if (def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) {
- /* we always have the renamed name here */
- g_string_append_printf(s, "interface-name:%s,",
- (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) ? def->id
- : (def->set_name ?: def->match.original_name));
- }
- } else {
- /* no matches → match all devices of that type */
- switch (def->type) {
- case NETPLAN_DEF_TYPE_ETHERNET:
- g_string_append(s, "type:ethernet,");
- break;
- /* This cannot be reached with just NM and networkd backends, as
- * networkd does not support wifi and thus we'll never blacklist a
- * wifi device from NM. This would become relevant with another
- * wifi-supporting backend, but until then this just spoils 100%
- * code coverage.
- case NETPLAN_DEF_TYPE_WIFI:
- g_string_append(s, "type:wifi");
- break;
- */
-
- // LCOV_EXCL_START
- default:
- g_assert_not_reached();
- // LCOV_EXCL_STOP
- }
- }
-}
/**
* Infer if this is a modem netdef of type GSM.
@@ -108,7 +65,11 @@ type_str(const NetplanNetDefinition* def)
const NetplanDefType type = def->type;
switch (type) {
case NETPLAN_DEF_TYPE_ETHERNET:
- return "ethernet";
+ /* 20-byte IPoIB MAC + colons */
+ if (def->ib_mode || (def->match.mac && strlen(def->match.mac) == 59))
+ return "infiniband";
+ else
+ return "ethernet";
case NETPLAN_DEF_TYPE_MODEM:
if (modem_is_gsm(def))
return "gsm";
@@ -122,13 +83,19 @@ type_str(const NetplanNetDefinition* def)
return "bond";
case NETPLAN_DEF_TYPE_VLAN:
return "vlan";
+ case NETPLAN_DEF_TYPE_VRF:
+ return "vrf";
case NETPLAN_DEF_TYPE_TUNNEL:
if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD)
return "wireguard";
+ else if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN)
+ return "vxlan";
return "ip-tunnel";
case NETPLAN_DEF_TYPE_NM:
/* needs to be overriden by passthrough "connection.type" setting */
- return NULL;
+ g_assert(def->backend_settings.nm.passthrough);
+ GData *passthrough = def->backend_settings.nm.passthrough;
+ return g_datalist_get_data(&passthrough, "connection.type");
// LCOV_EXCL_START
default:
g_assert_not_reached();
@@ -431,9 +398,6 @@ write_tunnel_params(const NetplanNetDefinition* def, GKeyFile *kf)
static void
write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
{
- if (auth->eap_method == NETPLAN_AUTH_EAP_NONE)
- return;
-
switch (auth->eap_method) {
case NETPLAN_AUTH_EAP_TLS:
g_key_file_set_string(kf, "802-1x", "eap", "tls");
@@ -468,14 +432,11 @@ write_dot1x_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile
static void
write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *kf)
{
- if (auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_NONE)
- return;
-
switch (auth->key_management) {
+ case NETPLAN_AUTH_KEY_MANAGEMENT_NONE:
+ break;
case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK:
g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-psk");
- if (auth->password)
- g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
break;
case NETPLAN_AUTH_KEY_MANAGEMENT_WPA_EAP:
g_key_file_set_string(kf, "wifi-security", "key-mgmt", "wpa-eap");
@@ -486,7 +447,10 @@ write_wifi_auth_parameters(const NetplanAuthenticationSettings* auth, GKeyFile *
default: break; // LCOV_EXCL_LINE
}
- write_dot1x_auth_parameters(auth, kf);
+ if (auth->eap_method != NETPLAN_AUTH_EAP_NONE)
+ write_dot1x_auth_parameters(auth, kf);
+ else if (auth->password)
+ g_key_file_set_string(kf, "wifi-security", "psk", auth->password);
}
static void
@@ -496,6 +460,60 @@ maybe_generate_uuid(const NetplanNetDefinition* def)
uuid_generate((unsigned char*)def->uuid);
}
+static void
+write_vxlan_parameters(const NetplanNetDefinition* def, GKeyFile* kf)
+{
+ g_assert(def->vxlan);
+ char uuidstr[37];
+ if (def->vxlan->ageing)
+ g_key_file_set_uint64(kf, "vxlan", "ageing", def->vxlan->ageing);
+ if (def->tunnel.port)
+ g_key_file_set_uint64(kf, "vxlan", "destination-port", def->tunnel.port);
+ if (def->vxlan->vni)
+ g_key_file_set_uint64(kf, "vxlan", "id", def->vxlan->vni);
+ if (def->vxlan->mac_learning)
+ g_key_file_set_boolean(kf, "vxlan", "learning", def->vxlan->mac_learning);
+ if (def->vxlan->limit)
+ g_key_file_set_uint64(kf, "vxlan", "limit", def->vxlan->limit);
+ if (def->tunnel.local_ip)
+ g_key_file_set_string(kf, "vxlan", "local", def->tunnel.local_ip);
+ if (def->tunnel.remote_ip)
+ g_key_file_set_string(kf, "vxlan", "remote", def->tunnel.remote_ip);
+ if (def->vxlan->arp_proxy)
+ g_key_file_set_boolean(kf, "vxlan", "proxy", def->vxlan->arp_proxy);
+ if (def->vxlan->notifications) {
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L2_MISS)
+ g_key_file_set_boolean(kf, "vxlan", "l2-miss", TRUE);
+ if (def->vxlan->notifications & NETPLAN_VXLAN_NOTIFICATION_L3_MISS)
+ g_key_file_set_boolean(kf, "vxlan", "l3-miss", TRUE);
+ }
+ if (def->vxlan->source_port_min && def->vxlan->source_port_max) {
+ g_key_file_set_uint64(kf, "vxlan", "source-port-min", def->vxlan->source_port_min);
+ g_key_file_set_uint64(kf, "vxlan", "source-port-max", def->vxlan->source_port_max);
+ }
+ if (def->vxlan->tos)
+ g_key_file_set_uint64(kf, "vxlan", "tos", def->vxlan->tos);
+ if (def->tunnel_ttl)
+ g_key_file_set_uint64(kf, "vxlan", "ttl", def->tunnel_ttl);
+ if (def->vxlan->short_circuit)
+ g_key_file_set_boolean(kf, "vxlan", "rsc", def->vxlan->short_circuit);
+ if (def->vxlan->link) {
+ if (def->vxlan->link->has_match) {
+ /* we need to refer to the parent's UUID as we don't have an
+ * interface name with match: */
+ maybe_generate_uuid(def->vxlan->link);
+ uuid_unparse(def->vxlan->link->uuid, uuidstr);
+ g_key_file_set_string(kf, "vxlan", "parent", uuidstr);
+ } else {
+ /* if we have an interface name, use that as parent */
+ g_key_file_set_string(kf, "vxlan", "parent", def->vxlan->link->id);
+ }
+ }
+
+ if (def->vxlan->checksums || def->vxlan->extensions || def->vxlan->flow_label != G_MAXUINT || def->vxlan->do_not_fragment)
+ g_warning("%s: checksums/extensions/flow-lable/do-not-fragment are not supported by NetworkManager\n", def->id);
+}
+
/**
* Special handling for passthrough mode: read key-value pairs from
* "backend_settings.nm.passthrough" and inject them into the keyfile as-is.
@@ -595,17 +613,17 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
}
nm_type = type_str(def);
- if (nm_type)
+ if (nm_type && def->type != NETPLAN_DEF_TYPE_NM)
g_key_file_set_string(kf, "connection", "type", nm_type);
if (ap && ap->backend_settings.nm.uuid)
g_key_file_set_string(kf, "connection", "uuid", ap->backend_settings.nm.uuid);
else if (def->backend_settings.nm.uuid)
g_key_file_set_string(kf, "connection", "uuid", def->backend_settings.nm.uuid);
- /* VLAN devices refer to us as their parent; if our ID is not a name but we
- * have matches, parent= must be the connection UUID, so put it into the
- * connection */
- if (def->has_vlans && def->has_match) {
+ /* VLAN/VXLAN devices refer to us as their parent; if our ID is not a name
+ * but we have matches, parent= must be the connection UUID, so put it into
+ * the connection */
+ if ((def->has_vlans || def->has_vxlans) && def->has_match) {
maybe_generate_uuid(def);
uuid_unparse(def->uuid, uuidstr);
g_key_file_set_string(kf, "connection", "uuid", uuidstr);
@@ -646,6 +664,11 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
if (def->type == NETPLAN_DEF_TYPE_BRIDGE)
write_bridge_params(def, kf);
+ if (def->type == NETPLAN_DEF_TYPE_VRF) {
+ g_key_file_set_uint64(kf, "vrf", "table", def->vrf_table);
+ write_routes(def, kf, AF_INET, error);
+ write_routes(def, kf, AF_INET6, error);
+ }
}
if (def->type == NETPLAN_DEF_TYPE_MODEM) {
const char* modem_type = modem_is_gsm(def) ? "gsm" : "cdma";
@@ -692,35 +715,31 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
g_key_file_set_string(kf, "connection", "master", def->bond);
}
+ if (def->vrf_link) {
+ g_key_file_set_string(kf, "connection", "slave-type", "vrf");
+ g_key_file_set_string(kf, "connection", "master", def->vrf_link->id);
+ }
+
if (def->ipv6_mtubytes) {
g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "ERROR: %s: NetworkManager definitions do not support ipv6-mtu\n", def->id);
return FALSE;
}
if (def->type < NETPLAN_DEF_TYPE_VIRTUAL) {
- if (def->type == NETPLAN_DEF_TYPE_ETHERNET)
- g_key_file_set_integer(kf, "ethernet", "wake-on-lan", def->wake_on_lan ? 1 : 0);
-
- const char* con_type = NULL;
- switch (def->type) {
- case NETPLAN_DEF_TYPE_WIFI:
- con_type = "wifi";
- case NETPLAN_DEF_TYPE_MODEM:
- /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
- break;
- default:
- con_type = "ethernet";
- }
-
- if (con_type) {
+ /* Avoid adding an [ethernet] section into the [gsm/cdma] description. */
+ if (g_strcmp0(nm_type, "gsm") != 0 || g_strcmp0(nm_type, "cdma") != 0) {
+ if (g_strcmp0(nm_type, "ethernet") == 0)
+ g_key_file_set_integer(kf, nm_type, "wake-on-lan", def->wake_on_lan ? 1 : 0);
if (!def->set_name && def->match.mac)
- g_key_file_set_string(kf, con_type, "mac-address", def->match.mac);
+ g_key_file_set_string(kf, nm_type, "mac-address", def->match.mac);
if (def->set_mac)
- g_key_file_set_string(kf, con_type, "cloned-mac-address", def->set_mac);
+ g_key_file_set_string(kf, nm_type, "cloned-mac-address", def->set_mac);
if (def->mtubytes)
- g_key_file_set_uint64(kf, con_type, "mtu", def->mtubytes);
+ g_key_file_set_uint64(kf, nm_type, "mtu", def->mtubytes);
if (def->wowlan && def->wowlan > NETPLAN_WIFI_WOWLAN_DEFAULT)
- g_key_file_set_uint64(kf, con_type, "wake-on-wlan", def->wowlan);
+ g_key_file_set_uint64(kf, nm_type, "wake-on-wlan", def->wowlan);
+ if (def->ib_mode != NETPLAN_IB_MODE_KERNEL)
+ g_key_file_set_string(kf, nm_type, "transport-mode", netplan_infiniband_mode_name(def->ib_mode));
}
} else {
if (def->set_mac)
@@ -752,6 +771,8 @@ write_nm_conf_access_point(const NetplanNetDefinition* def, const char* rootdir,
if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_WIREGUARD) {
if (!write_wireguard_params(def, kf, error))
return FALSE;
+ } else if (def->tunnel.mode == NETPLAN_TUNNEL_MODE_VXLAN) {
+ write_vxlan_parameters(def, kf);
} else
write_tunnel_params(def, kf);
}
@@ -960,49 +981,108 @@ netplan_netdef_write_nm(
return no_error;
}
-static void
-nd_append_non_nm_ids(gpointer data, gpointer str)
-{
- const NetplanNetDefinition* nd = data;
-
- if (nd->backend != NETPLAN_BACKEND_NM) {
- if (nd->match.driver) {
- /* TODO: NetworkManager supports (non-globbing) "driver:..." matching nowadays */
- /* NM cannot match on drivers, so ignore these via udev rules */
- if (!udev_rules)
- udev_rules = g_string_new(NULL);
- g_string_append_printf(udev_rules, "ACTION==\"add|change\", SUBSYSTEM==\"net\", ENV{ID_NET_DRIVER}==\"%s\", ENV{NM_UNMANAGED}=\"1\"\n", nd->match.driver);
- } else {
- g_string_append_netdef_match((GString*) str, nd);
- }
- }
-}
-
gboolean
netplan_state_finish_nm_write(
const NetplanState* np_state,
const char* rootdir,
GError** error)
{
- GString *s = NULL;
- gsize len;
+ GString* udev_rules = g_string_new(NULL);
+ GString *nm_conf = g_string_new(NULL);
if (netplan_state_get_netdefs_size(np_state) == 0)
return TRUE; // LCOV_EXCL_LINE as generate.c already deals with it.
/* Set all devices not managed by us to unmanaged, so that NM does not
- * auto-connect and interferes */
- s = g_string_new("[keyfile]\n# devices managed by networkd\nunmanaged-devices+=");
- len = s->len;
- g_list_foreach(np_state->netdefs_ordered, nd_append_non_nm_ids, s);
- if (s->len > len)
- g_string_free_to_file(s, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL);
+ * auto-connect and interferes.
+ * Also, mark all devices managed by us explicitly, so it won't get in
+ * conflict with the system's udev rules that might ignore some devices
+ * in containers via usr/lib/udev/rules.d/85-nm-unmanaged-devices.rules */
+ GList* iter = np_state->netdefs_ordered;
+ while (iter) {
+ const NetplanNetDefinition* nd = iter->data;
+ const gchar* nm_type;
+ GString *tmp = NULL;
+ guint unmanaged = nd->backend == NETPLAN_BACKEND_NM ? 0 : 1;
+
+ /* Special case: manage or ignore any device of given type on empty "match: {}" stanza */
+ if (nd->has_match && !nd->match.driver && !nd->match.mac && !nd->match.original_name) {
+ nm_type = type_str(nd);
+ g_assert(nm_type);
+ g_string_append_printf(nm_conf, "[device-netplan.%s.%s]\nmatch-device=type:%s\n"
+ "managed=%d\n\n", netplan_def_type_name(nd->type),
+ nd->id, nm_type, !unmanaged);
+ }
+ /* Normal case: manage or ignore devices by specific udev rules */
+ else {
+ const gchar *prefix = "SUBSYSTEM==\"net\", ACTION==\"add|change|move\",";
+ const gchar *suffix = nd->backend == NETPLAN_BACKEND_NM ? " ENV{NM_UNMANAGED}=\"0\"\n" : " ENV{NM_UNMANAGED}=\"1\"\n";
+ g_string_append_printf(udev_rules, "# netplan: network.%s.%s (on NetworkManager %s)\n",
+ netplan_def_type_name(nd->type), nd->id,
+ unmanaged ? "deny-list" : "allow-list");
+ /* Match by explicit interface name, if possible */
+ if (nd->set_name) {
+ // simple case: explicit new interface name
+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->set_name, suffix);
+ } else if (!nd->has_match) {
+ // simple case: explicit netplan ID is interface name
+ g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->id, suffix);
+ }
+ /* Also, match by explicit (new) MAC, if available */
+ if (nd->set_mac) {
+ tmp = g_string_new(nd->set_mac);
+ g_string_append_printf(udev_rules, "%s ATTR{address}==\"%s\",%s", prefix, g_string_ascii_down(tmp)->str, suffix);
+ g_string_free(tmp, TRUE);
+ }
+ /* Finally, add a full match, using all rules & globs available
+ * from the "match" stanza (e.g. original_name/mac/drivers)
+ * This will match the "old" interface (i.e. original MAC and/or
+ * interface name) if it got changed */
+ if (nd->has_match && (nd->match.original_name || nd->match.mac || nd->match.driver)) {
+ // match on original name glob
+ // TODO: maybe support matching on multiple name globs in the future (like drivers)
+ g_string_append(udev_rules, prefix);
+ if (nd->match.original_name)
+ g_string_append_printf(udev_rules, " ENV{ID_NET_NAME}==\"%s\",", nd->match.original_name);
+
+ // match on (explicit) MAC address. Yes this would be unique on its own, but we
+ // keep it within the "full match" to make the logic more comprehensible.
+ if (nd->match.mac) {
+ tmp = g_string_new(nd->match.mac);
+ g_string_append_printf(udev_rules, " ATTR{address}==\"%s\",", g_string_ascii_down(tmp)->str);
+ g_string_free(tmp, TRUE);
+ }
+
+ // match on (multiple) driver globs
+ if (nd->match.driver) {
+ gchar *drivers = NULL;
+ if (strchr(nd->match.driver, '\t')) {
+ gchar **split = g_strsplit(nd->match.driver, "\t", -1);
+ drivers = g_strjoinv("|", split);
+ g_strfreev(split);
+ } else
+ drivers = g_strdup(nd->match.driver);
+ g_string_append_printf(udev_rules, " ENV{ID_NET_DRIVER}==\"%s\",", drivers);
+ g_free(drivers);
+ }
+ g_string_append(udev_rules, suffix);
+ }
+ }
+ iter = iter->next;
+ }
+
+ /* write generated NetworkManager drop-in config */
+ if (nm_conf->len > 0)
+ g_string_free_to_file(nm_conf, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL);
else
- g_string_free(s, TRUE);
+ g_string_free(nm_conf, TRUE);
/* write generated udev rules */
- if (udev_rules)
+ if (udev_rules->len > 0)
g_string_free_to_file(udev_rules, rootdir, "run/udev/rules.d/90-netplan.rules", NULL);
+ else
+ g_string_free(udev_rules, TRUE);
+
return TRUE;
}