diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 8510699da4..60774c49b3 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -752,4 +752,8 @@ gboolean nm_utils_base64secret_normalize (const char *base64_key, /*****************************************************************************/ +void _nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan, + GString *string, + gboolean leading_space); + #endif diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 04a4f8c2ff..ae70942c09 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -358,6 +358,124 @@ nm_bridge_vlan_new_clone (const NMBridgeVlan *vlan) return copy; } +void +_nm_bridge_vlan_str_append_rest (const NMBridgeVlan *vlan, + GString *string, + gboolean leading_space) +{ + if (nm_bridge_vlan_is_pvid (vlan)) { + if (leading_space) + g_string_append_c (string, ' '); + g_string_append (string, "pvid"); + leading_space = TRUE; + } + if (nm_bridge_vlan_is_untagged (vlan)) { + if (leading_space) + g_string_append_c (string, ' '); + g_string_append (string, "untagged"); + leading_space = TRUE; + } +} + +/** + * nm_bridge_vlan_to_str: + * @vlan: the %NMBridgeVlan + * @error: location of the error + * + * Convert a %NMBridgeVlan to a string. + * + * Returns: formatted string or %NULL + * + * Since: 1.18 + */ +char * +nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error) +{ + GString *string; + + g_return_val_if_fail (vlan, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + /* The function never fails at the moment, but it might in the + * future if more parameters are added to the object that could + * make it invalid. */ + + string = g_string_sized_new (20); + + g_string_append_printf (string, "%u", nm_bridge_vlan_get_vid (vlan)); + _nm_bridge_vlan_str_append_rest (vlan, string, TRUE); + + return g_string_free (string, FALSE); +} + +/** + * nm_bridge_vlan_from_str: + * @str: the string representation of a bridge VLAN + * @error: location of the error + * + * Parses the string representation of the queueing + * discipline to a %NMBridgeVlan instance. + * + * Returns: the %NMBridgeVlan or %NULL + * + * Since: 1.18 + */ +NMBridgeVlan * +nm_bridge_vlan_from_str (const char *str, GError **error) +{ + NMBridgeVlan *vlan = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + gs_free const char **tokens = NULL; + guint i, vid; + gboolean pvid = FALSE; + gboolean untagged = FALSE; + + g_return_val_if_fail (str, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + tokens = nm_utils_strsplit_set (str, " ", FALSE); + if (!tokens || !tokens[0]) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "missing VLAN id"); + return NULL; + } + + vid = _nm_utils_ascii_str_to_uint64 (tokens[0], + 10, + NM_BRIDGE_VLAN_VID_MIN, + NM_BRIDGE_VLAN_VID_MAX, + G_MAXUINT); + if (vid == G_MAXUINT) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VLAN id '%s', must be in [1,4094]", tokens[0]); + return NULL; + } + + for (i = 1; tokens[i]; i++) { + if (nm_streq (tokens[i], "pvid")) + pvid = TRUE; + else if (nm_streq (tokens[i], "untagged")) + untagged = TRUE; + else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid option '%s'", tokens[i]); + return NULL; + } + } + + vlan = nm_bridge_vlan_new (vid); + nm_bridge_vlan_set_pvid (vlan, pvid); + nm_bridge_vlan_set_untagged (vlan, untagged); + + return vlan; +} + /*****************************************************************************/ static int diff --git a/libnm-core/nm-setting-bridge.h b/libnm-core/nm-setting-bridge.h index 2afd1bff61..0ec0b2a8e3 100644 --- a/libnm-core/nm-setting-bridge.h +++ b/libnm-core/nm-setting-bridge.h @@ -139,6 +139,11 @@ gboolean nm_bridge_vlan_is_untagged (const NMBridgeVlan *vlan); NM_AVAILABLE_IN_1_18 gboolean nm_bridge_vlan_is_pvid (const NMBridgeVlan *vlan); +NM_AVAILABLE_IN_1_18 +char * nm_bridge_vlan_to_str (const NMBridgeVlan *vlan, GError **error); +NM_AVAILABLE_IN_1_18 +NMBridgeVlan * nm_bridge_vlan_from_str (const char *str, GError **error); + G_END_DECLS #endif /* __NM_SETTING_BRIDGE_H__ */ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 882dfd838c..88cdee36e3 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -6849,4 +6849,3 @@ _nm_utils_bridge_vlan_verify_list (GPtrArray *vlans, return TRUE; } - diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index 2011273a2b..dc624d1ba3 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -1658,6 +1658,63 @@ test_sriov_parse_vlans (void) test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0}); } +static void +test_bridge_vlans (void) +{ + NMBridgeVlan *v1, *v2; + GError *error = NULL; + char *str; + + v1 = nm_bridge_vlan_from_str ("1 foobar", &error); + nmtst_assert_no_success (v1, error); + g_clear_error (&error); + + v1 = nm_bridge_vlan_from_str ("4095", &error); + nmtst_assert_no_success (v1, error); + g_clear_error (&error); + + /* test comparison (1) */ + v1 = nm_bridge_vlan_from_str ("10 untagged", &error); + nmtst_assert_success (v1, error); + + g_assert_cmpuint (nm_bridge_vlan_get_vid (v1), ==, 10); + g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE); + + nm_bridge_vlan_set_pvid (v1, TRUE); + nm_bridge_vlan_set_untagged (v1, FALSE); + nm_bridge_vlan_seal (v1); + + g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, TRUE); + g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, FALSE); + + str = nm_bridge_vlan_to_str (v1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "10 pvid"); + g_clear_pointer (&str, g_free); + + v2 = nm_bridge_vlan_from_str (" 10 pvid ", &error); + nmtst_assert_success (v2, error); + + g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), ==, 0); + + nm_bridge_vlan_unref (v1); + nm_bridge_vlan_unref (v2); + + /* test comparison (2) */ + v1 = nm_bridge_vlan_from_str ("10", &error); + nmtst_assert_success (v1, error); + v2 = nm_bridge_vlan_from_str ("20", &error); + nmtst_assert_success (v2, error); + + g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), <, 0); + + nm_bridge_vlan_unref (v1); + nm_bridge_vlan_unref (v2); +} + /*****************************************************************************/ static void @@ -2663,6 +2720,8 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/tc_config/setting/duplicates", test_tc_config_setting_duplicates); g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus); + g_test_add_func ("/libnm/settings/bridge/vlans", test_bridge_vlans); + #if WITH_JSON_VALIDATION g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin", test_runner_roundrobin_sync_from_config); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index dcf43921dc..b705ca4589 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1522,6 +1522,7 @@ global: libnm_1_18_0 { global: nm_bridge_vlan_cmp; + nm_bridge_vlan_from_str; nm_bridge_vlan_get_type; nm_bridge_vlan_get_vid; nm_bridge_vlan_is_pvid; @@ -1533,6 +1534,7 @@ global: nm_bridge_vlan_seal; nm_bridge_vlan_set_pvid; nm_bridge_vlan_set_untagged; + nm_bridge_vlan_to_str; nm_bridge_vlan_unref; nm_setting_bridge_add_vlan; nm_setting_bridge_clear_vlans;