Skip to content

Commit

Permalink
parser: accept special options for MAC address
Browse files Browse the repository at this point in the history
Besides the actual MAC address value, Network Manager and networkd
also accept special options to control the assignment of a MAC address.

The options supported are: permanent, random, stable and preserve.

All these options are supported by Network Manager. Only 'permanent'
(which will be translated to 'persistent') and 'random' are supported by
networkd and will be added to the .link file.
  • Loading branch information
daniloegea committed Feb 22, 2024
1 parent 19917d7 commit ec0d531
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 30 deletions.
5 changes: 4 additions & 1 deletion doc/netplan-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,10 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
- **`macaddress`** (scalar)

> Set the device's MAC address. The MAC address must be in the form
> `XX:XX:XX:XX:XX:XX`.
> "XX:XX:XX:XX:XX:XX". The following special options are also accepted:
> `permanent` and `random`.
> In addition to these options, the NetworkManager renderer also accepts
> `stable` and `preserve`.
>
> **Note:** This will not work reliably for devices matched by name
> only and rendered by networkd, due to interactions with device
Expand Down
23 changes: 21 additions & 2 deletions src/networkd.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
if (!def->set_name &&
!def->wake_on_lan &&
!def->mtubytes &&
!(_is_macaddress_special_nd_option(def->set_mac) && def->backend == NETPLAN_BACKEND_NETWORKD) &&
(def->receive_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
(def->transmit_checksum_offload == NETPLAN_TRISTATE_UNSET) &&
(def->tcp_segmentation_offload == NETPLAN_TRISTATE_UNSET) &&
Expand All @@ -254,6 +255,15 @@ write_link_file(const NetplanNetDefinition* def, const char* rootdir, const char
if (def->mtubytes)
g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);

if (_is_macaddress_special_nd_option(def->set_mac) && def->backend == NETPLAN_BACKEND_NETWORKD) {
if (!g_strcmp0(def->set_mac, "permanent")) {
/* "permanent" is used for both NM and ND, but the actual setting value for ND is "persistent" */
g_string_append_printf(s, "MACAddressPolicy=persistent\n");
} else {
g_string_append_printf(s, "MACAddressPolicy=%s\n", def->set_mac);
}
}

/* Offload options */
if (def->receive_checksum_offload != NETPLAN_TRISTATE_UNSET)
g_string_append_printf(s, "ReceiveChecksumOffload=%s\n",
Expand Down Expand Up @@ -496,7 +506,7 @@ write_netdev_file(const NetplanNetDefinition* def, const char* rootdir, const ch
s = g_string_sized_new(200);
g_string_append_printf(s, "[NetDev]\nName=%s\n", def->id);

if (def->set_mac)
if (def->set_mac && _is_valid_macaddress(def->set_mac))
g_string_append_printf(s, "MACAddress=%s\n", def->set_mac);
if (def->mtubytes)
g_string_append_printf(s, "MTUBytes=%u\n", def->mtubytes);
Expand Down Expand Up @@ -771,7 +781,7 @@ _netplan_netdef_write_network_file(

if (def->mtubytes)
g_string_append_printf(link, "MTUBytes=%u\n", def->mtubytes);
if (def->set_mac)
if (def->set_mac && _is_valid_macaddress(def->set_mac))
g_string_append_printf(link, "MACAddress=%s\n", def->set_mac);

if (def->emit_lldp)
Expand Down Expand Up @@ -1369,6 +1379,15 @@ _netplan_netdef_write_networkd(

}

if (def->set_mac &&
!_is_valid_macaddress(def->set_mac) &&
!_is_macaddress_special_nd_option(def->set_mac)) {
g_set_error(error, NETPLAN_BACKEND_ERROR, NETPLAN_ERROR_UNSUPPORTED,
"ERROR: %s: networkd backend does not support the MAC address option '%s'\n",
def->id, def->set_mac);
return FALSE;
}

if (def->type >= NETPLAN_DEF_TYPE_VIRTUAL)
write_netdev_file(def, rootdir, path_base);
if (!_netplan_netdef_write_network_file(np_state, def, rootdir, path_base, has_been_written, error))
Expand Down
2 changes: 1 addition & 1 deletion src/nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,7 @@ netplan_state_finish_nm_write(
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) {
if (nd->set_mac && _is_valid_macaddress(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);
Expand Down
13 changes: 2 additions & 11 deletions src/parse-nm.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,8 @@ keyfile_handle_cloned_mac_address(GKeyFile *kf, NetplanNetDefinition* nd, const

if (!mac) return;

/* If the value of "cloned-mac-address" is one of the below we don't try to
* parse it and leave it in the passthrough section.
*/
if ( g_strcmp0(mac, "preserve")
&& g_strcmp0(mac, "permanent")
&& g_strcmp0(mac, "random")
&& g_strcmp0(mac, "stable")
) {
nd->set_mac = g_strdup(mac);
_kf_clear_key(kf, group, "cloned-mac-address");
}
nd->set_mac = g_strdup(mac);
_kf_clear_key(kf, group, "cloned-mac-address");
}

STATIC void
Expand Down
54 changes: 41 additions & 13 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,19 @@ handle_generic_str(NetplanParser* npp, yaml_node_t* node, void* entryptr, const
return TRUE;
}

STATIC gboolean
handle_special_macaddress_option(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
{
g_assert(entryptr);
g_assert(node->type == YAML_SCALAR_NODE);

if (!_is_macaddress_special_nm_option(scalar(node)) &&
!_is_macaddress_special_nd_option(scalar(node)))
return FALSE;

return handle_generic_str(npp, node, entryptr, data, error);
}

/*
* Handler for setting a MAC address field from a scalar node, inside a given struct
* @entryptr: pointer to the beginning of the to-be-modified data structure
Expand All @@ -376,17 +389,9 @@ STATIC gboolean
handle_generic_mac(NetplanParser* npp, yaml_node_t* node, void* entryptr, const void* data, GError** error)
{
g_assert(entryptr);
static regex_t re;
static gboolean re_inited = FALSE;

g_assert(node->type == YAML_SCALAR_NODE);

if (!re_inited) {
g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]](:[[:xdigit:]][[:xdigit:]]){5}((:[[:xdigit:]][[:xdigit:]]){14})?$", REG_EXTENDED|REG_NOSUB) == 0);
re_inited = TRUE;
}

if (regexec(&re, scalar(node), 0, NULL, 0) != 0)
if (!_is_valid_macaddress(scalar(node)))
return yaml_error(npp, node, error, "Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX or XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX", scalar(node));

return handle_generic_str(npp, node, entryptr, data, error);
Expand Down Expand Up @@ -635,16 +640,39 @@ handle_vxlan_id_ref(NetplanParser* npp, yaml_node_t* node, const void* data, __u


/**
* Generic handler for setting a npp->current.netdef MAC address field from a scalar node
* Generic handler for setting a npp->current.netdef match MAC address field from a scalar node
* @data: offset into NetplanNetDefinition where the const char* field to write is
* located
*/
STATIC gboolean
handle_netdef_mac(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
handle_netdef_match_mac(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
return handle_generic_mac(npp, node, npp->current.netdef, data, error);
}

/**
* Generic handler for setting a npp->current.netdef MAC address field from a scalar node
* @data: offset into NetplanNetDefinition where the const char* field to write is
* located
*/
STATIC gboolean
handle_netdef_set_mac(NetplanParser* npp, yaml_node_t* node, const void* data, GError** error)
{
int res = handle_generic_mac(npp, node, npp->current.netdef, data, NULL);

/* If the generic MAC parsing fails, we check to see if the value is one of the special values */
if (!res) {
if (!handle_special_macaddress_option(npp, node, npp->current.netdef, data, NULL)) {
return yaml_error(npp, node, error,
"Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX, XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
" or one of 'permanent', 'random', 'stable', 'preserve'.",
scalar(node));
}
}

return TRUE;
}

/**
* Generic handler for setting a npp->current.netdef gboolean field from a scalar node
* @data: offset into NetplanNetDefinition where the gboolean field to write is located
Expand Down Expand Up @@ -882,7 +910,7 @@ handle_match_driver(NetplanParser* npp, yaml_node_t* node, __unused const char*

STATIC const mapping_entry_handler match_handlers[] = {
{"driver", YAML_NO_NODE, {.variable=handle_match_driver}, NULL},
{"macaddress", YAML_SCALAR_NODE, {.generic=handle_netdef_mac}, netdef_offset(match.mac)},
{"macaddress", YAML_SCALAR_NODE, {.generic=handle_netdef_match_mac}, netdef_offset(match.mac)},
{"name", YAML_SCALAR_NODE, {.generic=handle_netdef_id}, netdef_offset(match.original_name)},
{NULL}
};
Expand Down Expand Up @@ -2832,7 +2860,7 @@ static const mapping_entry_handler dhcp6_overrides_handlers[] = {
{"ipv6-mtu", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(ipv6_mtubytes)}, \
{"ipv6-privacy", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(ip6_privacy)}, \
{"link-local", YAML_SEQUENCE_NODE, {.generic=handle_link_local}, NULL}, \
{"macaddress", YAML_SCALAR_NODE, {.generic=handle_netdef_mac}, netdef_offset(set_mac)}, \
{"macaddress", YAML_SCALAR_NODE, {.generic=handle_netdef_set_mac}, netdef_offset(set_mac)}, \
{"mtu", YAML_SCALAR_NODE, {.generic=handle_netdef_guint}, netdef_offset(mtubytes)}, \
{"nameservers", YAML_MAPPING_NODE, {.map={.handlers=nameservers_handlers}}, NULL}, \
{"optional", YAML_SCALAR_NODE, {.generic=handle_netdef_bool}, netdef_offset(optional)}, \
Expand Down
9 changes: 9 additions & 0 deletions src/util-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ is_string_in_array(GArray* array, const char* value);
gboolean
_is_auth_key_management_psk(const NetplanAuthenticationSettings* auth);

gboolean
_is_macaddress_special_nm_option(const char* value);

gboolean
_is_macaddress_special_nd_option(const char* value);

gboolean
_is_valid_macaddress(const char* value);

NETPLAN_INTERNAL struct address_iter*
_netplan_netdef_new_address_iter(NetplanNetDefinition* netdef);

Expand Down
31 changes: 31 additions & 0 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <arpa/inet.h>
#include <fnmatch.h>
#include <errno.h>
#include <regex.h>
#include <string.h>
#include <sys/mman.h>

Expand Down Expand Up @@ -1141,3 +1142,33 @@ _is_auth_key_management_psk(const NetplanAuthenticationSettings* auth)
return ( auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_PSK
|| auth->key_management == NETPLAN_AUTH_KEY_MANAGEMENT_WPA_SAE);
}

gboolean
_is_macaddress_special_nm_option(const char* value)
{
return ( !g_strcmp0(value, "preserve")
|| !g_strcmp0(value, "permanent")
|| !g_strcmp0(value, "random")
|| !g_strcmp0(value, "stable"));
}

gboolean
_is_macaddress_special_nd_option(const char* value)
{
return ( !g_strcmp0(value, "permanent")
|| !g_strcmp0(value, "random"));
}

gboolean
_is_valid_macaddress(const char* value)
{
static regex_t re;
static gboolean re_inited = FALSE;

if (!re_inited) {
g_assert(regcomp(&re, "^[[:xdigit:]][[:xdigit:]](:[[:xdigit:]][[:xdigit:]]){5}((:[[:xdigit:]][[:xdigit:]]){14})?$", REG_EXTENDED|REG_NOSUB) == 0);
re_inited = TRUE;
}

return regexec(&re, value, 0, NULL, 0) == 0;
}

0 comments on commit ec0d531

Please sign in to comment.