diff --git a/cli/completion/nmcli b/cli/completion/nmcli index 0a185a11ca..43a62f7628 100644 --- a/cli/completion/nmcli +++ b/cli/completion/nmcli @@ -947,7 +947,7 @@ _nmcli() OPTIONS_IP=() ;; bridge) - OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time) + OPTIONS_TYPED=(stp priority forward-delay hello-time max-age ageing-time mac) ;; bridge-|bridge-s|bridge-sl|bridge-sla|bridge-slav|bridge-slave) OPTIONS_TYPED=(master priority path-cost hairpin) diff --git a/cli/src/connections.c b/cli/src/connections.c index 3580c79b67..1129755b37 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -395,7 +395,8 @@ usage_connection_add (void) " [forward-delay <2-30>]\n" " [hello-time <1-10>]\n" " [max-age <6-40>]\n" - " [ageing-time <0-1000000>]\n\n" + " [ageing-time <0-1000000>]\n" + " [mac ]\n\n" " bridge-slave: master \n" " [priority <0-63>]\n" " [path-cost <1-65535>]\n" @@ -3320,8 +3321,8 @@ do_questionnaire_team_slave (char **config) } static void -do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, - char **hello_time, char **max_age, char **ageing_time) +do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, char **hello_time, + char **max_age, char **ageing_time, char **mac) { char *answer; gboolean answer_bool; @@ -3330,7 +3331,7 @@ do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, GError *error = NULL; /* Ask for optional 'bridge' arguments. */ - printf (_("There are 6 optional arguments for 'bridge' connection type.\n")); + printf (_("There are 7 optional arguments for 'bridge' connection type.\n")); answer = nmc_get_user_input (_("Do you want to provide them? (yes/no) [yes] ")); if (answer && (!nmc_string_to_bool (answer, &answer_bool, NULL) || !answer_bool)) { g_free (answer); @@ -3411,6 +3412,17 @@ do_questionnaire_bridge (char **stp, char **priority, char **fwd_delay, } } while (once_more); } + if (!*mac) { + do { + *mac = nmc_get_user_input (_("MAC [none]: ")); + once_more = !check_and_convert_mac (*mac, NULL, ARPHRD_ETHER, "mac", &error); + if (once_more) { + printf ("%s\n", error->message); + g_clear_error (&error); + g_free (*mac); + } + } while (once_more); + } g_free (answer); return; @@ -4562,12 +4574,16 @@ cleanup_team_slave: gboolean stp_bool; unsigned long stp_prio_int, fwd_delay_int, hello_time_int, max_age_int, ageing_time_int; + const char *mac_c = NULL; + char *mac = NULL; + GByteArray *mac_array = NULL; nmc_arg_t exp_args[] = { {"stp", TRUE, &stp_c, FALSE}, {"priority", TRUE, &priority_c, FALSE}, {"forward-delay", TRUE, &fwd_delay_c, FALSE}, {"hello-time", TRUE, &hello_time_c, FALSE}, {"max-age", TRUE, &max_age_c, FALSE}, {"ageing-time", TRUE, &ageing_time_c, FALSE}, + {"mac", TRUE, &mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) @@ -4580,9 +4596,10 @@ cleanup_team_slave: hello_time = hello_time_c ? g_strdup (hello_time_c) : NULL; max_age = max_age_c ? g_strdup (max_age_c) : NULL; ageing_time = ageing_time_c ? g_strdup (ageing_time_c) : NULL; + mac = g_strdup (mac_c); if (ask) do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, - &max_age, &ageing_time); + &max_age, &ageing_time, &mac); /* Use connection's ifname as 'bridge' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); @@ -4629,6 +4646,8 @@ cleanup_team_slave: if (!bridge_prop_string_to_uint (ageing_time, "ageing-time", NM_TYPE_SETTING_BRIDGE, NM_SETTING_BRIDGE_AGEING_TIME, &ageing_time_int, error)) goto cleanup_bridge; + if (!check_and_convert_mac (mac, &mac_array, ARPHRD_ETHER, "mac", error)) + goto cleanup_bridge; /* Set bridge options */ g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, bridge_ifname, NULL); @@ -4644,6 +4663,8 @@ cleanup_team_slave: g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, max_age_int, NULL); if (ageing_time) g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, ageing_time_int, NULL); + if (mac_array) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, mac_array, NULL); success = TRUE; cleanup_bridge: @@ -4654,6 +4675,9 @@ cleanup_bridge: g_free (hello_time); g_free (max_age); g_free (ageing_time); + g_free (mac); + if (mac_array) + g_byte_array_free (mac_array, TRUE); if (!success) return FALSE; diff --git a/cli/src/settings.c b/cli/src/settings.c index 50a96eba33..b0c1ecad99 100644 --- a/cli/src/settings.c +++ b/cli/src/settings.c @@ -564,16 +564,18 @@ NmcOutputField nmc_fields_setting_vlan[] = { NmcOutputField nmc_fields_setting_bridge[] = { SETTING_FIELD ("name", 8), /* 0 */ SETTING_FIELD (NM_SETTING_BRIDGE_INTERFACE_NAME, 15), /* 1 */ - SETTING_FIELD (NM_SETTING_BRIDGE_STP, 5), /* 2 */ - SETTING_FIELD (NM_SETTING_BRIDGE_PRIORITY, 6), /* 3 */ - SETTING_FIELD (NM_SETTING_BRIDGE_FORWARD_DELAY, 6), /* 4 */ - SETTING_FIELD (NM_SETTING_BRIDGE_HELLO_TIME, 6), /* 5 */ - SETTING_FIELD (NM_SETTING_BRIDGE_MAX_AGE, 6), /* 6 */ - SETTING_FIELD (NM_SETTING_BRIDGE_AGEING_TIME, 6), /* 7 */ + SETTING_FIELD (NM_SETTING_BRIDGE_MAC_ADDRESS, 19), /* 2 */ + SETTING_FIELD (NM_SETTING_BRIDGE_STP, 5), /* 3 */ + SETTING_FIELD (NM_SETTING_BRIDGE_PRIORITY, 6), /* 4 */ + SETTING_FIELD (NM_SETTING_BRIDGE_FORWARD_DELAY, 6), /* 5 */ + SETTING_FIELD (NM_SETTING_BRIDGE_HELLO_TIME, 6), /* 6 */ + SETTING_FIELD (NM_SETTING_BRIDGE_MAX_AGE, 6), /* 7 */ + SETTING_FIELD (NM_SETTING_BRIDGE_AGEING_TIME, 6), /* 8 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_BRIDGE_ALL "name"","\ NM_SETTING_BRIDGE_INTERFACE_NAME","\ + NM_SETTING_BRIDGE_MAC_ADDRESS","\ NM_SETTING_BRIDGE_STP","\ NM_SETTING_BRIDGE_PRIORITY","\ NM_SETTING_BRIDGE_FORWARD_DELAY","\ @@ -1053,6 +1055,7 @@ nmc_property_bond_get_options (NMSetting *setting) /* --- NM_SETTING_BRIDGE_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_bridge_get_interface_name, NM_SETTING_BRIDGE_INTERFACE_NAME) +DEFINE_HWADDR_GETTER (nmc_property_bridge_get_mac_address, NM_SETTING_BRIDGE_MAC_ADDRESS) DEFINE_GETTER (nmc_property_bridge_get_stp, NM_SETTING_BRIDGE_STP) DEFINE_GETTER (nmc_property_bridge_get_priority, NM_SETTING_BRIDGE_PRIORITY) DEFINE_GETTER (nmc_property_bridge_get_forward_delay, NM_SETTING_BRIDGE_FORWARD_DELAY) @@ -4926,6 +4929,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (BRIDGE, MAC_ADDRESS), + nmc_property_bridge_get_mac_address, + nmc_property_set_mac, + NULL, + NULL, + NULL, + NULL); nmc_add_prop_funcs (GLUE (BRIDGE, STP), nmc_property_bridge_get_stp, nmc_property_set_bool, @@ -7127,12 +7137,13 @@ setting_bridge_details (NMSetting *setting, NmCli *nmc, const char *one_prop) arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_SECTION_PREFIX); set_val_str (arr, 0, g_strdup (nm_setting_get_name (setting))); set_val_str (arr, 1, nmc_property_bridge_get_interface_name (setting)); - set_val_str (arr, 2, nmc_property_bridge_get_stp (setting)); - set_val_str (arr, 3, nmc_property_bridge_get_priority (setting)); - set_val_str (arr, 4, nmc_property_bridge_get_forward_delay (setting)); - set_val_str (arr, 5, nmc_property_bridge_get_hello_time (setting)); - set_val_str (arr, 6, nmc_property_bridge_get_max_age (setting)); - set_val_str (arr, 7, nmc_property_bridge_get_ageing_time (setting)); + set_val_str (arr, 2, nmc_property_bridge_get_mac_address (setting)); + set_val_str (arr, 3, nmc_property_bridge_get_stp (setting)); + set_val_str (arr, 4, nmc_property_bridge_get_priority (setting)); + set_val_str (arr, 5, nmc_property_bridge_get_forward_delay (setting)); + set_val_str (arr, 6, nmc_property_bridge_get_hello_time (setting)); + set_val_str (arr, 7, nmc_property_bridge_get_max_age (setting)); + set_val_str (arr, 8, nmc_property_bridge_get_ageing_time (setting)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 17133cc18f..c7a2e87687 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -220,6 +220,7 @@ global: nm_setting_bridge_get_forward_delay; nm_setting_bridge_get_hello_time; nm_setting_bridge_get_interface_name; + nm_setting_bridge_get_mac_address; nm_setting_bridge_get_max_age; nm_setting_bridge_get_priority; nm_setting_bridge_get_stp; diff --git a/libnm-util/nm-setting-bridge.c b/libnm-util/nm-setting-bridge.c index 32bfa3380e..f463259ff7 100644 --- a/libnm-util/nm-setting-bridge.c +++ b/libnm-util/nm-setting-bridge.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "nm-setting-bridge.h" #include "nm-param-spec-specialized.h" @@ -76,6 +77,7 @@ NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BRIDGE) typedef struct { char * interface_name; + GByteArray *mac_address; gboolean stp; guint16 priority; guint16 forward_delay; @@ -87,6 +89,7 @@ typedef struct { enum { PROP_0, PROP_INTERFACE_NAME, + PROP_MAC_ADDRESS, PROP_STP, PROP_PRIORITY, PROP_FORWARD_DELAY, @@ -127,6 +130,22 @@ nm_setting_bridge_get_interface_name (NMSettingBridge *setting) return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->interface_name; } +/** + * nm_setting_bridge_get_mac_address: + * @setting: the #NMSettingBridge + * + * Returns: the #NMSettingBridge:mac-address property of the setting + * + * Since: 0.9.10 + **/ +const GByteArray * +nm_setting_bridge_get_mac_address (NMSettingBridge *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BRIDGE (setting), NULL); + + return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->mac_address; +} + /** * nm_setting_bridge_get_stp: * @setting: the #NMSettingBridge @@ -280,6 +299,15 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return FALSE; } + if (priv->mac_address && priv->mac_address->len != ETH_ALEN) { + g_set_error_literal (error, + NM_SETTING_BRIDGE_ERROR, + NM_SETTING_BRIDGE_ERROR_INVALID_PROPERTY, + _("is not a valid MAC address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BRIDGE_SETTING_NAME, NM_SETTING_BRIDGE_MAC_ADDRESS); + return FALSE; + } + if (!check_range (priv->forward_delay, BR_MIN_FORWARD_DELAY, BR_MAX_FORWARD_DELAY, @@ -331,6 +359,9 @@ finalize (GObject *object) g_free (priv->interface_name); + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object); } @@ -345,6 +376,11 @@ set_property (GObject *object, guint prop_id, g_free (priv->interface_name); priv->interface_name = g_value_dup_string (value); break; + case PROP_MAC_ADDRESS: + if (priv->mac_address) + g_byte_array_free (priv->mac_address, TRUE); + priv->mac_address = g_value_dup_boxed (value); + break; case PROP_STP: priv->stp = g_value_get_boolean (value); break; @@ -380,6 +416,9 @@ get_property (GObject *object, guint prop_id, case PROP_INTERFACE_NAME: g_value_set_string (value, nm_setting_bridge_get_interface_name (setting)); break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, nm_setting_bridge_get_mac_address (setting)); + break; case PROP_STP: g_value_set_boolean (value, priv->stp); break; @@ -435,6 +474,23 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) NULL, G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE)); + /** + * NMSettingBridge:mac-address: + * + * If specified, the MAC address of bridge. When creating a new bridge, this MAC address + * will be set. When matching an existing (outside NetworkManager created) bridge, this + * MAC address must match. + * + * Since: 0.9.10 + **/ + g_object_class_install_property + (object_class, PROP_MAC_ADDRESS, + _nm_param_spec_specialized (NM_SETTING_BRIDGE_MAC_ADDRESS, + "MAC Address", + "The MAC address of the bridge", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE)); + /** * NMSettingBridge:stp: * diff --git a/libnm-util/nm-setting-bridge.h b/libnm-util/nm-setting-bridge.h index b0b53fd5f1..1e8957045b 100644 --- a/libnm-util/nm-setting-bridge.h +++ b/libnm-util/nm-setting-bridge.h @@ -56,6 +56,7 @@ typedef enum { GQuark nm_setting_bridge_error_quark (void); #define NM_SETTING_BRIDGE_INTERFACE_NAME "interface-name" +#define NM_SETTING_BRIDGE_MAC_ADDRESS "mac-address" #define NM_SETTING_BRIDGE_STP "stp" #define NM_SETTING_BRIDGE_PRIORITY "priority" #define NM_SETTING_BRIDGE_FORWARD_DELAY "forward-delay" @@ -83,6 +84,9 @@ NMSetting * nm_setting_bridge_new (void); const char * nm_setting_bridge_get_interface_name (NMSettingBridge *setting); +NM_AVAILABLE_IN_0_9_10 +const GByteArray *nm_setting_bridge_get_mac_address (NMSettingBridge *setting); + gboolean nm_setting_bridge_get_stp (NMSettingBridge *setting); guint16 nm_setting_bridge_get_priority (NMSettingBridge *setting); diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 1d91e89dcc..08f07b4ab7 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -555,6 +555,9 @@ The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disamb \(en STP maximum message age, in seconds (default: 20) .IP "\fI[ageing-time <0-1000000>]\fP" 42 \(en the Ethernet MAC address aging time, in seconds (default: 300) +.IP "\fI[mac ]\fP" 42 +\(en MAC address of the bridge (note: this requires a recent kernel feature, +originally introduced in 3.15 upstream kernel) .RE .RS .TP diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 99aa0aff2c..76138ae712 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -102,6 +102,7 @@ check_connection_compatible (NMDevice *device, { const char *iface; NMSettingBridge *s_bridge; + const GByteArray *mac_address; if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_compatible (device, connection, error)) return FALSE; @@ -121,6 +122,21 @@ check_connection_compatible (NMDevice *device, return FALSE; } + mac_address = nm_setting_bridge_get_mac_address (s_bridge); + if (mac_address) { + guint hw_len; + const guint8 *hw_addr; + + hw_addr = nm_device_get_hw_address (device, &hw_len); + if ( !hw_addr + || hw_len != mac_address->len + || memcmp (mac_address->data, hw_addr, hw_len) != 0) { + g_set_error (error, NM_BRIDGE_ERROR, NM_BRIDGE_ERROR_CONNECTION_INVALID, + "The bridge mac-address does not match the address of the device."); + return FALSE; + } + } + return TRUE; } @@ -438,13 +454,22 @@ NMDevice * nm_device_bridge_new_for_connection (NMConnection *connection) { const char *iface; + NMSettingBridge *s_bridge; + const GByteArray *mac_address; g_return_val_if_fail (connection != NULL, NULL); iface = nm_connection_get_virtual_iface_name (connection); g_return_val_if_fail (iface != NULL, NULL); - if ( !nm_platform_bridge_add (iface) + s_bridge = nm_connection_get_setting_bridge (connection); + g_return_val_if_fail (s_bridge, NULL); + + mac_address = nm_setting_bridge_get_mac_address (s_bridge); + + if ( !nm_platform_bridge_add (iface, + mac_address ? mac_address->data : NULL, + mac_address ? mac_address->len : 0) && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) { nm_log_warn (LOGD_DEVICE | LOGD_BRIDGE, "(%s): failed to create bridge master interface for '%s': %s", iface, nm_connection_get_id (connection), diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 2da3181fe0..dce98d8a9b 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -185,7 +185,7 @@ link_added_emit (gpointer user_data) } static gboolean -link_add (NMPlatform *platform, const char *name, NMLinkType type) +link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); NMFakePlatformLink device; @@ -600,7 +600,7 @@ vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint { NMFakePlatformLink *device; - if (!link_add (platform, name, NM_LINK_TYPE_VLAN)) + if (!link_add (platform, name, NM_LINK_TYPE_VLAN, NULL, 0)) return FALSE; device = link_get (platform, link_get_ifindex (platform, name)); @@ -651,7 +651,7 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key) g_return_val_if_fail (parent_device != NULL, FALSE); name = g_strdup_printf ("%s.%04x", parent_device->link.name, p_key); - success = link_add (platform, name, NM_LINK_TYPE_INFINIBAND); + success = link_add (platform, name, NM_LINK_TYPE_INFINIBAND, NULL, 0); g_free (name); return success; @@ -1232,15 +1232,15 @@ static gboolean setup (NMPlatform *platform) { /* skip zero element */ - link_add (platform, NULL, NM_LINK_TYPE_NONE); + link_add (platform, NULL, NM_LINK_TYPE_NONE, NULL, 0); /* add loopback interface */ - link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK); + link_add (platform, "lo", NM_LINK_TYPE_LOOPBACK, NULL, 0); /* add some ethernets */ - link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET); - link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET); - link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET); + link_add (platform, "eth0", NM_LINK_TYPE_ETHERNET, NULL, 0); + link_add (platform, "eth1", NM_LINK_TYPE_ETHERNET, NULL, 0); + link_add (platform, "eth2", NM_LINK_TYPE_ETHERNET, NULL, 0); return TRUE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 9b98bda051..3d7c307fb0 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1871,9 +1871,10 @@ build_rtnl_link (int ifindex, const char *name, NMLinkType type) } static gboolean -link_add (NMPlatform *platform, const char *name, NMLinkType type) +link_add (NMPlatform *platform, const char *name, NMLinkType type, const void *address, size_t address_len) { int r; + struct nl_object *link; if (type == NM_LINK_TYPE_BOND) { /* When the kernel loads the bond module, either via explicit modprobe @@ -1891,7 +1892,15 @@ link_add (NMPlatform *platform, const char *name, NMLinkType type) debug ("link: add link '%s' of type '%s' (%d)", name, type_to_string (type), (int) type); - return add_object (platform, build_rtnl_link (0, name, type)); + link = build_rtnl_link (0, name, type); + + g_assert ( (address != NULL) ^ (address_len == 0) ); + if (address) { + auto_nl_addr struct nl_addr *nladdr = _nm_nl_addr_build (AF_LLC, address, address_len); + + rtnl_link_set_addr ((struct rtnl_link *) link, nladdr); + } + return add_object (platform, link); } static struct rtnl_link * diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 64802212ec..daa023ffdd 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -443,18 +443,21 @@ nm_platform_link_get_all (void) * nm_platform_link_add: * @name: Interface name * @type: Interface type + * @address: (allow-none): set the mac address of the link + * @address_len: the length of the @address * * Add a software interface. Sets platform->error to NM_PLATFORM_ERROR_EXISTS * if interface is already already exists. Any link-added signal will be * emitted from an idle handler and not within this function. */ static gboolean -nm_platform_link_add (const char *name, NMLinkType type) +nm_platform_link_add (const char *name, NMLinkType type, const void *address, size_t address_len) { reset_error (); g_return_val_if_fail (name, FALSE); g_return_val_if_fail (klass->link_add, FALSE); + g_return_val_if_fail ( (address != NULL) ^ (address_len == 0) , FALSE); if (nm_platform_link_exists (name)) { debug ("link: already exists"); @@ -462,7 +465,7 @@ nm_platform_link_add (const char *name, NMLinkType type) return FALSE; } - return klass->link_add (platform, name, type); + return klass->link_add (platform, name, type, address, address_len); } /** @@ -477,7 +480,7 @@ nm_platform_dummy_add (const char *name) g_return_val_if_fail (name, FALSE); debug ("link: adding dummy '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY); + return nm_platform_link_add (name, NM_LINK_TYPE_DUMMY, NULL, 0); } /** @@ -985,14 +988,16 @@ nm_platform_link_get_master (int slave) /** * nm_platform_bridge_add: * @name: New interface name + * @address: (allow-none): set the mac address of the new bridge + * @address_len: the length of the @address * * Create a software bridge. */ gboolean -nm_platform_bridge_add (const char *name) +nm_platform_bridge_add (const char *name, const void *address, size_t address_len) { debug ("link: adding bridge '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_BRIDGE); + return nm_platform_link_add (name, NM_LINK_TYPE_BRIDGE, address, address_len); } /** @@ -1005,7 +1010,7 @@ gboolean nm_platform_bond_add (const char *name) { debug ("link: adding bond '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_BOND); + return nm_platform_link_add (name, NM_LINK_TYPE_BOND, NULL, 0); } /** @@ -1018,7 +1023,7 @@ gboolean nm_platform_team_add (const char *name) { debug ("link: adding team '%s'", name); - return nm_platform_link_add (name, NM_LINK_TYPE_TEAM); + return nm_platform_link_add (name, NM_LINK_TYPE_TEAM, NULL, 0); } /** diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 28800f895e..2353e6aab5 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -347,7 +347,7 @@ typedef struct { char * (*sysctl_get) (NMPlatform *, const char *path); GArray *(*link_get_all) (NMPlatform *); - gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type); + gboolean (*link_add) (NMPlatform *, const char *name, NMLinkType type, const void *address, size_t address_len); gboolean (*link_delete) (NMPlatform *, int ifindex); int (*link_get_ifindex) (NMPlatform *, const char *name); const char *(*link_get_name) (NMPlatform *, int ifindex); @@ -479,7 +479,7 @@ gint64 nm_platform_sysctl_get_int_checked (const char *path, guint base, gint64 GArray *nm_platform_link_get_all (void); gboolean nm_platform_dummy_add (const char *name); -gboolean nm_platform_bridge_add (const char *name); +gboolean nm_platform_bridge_add (const char *name, const void *address, size_t address_len); gboolean nm_platform_bond_add (const char *name); gboolean nm_platform_team_add (const char *name); gboolean nm_platform_link_exists (const char *name); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index cd42c566f0..1802ac2c08 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -96,7 +96,7 @@ do_dummy_add (char **argv) static gboolean do_bridge_add (char **argv) { - return nm_platform_bridge_add (argv[0]); + return nm_platform_bridge_add (argv[0], NULL, 0); } static gboolean diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 2be4b5f19f..1767d3c874 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -89,7 +89,7 @@ software_add (NMLinkType link_type, const char *name) case NM_LINK_TYPE_DUMMY: return nm_platform_dummy_add (name); case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (name); + return nm_platform_bridge_add (name, NULL, 0); case NM_LINK_TYPE_BOND: { gboolean bond0_exists = nm_platform_link_exists ("bond0"); @@ -111,7 +111,7 @@ software_add (NMLinkType link_type, const char *name) /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_bridge_add (PARENT_NAME)) + if (nm_platform_bridge_add (PARENT_NAME, NULL, 0)) wait_signal (parent_added); free_signal (parent_added); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index be480d8616..540a30c774 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -4508,6 +4508,7 @@ make_bridge_setting (shvarFile *ifcfg, guint32 u; gboolean stp = FALSE; gboolean stp_set = FALSE; + GByteArray *array = NULL; s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ()); @@ -4520,6 +4521,16 @@ make_bridge_setting (shvarFile *ifcfg, g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, value, NULL); g_free (value); + if (read_mac_address (ifcfg, "MACADDR", ARPHRD_ETHER, &array, error)) { + if (array) { + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, array, NULL); + g_byte_array_free (array, TRUE); + } + } else { + PARSE_WARNING ("%s", (*error)->message); + g_clear_error (error); + } + value = svGetValue (ifcfg, "STP", FALSE); if (value) { if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) { diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index c406bbb342..8507628e3f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -5,3 +5,4 @@ BOOTPROTO=dhcp STP=on DELAY=0 BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352" +MACADDR=00:16:41:11:22:33 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index bd85c03f01..cb57e04227 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -12056,6 +12056,8 @@ test_read_bridge_main (void) { NMConnection *connection; NMSettingBridge *s_bridge; + const GByteArray *array; + char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 }; char *unmanaged = NULL; char *keyfile = NULL; char *routefile = NULL; @@ -12088,6 +12090,11 @@ test_read_bridge_main (void) g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); + /* MAC address */ + array = nm_setting_bridge_get_mac_address (s_bridge); + g_assert (array); + g_assert_cmpint (array->len, ==, ETH_ALEN); + g_assert (memcmp (array->data, &expected_mac_address[0], ETH_ALEN) == 0); g_free (unmanaged); g_free (keyfile); @@ -12110,6 +12117,8 @@ test_write_bridge_main (void) const guint32 gw = htonl (0x01010101); const guint32 prefix = 24; NMIP4Address *addr; + static unsigned char bridge_mac[] = { 0x31, 0x33, 0x33, 0x37, 0xbe, 0xcd }; + GByteArray *mac_array; gboolean success; GError *error = NULL; char *testfile = NULL; @@ -12141,9 +12150,13 @@ test_write_bridge_main (void) g_assert (s_bridge); nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + mac_array = g_byte_array_sized_new (sizeof (bridge_mac)); + g_byte_array_append (mac_array, bridge_mac, sizeof (bridge_mac)); g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, "br0", + NM_SETTING_BRIDGE_MAC_ADDRESS, mac_array, NULL); + g_byte_array_free (mac_array, TRUE); /* IP4 setting */ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 26d37ffe7a..f583366cf8 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1379,6 +1379,7 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error const char *iface; guint32 i; GString *opts; + const GByteArray *mac; char *s; s_bridge = nm_connection_get_setting_bridge (connection); @@ -1398,6 +1399,11 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error svSetValue (ifcfg, "BRIDGING_OPTS", NULL, FALSE); svSetValue (ifcfg, "STP", "no", FALSE); svSetValue (ifcfg, "DELAY", NULL, FALSE); + svSetValue (ifcfg, "MACADDR", NULL, FALSE); + + mac = nm_setting_bridge_get_mac_address (s_bridge); + if (mac) + svSetValue_free (ifcfg, "MACADDR", nm_utils_hwaddr_ntoa_len (mac->data, mac->len), FALSE); /* Bridge options */ opts = g_string_sized_new (32); diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c index c7de6f4108..7fdfbee964 100644 --- a/src/settings/plugins/keyfile/reader.c +++ b/src/settings/plugins/keyfile/reader.c @@ -901,6 +901,10 @@ static KeyParser key_parsers[] = { NM_SETTING_CONNECTION_TYPE, TRUE, setting_alias_parser }, + { NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_BRIDGE_MAC_ADDRESS, + TRUE, + mac_address_parser_ETHER }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES, FALSE, diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c index 82bea5b8aa..9dd49c7762 100644 --- a/src/settings/plugins/keyfile/writer.c +++ b/src/settings/plugins/keyfile/writer.c @@ -774,6 +774,9 @@ static KeyWriter key_writers[] = { { NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, setting_alias_writer }, + { NM_SETTING_BRIDGE_SETTING_NAME, + NM_SETTING_BRIDGE_MAC_ADDRESS, + mac_address_writer }, { NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES, ip4_addr_writer },