diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 32d16fc097..41b8549bd5 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include "wifi-utils.h" @@ -65,12 +66,27 @@ static gboolean get_int (const char *str, int *value) { char *e; + long int tmp; errno = 0; - *value = strtol (str, &e, 0); + tmp = strtol (str, &e, 0); if (errno || *e != '\0') return FALSE; + *value = (int) tmp; + return TRUE; +} +static gboolean +get_uint (const char *str, guint32 *value) +{ + char *e; + long unsigned int tmp; + + errno = 0; + tmp = strtoul (str, &e, 0); + if (errno || *e != '\0') + return FALSE; + *value = (guint32) tmp; return TRUE; } @@ -3699,6 +3715,167 @@ bond_connection_from_ifcfg (const char *file, return connection; } +static void +handle_bridge_option (NMSettingBridge *s_bridge, + gboolean stp, + const char *key, + const char *value) +{ + guint32 u = 0; + + if (!strcmp (key, "priority")) { + if (stp == FALSE) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'priority' invalid when STP is disabled"); + } else if (get_uint (value, &u)) + g_object_set (s_bridge, NM_SETTING_BRIDGE_PRIORITY, u, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid priority value '%s'", value); + } else if (!strcmp (key, "hello_time")) { + if (stp == FALSE) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'hello_time' invalid when STP is disabled"); + } else if (get_uint (value, &u)) + g_object_set (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, u, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid hello_time value '%s'", value); + } else if (!strcmp (key, "max_age")) { + if (stp == FALSE) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'max_age' invalid when STP is disabled"); + } else if (get_uint (value, &u)) + g_object_set (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, u, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid max_age value '%s'", value); + } else if (!strcmp (key, "ageing_time")) { + if (get_uint (value, &u)) + g_object_set (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, u, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid ageing_time value '%s'", value); + } else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unhandled bridge option '%s'", key); +} + +static NMSetting * +make_bridge_setting (shvarFile *ifcfg, + const char *file, + gboolean nm_controlled, + char **unmanaged, + GError **error) +{ + NMSettingBridge *s_bridge; + char *value; + guint32 u; + gboolean stp = FALSE; + + s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ()); + + value = svGetValue (ifcfg, "DEVICE", FALSE); + if (!value || !strlen (value)) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "mandatory DEVICE keyword missing"); + goto error; + } + + g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, value, NULL); + g_free (value); + + value = svGetValue (ifcfg, "STP", FALSE); + if (value) { + if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) { + g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL); + stp = TRUE; + } else if (!strcasecmp (value, "off") || !strcasecmp (value, "no")) + g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid STP value '%s'", value); + g_free (value); + } + + value = svGetValue (ifcfg, "DELAY", FALSE); + if (value) { + if (stp) { + if (get_uint (value, &u)) + g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, u, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid forward delay value '%s'", value); + } else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: DELAY invalid when STP is disabled"); + g_free (value); + } + + value = svGetValue (ifcfg, "BRIDGING_OPTS", FALSE); + if (value) { + char **items, **iter; + + items = g_strsplit_set (value, " ", -1); + for (iter = items; iter && *iter; iter++) { + if (strlen (*iter)) { + char **keys, *key, *val; + + keys = g_strsplit_set (*iter, "=", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && strlen(key) && strlen(val)) + handle_bridge_option (s_bridge, stp, key, val); + } + + g_strfreev (keys); + } + } + g_free (value); + g_strfreev (items); + } + + return (NMSetting *) s_bridge; + +error: + g_object_unref (s_bridge); + return NULL; +} + +static NMConnection * +bridge_connection_from_ifcfg (const char *file, + shvarFile *ifcfg, + gboolean nm_controlled, + char **unmanaged, + GError **error) +{ + NMConnection *connection = NULL; + NMSetting *con_setting = NULL; + NMSetting *bridge_setting = NULL; + + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (ifcfg != NULL, NULL); + + connection = nm_connection_new (); + if (!connection) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Failed to allocate new connection for %s.", file); + return NULL; + } + + con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BRIDGE_SETTING_NAME, NULL, _("Bridge")); + if (!con_setting) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Failed to create connection setting."); + g_object_unref (connection); + return NULL; + } + nm_connection_add_setting (connection, con_setting); + + bridge_setting = make_bridge_setting (ifcfg, file, nm_controlled, unmanaged, error); + if (!bridge_setting) { + g_object_unref (connection); + return NULL; + } + nm_connection_add_setting (connection, bridge_setting); + + if (!nm_connection_verify (connection, error)) { + g_object_unref (connection); + return NULL; + } + + return connection; +} + static gboolean is_bond_device (const char *name, shvarFile *parsed) { @@ -4075,8 +4252,7 @@ connection_from_file (const char *filename, else if (!strcasecmp (type, TYPE_VLAN)) connection = vlan_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, &error); else if (!strcasecmp (type, TYPE_BRIDGE)) - g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, - "Bridge connections are not yet supported"); + connection = bridge_connection_from_ifcfg (filename, parsed, nm_controlled, unmanaged, &error); else { g_set_error (&error, IFCFG_PLUGIN_ERROR, 0, "Unknown connection type '%s'", type); diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main index c5caf3fc9b..c406bbb342 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bridge-main @@ -4,4 +4,4 @@ TYPE=Bridge BOOTPROTO=dhcp STP=on DELAY=0 - +BRIDGING_OPTS="priority=32744 hello_time=7 max_age=39 ageing_time=235352" diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 09c52c9c5f..e2fe28cc70 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -11929,6 +11929,7 @@ static void test_read_bridge_main (void) { NMConnection *connection; + NMSettingBridge *s_bridge; char *unmanaged = NULL; char *keyfile = NULL; char *routefile = NULL; @@ -11946,13 +11947,140 @@ test_read_bridge_main (void) &route6file, &error, &ignore_error); - ASSERT (connection == NULL, - "bridge-main-read", "unexpected success reading %s", TEST_IFCFG_BRIDGE_MAIN); + g_assert (connection); + g_assert (nm_connection_verify (connection, &error)); + g_assert_no_error (error); + + /* ===== Bridging SETTING ===== */ + + s_bridge = nm_connection_get_setting_bridge (connection); + g_assert (s_bridge); + g_assert_cmpstr (nm_setting_bridge_get_interface_name (s_bridge), ==, "br0"); + g_assert_cmpuint (nm_setting_bridge_get_forward_delay (s_bridge), ==, 0); + g_assert_cmpuint (nm_setting_bridge_get_stp (s_bridge), ==, TRUE); + g_assert_cmpuint (nm_setting_bridge_get_priority (s_bridge), ==, 32744); + g_assert_cmpuint (nm_setting_bridge_get_hello_time (s_bridge), ==, 7); + g_assert_cmpuint (nm_setting_bridge_get_max_age (s_bridge), ==, 39); + g_assert_cmpuint (nm_setting_bridge_get_ageing_time (s_bridge), ==, 235352); g_free (unmanaged); g_free (keyfile); g_free (routefile); g_free (route6file); + g_object_unref (connection); +} + +static void +test_write_bridge_main (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingBridge *s_bridge; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + const guint32 ip1 = htonl (0x01010103); + const guint32 gw = htonl (0x01010101); + const guint32 prefix = 24; + NMIP4Address *addr; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + + connection = nm_connection_new (); + g_assert (connection); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + g_assert (s_con); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Bridge Main", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_BRIDGE_SETTING_NAME, + NULL); + g_free (uuid); + + /* bridge setting */ + s_bridge = (NMSettingBridge *) nm_setting_bridge_new (); + g_assert (s_bridge); + nm_connection_add_setting (connection, NM_SETTING (s_bridge)); + + g_object_set (s_bridge, + NM_SETTING_BRIDGE_INTERFACE_NAME, "br0", + NULL); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + g_assert (s_ip4); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP4_CONFIG_MAY_FAIL, TRUE, + NULL); + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, ip1); + nm_ip4_address_set_prefix (addr, prefix); + nm_ip4_address_set_gateway (addr, gw); + nm_setting_ip4_config_add_address (s_ip4, addr); + nm_ip4_address_unref (addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + g_assert (s_ip6); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + g_assert (nm_connection_verify (connection, &error)); + g_assert_no_error (error); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + g_assert (success); + g_assert_cmpstr (testfile, !=, NULL); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_BRIDGE, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + + g_assert (reread); + g_assert (nm_connection_verify (reread, &error)); + g_assert_no_error (error); + g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT)); + + g_free (testfile); + g_free (unmanaged); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); + g_object_unref (reread); } #define TEST_IFCFG_BRIDGE_COMPONENT TEST_IFCFG_DIR"/network-scripts/ifcfg-test-bridge-component" @@ -13176,12 +13304,14 @@ int main (int argc, char **argv) test_write_bond_slave (); test_write_bond_slave_ib (); + test_read_bridge_main (); + test_write_bridge_main (); + /* Stuff we expect to fail for now */ test_write_wired_pppoe (); test_write_vpn (); test_write_mobile_broadband (TRUE); test_write_mobile_broadband (FALSE); - test_read_bridge_main (); test_read_bridge_component (); base = g_path_get_basename (argv[0]); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index a2d5be72da..3817ec8b27 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1288,6 +1288,96 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, GError **erro return TRUE; } +static guint32 +get_bridge_default (NMSettingBridge *s_br, const char *prop) +{ + GParamSpec *pspec; + GValue val = { 0 }; + guint32 ret = 0; + + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (s_br), prop); + g_assert (pspec); + g_value_init (&val, pspec->value_type); + g_param_value_set_default (pspec, &val); + g_assert (G_VALUE_HOLDS_UINT (&val)); + ret = g_value_get_uint (&val); + g_value_unset (&val); + return ret; +} + +static gboolean +write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingBridge *s_bridge; + const char *iface; + guint32 i; + GString *opts; + char *s; + + s_bridge = nm_connection_get_setting_bridge (connection); + if (!s_bridge) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Missing '%s' setting", NM_SETTING_BRIDGE_SETTING_NAME); + return FALSE; + } + + iface = nm_setting_bridge_get_interface_name (s_bridge); + if (!iface) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing interface name"); + return FALSE; + } + + svSetValue (ifcfg, "DEVICE", iface, FALSE); + svSetValue (ifcfg, "BRIDGING_OPTS", NULL, FALSE); + svSetValue (ifcfg, "STP", NULL, FALSE); + svSetValue (ifcfg, "DELAY", NULL, FALSE); + + /* Bridge options */ + opts = g_string_sized_new (32); + + if (nm_setting_bridge_get_stp (s_bridge)) { + svSetValue (ifcfg, "STP", "yes", FALSE); + + i = nm_setting_bridge_get_forward_delay (s_bridge); + if (i && i != get_bridge_default (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY)) { + s = g_strdup_printf ("%u", i); + svSetValue (ifcfg, "DELAY", s, FALSE); + g_free (s); + } + + g_string_append_printf (opts, "priority=%u", nm_setting_bridge_get_priority (s_bridge)); + + i = nm_setting_bridge_get_hello_time (s_bridge); + if (i && i != get_bridge_default (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "hello_time=%u", i); + } + + i = nm_setting_bridge_get_max_age (s_bridge); + if (i && i != get_bridge_default (s_bridge, NM_SETTING_BRIDGE_MAX_AGE)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "max_age=%u", i); + } + } + + i = nm_setting_bridge_get_ageing_time (s_bridge); + if (i != get_bridge_default (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME)) { + if (opts->len) + g_string_append_c (opts, ' '); + g_string_append_printf (opts, "ageing_time=%u", i); + } + + if (opts->len) + svSetValue (ifcfg, "BRIDGING_OPTS", opts->str, FALSE); + g_string_free (opts, TRUE); + + svSetValue (ifcfg, "TYPE", TYPE_BRIDGE, FALSE); + + return TRUE; +} + static void write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) { @@ -2055,6 +2145,9 @@ write_connection (NMConnection *connection, } else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) { if (!write_bonding_setting (connection, ifcfg, error)) goto out; + } else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) { + if (!write_bridge_setting (connection, ifcfg, error)) + goto out; } else { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Can't write connection type '%s'", type);