cli: add macvlan support
authorBeniamino Galvani <bgalvani@redhat.com>
Fri, 18 Sep 2015 09:03:17 +0000 (11:03 +0200)
committerBeniamino Galvani <bgalvani@redhat.com>
Wed, 9 Dec 2015 13:30:08 +0000 (14:30 +0100)
clients/cli/connections.c
clients/cli/nmcli-completion
clients/cli/settings.c
man/nmcli.1.in

index 82f68c7..e5fa81b 100644 (file)
@@ -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;
 
index aff78f7..aefd902 100644 (file)
@@ -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
index 3e8c10f..05e5580 100644 (file)
@@ -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 },
 };
 
index 58f9601..e78c72e 100644 (file)
@@ -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