ifcfg-rh: add support for reading/writing bridge connections

Including testcase.
This commit is contained in:
Thomas Graf 2012-05-23 16:19:27 +02:00 committed by Dan Williams
parent d723457ac7
commit 6bc5213b66
4 changed files with 406 additions and 7 deletions

View file

@ -44,6 +44,7 @@
#include <nm-setting-wireless.h>
#include <nm-setting-8021x.h>
#include <nm-setting-bond.h>
#include <nm-setting-bridge.h>
#include <nm-utils.h>
#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);

View file

@ -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"

View file

@ -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]);

View file

@ -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);