diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-12-09 14:30:27 +0100 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-12-09 14:30:27 +0100 |
commit | e25d144d770df2d18c5f37ee72b971b7a40ea2fd (patch) | |
tree | 333e89ed200ec4f8fba4ba804ec93da097c1c96e | |
parent | 783b3642d08eab71146aaee267d41d71e611b7bf (diff) | |
parent | f841f17882f4b965ba64e9c6abb51155f2100ec3 (diff) | |
download | NetworkManager-e25d144d770df2d18c5f37ee72b971b7a40ea2fd.tar.gz |
merge: macvlan and macvtap devices creation support
https://bugzilla.gnome.org/show_bug.cgi?id=755986
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); |