summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-12-09 14:30:27 +0100
committerBeniamino Galvani <bgalvani@redhat.com>2015-12-09 14:30:27 +0100
commite25d144d770df2d18c5f37ee72b971b7a40ea2fd (patch)
tree333e89ed200ec4f8fba4ba804ec93da097c1c96e
parent783b3642d08eab71146aaee267d41d71e611b7bf (diff)
parentf841f17882f4b965ba64e9c6abb51155f2100ec3 (diff)
downloadNetworkManager-e25d144d770df2d18c5f37ee72b971b7a40ea2fd.tar.gz
merge: macvlan and macvtap devices creation support
https://bugzilla.gnome.org/show_bug.cgi?id=755986
-rw-r--r--clients/cli/connections.c139
-rw-r--r--clients/cli/nmcli-completion2
-rw-r--r--clients/cli/settings.c131
-rw-r--r--introspection/nm-device-macvlan.xml6
-rw-r--r--libnm-core/Makefile.libnm-core2
-rw-r--r--libnm-core/nm-connection.c21
-rw-r--r--libnm-core/nm-connection.h2
-rw-r--r--libnm-core/nm-core-internal.h1
-rw-r--r--libnm-core/nm-core-types.h1
-rw-r--r--libnm-core/nm-dbus-interface.h3
-rw-r--r--libnm-core/nm-setting-macvlan.c350
-rw-r--r--libnm-core/nm-setting-macvlan.h94
-rw-r--r--libnm/Makefile.am2
-rw-r--r--libnm/NetworkManager.h1
-rw-r--r--libnm/libnm.ver14
-rw-r--r--libnm/nm-device-macvlan.c347
-rw-r--r--libnm/nm-device-macvlan.h72
-rw-r--r--libnm/nm-device.c3
-rw-r--r--libnm/nm-types.h1
-rw-r--r--man/nmcli.1.in10
-rw-r--r--po/POTFILES.in3
-rw-r--r--src/devices/nm-device-macvlan.c616
-rw-r--r--src/devices/nm-device-macvlan.h7
-rw-r--r--src/devices/nm-device.c2
-rw-r--r--src/nm-types.h1
-rw-r--r--src/platform/nm-linux-platform.c85
-rw-r--r--src/platform/nm-platform.c56
-rw-r--r--src/platform/nm-platform.h11
-rw-r--r--src/platform/nmp-object.c9
-rw-r--r--src/platform/nmp-object.h2
-rw-r--r--src/platform/tests/test-common.c34
-rw-r--r--src/platform/tests/test-common.h3
-rw-r--r--src/platform/tests/test-link.c34
33 files changed, 2009 insertions, 56 deletions
diff --git a/clients/cli/connections.c b/clients/cli/connections.c
index 82f68c72a8..e5fa81b814 100644
--- a/clients/cli/connections.c
+++ b/clients/cli/connections.c
@@ -119,6 +119,7 @@ extern NmcOutputField nmc_fields_setting_team_port[];
extern NmcOutputField nmc_fields_setting_dcb[];
extern NmcOutputField nmc_fields_setting_tun[];
extern NmcOutputField nmc_fields_setting_ip_tunnel[];
+extern NmcOutputField nmc_fields_setting_macvlan[];
/* Available settings for 'connection show <con>' - profile part */
static NmcOutputField nmc_fields_settings_names[] = {
@@ -149,6 +150,7 @@ static NmcOutputField nmc_fields_settings_names[] = {
SETTING_FIELD (NM_SETTING_DCB_SETTING_NAME, nmc_fields_setting_dcb + 1), /* 24 */
SETTING_FIELD (NM_SETTING_TUN_SETTING_NAME, nmc_fields_setting_tun + 1), /* 25 */
SETTING_FIELD (NM_SETTING_IP_TUNNEL_SETTING_NAME, nmc_fields_setting_ip_tunnel + 1), /* 26 */
+ SETTING_FIELD (NM_SETTING_MACVLAN_SETTING_NAME, nmc_fields_setting_macvlan + 1), /* 27 */
{NULL, NULL, 0, NULL, NULL, FALSE, FALSE, 0}
};
#define NMC_FIELDS_SETTINGS_NAMES_ALL_X NM_SETTING_CONNECTION_SETTING_NAME","\
@@ -176,7 +178,8 @@ static NmcOutputField nmc_fields_settings_names[] = {
NM_SETTING_TEAM_PORT_SETTING_NAME"," \
NM_SETTING_DCB_SETTING_NAME"," \
NM_SETTING_TUN_SETTING_NAME"," \
- NM_SETTING_IP_TUNNEL_SETTING_NAME
+ NM_SETTING_IP_TUNNEL_SETTING_NAME"," \
+ NM_SETTING_MACVLAN_SETTING_NAME
#define NMC_FIELDS_SETTINGS_NAMES_ALL NMC_FIELDS_SETTINGS_NAMES_ALL_X
/* Active connection data */
@@ -423,6 +426,9 @@ usage_connection_add (void)
" [pi yes|no]\n"
" [vnet-hdr yes|no]\n"
" [multi-queue yes|no]\n\n"
+ " macvlan: dev <parent device (connection UUID, ifname, or MAC)>\n"
+ " mode vepa|bridge|private|passthru|source\n"
+ " [tap yes|no]\n\n"
" SLAVE_OPTIONS:\n"
" bridge: [priority <0-63>]\n"
" [path-cost <1-65535>]\n"
@@ -2811,6 +2817,15 @@ static const NameItem nmc_ip_tunnel_settings [] = {
{ NULL, NULL, NULL, FALSE }
};
+static const NameItem nmc_macvlan_settings [] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME, NULL, NULL, TRUE },
+ { NM_SETTING_WIRED_SETTING_NAME, "ethernet", NULL, FALSE },
+ { NM_SETTING_MACVLAN_SETTING_NAME, NULL, NULL, TRUE },
+ { NM_SETTING_IP4_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
+ { NM_SETTING_IP6_CONFIG_SETTING_NAME, NULL, NULL, FALSE },
+ { NULL, NULL, NULL, FALSE }
+};
+
/* Available connection types */
static const NameItem nmc_valid_connection_types[] = {
{ NM_SETTING_GENERIC_SETTING_NAME, NULL, nmc_generic_settings },
@@ -2834,6 +2849,7 @@ static const NameItem nmc_valid_connection_types[] = {
{ "bridge-slave", NULL, nmc_bridge_slave_settings },
{ NM_SETTING_TUN_SETTING_NAME, NULL, nmc_tun_settings },
{ NM_SETTING_IP_TUNNEL_SETTING_NAME, NULL, nmc_ip_tunnel_settings },
+ { NM_SETTING_MACVLAN_SETTING_NAME, NULL, nmc_macvlan_settings },
{ NULL, NULL, NULL }
};
@@ -4206,6 +4222,33 @@ do_questionnaire_adsl (gboolean echo, char **password, char **encapsulation)
}
}
+
+static void
+do_questionnaire_macvlan (char **tap)
+{
+ gboolean once_more;
+ GError *error = NULL;
+
+ /* Ask for optional 'bridge-slave' arguments. */
+ if (!want_provide_opt_args (_("macvlan"), 1))
+ return;
+
+ if (!*tap) {
+ gboolean tap_bool;
+ do {
+ *tap = nmc_readline (_("Tap %s"), prompt_yes_no (FALSE, ":"));
+ *tap = *tap ? *tap : g_strdup ("yes");
+ normalize_yes_no (tap);
+ once_more = !nmc_string_to_bool (*tap, &tap_bool, &error);
+ if (once_more) {
+ g_print (_("Error: 'tap': %s.\n"), error->message);
+ g_clear_error (&error);
+ g_free (*tap);
+ }
+ } while (once_more);
+ }
+}
+
static gboolean
split_address (char* str, char **ip, char **rest)
{
@@ -4676,6 +4719,7 @@ complete_connection_by_type (NMConnection *connection,
NMSettingAdsl *s_adsl;
NMSettingTun *s_tun;
NMSettingIPTunnel *s_ip_tunnel;
+ NMSettingMacvlan *s_macvlan;
const char *slave_type;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -5783,6 +5827,99 @@ cleanup_adsl:
g_free (password);
g_free (protocol_ask);
g_free (encapsulation);
+
+ if (!success)
+ return FALSE;
+
+ } else if (!strcmp (con_type, NM_SETTING_MACVLAN_SETTING_NAME)) {
+ /* Build up the settings required for 'macvlan' */
+ gboolean success = FALSE;
+ const char *parent = NULL;
+ char *parent_ask = NULL;
+ const char *mode = NULL;
+ char *mode_ask = NULL;
+ const char *tap_c = NULL;
+ char *tap = NULL;
+ NMSettingMacvlanMode mode_enum;
+ gboolean valid_mac = FALSE;
+ gboolean tap_bool = FALSE;
+ nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask},
+ {"mode", TRUE, &mode, !ask},
+ {"tap", TRUE, &tap_c, FALSE},
+ {NULL} };
+
+ if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error))
+ return FALSE;
+
+ if (!parent && ask)
+ parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: "));
+ if (!parent) {
+ g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+ _("Error: 'dev' is required."));
+ return FALSE;
+ }
+
+ if ( !(valid_mac = nm_utils_hwaddr_valid (parent, ETH_ALEN))
+ && !nm_utils_is_uuid (parent)
+ && !nm_utils_iface_valid_name (parent)) {
+ g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+ _("Error: 'dev': '%s' is neither UUID, interface name, nor MAC."),
+ parent);
+ goto cleanup_macvlan;
+ }
+
+ if (!mode && ask)
+ mode = mode_ask = nmc_readline (_("MACVLAN mode: "));
+ if (!mode) {
+ g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+ _("Error: 'mode' is required."));
+ return FALSE;
+ }
+
+ if (!nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type(), mode, (int *) &mode_enum, NULL)) {
+ g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+ _("Error: 'mode' is not valid."));
+ return FALSE;
+ }
+
+ /* Also ask for all optional arguments if '--ask' is specified. */
+ tap = g_strdup (tap_c);
+ if (ask)
+ do_questionnaire_macvlan (&tap);
+
+ if (tap) {
+ GError *tmp_err = NULL;
+ if (!nmc_string_to_bool (tap, &tap_bool, &tmp_err)) {
+ g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
+ _("Error: 'tap': %s."), tmp_err->message);
+ g_clear_error (&tmp_err);
+ goto cleanup_macvlan;
+ }
+ }
+
+ /* Add 'macvlan' setting */
+ s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_macvlan));
+
+ /* Add 'wired' setting if necessary */
+ if (valid_mac) {
+ s_wired = (NMSettingWired *) nm_setting_wired_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wired));
+ g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, parent, NULL);
+ }
+
+ /* Set 'macvlan' properties */
+ if (!valid_mac)
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, parent, NULL);
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, mode_enum, NULL);
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, tap_bool, NULL);
+
+ success = TRUE;
+cleanup_macvlan:
+ g_free (parent_ask);
+ g_free (mode_ask);
+ g_free (tap);
+
if (!success)
return FALSE;
diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion
index aff78f7ace..aefd902a23 100644
--- a/clients/cli/nmcli-completion
+++ b/clients/cli/nmcli-completion
@@ -412,7 +412,7 @@ _nmcli_compl_ARGS()
# user friendly. Only complete them, if the current word already starts with an "8".
_nmcli_list "802-3-ethernet 802-11-wireless 802-11-olpc-mesh"
else
- _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bridge team pppoe adsl tun ip-tunnel"
+ _nmcli_list "ethernet wifi wimax gsm cdma infiniband bluetooth vpn olpc-mesh vlan bond bridge team pppoe adsl tun ip-tunnel macvlan"
fi
return 0
fi
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 3e8c10f4c2..05e5580165 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -748,6 +748,22 @@ NmcOutputField nmc_fields_setting_ip_tunnel[] = {
NM_SETTING_IP_TUNNEL_FLOW_LABEL
#define NMC_FIELDS_SETTING_IP_TUNNEL_COMMON NMC_FIELDS_SETTING_IP_TUNNEL_ALL
+/* Available fields for NM_SETTING_MACVLAN_SETTING_NAME */
+NmcOutputField nmc_fields_setting_macvlan[] = {
+ SETTING_FIELD ("name"), /* 0 */
+ SETTING_FIELD (NM_SETTING_MACVLAN_PARENT), /* 1 */
+ SETTING_FIELD (NM_SETTING_MACVLAN_MODE), /* 2 */
+ SETTING_FIELD (NM_SETTING_MACVLAN_PROMISCUOUS), /* 3 */
+ SETTING_FIELD (NM_SETTING_MACVLAN_TAP), /* 4 */
+ {NULL, NULL, 0, NULL, FALSE, FALSE, 0}
+};
+#define NMC_FIELDS_SETTING_MACVLAN_ALL "name"","\
+ NM_SETTING_MACVLAN_PARENT","\
+ NM_SETTING_MACVLAN_MODE","\
+ NM_SETTING_MACVLAN_PROMISCUOUS","\
+ NM_SETTING_MACVLAN_TAP
+#define NMC_FIELDS_SETTING_MACVLAN_COMMON NMC_FIELDS_SETTING_MACVLAN_ALL
+
/*----------------------------------------------------------------------------*/
static char *
wep_key_type_to_string (NMWepKeyType type)
@@ -1866,6 +1882,61 @@ nmc_property_wifi_sec_get_wep_key_type (NMSetting *setting, NmcPropertyGetType g
return wep_key_type_to_string (nm_setting_wireless_security_get_wep_key_type (s_wireless_sec));
}
+/* --- NM_SETTING_MACVLAN_SETTING_NAME property get functions --- */
+DEFINE_GETTER (nmc_property_macvlan_get_parent, NM_SETTING_MACVLAN_PARENT)
+DEFINE_GETTER (nmc_property_macvlan_get_promiscuous, NM_SETTING_MACVLAN_PROMISCUOUS)
+DEFINE_GETTER (nmc_property_macvlan_get_tap, NM_SETTING_MACVLAN_TAP)
+
+static char *
+nmc_property_macvlan_get_mode (NMSetting *setting, NmcPropertyGetType get_type)
+{
+ NMSettingMacvlan *s_macvlan = NM_SETTING_MACVLAN (setting);
+ NMSettingMacvlanMode mode;
+ char *tmp, *str;
+
+ mode = nm_setting_macvlan_get_mode (s_macvlan);
+ tmp = nm_utils_enum_to_str (nm_setting_macvlan_mode_get_type (), mode);
+
+ if (get_type == NMC_PROPERTY_GET_PARSABLE)
+ str = g_strdup (tmp ? tmp : "");
+ else
+ str = g_strdup_printf ("%d (%s)", mode, tmp ? tmp : "");
+ g_free (tmp);
+
+ return str;
+}
+
+static gboolean
+nmc_property_macvlan_set_mode (NMSetting *setting, const char *prop,
+ const char *val, GError **error)
+{
+ NMSettingMacvlanMode mode;
+ gs_free const char **options = NULL;
+ gs_free char *options_str = NULL;
+ long int t;
+ gboolean ret;
+
+ if (nmc_string_to_int_base (val, 0, TRUE, 0, _NM_SETTING_MACVLAN_MODE_NUM - 1, &t))
+ mode = (NMSettingMacvlanMode) t;
+ else {
+ ret = nm_utils_enum_from_str (nm_setting_macvlan_mode_get_type (), val,
+ (int *) &mode, NULL);
+
+ if (!ret) {
+ options = nm_utils_enum_get_values (nm_setting_macvlan_mode_get_type(),
+ NM_SETTING_MACVLAN_MODE_UNKNOWN + 1,
+ G_MAXINT);
+ options_str = g_strjoinv (",", (char **) options);
+ g_set_error (error, 1, 0, _("invalid option '%s', use one of [%s]"),
+ val, options_str);
+ return FALSE;
+ }
+ }
+
+ g_object_set (setting, prop, (guint) mode, NULL);
+ return TRUE;
+}
+
/*----------------------------------------------------------------------------*/
static void
@@ -7231,6 +7302,36 @@ nmc_properties_init (void)
NULL,
NULL,
NULL);
+
+ /* Add editable properties for NM_SETTING_MACVLAN_SETTING_NAME */
+ nmc_add_prop_funcs (GLUE (MACVLAN, PARENT),
+ nmc_property_macvlan_get_parent,
+ nmc_property_set_string,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ nmc_add_prop_funcs (GLUE (MACVLAN, MODE),
+ nmc_property_macvlan_get_mode,
+ nmc_property_macvlan_set_mode,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ nmc_add_prop_funcs (GLUE (MACVLAN, PROMISCUOUS),
+ nmc_property_macvlan_get_promiscuous,
+ nmc_property_set_bool,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ nmc_add_prop_funcs (GLUE (MACVLAN, TAP),
+ nmc_property_macvlan_get_tap,
+ nmc_property_set_bool,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
}
void
@@ -8447,6 +8548,35 @@ setting_ip_tunnel_details (NMSetting *setting, NmCli *nmc, const char *one_prop
return TRUE;
}
+static gboolean
+setting_macvlan_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets)
+{
+ NMSettingMacvlan *s_macvlan = NM_SETTING_MACVLAN (setting);
+ NmcOutputField *tmpl, *arr;
+ size_t tmpl_len;
+
+ g_return_val_if_fail (NM_IS_SETTING_MACVLAN (s_macvlan), FALSE);
+
+ tmpl = nmc_fields_setting_macvlan;
+ tmpl_len = sizeof (nmc_fields_setting_macvlan);
+ nmc->print_fields.indices = parse_output_fields (one_prop ? one_prop : NMC_FIELDS_SETTING_MACVLAN_ALL,
+ tmpl, FALSE, NULL, NULL);
+ arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_FIELD_NAMES);
+ g_ptr_array_add (nmc->output_data, arr);
+
+ 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_macvlan_get_parent (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 2, nmc_property_macvlan_get_mode (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 3, nmc_property_macvlan_get_promiscuous (setting, NMC_PROPERTY_GET_PRETTY));
+ set_val_str (arr, 4, nmc_property_macvlan_get_tap (setting, NMC_PROPERTY_GET_PRETTY));
+ g_ptr_array_add (nmc->output_data, arr);
+
+ print_data (nmc); /* Print all data */
+
+ return TRUE;
+}
+
typedef struct {
const char *sname;
gboolean (*func) (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets);
@@ -8480,6 +8610,7 @@ static const SettingDetails detail_printers[] = {
{ NM_SETTING_DCB_SETTING_NAME, setting_dcb_details },
{ NM_SETTING_TUN_SETTING_NAME, setting_tun_details },
{ NM_SETTING_IP_TUNNEL_SETTING_NAME, setting_ip_tunnel_details },
+ { NM_SETTING_MACVLAN_SETTING_NAME, setting_macvlan_details },
{ NULL },
};
diff --git a/introspection/nm-device-macvlan.xml b/introspection/nm-device-macvlan.xml
index a387bf5647..c4dcb4e602 100644
--- a/introspection/nm-device-macvlan.xml
+++ b/introspection/nm-device-macvlan.xml
@@ -21,6 +21,12 @@
</tp:docstring>
</property>
+ <property name="Tap" type="b" access="read">
+ <tp:docstring>
+ Whether the device is a macvtap.
+ </tp:docstring>
+ </property>
+
<signal name="PropertiesChanged">
<arg name="properties" type="a{sv}" tp:type="String_Variant_Map">
<tp:docstring>
diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core
index 02c1d1021b..8a81f00f10 100644
--- a/libnm-core/Makefile.libnm-core
+++ b/libnm-core/Makefile.libnm-core
@@ -28,6 +28,7 @@ libnm_core_headers = \
$(core)/nm-setting-ip-tunnel.h \
$(core)/nm-setting-ip4-config.h \
$(core)/nm-setting-ip6-config.h \
+ $(core)/nm-setting-macvlan.h \
$(core)/nm-setting-olpc-mesh.h \
$(core)/nm-setting-ppp.h \
$(core)/nm-setting-pppoe.h \
@@ -86,6 +87,7 @@ libnm_core_sources = \
$(core)/nm-setting-ip-tunnel.c \
$(core)/nm-setting-ip4-config.c \
$(core)/nm-setting-ip6-config.c \
+ $(core)/nm-setting-macvlan.c \
$(core)/nm-setting-olpc-mesh.c \
$(core)/nm-setting-ppp.c \
$(core)/nm-setting-pppoe.c \
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index e801f2fd47..73ff9e5558 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -1596,7 +1596,8 @@ nm_connection_is_virtual (NMConnection *connection)
|| !strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)
|| !strcmp (type, NM_SETTING_VLAN_SETTING_NAME)
|| !strcmp (type, NM_SETTING_TUN_SETTING_NAME)
- || !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME))
+ || !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME)
+ || !strcmp (type, NM_SETTING_MACVLAN_SETTING_NAME))
return TRUE;
if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
@@ -1906,6 +1907,24 @@ nm_connection_get_setting_ip6_config (NMConnection *connection)
}
/**
+ * nm_connection_get_setting_macvlan:
+ * @connection: the #NMConnection
+ *
+ * A shortcut to return any #NMSettingMacvlan the connection might contain.
+ *
+ * Returns: (transfer none): an #NMSettingMacvlan if the connection contains one, otherwise %NULL
+ *
+ * Since: 1.2
+ **/
+NMSettingMacvlan *
+nm_connection_get_setting_macvlan (NMConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+
+ return (NMSettingMacvlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MACVLAN);
+}
+
+/**
* nm_connection_get_setting_olpc_mesh:
* @connection: the #NMConnection
*
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index 8896e59bab..855fbc4c5b 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -206,6 +206,8 @@ NM_AVAILABLE_IN_1_2
NMSettingIPTunnel * nm_connection_get_setting_ip_tunnel (NMConnection *connection);
NMSettingIPConfig * nm_connection_get_setting_ip4_config (NMConnection *connection);
NMSettingIPConfig * nm_connection_get_setting_ip6_config (NMConnection *connection);
+NM_AVAILABLE_IN_1_2
+NMSettingMacvlan * nm_connection_get_setting_macvlan (NMConnection *connection);
NMSettingOlpcMesh * nm_connection_get_setting_olpc_mesh (NMConnection *connection);
NMSettingPpp * nm_connection_get_setting_ppp (NMConnection *connection);
NMSettingPppoe * nm_connection_get_setting_pppoe (NMConnection *connection);
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index c1eea777f2..f2c6c6c07e 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -52,6 +52,7 @@
#include "nm-setting-ip-tunnel.h"
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
+#include "nm-setting-macvlan.h"
#include "nm-setting-olpc-mesh.h"
#include "nm-setting-ppp.h"
#include "nm-setting-pppoe.h"
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 29a34cc68d..68a1ebc6e6 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -45,6 +45,7 @@ typedef struct _NMSettingIPConfig NMSettingIPConfig;
typedef struct _NMSettingIPTunnel NMSettingIPTunnel;
typedef struct _NMSettingIP4Config NMSettingIP4Config;
typedef struct _NMSettingIP6Config NMSettingIP6Config;
+typedef struct _NMSettingMacvlan NMSettingMacvlan;
typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh;
typedef struct _NMSettingPpp NMSettingPpp;
typedef struct _NMSettingPppoe NMSettingPppoe;
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index f901028f9b..df10e9d80c 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -149,6 +149,8 @@ typedef enum {
* @NM_DEVICE_TYPE_BRIDGE: a bridge master interface
* @NM_DEVICE_TYPE_TEAM: a team master interface
* @NM_DEVICE_TYPE_TUN: a TUN or TAP interface
+ * @NM_DEVICE_TYPE_IP_TUNNEL: a IP tunnel interface
+ * @NM_DEVICE_TYPE_MACVLAN: a MACVLAN interface
*
* #NMDeviceType values indicate the type of hardware represented by
* an #NMDevice.
@@ -174,6 +176,7 @@ typedef enum {
NM_DEVICE_TYPE_TEAM = 15,
NM_DEVICE_TYPE_TUN = 16,
NM_DEVICE_TYPE_IP_TUNNEL = 17,
+ NM_DEVICE_TYPE_MACVLAN = 18,
} NMDeviceType;
/**
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
new file mode 100644
index 0000000000..7bedf5143d
--- /dev/null
+++ b/libnm-core/nm-setting-macvlan.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "nm-setting-macvlan.h"
+#include "nm-utils.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-private.h"
+#include "nm-setting-wired.h"
+#include "nm-connection-private.h"
+
+/**
+ * SECTION:nm-setting-macvlan
+ * @short_description: Describes connection properties for macvlan interfaces
+ *
+ * The #NMSettingMacvlan object is a #NMSetting subclass that describes properties
+ * necessary for connection to macvlan interfaces.
+ **/
+
+G_DEFINE_TYPE_WITH_CODE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING,
+ _nm_register_setting (MACVLAN, 1))
+NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_MACVLAN)
+
+#define NM_SETTING_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanPrivate))
+
+typedef struct {
+ char *parent;
+ NMSettingMacvlanMode mode;
+ gboolean promiscuous;
+ gboolean tap;
+} NMSettingMacvlanPrivate;
+
+enum {
+ PROP_0,
+ PROP_PARENT,
+ PROP_MODE,
+ PROP_PROMISCUOUS,
+ PROP_TAP,
+ LAST_PROP
+};
+
+/**
+ * nm_setting_macvlan_new:
+ *
+ * Creates a new #NMSettingMacvlan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingMacvlan object
+ *
+ * Since: 1.2
+ **/
+NMSetting *
+nm_setting_macvlan_new (void)
+{
+ return (NMSetting *) g_object_new (NM_TYPE_SETTING_MACVLAN, NULL);
+}
+
+/**
+ * nm_setting_macvlan_get_parent:
+ * @setting: the #NMSettingMacvlan
+ *
+ * Returns: the #NMSettingMacvlan:parent property of the setting
+ *
+ * Since: 1.2
+ **/
+const char *
+nm_setting_macvlan_get_parent (NMSettingMacvlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_MACVLAN (setting), NULL);
+ return NM_SETTING_MACVLAN_GET_PRIVATE (setting)->parent;
+}
+
+/**
+ * nm_setting_macvlan_get_mode:
+ * @setting: the #NMSettingMacvlan
+ *
+ * Returns: the #NMSettingMacvlan:mode property of the setting
+ *
+ * Since: 1.2
+ **/
+NMSettingMacvlanMode
+nm_setting_macvlan_get_mode (NMSettingMacvlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_MACVLAN (setting), NM_SETTING_MACVLAN_MODE_UNKNOWN);
+ return NM_SETTING_MACVLAN_GET_PRIVATE (setting)->mode;
+}
+
+/**
+ * nm_setting_macvlan_get_promiscuous:
+ * @setting: the #NMSettingMacvlan
+ *
+ * Returns: the #NMSettingMacvlan:promiscuous property of the setting
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_setting_macvlan_get_promiscuous (NMSettingMacvlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_MACVLAN (setting), FALSE);
+ return NM_SETTING_MACVLAN_GET_PRIVATE (setting)->promiscuous;
+}
+
+/**
+ * nm_setting_macvlan_get_tap:
+ * @setting: the #NMSettingMacvlan
+ *
+ * Returns: the #NMSettingMacvlan:tap property of the setting
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_setting_macvlan_get_tap (NMSettingMacvlan *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_MACVLAN (setting), FALSE);
+ return NM_SETTING_MACVLAN_GET_PRIVATE (setting)->tap;
+}
+
+/*********************************************************************/
+
+static void
+nm_setting_macvlan_init (NMSettingMacvlan *setting)
+{
+}
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+ NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+
+ if (connection) {
+ s_con = nm_connection_get_setting_connection (connection);
+ s_wired = nm_connection_get_setting_wired (connection);
+ } else {
+ s_con = NULL;
+ s_wired = NULL;
+ }
+
+ if (priv->parent) {
+ if ( !nm_utils_is_uuid (priv->parent)
+ && !nm_utils_iface_valid_name (priv->parent)) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("'%s' is neither an UUID nor an interface name"),
+ priv->parent);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_MACVLAN_PARENT);
+ return FALSE;
+ }
+ } else {
+ /* If parent is NULL, the parent must be specified via
+ * NMSettingWired:mac-address.
+ */
+ if ( connection
+ && (!s_wired || !nm_setting_wired_get_mac_address (s_wired))) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_MISSING_PROPERTY,
+ _("property is not specified and neither is '%s:%s'"),
+ NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_MACVLAN_SETTING_NAME, NM_SETTING_MACVLAN_PARENT);
+ return FALSE;
+ }
+ }
+
+ if (!priv->promiscuous && priv->mode != NM_SETTING_MACVLAN_MODE_PASSTHRU) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("non promiscuous operation is allowed only in passthru mode'"));
+ g_prefix_error (error, "%s.%s: ",
+ NM_SETTING_MACVLAN_SETTING_NAME,
+ NM_SETTING_MACVLAN_PROMISCUOUS);
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMSettingMacvlan *setting = NM_SETTING_MACVLAN (object);
+ NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_PARENT:
+ g_free (priv->parent);
+ priv->parent = g_value_dup_string (value);
+ break;
+ case PROP_MODE:
+ priv->mode = g_value_get_uint (value);
+ break;
+ case PROP_PROMISCUOUS:
+ priv->promiscuous = g_value_get_boolean (value);
+ break;
+ case PROP_TAP:
+ priv->tap = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMSettingMacvlan *setting = NM_SETTING_MACVLAN (object);
+ NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
+
+ switch (prop_id) {
+ case PROP_PARENT:
+ g_value_set_string (value, priv->parent);
+ break;
+ case PROP_MODE:
+ g_value_set_uint (value, priv->mode);
+ break;
+ case PROP_PROMISCUOUS:
+ g_value_set_boolean (value, priv->promiscuous);
+ break;
+ case PROP_TAP:
+ g_value_set_boolean (value, priv->tap);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ NMSettingMacvlan *setting = NM_SETTING_MACVLAN (object);
+ NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
+
+ g_free (priv->parent);
+
+ G_OBJECT_CLASS (nm_setting_macvlan_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_macvlan_class_init (NMSettingMacvlanClass *setting_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
+ NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
+
+ g_type_class_add_private (setting_class, sizeof (NMSettingMacvlanPrivate));
+
+ /* virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->verify = verify;
+
+ /* Properties */
+
+ /**
+ * NMSettingMacvlan:parent:
+ *
+ * If given, specifies the parent interface name or parent connection UUID
+ * from which this MAC-VLAN interface should be created. If this property is
+ * not specified, the connection must contain an #NMSettingWired setting
+ * with a #NMSettingWired:mac-address property.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_string (NM_SETTING_MACVLAN_PARENT, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingMacvlan:mode:
+ *
+ * The macvlan mode, which specifies the communication mechanism between multiple
+ * macvlans on the same lower device.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODE,
+ g_param_spec_uint (NM_SETTING_MACVLAN_MODE, "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingMacvlan:promiscuous:
+ *
+ * Whether the interface should be put in promiscuous mode.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PROMISCUOUS,
+ g_param_spec_boolean (NM_SETTING_MACVLAN_PROMISCUOUS, "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMSettingMacvlan:tap:
+ *
+ * Whether the interface should be a MACVTAP.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TAP,
+ g_param_spec_boolean (NM_SETTING_MACVLAN_TAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm-core/nm-setting-macvlan.h b/libnm-core/nm-setting-macvlan.h
new file mode 100644
index 0000000000..3922cd93f6
--- /dev/null
+++ b/libnm-core/nm-setting-macvlan.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_MACVLAN_H__
+#define __NM_SETTING_MACVLAN_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_MACVLAN (nm_setting_macvlan_get_type ())
+#define NM_SETTING_MACVLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlan))
+#define NM_SETTING_MACVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_MACVLANCONFIG, NMSettingMacvlanClass))
+#define NM_IS_SETTING_MACVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_MACVLAN))
+#define NM_IS_SETTING_MACVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_MACVLAN))
+#define NM_SETTING_MACVLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanClass))
+
+#define NM_SETTING_MACVLAN_SETTING_NAME "macvlan"
+
+#define NM_SETTING_MACVLAN_PARENT "parent"
+#define NM_SETTING_MACVLAN_MODE "mode"
+#define NM_SETTING_MACVLAN_PROMISCUOUS "promiscuous"
+#define NM_SETTING_MACVLAN_TAP "tap"
+
+struct _NMSettingMacvlan {
+ NMSetting parent;
+};
+
+typedef struct {
+ NMSettingClass parent;
+
+ /*< private >*/
+ gpointer padding[4];
+} NMSettingMacvlanClass;
+
+/**
+ * NMSettingMacvlanMode:
+ * @NM_SETTING_MACVLAN_MODE_UNKNOWN: unknown/unset mode
+ * @NM_SETTING_MACVLAN_MODE_VEPA: Virtual Ethernet Port Aggregator mode
+ * @NM_SETTING_MACVLAN_MODE_BRIDGE: bridge mode
+ * @NM_SETTING_MACVLAN_MODE_PRIVATE: private mode
+ * @NM_SETTING_MACVLAN_MODE_PASSTHRU: passthru mode
+ * @NM_SETTING_MACVLAN_MODE_SOURCE: source mode
+ **/
+typedef enum {
+ NM_SETTING_MACVLAN_MODE_UNKNOWN = 0,
+ NM_SETTING_MACVLAN_MODE_VEPA = 1,
+ NM_SETTING_MACVLAN_MODE_BRIDGE = 2,
+ NM_SETTING_MACVLAN_MODE_PRIVATE = 3,
+ NM_SETTING_MACVLAN_MODE_PASSTHRU = 4,
+ NM_SETTING_MACVLAN_MODE_SOURCE = 5,
+ _NM_SETTING_MACVLAN_MODE_NUM, /*< skip >*/
+ NM_SETTING_MACVLAN_MODE_LAST = _NM_SETTING_MACVLAN_MODE_NUM - 1, /*< skip >*/
+} NMSettingMacvlanMode;
+
+NM_AVAILABLE_IN_1_2
+GType nm_setting_macvlan_get_type (void);
+NM_AVAILABLE_IN_1_2
+NMSetting *nm_setting_macvlan_new (void);
+
+NM_AVAILABLE_IN_1_2
+const char *nm_setting_macvlan_get_parent (NMSettingMacvlan *setting);
+NM_AVAILABLE_IN_1_2
+NMSettingMacvlanMode nm_setting_macvlan_get_mode (NMSettingMacvlan *setting);
+NM_AVAILABLE_IN_1_2
+gboolean nm_setting_macvlan_get_promiscuous (NMSettingMacvlan *setting);
+NM_AVAILABLE_IN_1_2
+gboolean nm_setting_macvlan_get_tap (NMSettingMacvlan *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_MACVLAN_H__ */
diff --git a/libnm/Makefile.am b/libnm/Makefile.am
index 4ce9655554..d1e8d12a2e 100644
--- a/libnm/Makefile.am
+++ b/libnm/Makefile.am
@@ -39,6 +39,7 @@ libnminclude_hfiles = \
nm-device-generic.h \
nm-device-infiniband.h \
nm-device-ip-tunnel.h \
+ nm-device-macvlan.h \
nm-device-modem.h \
nm-device-olpc-mesh.h \
nm-device-team.h \
@@ -92,6 +93,7 @@ libnm_la_csources = \
nm-device-generic.c \
nm-device-infiniband.c \
nm-device-ip-tunnel.c \
+ nm-device-macvlan.c \
nm-device-modem.c \
nm-device-olpc-mesh.c \
nm-device-team.c \
diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h
index 17e7a3523b..51a703ed0e 100644
--- a/libnm/NetworkManager.h
+++ b/libnm/NetworkManager.h
@@ -64,6 +64,7 @@
#include <nm-setting-ip-tunnel.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-ip6-config.h>
+#include <nm-setting-macvlan.h>
#include <nm-setting-olpc-mesh.h>
#include <nm-setting-ppp.h>
#include <nm-setting-pppoe.h>
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index e092a5845a..140c5004df 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -859,6 +859,7 @@ libnm_1_2_0 {
global:
nm_access_point_get_last_seen;
nm_connection_get_setting_ip_tunnel;
+ nm_connection_get_setting_macvlan;
nm_connection_verify_secrets;
nm_device_ethernet_get_s390_subchannels;
nm_client_get_all_devices;
@@ -876,6 +877,12 @@ global:
nm_device_ip_tunnel_get_tos;
nm_device_ip_tunnel_get_ttl;
nm_device_ip_tunnel_get_type;
+ nm_device_macvlan_get_hw_address;
+ nm_device_macvlan_get_mode;
+ nm_device_macvlan_get_no_promisc;
+ nm_device_macvlan_get_parent;
+ nm_device_macvlan_get_tap;
+ nm_device_macvlan_get_type;
nm_device_get_nm_plugin_missing;
nm_device_is_real;
nm_device_set_managed;
@@ -944,6 +951,13 @@ global:
nm_setting_tun_new;
nm_setting_verify_secrets;
nm_setting_vpn_get_timeout;
+ nm_setting_macvlan_get_mode;
+ nm_setting_macvlan_get_parent;
+ nm_setting_macvlan_get_promiscuous;
+ nm_setting_macvlan_get_tap;
+ nm_setting_macvlan_get_type;
+ nm_setting_macvlan_mode_get_type;
+ nm_setting_macvlan_new;
nm_setting_wired_get_wake_on_lan;
nm_setting_wired_get_wake_on_lan_password;
nm_setting_wired_wake_on_lan_get_type;
diff --git a/libnm/nm-device-macvlan.c b/libnm/nm-device-macvlan.c
new file mode 100644
index 0000000000..3b4bfd35c8
--- /dev/null
+++ b/libnm/nm-device-macvlan.c
@@ -0,0 +1,347 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-macvlan.h>
+#include <nm-setting-wired.h>
+#include <nm-utils.h>
+
+#include "nm-default.h"
+#include "nm-device-macvlan.h"
+#include "nm-device-private.h"
+#include "nm-object-private.h"
+
+G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanPrivate))
+
+typedef struct {
+ NMDevice *parent;
+ char *mode;
+ gboolean no_promisc;
+ gboolean tap;
+ char *hw_address;
+} NMDeviceMacvlanPrivate;
+
+enum {
+ PROP_0,
+ PROP_PARENT,
+ PROP_MODE,
+ PROP_NO_PROMISC,
+ PROP_TAP,
+ PROP_HW_ADDRESS,
+
+ LAST_PROP
+};
+
+/**
+ * nm_device_macvlan_get_parent:
+ * @device: a #NMDeviceMacvlan
+ *
+ * Returns: (transfer none): the device's parent device
+ *
+ * Since: 1.2
+ **/
+NMDevice *
+nm_device_macvlan_get_parent (NMDeviceMacvlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MACVLAN (device), FALSE);
+
+ return NM_DEVICE_MACVLAN_GET_PRIVATE (device)->parent;
+}
+
+/**
+ * nm_device_macvlan_get_mode:
+ * @device: a #NMDeviceMacvlan
+ *
+ * Gets the MACVLAN mode of the device.
+ *
+ * Returns: the MACVLAN mode. This is the internal string used by the
+ * device, and must not be modified.
+ *
+ * Since: 1.2
+ **/
+const char *
+nm_device_macvlan_get_mode (NMDeviceMacvlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MACVLAN (device), NULL);
+
+ return NM_DEVICE_MACVLAN_GET_PRIVATE (device)->mode;
+}
+
+/**
+ * nm_device_macvlan_get_no_promisc
+ * @device: a #NMDeviceMacvlan
+ *
+ * Gets the no-promiscuous flag of the device.
+ *
+ * Returns: the no-promiscuous flag of the device.
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_device_macvlan_get_no_promisc (NMDeviceMacvlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MACVLAN (device), FALSE);
+
+ return NM_DEVICE_MACVLAN_GET_PRIVATE (device)->no_promisc;
+}
+
+/**
+ * nm_device_macvlan_get_tap:
+ * @device: a #NMDeviceMacvlan
+ *
+ * Gets the device type (MACVLAN or MACVTAP).
+ *
+ * Returns: %TRUE if the device is a MACVTAP, %FALSE if it is a MACVLAN.
+ *
+ * Since: 1.2
+ **/
+gboolean
+nm_device_macvlan_get_tap (NMDeviceMacvlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MACVLAN (device), FALSE);
+
+ return NM_DEVICE_MACVLAN_GET_PRIVATE (device)->tap;
+}
+
+/**
+ * nm_device_macvlan_get_hw_address:
+ * @device: a #NMDeviceMacvlan
+ *
+ * Gets the hardware (MAC) address of the #NMDeviceMacvlan
+ *
+ * Returns: the hardware address. This is the internal string used by the
+ * device, and must not be modified.
+ *
+ * Since: 1.2
+ **/
+const char *
+nm_device_macvlan_get_hw_address (NMDeviceMacvlan *device)
+{
+ g_return_val_if_fail (NM_IS_DEVICE_MACVLAN (device), NULL);
+
+ return NM_DEVICE_MACVLAN_GET_PRIVATE (device)->hw_address;
+}
+
+
+static gboolean
+connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+ NMSettingMacvlan *s_macvlan;
+
+ if (!NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->connection_compatible (device, connection, error))
+ return FALSE;
+
+ if (!nm_connection_is_type (connection, NM_SETTING_MACVLAN_SETTING_NAME)) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+ _("The connection was not a MAC-VLAN connection."));
+ return FALSE;
+ }
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ if (s_macvlan) {
+ if (nm_setting_macvlan_get_tap (s_macvlan) != priv->tap)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const char *
+get_hw_address (NMDevice *device)
+{
+ return nm_device_macvlan_get_hw_address (NM_DEVICE_MACVLAN (device));
+}
+
+static GType
+get_setting_type (NMDevice *device)
+{
+ return NM_TYPE_SETTING_MACVLAN;
+}
+
+/***********************************************************/
+
+static void
+nm_device_macvlan_init (NMDeviceMacvlan *device)
+{
+ _nm_device_set_device_type (NM_DEVICE (device), NM_DEVICE_TYPE_MACVLAN);
+}
+
+static void
+init_dbus (NMObject *object)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (object);
+ const NMPropertiesInfo property_info[] = {
+ { NM_DEVICE_MACVLAN_PARENT, &priv->parent, NULL, NM_TYPE_DEVICE },
+ { NM_DEVICE_MACVLAN_MODE, &priv->mode },
+ { NM_DEVICE_MACVLAN_NO_PROMISC, &priv->no_promisc },
+ { NM_DEVICE_MACVLAN_TAP, &priv->tap },
+ { NM_DEVICE_MACVLAN_HW_ADDRESS, &priv->hw_address },
+ { NULL },
+ };
+
+ NM_OBJECT_CLASS (nm_device_macvlan_parent_class)->init_dbus (object);
+
+ _nm_object_register_properties (object,
+ NM_DBUS_INTERFACE_DEVICE_MACVLAN,
+ property_info);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (object);
+
+ g_free (priv->mode);
+ g_free (priv->hw_address);
+ g_clear_object (&priv->parent);
+
+ G_OBJECT_CLASS (nm_device_macvlan_parent_class)->finalize (object);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMDeviceMacvlan *device = NM_DEVICE_MACVLAN (object);
+
+ switch (prop_id) {
+ case PROP_PARENT:
+ g_value_set_object (value, nm_device_macvlan_get_parent (device));
+ break;
+ case PROP_MODE:
+ g_value_set_string (value, nm_device_macvlan_get_mode (device));
+ break;
+ case PROP_NO_PROMISC:
+ g_value_set_boolean (value, nm_device_macvlan_get_no_promisc (device));
+ break;
+ case PROP_TAP:
+ g_value_set_boolean (value, nm_device_macvlan_get_tap (device));
+ break;
+ case PROP_HW_ADDRESS:
+ g_value_set_string (value, nm_device_macvlan_get_hw_address (device));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nm_device_macvlan_class_init (NMDeviceMacvlanClass *gre_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (gre_class);
+ NMObjectClass *nm_object_class = NM_OBJECT_CLASS (gre_class);
+ NMDeviceClass *device_class = NM_DEVICE_CLASS (gre_class);
+
+ g_type_class_add_private (gre_class, sizeof (NMDeviceMacvlanPrivate));
+
+ _nm_object_class_add_interface (nm_object_class, NM_DBUS_INTERFACE_DEVICE_MACVLAN);
+
+ /* virtual methods */
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+
+ nm_object_class->init_dbus = init_dbus;
+
+ device_class->connection_compatible = connection_compatible;
+ device_class->get_setting_type = get_setting_type;
+ device_class->get_hw_address = get_hw_address;
+
+ /* properties */
+
+ /**
+ * NMDeviceMacvlan:parent:
+ *
+ * The devices's parent device.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_object (NM_DEVICE_MACVLAN_PARENT, "", "",
+ NM_TYPE_DEVICE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceMacvlan:mode:
+ *
+ * The MACVLAN mode.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_MODE,
+ g_param_spec_string (NM_DEVICE_MACVLAN_MODE, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceMacvlan:no-promisc:
+ *
+ * Whether the device has the no-promiscuos flag.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_NO_PROMISC,
+ g_param_spec_boolean (NM_DEVICE_MACVLAN_NO_PROMISC, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceMacvlan:tap:
+ *
+ * Whether the device is a MACVTAP.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_TAP,
+ g_param_spec_boolean (NM_DEVICE_MACVLAN_TAP, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NMDeviceMacvlan:hw-address:
+ *
+ * The hardware (MAC) address of the device.
+ *
+ * Since: 1.2
+ **/
+ g_object_class_install_property
+ (object_class, PROP_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_MACVLAN_HW_ADDRESS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/libnm/nm-device-macvlan.h b/libnm/nm-device-macvlan.h
new file mode 100644
index 0000000000..7ffb80c153
--- /dev/null
+++ b/libnm/nm-device-macvlan.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2015 Red Hat, Inc.
+ */
+
+#ifndef __NM_DEVICE_MACVLAN_H__
+#define __NM_DEVICE_MACVLAN_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include <nm-device.h>
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_MACVLAN (nm_device_macvlan_get_type ())
+#define NM_DEVICE_MACVLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlan))
+#define NM_DEVICE_MACVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanClass))
+#define NM_IS_DEVICE_MACVLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_MACVLAN))
+#define NM_IS_DEVICE_MACVLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_MACVLAN))
+#define NM_DEVICE_MACVLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanClass))
+
+#define NM_DEVICE_MACVLAN_PARENT "parent"
+#define NM_DEVICE_MACVLAN_MODE "mode"
+#define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc"
+#define NM_DEVICE_MACVLAN_TAP "tap"
+#define NM_DEVICE_MACVLAN_HW_ADDRESS "hw-address"
+
+struct _NMDeviceMacvlan {
+ NMDevice parent;
+};
+
+typedef struct {
+ NMDeviceClass parent;
+
+ /*< private >*/
+ gpointer padding[4];
+} NMDeviceMacvlanClass;
+
+NM_AVAILABLE_IN_1_2
+GType nm_device_macvlan_get_type (void);
+
+NM_AVAILABLE_IN_1_2
+NMDevice * nm_device_macvlan_get_parent (NMDeviceMacvlan *device);
+NM_AVAILABLE_IN_1_2
+const char * nm_device_macvlan_get_mode (NMDeviceMacvlan *device);
+NM_AVAILABLE_IN_1_2
+gboolean nm_device_macvlan_get_no_promisc (NMDeviceMacvlan *device);
+NM_AVAILABLE_IN_1_2
+gboolean nm_device_macvlan_get_tap (NMDeviceMacvlan *device);
+NM_AVAILABLE_IN_1_2
+const char * nm_device_macvlan_get_hw_address (NMDeviceMacvlan *device);
+
+G_END_DECLS
+
+#endif /* __NM_DEVICE_MACVLAN_H__ */
diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index 9a0cbf556c..119e54fa0b 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -42,6 +42,7 @@
#include "nm-device-vlan.h"
#include "nm-device-generic.h"
#include "nm-device-ip-tunnel.h"
+#include "nm-device-macvlan.h"
#include "nm-device.h"
#include "nm-device-private.h"
#include "nm-dhcp4-config.h"
@@ -371,6 +372,8 @@ _nm_device_gtype_from_dtype (NMDeviceType dtype)
return NM_TYPE_DEVICE_TUN;
case NM_DEVICE_TYPE_IP_TUNNEL:
return NM_TYPE_DEVICE_IP_TUNNEL;
+ case NM_DEVICE_TYPE_MACVLAN:
+ return NM_TYPE_DEVICE_MACVLAN;
default:
g_warning ("Unknown device type %d", dtype);
return G_TYPE_INVALID;
diff --git a/libnm/nm-types.h b/libnm/nm-types.h
index f69deffaec..82001c0fb9 100644
--- a/libnm/nm-types.h
+++ b/libnm/nm-types.h
@@ -38,6 +38,7 @@ typedef struct _NMDeviceEthernet NMDeviceEthernet;
typedef struct _NMDeviceGeneric NMDeviceGeneric;
typedef struct _NMDeviceInfiniband NMDeviceInfiniband;
typedef struct _NMDeviceIPTunnel NMDeviceIPTunnel;
+typedef struct _NMDeviceMacvlan NMDeviceMacvlan;
typedef struct _NMDeviceModem NMDeviceModem;
typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh;
typedef struct _NMDeviceTeam NMDeviceTeam;
diff --git a/man/nmcli.1.in b/man/nmcli.1.in
index 58f96018f9..e78c72ea32 100644
--- a/man/nmcli.1.in
+++ b/man/nmcli.1.in
@@ -684,6 +684,16 @@ The value can be prefixed with \fBifname/\fP, \fBuuid/\fP or \fBid/\fP to disamb
.RE
.RS
.TP
+.B macvlan:
+.IP "\fIdev <parent device (connection UUID, ifname, or MAC)>\fP" 42
+\(en parent device this MACVLAN is on
+.IP "\fImode vepa|bridge|private|passthru|source\fP" 42
+\(en MACVLAN mode, which specifies the communication mechanism between multiple MACVLANs on the same lower device
+.IP "\fI[tap yes|no]\fP" 42
+\(en controls the device type. If set to 'yes' a MACVTAP will be created (default: no)
+.RE
+.RS
+.TP
.B SLAVE_OPTIONS:
.RE
.RS
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 90dc131304..2d1afcc3c6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -67,6 +67,7 @@ libnm-core/nm-setting-ip-config.c
libnm-core/nm-setting-ip4-config.c
libnm-core/nm-setting-ip6-config.c
libnm-core/nm-setting-ip-tunnel.c
+libnm-core/nm-setting-macvlan.c
libnm-core/nm-setting-olpc-mesh.c
libnm-core/nm-setting-ppp.c
libnm-core/nm-setting-pppoe.c
@@ -122,6 +123,7 @@ libnm/nm-device-generic.c
libnm/nm-device-tun.c
libnm/nm-device-infiniband.c
libnm/nm-device-ip-tunnel.c
+libnm/nm-device-macvlan.c
libnm/nm-device-modem.c
libnm/nm-device-olpc-mesh.c
libnm/nm-device-team.c
@@ -152,6 +154,7 @@ src/devices/nm-device-ethernet.c
src/devices/nm-device-ethernet-utils.c
src/devices/nm-device-infiniband.c
src/devices/nm-device-ip-tunnel.c
+src/devices/nm-device-macvlan.c
src/devices/nm-device-tun.c
src/devices/nm-device-vlan.c
src/devices/team/nm-device-team.c
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index ab650acab7..c980fd17a2 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -15,31 +15,40 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright 2013 Red Hat, Inc.
+ * Copyright 2013 - 2015 Red Hat, Inc.
*/
#include "config.h"
#include <string.h>
+#include "nm-default.h"
#include "nm-device-macvlan.h"
#include "nm-device-private.h"
-#include "nm-default.h"
+#include "nm-connection-provider.h"
+#include "nm-activation-request.h"
#include "nm-manager.h"
#include "nm-platform.h"
#include "nm-device-factory.h"
+#include "nm-setting-macvlan.h"
+#include "nm-setting-wired.h"
+#include "nm-active-connection.h"
+#include "nm-ip4-config.h"
+#include "nm-utils.h"
#include "nmdbus-device-macvlan.h"
#include "nm-device-logging.h"
_LOG_DECLARE_SELF(NMDeviceMacvlan);
-G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE_GENERIC)
+G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE)
#define NM_DEVICE_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanPrivate))
typedef struct {
int parent_ifindex;
+ guint parent_state_id;
+ NMDevice *parent;
NMPlatformLnkMacvlan props;
} NMDeviceMacvlanPrivate;
@@ -48,15 +57,115 @@ enum {
PROP_PARENT,
PROP_MODE,
PROP_NO_PROMISC,
+ PROP_TAP,
LAST_PROP
};
-/**************************************************************/
+static int modes[][2] = {
+ { NM_SETTING_MACVLAN_MODE_VEPA, MACVLAN_MODE_VEPA },
+ { NM_SETTING_MACVLAN_MODE_BRIDGE, MACVLAN_MODE_BRIDGE },
+ { NM_SETTING_MACVLAN_MODE_PRIVATE, MACVLAN_MODE_PRIVATE },
+ { NM_SETTING_MACVLAN_MODE_PASSTHRU, MACVLAN_MODE_PASSTHRU },
+};
+
+static int
+setting_mode_to_platform (int mode)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (modes); i++) {
+ if (modes[i][0] == mode)
+ return modes[i][1];
+ }
+
+ return 0;
+}
+
+static int
+platform_mode_to_setting (int mode)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (modes); i++) {
+ if (modes[i][1] == mode)
+ return modes[i][0];
+ }
+
+ return 0;
+}
+
+static const char *
+platform_mode_to_string (guint mode)
+{
+ switch (mode) {
+ case MACVLAN_MODE_PRIVATE:
+ return "private";
+ case MACVLAN_MODE_VEPA:
+ return "vepa";
+ case MACVLAN_MODE_BRIDGE:
+ return "bridge";
+ case MACVLAN_MODE_PASSTHRU:
+ return "passthru";
+ default:
+ return "unknown";
+ }
+}
/**************************************************************/
static void
+parent_state_changed (NMDevice *parent,
+ NMDeviceState new_state,
+ NMDeviceState old_state,
+ NMDeviceStateReason reason,
+ gpointer user_data)
+{
+ NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (user_data);
+
+ /* We'll react to our own carrier state notifications. Ignore the parent's. */
+ if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+ return;
+
+ nm_device_set_unmanaged_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent), reason);
+}
+
+static void
+nm_device_macvlan_set_parent (NMDeviceMacvlan *self, NMDevice *parent)
+ {
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+
+ if (parent == priv->parent)
+ return;
+
+ if (priv->parent_state_id)
+ nm_clear_g_signal_handler (priv->parent, &priv->parent_state_id);
+
+ g_clear_object (&priv->parent);
+
+ if (parent) {
+ priv->parent = g_object_ref (parent);
+ priv->parent_state_id = g_signal_connect (priv->parent,
+ "state-changed",
+ G_CALLBACK (parent_state_changed),
+ device);
+
+ /* Set parent-dependent unmanaged flag */
+ nm_device_set_unmanaged_flags (device,
+ NM_UNMANAGED_PARENT,
+ !nm_device_get_managed (parent),
+ NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
+ }
+
+ /* Recheck availability now that the parent has changed */
+ nm_device_queue_recheck_available (self,
+ NM_DEVICE_STATE_REASON_PARENT_CHANGED,
+ NM_DEVICE_STATE_REASON_PARENT_CHANGED);
+ g_object_notify (G_OBJECT (device), NM_DEVICE_MACVLAN_PARENT);
+}
+
+static void
update_properties (NMDevice *device)
{
NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (device);
@@ -64,18 +173,25 @@ update_properties (NMDevice *device)
GObject *object = G_OBJECT (device);
const NMPlatformLnkMacvlan *props;
const NMPlatformLink *plink;
+ NMDevice *parent = NULL;
+
+ if (priv->props.tap)
+ props = nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+ else
+ props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
- props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
if (!props) {
- _LOGW (LOGD_HW, "could not get macvlan properties");
+ _LOGW (LOGD_HW, "could not get %s properties", priv->props.tap ? "macvtap" : "macvlan");
return;
}
g_object_freeze_notify (object);
- if (priv->parent_ifindex != plink->parent)
- g_object_notify (object, NM_DEVICE_MACVLAN_PARENT);
- if (g_strcmp0 (priv->props.mode, props->mode) != 0)
+ if (priv->parent_ifindex != plink->parent) {
+ parent = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->parent);
+ nm_device_macvlan_set_parent (self, parent);
+ }
+ if (priv->props.mode != props->mode)
g_object_notify (object, NM_DEVICE_MACVLAN_MODE);
if (priv->props.no_promisc != props->no_promisc)
g_object_notify (object, NM_DEVICE_MACVLAN_NO_PROMISC);
@@ -93,19 +209,378 @@ link_changed (NMDevice *device, NMPlatformLink *info)
update_properties (device);
}
+static gboolean
+realize (NMDevice *device, NMPlatformLink *plink, GError **error)
+{
+ update_properties (device);
+ return TRUE;
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+ NMConnection *connection,
+ NMDevice *parent,
+ NMPlatformLink *out_plink,
+ GError **error)
+{
+ const char *iface = nm_device_get_iface (device);
+ NMPlatformError plerr;
+ NMSettingMacvlan *s_macvlan;
+ NMPlatformLnkMacvlan lnk = { };
+ int parent_ifindex;
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ g_assert (s_macvlan);
+ g_assert (out_plink);
+
+ parent_ifindex = nm_device_get_ifindex (parent);
+ g_warn_if_fail (parent_ifindex > 0);
+
+ lnk.mode = setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan));
+ if (!lnk.mode) {
+ nm_log_info (LOGD_DEVICE, "unsupported MACVLAN mode %u in connection %s",
+ nm_setting_macvlan_get_mode (s_macvlan),
+ nm_connection_get_uuid (connection));
+ return FALSE;
+ }
+ lnk.no_promisc = !nm_setting_macvlan_get_promiscuous (s_macvlan);
+ lnk.tap = nm_setting_macvlan_get_tap (s_macvlan);
+
+ plerr = nm_platform_link_macvlan_add (NM_PLATFORM_GET, iface, parent_ifindex, &lnk, out_plink);
+ if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+ g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+ "Failed to create %s interface '%s' for '%s': %s",
+ lnk.tap ? "macvtap" : "macvlan",
+ iface,
+ nm_connection_get_id (connection),
+ nm_platform_error_to_string (plerr));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/******************************************************************/
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *dev)
+{
+ /* We assume MACVLAN interfaces always support carrier detect */
+ return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static gboolean
+bring_up (NMDevice *dev, gboolean *no_firmware)
+{
+ gboolean success = FALSE;
+ guint i = 20;
+
+ while (i-- > 0 && !success) {
+ success = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->bring_up (dev, no_firmware);
+ g_usleep (50);
+ }
+
+ return success;
+}
+
+/******************************************************************/
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+ if (!NM_DEVICE_MACVLAN_GET_PRIVATE (device)->parent)
+ return FALSE;
+
+ return NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->is_available (device, flags);
+}
+
+static void
+notify_new_device_added (NMDevice *device, NMDevice *new_device)
+{
+ NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (device);
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+
+ if (priv->parent)
+ return;
+
+ if (!nm_device_is_real (device))
+ return;
+
+ update_properties (device);
+
+ if ( priv->parent_ifindex <= 0
+ || nm_device_get_ifindex (new_device) != priv->parent_ifindex)
+ return;
+
+ priv->parent_ifindex = nm_device_get_ifindex (new_device);
+ nm_device_macvlan_set_parent (self, new_device);
+}
+
/**************************************************************/
+
+static gboolean
+match_parent (NMDeviceMacvlan *self, const char *parent)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (self);
+
+ g_return_val_if_fail (parent != NULL, FALSE);
+
+ if (!priv->parent)
+ return FALSE;
+
+ if (nm_utils_is_uuid (parent)) {
+ NMActRequest *parent_req;
+ NMConnection *parent_connection;
+
+ /* If the parent is a UUID, the connection matches if our parent
+ * device has that connection activated.
+ */
+
+ parent_req = nm_device_get_act_request (priv->parent);
+ if (!parent_req)
+ return FALSE;
+
+ parent_connection = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (parent_req));
+ if (!parent_connection)
+ return FALSE;
+
+ if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0)
+ return FALSE;
+ } else {
+ /* interface name */
+ if (g_strcmp0 (parent, nm_device_get_ip_iface (priv->parent)) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+ NMSettingWired *s_wired;
+ const char *setting_mac;
+ const char *parent_mac;
+
+ s_wired = nm_connection_get_setting_wired (connection);
+ if (!s_wired)
+ return !fail_if_no_hwaddr;
+
+ setting_mac = nm_setting_wired_get_mac_address (s_wired);
+ if (!setting_mac)
+ return !fail_if_no_hwaddr;
+
+ if (!priv->parent)
+ return !fail_if_no_hwaddr;
+
+ parent_mac = nm_device_get_hw_address (priv->parent);
+
+ return nm_utils_hwaddr_matches (setting_mac, -1, parent_mac, -1);
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+ NMSettingMacvlan *s_macvlan;
+ const char *parent, *iface = NULL;
+
+ if (!NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ if (!s_macvlan)
+ return FALSE;
+
+ if (nm_setting_macvlan_get_tap (s_macvlan) != priv->props.tap)
+ return FALSE;
+
+ /* Before the device is realized some properties will not be set */
+ if (nm_device_is_real (device)) {
+
+ if (setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan)) != priv->props.mode)
+ return FALSE;
+
+ if (nm_setting_macvlan_get_promiscuous (s_macvlan) == priv->props.no_promisc)
+ return FALSE;
+
+ /* Check parent interface; could be an interface name or a UUID */
+ parent = nm_setting_macvlan_get_parent (s_macvlan);
+ if (parent) {
+ if (!match_parent (NM_DEVICE_MACVLAN (device), parent))
+ return FALSE;
+ } else {
+ /* Parent could be a MAC address in an NMSettingWired */
+ if (!match_hwaddr (device, connection, TRUE))
+ return FALSE;
+ }
+ }
+
+ /* Ensure the interface name matches */
+ iface = nm_connection_get_interface_name (connection);
+ if (iface) {
+ if (g_strcmp0 (nm_device_get_ip_iface (device), iface) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+ NMConnection *connection,
+ const char *specific_object,
+ const GSList *existing_connections,
+ GError **error)
+{
+ NMSettingMacvlan *s_macvlan;
+
+ nm_utils_complete_generic (connection,
+ NM_SETTING_MACVLAN_SETTING_NAME,
+ existing_connections,
+ NULL,
+ _("MACVLAN connection"),
+ NULL,
+ TRUE);
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ if (!s_macvlan) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "A 'macvlan' setting is required.");
+ return FALSE;
+ }
+
+ /* If there's no MACVLAN interface, no parent, and no hardware address in the
+ * settings, then there's not enough information to complete the setting.
+ */
+ if ( !nm_setting_macvlan_get_parent (s_macvlan)
+ && !match_hwaddr (device, connection, TRUE)) {
+ g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "The 'macvlan' setting had no interface name, parent, or hardware address.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
-nm_device_macvlan_init (NMDeviceMacvlan *self)
+update_connection (NMDevice *device, NMConnection *connection)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device);
+ NMSettingMacvlan *s_macvlan = nm_connection_get_setting_macvlan (connection);
+ const char *setting_parent, *new_parent;
+ int new_mode;
+
+ if (!s_macvlan) {
+ s_macvlan = (NMSettingMacvlan *) nm_setting_macvlan_new ();
+ nm_connection_add_setting (connection, (NMSetting *) s_macvlan);
+ }
+
+ new_mode = platform_mode_to_setting (priv->props.mode);
+ if (new_mode != nm_setting_macvlan_get_mode (s_macvlan))
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_MODE, new_mode, NULL);
+
+ if (priv->props.no_promisc == nm_setting_macvlan_get_promiscuous (s_macvlan))
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_PROMISCUOUS, !priv->props.no_promisc, NULL);
+
+
+ if (priv->props.tap != nm_setting_macvlan_get_tap (s_macvlan))
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, !!priv->props.tap, NULL);
+
+ /* Update parent in the connection; default to parent's interface name */
+ if (priv->parent) {
+ new_parent = nm_device_get_iface (priv->parent);
+ setting_parent = nm_setting_macvlan_get_parent (s_macvlan);
+ if (setting_parent && nm_utils_is_uuid (setting_parent)) {
+ NMConnection *parent_connection;
+
+ /* Don't change a parent specified by UUID if it's still valid */
+ parent_connection = nm_connection_provider_get_connection_by_uuid (nm_connection_provider_get (), setting_parent);
+ if (parent_connection && nm_device_check_connection_compatible (priv->parent, parent_connection))
+ new_parent = NULL;
+ }
+ if (new_parent)
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, new_parent, NULL);
+ } else
+ g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, NULL, NULL);
+
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+ NMActRequest *req;
+ NMConnection *connection;
+ NMSettingWired *s_wired;
+ const char *cloned_mac;
+ NMActStageReturn ret;
+
+ g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (dev, reason);
+ if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+ return ret;
+
+ req = nm_device_get_act_request (dev);
+ g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ connection = nm_act_request_get_applied_connection (req);
+ g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ s_wired = nm_connection_get_setting_wired (connection);
+ if (s_wired) {
+ /* Set device MAC address if the connection wants to change it */
+ cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
+ if (cloned_mac)
+ nm_device_set_hw_addr (dev, cloned_mac, "set", LOGD_HW);
+ }
+
+ return TRUE;
+}
+
+static void
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
{
+ NMConnection *connection;
+ NMSettingWired *s_wired;
+ guint32 mtu;
+
+ connection = nm_device_get_applied_connection (device);
+ g_assert (connection);
+
+ s_wired = nm_connection_get_setting_wired (connection);
+ if (s_wired) {
+ mtu = nm_setting_wired_get_mtu (s_wired);
+ if (mtu)
+ nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER);
+ }
+}
+
+static void
+setup_start (NMDevice *device, NMPlatformLink *plink)
+{
+ NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->setup_start (device, plink);
+
+ update_properties (device);
}
static void
-constructed (GObject *object)
+deactivate (NMDevice *device)
{
- update_properties (NM_DEVICE (object));
+ /* Reset MAC address back to initial address */
+ if (nm_device_get_initial_hw_address (device)) {
+ nm_device_set_hw_addr (device, nm_device_get_initial_hw_address (device),
+ "reset", LOGD_DEVICE);
+ }
+}
- G_OBJECT_CLASS (nm_device_macvlan_parent_class)->constructed (object);
+/******************************************************************/
+
+static void
+nm_device_macvlan_init (NMDeviceMacvlan *self)
+{
}
static void
@@ -124,11 +599,14 @@ get_property (GObject *object, guint prop_id,
nm_utils_g_value_set_object_path (value, parent);
break;
case PROP_MODE:
- g_value_set_string (value, priv->props.mode);
+ g_value_set_string (value, platform_mode_to_string (priv->props.mode));
break;
case PROP_NO_PROMISC:
g_value_set_boolean (value, priv->props.no_promisc);
break;
+ case PROP_TAP:
+ g_value_set_boolean (value, priv->props.tap);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -136,6 +614,21 @@ get_property (GObject *object, guint prop_id,
}
static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TAP:
+ priv->props.tap = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -145,10 +638,24 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
- object_class->constructed = constructed;
object_class->get_property = get_property;
-
+ object_class->set_property = set_property;
+
+ device_class->act_stage1_prepare = act_stage1_prepare;
+ device_class->bring_up = bring_up;
+ device_class->check_connection_compatible = check_connection_compatible;
+ device_class->complete_connection = complete_connection;
+ device_class->connection_type = NM_SETTING_MACVLAN_SETTING_NAME;
+ device_class->create_and_realize = create_and_realize;
+ device_class->deactivate = deactivate;
+ device_class->get_generic_capabilities = get_generic_capabilities;
+ device_class->ip4_config_pre_commit = ip4_config_pre_commit;
+ device_class->is_available = is_available;
device_class->link_changed = link_changed;
+ device_class->notify_new_device_added = notify_new_device_added;
+ device_class->realize = realize;
+ device_class->setup_start = setup_start;
+ device_class->update_connection = update_connection;
/* properties */
g_object_class_install_property
@@ -172,6 +679,14 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_TAP,
+ g_param_spec_boolean (NM_DEVICE_MACVLAN_TAP, "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
NMDBUS_TYPE_DEVICE_MACVLAN_SKELETON,
NULL);
@@ -189,18 +704,79 @@ create_device (NMDeviceFactory *factory,
NMConnection *connection,
gboolean *out_ignore)
{
- g_return_val_if_fail (plink, NULL);
+ NMSettingMacvlan *s_macvlan;
+ NMLinkType link_type;
+ gboolean tap;
+
+ if (connection) {
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ g_assert (s_macvlan);
+ tap = nm_setting_macvlan_get_tap (s_macvlan);
+ } else {
+ g_assert (plink);
+ tap = plink->type == NM_LINK_TYPE_MACVTAP;
+ }
+
+ link_type = tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN;
return (NMDevice *) g_object_new (NM_TYPE_DEVICE_MACVLAN,
NM_DEVICE_IFACE, iface,
NM_DEVICE_TYPE_DESC, "Macvlan",
- NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC,
- NM_DEVICE_LINK_TYPE, plink->type,
+ NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MACVLAN,
+ NM_DEVICE_LINK_TYPE, link_type,
+ NM_DEVICE_MACVLAN_TAP, tap,
NULL);
}
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+ NMSettingMacvlan *s_macvlan;
+ NMSettingWired *s_wired;
+ const char *parent = NULL;
+
+ g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_MACVLAN_SETTING_NAME), NULL);
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ g_assert (s_macvlan);
+
+ parent = nm_setting_macvlan_get_parent (s_macvlan);
+ if (parent)
+ return parent;
+
+ /* Try the hardware address from the MACVLAN connection's hardware setting */
+ s_wired = nm_connection_get_setting_wired (connection);
+ if (s_wired)
+ return nm_setting_wired_get_mac_address (s_wired);
+
+ return NULL;
+}
+
+static char *
+get_virtual_iface_name (NMDeviceFactory *factory,
+ NMConnection *connection,
+ const char *parent_iface)
+{
+ NMSettingMacvlan *s_macvlan;
+ const char *ifname;
+
+ g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_MACVLAN_SETTING_NAME), NULL);
+
+ s_macvlan = nm_connection_get_setting_macvlan (connection);
+ g_assert (s_macvlan);
+
+ if (!parent_iface)
+ return NULL;
+
+ ifname = nm_connection_get_interface_name (connection);
+ return g_strdup (ifname);
+}
+
NM_DEVICE_FACTORY_DEFINE_INTERNAL (MACVLAN, Macvlan, macvlan,
- NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP),
+ NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
+ NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_MACVLAN_SETTING_NAME),
factory_iface->create_device = create_device;
+ factory_iface->get_connection_parent = get_connection_parent;
+ factory_iface->get_virtual_iface_name = get_virtual_iface_name;
)
diff --git a/src/devices/nm-device-macvlan.h b/src/devices/nm-device-macvlan.h
index 189a9df02e..89f670bd82 100644
--- a/src/devices/nm-device-macvlan.h
+++ b/src/devices/nm-device-macvlan.h
@@ -21,7 +21,7 @@
#ifndef __NETWORKMANAGER_DEVICE_MACVLAN_H__
#define __NETWORKMANAGER_DEVICE_MACVLAN_H__
-#include "nm-device-generic.h"
+#include "nm-device.h"
G_BEGIN_DECLS
@@ -35,9 +35,10 @@ G_BEGIN_DECLS
#define NM_DEVICE_MACVLAN_PARENT "parent"
#define NM_DEVICE_MACVLAN_MODE "mode"
#define NM_DEVICE_MACVLAN_NO_PROMISC "no-promisc"
+#define NM_DEVICE_MACVLAN_TAP "tap"
-typedef NMDeviceGeneric NMDeviceMacvlan;
-typedef NMDeviceGenericClass NMDeviceMacvlanClass;
+typedef NMDevice NMDeviceMacvlan;
+typedef NMDeviceClass NMDeviceMacvlanClass;
GType nm_device_macvlan_get_type (void);
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 8ff369e8a5..36971049dd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -781,6 +781,8 @@ nm_device_get_priority (NMDevice *self)
return 350;
case NM_DEVICE_TYPE_VLAN:
return 400;
+ case NM_DEVICE_TYPE_MACVLAN:
+ return 410;
case NM_DEVICE_TYPE_BRIDGE:
return 425;
case NM_DEVICE_TYPE_TUN:
diff --git a/src/nm-types.h b/src/nm-types.h
index 65d6193be2..7934fcbbed 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -143,6 +143,7 @@ typedef enum {
NMP_OBJECT_TYPE_LNK_IP6TNL,
NMP_OBJECT_TYPE_LNK_IPIP,
NMP_OBJECT_TYPE_LNK_MACVLAN,
+ NMP_OBJECT_TYPE_LNK_MACVTAP,
NMP_OBJECT_TYPE_LNK_SIT,
NMP_OBJECT_TYPE_LNK_VLAN,
NMP_OBJECT_TYPE_LNK_VXLAN,
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 354641fee8..d7417b76d6 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -1026,9 +1026,16 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data)
struct nlattr *tb[IFLA_MACVLAN_MAX + 1];
int err;
NMPObject *obj;
- const char *mode;
+ gboolean tap;
+
+ if (!info_data)
+ return NULL;
- if (!info_data || g_strcmp0 (kind, "macvlan"))
+ if (!g_strcmp0 (kind, "macvlan"))
+ tap = FALSE;
+ else if (!g_strcmp0 (kind, "macvtap"))
+ tap = TRUE;
+ else
return NULL;
err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, policy);
@@ -1038,26 +1045,10 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data)
if (!tb[IFLA_MACVLAN_MODE])
return NULL;
- switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) {
- case MACVLAN_MODE_PRIVATE:
- mode = "private";
- break;
- case MACVLAN_MODE_VEPA:
- mode = "vepa";
- break;
- case MACVLAN_MODE_BRIDGE:
- mode = "bridge";
- break;
- case MACVLAN_MODE_PASSTHRU:
- mode = "passthru";
- break;
- default:
- return NULL;
- }
-
- obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_MACVLAN, NULL);
+ obj = nmp_object_new (tap ? NMP_OBJECT_TYPE_LNK_MACVTAP : NMP_OBJECT_TYPE_LNK_MACVLAN, NULL);
props = &obj->lnk_macvlan;
- props->mode = mode;
+ props->mode = nla_get_u32 (tb[IFLA_MACVLAN_MODE]);
+ props->tap = tap;
if (tb[IFLA_MACVLAN_FLAGS])
props->no_promisc = NM_FLAGS_HAS (nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC);
@@ -1493,6 +1484,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr
lnk_data = _parse_lnk_ipip (nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_MACVLAN:
+ case NM_LINK_TYPE_MACVTAP:
lnk_data = _parse_lnk_macvlan (nl_info_kind, nl_info_data);
break;
case NM_LINK_TYPE_SIT:
@@ -2907,6 +2899,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
case NM_LINK_TYPE_IP6TNL:
case NM_LINK_TYPE_INFINIBAND:
case NM_LINK_TYPE_MACVLAN:
+ case NM_LINK_TYPE_MACVTAP:
case NM_LINK_TYPE_SIT:
case NM_LINK_TYPE_VLAN:
case NM_LINK_TYPE_VXLAN:
@@ -4374,6 +4367,55 @@ nla_put_failure:
}
static int
+link_macvlan_add (NMPlatform *platform,
+ const char *name,
+ int parent,
+ NMPlatformLnkMacvlan *props,
+ NMPlatformLink *out_link)
+{
+ nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+ struct nlattr *info;
+ struct nlattr *data;
+
+ _LOGD ("adding %s '%s' parent %u mode %u",
+ props->tap ? "macvtap" : "macvlan",
+ name,
+ parent,
+ props->mode);
+
+ nlmsg = _nl_msg_new_link (RTM_NEWLINK,
+ NLM_F_CREATE,
+ 0,
+ name,
+ 0,
+ 0);
+ if (!nlmsg)
+ return FALSE;
+
+ NLA_PUT_U32 (nlmsg, IFLA_LINK, parent);
+
+ if (!(info = nla_nest_start (nlmsg, IFLA_LINKINFO)))
+ goto nla_put_failure;
+
+ NLA_PUT_STRING (nlmsg, IFLA_INFO_KIND, props->tap ? "macvtap" : "macvlan");
+
+ if (!(data = nla_nest_start (nlmsg, IFLA_INFO_DATA)))
+ goto nla_put_failure;
+
+ NLA_PUT_U32 (nlmsg, IFLA_MACVLAN_MODE, props->mode);
+ NLA_PUT_U16 (nlmsg, IFLA_MACVLAN_FLAGS, props->no_promisc ? MACVLAN_FLAG_NOPROMISC : 0);
+
+ nla_nest_end (nlmsg, data);
+ nla_nest_end (nlmsg, info);
+
+ return do_add_link_with_lookup (platform,
+ props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN,
+ name, nlmsg, out_link);
+nla_put_failure:
+ g_return_val_if_reached (FALSE);
+}
+
+static int
link_sit_add (NMPlatform *platform,
const char *name,
NMPlatformLnkSit *props,
@@ -5879,6 +5921,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
platform_class->link_gre_add = link_gre_add;
platform_class->link_ip6tnl_add = link_ip6tnl_add;
+ platform_class->link_macvlan_add = link_macvlan_add;
platform_class->link_ipip_add = link_ipip_add;
platform_class->link_sit_add = link_sit_add;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 623ba2edd0..f4e24d78e1 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -1447,6 +1447,12 @@ nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatfor
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVLAN, out_link);
}
+const NMPlatformLnkMacvtap *
+nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
+{
+ return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVTAP, out_link);
+}
+
const NMPlatformLnkSit *
nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
{
@@ -1944,6 +1950,47 @@ nm_platform_link_ipip_add (NMPlatform *self,
}
/**
+ * nm_platform_macvlan_add:
+ * @self: platform instance
+ * @name: name of the new interface
+ * @props: interface properties
+ * @out_link: on success, the link object
+ *
+ * Create a MACVLAN or MACVTAP device.
+ */
+NMPlatformError
+nm_platform_link_macvlan_add (NMPlatform *self,
+ const char *name,
+ int parent,
+ NMPlatformLnkMacvlan *props,
+ NMPlatformLink *out_link)
+{
+ NMPlatformError plerr;
+ NMLinkType type;
+
+ _CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
+
+ g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
+ g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
+
+ type = props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN;
+
+ plerr = _link_add_check_existing (self, name, type, out_link);
+ if (plerr != NM_PLATFORM_ERROR_SUCCESS)
+ return plerr;
+
+ _LOGD ("adding %s '%s' parent %u mode %u",
+ props->tap ? "macvtap" : "macvlan",
+ name,
+ parent,
+ props->mode);
+
+ if (!klass->link_macvlan_add (self, name, parent, props, out_link))
+ return NM_PLATFORM_ERROR_UNSPECIFIED;
+ return NM_PLATFORM_ERROR_SUCCESS;
+}
+
+/**
* nm_platform_sit_add:
* @self: platform instance
* @name: name of the new interface
@@ -3059,10 +3106,9 @@ nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, g
return buf;
g_snprintf (buf, len,
- "macvlan%s%s%s",
- lnk->mode ? " mode " : "",
- lnk->mode ?: "",
- lnk->no_promisc ? " not-promisc" : " promisc");
+ "macvlan mode %u %s",
+ lnk->mode,
+ lnk->no_promisc ? "not-promisc" : "promisc");
return buf;
}
@@ -3667,7 +3713,7 @@ int
nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b)
{
_CMP_SELF (a, b);
- _CMP_FIELD_STR_INTERNED (a, b, mode);
+ _CMP_FIELD (a, b, mode);
_CMP_FIELD_BOOL (a, b, no_promisc);
return 0;
}
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 5eb042cf78..de20e842a3 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -24,6 +24,7 @@
#include <netinet/in.h>
#include <linux/if.h>
#include <linux/if_addr.h>
+#include <linux/if_link.h>
#include "nm-dbus-interface.h"
#include "nm-default.h"
@@ -399,10 +400,13 @@ typedef struct {
} NMPlatformLnkIpIp;
typedef struct {
- const char *mode;
+ guint mode;
gboolean no_promisc;
+ gboolean tap;
} NMPlatformLnkMacvlan;
+typedef NMPlatformLnkMacvlan NMPlatformLnkMacvtap;
+
typedef struct {
int parent_ifindex;
in_addr_t local;
@@ -564,6 +568,8 @@ typedef struct {
NMPlatformLink *out_link);
gboolean (*link_ipip_add) (NMPlatform *, const char *name, NMPlatformLnkIpIp *props,
NMPlatformLink *out_link);
+ gboolean (*link_macvlan_add) (NMPlatform *, const char *name, int parent, NMPlatformLnkMacvlan *props,
+ NMPlatformLink *out_link);
gboolean (*link_sit_add) (NMPlatform *, const char *name, NMPlatformLnkSit *props,
NMPlatformLink *out_link);
@@ -760,6 +766,7 @@ const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int if
const NMPlatformLnkInfiniband *nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkIpIp *nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkMacvlan *nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
+const NMPlatformLnkMacvtap *nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkSit *nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link);
@@ -816,6 +823,8 @@ NMPlatformError nm_platform_link_ip6tnl_add (NMPlatform *self, const char *name,
NMPlatformLink *out_link);
NMPlatformError nm_platform_link_ipip_add (NMPlatform *self, const char *name, NMPlatformLnkIpIp *props,
NMPlatformLink *out_link);
+NMPlatformError nm_platform_link_macvlan_add (NMPlatform *self, const char *name, int parent, NMPlatformLnkMacvlan *props,
+ NMPlatformLink *out_link);
NMPlatformError nm_platform_link_sit_add (NMPlatform *self, const char *name, NMPlatformLnkSit *props,
NMPlatformLink *out_link);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index cb605c28f4..2b1ec2127f 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -2072,6 +2072,15 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string,
.cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp,
},
+ [NMP_OBJECT_TYPE_LNK_MACVTAP - 1] = {
+ .obj_type = NMP_OBJECT_TYPE_LNK_MACVTAP,
+ .sizeof_data = sizeof (NMPObjectLnkMacvtap),
+ .sizeof_public = sizeof (NMPlatformLnkMacvtap),
+ .obj_type_name = "macvtap",
+ .lnk_link_type = NM_LINK_TYPE_MACVTAP,
+ .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string,
+ .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp,
+ },
[NMP_OBJECT_TYPE_LNK_SIT - 1] = {
.obj_type = NMP_OBJECT_TYPE_LNK_SIT,
.sizeof_data = sizeof (NMPObjectLnkSit),
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 38224acd30..5acde46075 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -181,6 +181,8 @@ typedef struct {
NMPlatformLnkMacvlan _public;
} NMPObjectLnkMacvlan;
+typedef NMPObjectLnkMacvlan NMPObjectLnkMacvtap;
+
typedef struct {
NMPlatformLnkSit _public;
} NMPObjectLnkSit;
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 595096d2fb..f593c9cb80 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -775,6 +775,40 @@ nmtstp_link_ipip_add (gboolean external_command, const char *name, NMPlatformLnk
}
gboolean
+nmtstp_link_macvlan_add (gboolean external_command, const char *name, int parent, NMPlatformLnkMacvlan *lnk)
+{
+ gboolean success;
+
+ external_command = nmtstp_run_command_check_external (external_command);
+
+ if (external_command) {
+ const char *dev;
+ char *modes[] = {
+ [MACVLAN_MODE_BRIDGE] = "bridge",
+ [MACVLAN_MODE_VEPA] = "vepa",
+ [MACVLAN_MODE_PRIVATE] = "private",
+ [MACVLAN_MODE_PASSTHRU] = "passthru",
+ };
+
+ dev = nm_platform_link_get_name (NM_PLATFORM_GET, parent);
+ g_assert (dev);
+ g_assert_cmpint (lnk->mode, <, G_N_ELEMENTS (modes));
+
+ success = !nmtstp_run_command ("ip link add name %s link %s type %s mode %s %s",
+ name,
+ dev,
+ lnk->tap ? "macvtap" : "macvlan",
+ modes[lnk->mode],
+ lnk->no_promisc ? "nopromisc" : "");
+ if (success)
+ nmtstp_assert_wait_for_link (name, lnk->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN, 100);
+ } else
+ success = nm_platform_link_macvlan_add (NM_PLATFORM_GET, name, parent, lnk, NULL) == NM_PLATFORM_ERROR_SUCCESS;
+
+ return success;
+}
+
+gboolean
nmtstp_link_sit_add (gboolean external_command, const char *name, NMPlatformLnkSit *lnk)
{
gboolean success;
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 6140f26406..b1adfc46ba 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -144,6 +144,9 @@ gboolean nmtstp_link_ip6tnl_add (gboolean external_command,
gboolean nmtstp_link_ipip_add (gboolean external_command,
const char *name,
NMPlatformLnkIpIp *lnk);
+gboolean nmtstp_link_macvlan_add (gboolean external_command,
+ const char *name,
+ int parent, NMPlatformLnkMacvlan *lnk);
gboolean nmtstp_link_sit_add (gboolean external_command,
const char *name,
NMPlatformLnkSit *lnk);
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 6655259bb9..47e1af48b2 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -740,9 +740,28 @@ test_software_detect (gconstpointer user_data)
}
break;
}
- case NM_LINK_TYPE_MACVLAN:
- nmtstp_run_command_check ("ip link add name %s link %s type macvlan", DEVICE_NAME, PARENT_NAME);
+ case NM_LINK_TYPE_MACVLAN: {
+ NMPlatformLnkMacvlan lnk_macvlan = { };
+
+ lnk_macvlan.mode = MACVLAN_MODE_BRIDGE;
+ lnk_macvlan.no_promisc = FALSE;
+ lnk_macvlan.tap = FALSE;
+
+ if (!nmtstp_link_macvlan_add (EX, DEVICE_NAME, ifindex_parent, &lnk_macvlan))
+ g_error ("Failed adding MACVLAN interface");
+ break;
+ }
+ case NM_LINK_TYPE_MACVTAP: {
+ NMPlatformLnkMacvtap lnk_macvtap = { };
+
+ lnk_macvtap.mode = MACVLAN_MODE_PRIVATE;
+ lnk_macvtap.no_promisc = FALSE;
+ lnk_macvtap.tap = TRUE;
+
+ if (!nmtstp_link_macvlan_add (EX, DEVICE_NAME, ifindex_parent, &lnk_macvtap))
+ g_error ("Failed adding MACVTAP interface");
break;
+ }
case NM_LINK_TYPE_SIT: {
NMPlatformLnkSit lnk_sit = { };
gboolean gracefully_skip = FALSE;
@@ -867,7 +886,15 @@ test_software_detect (gconstpointer user_data)
g_assert (plnk == nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, NULL));
g_assert_cmpint (plnk->no_promisc, ==, FALSE);
- g_assert_cmpstr (plnk->mode, ==, "vepa");
+ g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_BRIDGE);
+ break;
+ }
+ case NM_LINK_TYPE_MACVTAP: {
+ const NMPlatformLnkMacvtap *plnk = &lnk->lnk_macvlan;
+
+ g_assert (plnk == nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, ifindex, NULL));
+ g_assert_cmpint (plnk->no_promisc, ==, FALSE);
+ g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_PRIVATE);
break;
}
case NM_LINK_TYPE_SIT: {
@@ -1703,6 +1730,7 @@ setup_tests (void)
test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0);
test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0);
test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0);
+ test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0);
test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0);
test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0);
test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0);