mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-06-19 12:28:29 +02:00
keyfile: unify IPv4/IPv6 address and routing configuration (bgo #682943)
IPv4 and IPv6 address configuration is now handled together and supports the following syntax (slashes can be replaced with semicolons): address/plen address/plen,gateway IPv4 and IPv6 route configuration is also handled uniformly and supports the following syntax: address/plen (for device routes) address/plen,gateway (for gateway routes) address/plen,gateway,metric (for gateway routes with metric) For compatibility reasons, slash (/), comma (,) and semicolon (;) are considered equal by the parser. The /plen part is optional for both addresses and routes for compatibility reasons. Leaving out the prefix length is not considered a good idea. IPv6 addresses default to 64 and IPv4 now defaults to 24 which is the closest possible IPv4 counterpart. Routes default to single addresses. Example 1: [ipv4] method=manual addresses1=192.168.56.5/24,192.168.56.1 addresses2=192.168.57.5/24 routes1=4.5.6.0/24 routes2=1.2.3.0/24,4.5.6.7 routes3=7.8.9.0/24,4.5.6.7,99 [ipv6] method=manual addresses1=2001:db8:a🅱️:3/64,2001:db8:a🅱️:1 addresses2=2001:db8:c:d::3/64 routes1=2001:db8:e:f::/64,2001:db8:a🅱️:4 Example 2 (equivalent): [ipv4] method=manual addresses1=192.168.56.5;24;192.168.56.1 addresses2=192.168.57.5;24 routes1=4.5.6.0;24 routes2=1.2.3.0;24;4.5.6.7 routes3=7.8.9.0;24;4.5.6.7;99 [ipv6] method=manual addresses1=2001:db8:a🅱️:3;64;2001:db8:a🅱️:1 addresses2=2001:db8:c:d::3;64 routes1=2001:db8:e:f::;64;2001:db8:a🅱️:4 For writing, I have arbitrarily chosen one of the formats 'reader' can parse. Address and prefix length are separated by slash (/), everything else is separated by comma (,). addresses1=address/plen,gateway routes1=address/plen,gateway,metric Note: The modified 'reader' exposes a bug in the 'writer' and ignores out badly-formatted routes. This problem is also fixed by this commit. Keyfile tests now pass.
This commit is contained in:
parent
2e826b152d
commit
0d82ca5c04
2 changed files with 338 additions and 470 deletions
|
|
@ -84,195 +84,349 @@ get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *ou
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
read_ip4_addresses (GKeyFile *file,
|
||||
const char *setting_name,
|
||||
const char *key)
|
||||
static gpointer
|
||||
build_ip4_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
|
||||
{
|
||||
GPtrArray *addresses;
|
||||
int i = 0;
|
||||
GArray *result;
|
||||
struct in_addr addr;
|
||||
guint32 address = 0;
|
||||
guint32 gateway = 0;
|
||||
int err;
|
||||
|
||||
addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref);
|
||||
g_return_val_if_fail (address_str, NULL);
|
||||
|
||||
/* Address */
|
||||
err = inet_pton (AF_INET, address_str, &addr);
|
||||
if (err <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv4 address '%s'", __func__, address_str);
|
||||
return NULL;
|
||||
}
|
||||
address = addr.s_addr;
|
||||
/* Gateway */
|
||||
if (gateway_str) {
|
||||
err = inet_pton (AF_INET, gateway_str, &addr);
|
||||
if (err <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str);
|
||||
return NULL;
|
||||
}
|
||||
gateway = addr.s_addr;
|
||||
}
|
||||
else
|
||||
gateway = 0;
|
||||
|
||||
result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
|
||||
g_array_append_val (result, address);
|
||||
g_array_append_val (result, plen);
|
||||
g_array_append_val (result, gateway);
|
||||
if (route)
|
||||
g_array_append_val (result, metric);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
build_ip6_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
|
||||
{
|
||||
GValueArray *result;
|
||||
struct in6_addr addr;
|
||||
GByteArray *address;
|
||||
GByteArray *gateway;
|
||||
GValue value = { 0, };
|
||||
int err;
|
||||
|
||||
g_return_val_if_fail (address_str, NULL);
|
||||
|
||||
result = g_value_array_new (3);
|
||||
|
||||
/* add address */
|
||||
err = inet_pton (AF_INET6, address_str, &addr);
|
||||
if (err <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 address '%s'", __func__, address_str);
|
||||
g_value_array_free (result);
|
||||
return NULL;
|
||||
}
|
||||
address = g_byte_array_new ();
|
||||
g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, address);
|
||||
g_value_array_append (result, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* add prefix length */
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
g_value_set_uint (&value, plen);
|
||||
g_value_array_append (result, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* add gateway */
|
||||
if (gateway_str) {
|
||||
err = inet_pton (AF_INET6, gateway_str, &addr);
|
||||
if (err <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str);
|
||||
g_value_array_free (result);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
memset (&addr, 0, 16);
|
||||
gateway = g_byte_array_new ();
|
||||
g_byte_array_append (gateway, (guint8 *) addr.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, gateway);
|
||||
g_value_array_append (result, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* add metric (for routing) */
|
||||
if (route) {
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
g_value_set_uint (&value, metric);
|
||||
g_value_array_append (result, &value);
|
||||
g_value_unset (&value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* On success, returns pointer to the zero-terminated field (original @current).
|
||||
* The @current * pointer target is set to point to the rest of the input
|
||||
* or NULL if there is no more input. Sets error to NULL for convenience.
|
||||
*
|
||||
* On failure, returns NULL (unspecified). The @current pointer target is
|
||||
* resets to its original value to allow skipping fields. The @error target
|
||||
* is set to the character that breaks the parsing or NULL if @current was NULL.
|
||||
*
|
||||
* When @current target is NULL, gracefully fail returning NULL while
|
||||
* leaving the @current target NULL end setting @error to NULL;
|
||||
*/
|
||||
static char *
|
||||
read_field (char **current, char **error, const char *characters, const char *delimiters)
|
||||
{
|
||||
char *start;
|
||||
|
||||
g_return_val_if_fail (current, NULL);
|
||||
g_return_val_if_fail (error, NULL);
|
||||
g_return_val_if_fail (characters, NULL);
|
||||
g_return_val_if_fail (delimiters, NULL);
|
||||
|
||||
if (!*current) {
|
||||
/* graceful failure, leave '*current' NULL */
|
||||
*error = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* fail on empty input */
|
||||
g_return_val_if_fail (**current, NULL);
|
||||
|
||||
/* remember beginning of input */
|
||||
start = *current;
|
||||
|
||||
while (**current && strchr (characters, **current))
|
||||
(*current)++;
|
||||
if (**current)
|
||||
if (strchr (delimiters, **current)) {
|
||||
/* success, more data available */
|
||||
*error = NULL;
|
||||
*(*current)++ = '\0';
|
||||
return start;
|
||||
} else {
|
||||
/* error, bad character */
|
||||
*error = *current;
|
||||
*current = start;
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
/* success, end of input */
|
||||
*error = NULL;
|
||||
*current = NULL;
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
#define IP_ADDRESS_CHARS "0123456789abcdefABCDEF:.%"
|
||||
#define DIGITS "0123456789"
|
||||
#define DELIMITERS "/;,"
|
||||
|
||||
|
||||
/* The following IPv4 and IPv6 address formats are supported:
|
||||
*
|
||||
* address (DEPRECATED)
|
||||
* address/plen
|
||||
* address/gateway (DEPRECATED)
|
||||
* address/plen/gateway
|
||||
*
|
||||
* The following IPv4 and IPv6 route formats are supported:
|
||||
*
|
||||
* address/plen (NETWORK dev DEVICE)
|
||||
* address/plen/gateway (NETWORK via GATEWAY dev DEVICE)
|
||||
* address/plen//gateway (NETWORK dev DEVICE metric METRIC)
|
||||
* address/plen/gateway/metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
|
||||
*
|
||||
* For backward, forward and sideward compatibility, slash (/),
|
||||
* semicolon (;) and comma (,) are interchangable. The use of
|
||||
* slash in the above examples is therefore not significant.
|
||||
*
|
||||
* Leaving out the prefix length is discouraged and DEPRECATED. The
|
||||
* default value of IPv6 prefix length was 64 and has not been
|
||||
* changed. The default for IPv4 is now 24, which is the closest
|
||||
* IPv4 equivalent. These defaults may just as well be changed to
|
||||
* match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
|
||||
*
|
||||
* The returned result is GArray for IPv4 and GValueArray for IPv6.
|
||||
*/
|
||||
static gpointer
|
||||
read_one_ip_address_or_route (GKeyFile *file,
|
||||
const char *setting_name,
|
||||
const char *key_name,
|
||||
gboolean ipv6,
|
||||
gboolean route)
|
||||
{
|
||||
guint32 plen, metric;
|
||||
gpointer result;
|
||||
char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
|
||||
|
||||
current = value = g_key_file_get_string (file, setting_name, key_name, NULL);
|
||||
if (!value)
|
||||
return NULL;
|
||||
|
||||
/* get address field */
|
||||
address_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS);
|
||||
if (error) {
|
||||
g_warning ("keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
|
||||
*error, setting_name, key_name, error - current, current);
|
||||
goto error;
|
||||
}
|
||||
/* get prefix length field (skippable) */
|
||||
plen_str = read_field (¤t, &error, DIGITS, DELIMITERS);
|
||||
/* get gateway field */
|
||||
gateway_str = read_field (¤t, &error, IP_ADDRESS_CHARS, DELIMITERS);
|
||||
if (error) {
|
||||
g_warning ("keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
|
||||
*error, setting_name, key_name,
|
||||
plen_str ? "gateway" : "gateway or prefix length",
|
||||
error - current, current);
|
||||
goto error;
|
||||
}
|
||||
/* for routes, get metric */
|
||||
if (route) {
|
||||
metric_str = read_field (¤t, &error, DIGITS, DELIMITERS);
|
||||
if (error) {
|
||||
g_warning ("keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
|
||||
*error, setting_name, key_name, error - current, current);
|
||||
goto error;
|
||||
}
|
||||
} else
|
||||
metric_str = NULL;
|
||||
if (current) {
|
||||
/* there is still some data */
|
||||
if (*current) {
|
||||
/* another field follows */
|
||||
g_warning ("keyfile: %s.%s: Garbage at the and of the line: %s",
|
||||
setting_name, key_name, current);
|
||||
goto error;
|
||||
} else {
|
||||
/* semicolon at the end of input */
|
||||
g_message ("keyfile: %s.%s: Deprecated semicolon at the end of value.",
|
||||
setting_name, key_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse plen, fallback to defaults */
|
||||
if (plen_str)
|
||||
g_return_val_if_fail (get_one_int (plen_str, ipv6 ? 128 : 32,
|
||||
key_name, &plen), NULL);
|
||||
else {
|
||||
if (route)
|
||||
plen = ipv6 ? 128 : 24;
|
||||
else
|
||||
plen = ipv6 ? 64 : 24;
|
||||
g_warning ("keyfile: Missing prefix length in '%s.%s', defaulting to %d",
|
||||
setting_name, key_name, plen);
|
||||
}
|
||||
|
||||
/* parse metric, default to 0 */
|
||||
metric = 0;
|
||||
if (metric_str)
|
||||
g_return_val_if_fail (get_one_int (metric_str, G_MAXUINT32,
|
||||
key_name, &metric), NULL);
|
||||
|
||||
/* build the appropriate data structure for NetworkManager settings */
|
||||
if (route)
|
||||
g_debug ("keyfile: %s.%s: route %s/%d gateway %s metric %d", setting_name, key_name, address_str, plen, gateway_str, metric);
|
||||
else
|
||||
g_debug ("keyfile: %s.%s: address %s/%d gateway %s", setting_name, key_name, address_str, plen, gateway_str);
|
||||
result = (ipv6 ? build_ip6_address_or_route : build_ip4_address_or_route) (
|
||||
address_str, plen, gateway_str, metric, route);
|
||||
|
||||
g_free (value);
|
||||
return result;
|
||||
error:
|
||||
g_free (value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
ip_addr_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
gboolean ipv6 = !strcmp (setting_name, "ipv6");
|
||||
GPtrArray *addresses;
|
||||
int i;
|
||||
|
||||
addresses = g_ptr_array_new_with_free_func (
|
||||
ipv6 ? (GDestroyNotify) g_value_array_free : (GDestroyNotify) g_array_unref);
|
||||
|
||||
/* Look for individual addresses */
|
||||
while (i++ < 1000) {
|
||||
gchar **tmp, **iter;
|
||||
for (i = 0; i < 1000; i++) {
|
||||
char *key_name;
|
||||
gsize length = 0;
|
||||
int ret;
|
||||
GArray *address;
|
||||
guint32 empty = 0;
|
||||
int j;
|
||||
|
||||
/* address is a GArray of three guint32 for IPv4 and a GValueArray consisting
|
||||
* of GByteArray, guint32 and GByteArray for IPv6.
|
||||
*/
|
||||
gpointer address;
|
||||
|
||||
key_name = g_strdup_printf ("%s%d", key, i);
|
||||
tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
|
||||
|
||||
if (!tmp || !length) {
|
||||
g_free (key_name);
|
||||
break; /* all done */
|
||||
address = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, FALSE);
|
||||
if (address) {
|
||||
g_ptr_array_add (addresses, address);
|
||||
}
|
||||
|
||||
if ((length < 2) || (length > 3)) {
|
||||
g_warning ("%s: ignoring invalid IPv4 address item '%s'", __func__, key_name);
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* convert the string array into IP addresses */
|
||||
address = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
|
||||
for (iter = tmp, j = 0; *iter; iter++, j++) {
|
||||
struct in_addr addr;
|
||||
|
||||
if (j == 1) {
|
||||
guint32 prefix = 0;
|
||||
|
||||
/* prefix */
|
||||
if (!get_one_int (*iter, 32, key_name, &prefix)) {
|
||||
g_array_unref (address);
|
||||
goto next;
|
||||
}
|
||||
|
||||
g_array_append_val (address, prefix);
|
||||
} else {
|
||||
/* address and gateway */
|
||||
ret = inet_pton (AF_INET, *iter, &addr);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter);
|
||||
g_array_unref (address);
|
||||
goto next;
|
||||
}
|
||||
g_array_append_val (address, addr.s_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in blank gateway if not specified */
|
||||
if (address->len == 2)
|
||||
g_array_append_val (address, empty);
|
||||
|
||||
g_ptr_array_add (addresses, address);
|
||||
|
||||
next:
|
||||
g_free (key_name);
|
||||
g_strfreev (tmp);
|
||||
}
|
||||
|
||||
if (addresses->len < 1) {
|
||||
g_ptr_array_unref (addresses);
|
||||
addresses = NULL;
|
||||
}
|
||||
if (addresses->len >= 1)
|
||||
g_object_set (setting, key, addresses, NULL);
|
||||
|
||||
return addresses;
|
||||
g_ptr_array_unref (addresses);
|
||||
}
|
||||
|
||||
static void
|
||||
ip4_addr_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
ip_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
GPtrArray *addresses;
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
|
||||
addresses = read_ip4_addresses (keyfile, setting_name, key);
|
||||
|
||||
/* Work around for previous syntax */
|
||||
if (!addresses && !strcmp (key, NM_SETTING_IP4_CONFIG_ADDRESSES))
|
||||
addresses = read_ip4_addresses (keyfile, setting_name, "address");
|
||||
|
||||
if (addresses) {
|
||||
g_object_set (setting, key, addresses, NULL);
|
||||
g_ptr_array_unref (addresses);
|
||||
}
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
read_ip4_routes (GKeyFile *file,
|
||||
const char *setting_name,
|
||||
const char *key)
|
||||
{
|
||||
gboolean ipv6 = !strcmp (setting_name, "ipv6");
|
||||
GPtrArray *routes;
|
||||
int i = 0;
|
||||
int i;
|
||||
|
||||
routes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_array_unref);
|
||||
routes = g_ptr_array_new_with_free_func (
|
||||
ipv6 ? (GDestroyNotify) g_value_array_free : (GDestroyNotify) g_array_unref);
|
||||
|
||||
/* Look for individual routes */
|
||||
while (i++ < 1000) {
|
||||
gchar **tmp, **iter;
|
||||
for (i = -1; i < 1000; i++) {
|
||||
char *key_name;
|
||||
gsize length = 0;
|
||||
int ret;
|
||||
GArray *route;
|
||||
int j;
|
||||
|
||||
/* route is a GArray of three or four guint32 for IPv4 and a GValueArray
|
||||
* consisting of GByteArray, guint32, GByteArray and optional guint32
|
||||
* for IPv6.
|
||||
*/
|
||||
gpointer route;
|
||||
|
||||
key_name = g_strdup_printf ("%s%d", key, i);
|
||||
tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
|
||||
g_free (key_name);
|
||||
|
||||
if (!tmp || !length)
|
||||
break; /* all done */
|
||||
|
||||
if (length != 4) {
|
||||
g_warning ("%s: ignoring invalid IPv4 route item '%s'", __func__, key_name);
|
||||
goto next;
|
||||
route = read_one_ip_address_or_route (keyfile, setting_name, key_name, ipv6, TRUE);
|
||||
if (route) {
|
||||
g_ptr_array_add (routes, route);
|
||||
}
|
||||
|
||||
/* convert the string array into IP addresses */
|
||||
route = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
|
||||
for (iter = tmp, j = 0; *iter; iter++, j++) {
|
||||
struct in_addr addr;
|
||||
|
||||
if (j == 1) {
|
||||
guint32 prefix = 0;
|
||||
|
||||
/* prefix */
|
||||
if (!get_one_int (*iter, 32, key_name, &prefix)) {
|
||||
g_array_unref (route);
|
||||
goto next;
|
||||
}
|
||||
|
||||
g_array_append_val (route, prefix);
|
||||
} else if (j == 3) {
|
||||
guint32 metric = 0;
|
||||
|
||||
/* metric */
|
||||
if (!get_one_int (*iter, G_MAXUINT32, key_name, &metric)) {
|
||||
g_array_unref (route);
|
||||
goto next;
|
||||
}
|
||||
|
||||
g_array_append_val (route, metric);
|
||||
} else {
|
||||
/* address and next hop */
|
||||
ret = inet_pton (AF_INET, *iter, &addr);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter);
|
||||
g_array_unref (route);
|
||||
goto next;
|
||||
}
|
||||
g_array_append_val (route, addr.s_addr);
|
||||
}
|
||||
}
|
||||
g_ptr_array_add (routes, route);
|
||||
|
||||
next:
|
||||
g_strfreev (tmp);
|
||||
}
|
||||
|
||||
if (routes->len < 1) {
|
||||
g_ptr_array_unref (routes);
|
||||
routes = NULL;
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
static void
|
||||
ip4_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
GPtrArray *routes;
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
|
||||
routes = read_ip4_routes (keyfile, setting_name, key);
|
||||
if (routes) {
|
||||
if (routes->len >= 1)
|
||||
g_object_set (setting, key, routes, NULL);
|
||||
g_ptr_array_unref (routes);
|
||||
}
|
||||
|
||||
g_ptr_array_unref (routes);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -308,278 +462,6 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
|
|||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
split_prefix (char *addr)
|
||||
{
|
||||
char *slash;
|
||||
|
||||
g_return_val_if_fail (addr != NULL, NULL);
|
||||
|
||||
/* Find the prefix and split the string */
|
||||
slash = strchr (addr, '/');
|
||||
if (slash && slash > addr) {
|
||||
slash++;
|
||||
*(slash - 1) = '\0';
|
||||
}
|
||||
|
||||
return slash;
|
||||
}
|
||||
|
||||
static char *
|
||||
split_gw (char *str)
|
||||
{
|
||||
char *comma;
|
||||
|
||||
g_return_val_if_fail (str != NULL, NULL);
|
||||
|
||||
/* Find the prefix and split the string */
|
||||
comma = strchr (str, ',');
|
||||
if (comma && comma > str) {
|
||||
comma++;
|
||||
*(comma - 1) = '\0';
|
||||
return comma;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
read_ip6_addresses (GKeyFile *file,
|
||||
const char *setting_name,
|
||||
const char *key)
|
||||
{
|
||||
GPtrArray *addresses;
|
||||
struct in6_addr addr, gw;
|
||||
guint32 prefix;
|
||||
int i = 0;
|
||||
|
||||
addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free);
|
||||
|
||||
/* Look for individual addresses */
|
||||
while (i++ < 1000) {
|
||||
char *tmp, *key_name, *str_prefix, *str_gw;
|
||||
int ret;
|
||||
/* TODO: GValueArray is deprecated and should be replaced with GValue */
|
||||
GValueArray *values;
|
||||
GByteArray *address;
|
||||
GByteArray *gateway;
|
||||
GValue value = { 0 };
|
||||
|
||||
key_name = g_strdup_printf ("%s%d", key, i);
|
||||
tmp = g_key_file_get_string (file, setting_name, key_name, NULL);
|
||||
g_free (key_name);
|
||||
|
||||
if (!tmp)
|
||||
break; /* all done */
|
||||
|
||||
/* convert the string array into IPv6 addresses */
|
||||
values = g_value_array_new (2); /* NMIP6Address has 2 items */
|
||||
|
||||
/* Split the address and prefix */
|
||||
str_prefix = split_prefix (tmp);
|
||||
|
||||
/* address */
|
||||
ret = inet_pton (AF_INET6, tmp, &addr);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp);
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
|
||||
address = g_byte_array_new ();
|
||||
g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, address);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* prefix */
|
||||
prefix = 0;
|
||||
if (str_prefix) {
|
||||
if (!get_one_int (str_prefix, 128, key_name, &prefix)) {
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
} else {
|
||||
/* Missing prefix defaults to /64 */
|
||||
prefix = 64;
|
||||
}
|
||||
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
g_value_set_uint (&value, prefix);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* Gateway (optional) */
|
||||
str_gw = split_gw (str_prefix);
|
||||
if (str_gw) {
|
||||
ret = inet_pton (AF_INET6, str_gw, &gw);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 %s gateway '%s'", __func__, key_name, tmp);
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED (&gw)) {
|
||||
gateway = g_byte_array_new ();
|
||||
g_byte_array_append (gateway, (guint8 *) gw.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, gateway);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_add (addresses, values);
|
||||
|
||||
next:
|
||||
g_free (tmp);
|
||||
}
|
||||
|
||||
if (addresses->len < 1) {
|
||||
g_ptr_array_unref (addresses);
|
||||
addresses = NULL;
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
static void
|
||||
ip6_addr_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
GPtrArray *addresses;
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
|
||||
addresses = read_ip6_addresses (keyfile, setting_name, key);
|
||||
if (addresses) {
|
||||
g_object_set (setting, key, addresses, NULL);
|
||||
g_ptr_array_unref (addresses);
|
||||
}
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
read_ip6_routes (GKeyFile *file,
|
||||
const char *setting_name,
|
||||
const char *key)
|
||||
{
|
||||
GPtrArray *routes;
|
||||
struct in6_addr addr;
|
||||
guint32 prefix, metric;
|
||||
int i = 0;
|
||||
|
||||
routes = g_ptr_array_new_with_free_func ((GDestroyNotify) g_value_array_free);
|
||||
|
||||
/* Look for individual routes */
|
||||
while (i++ < 1000) {
|
||||
gchar **tmp;
|
||||
char *key_name, *str_prefix;
|
||||
gsize length = 0;
|
||||
int ret;
|
||||
/* TODO: GValueArray is deprecated and should be replaced with GValue */
|
||||
GValueArray *values;
|
||||
GByteArray *address;
|
||||
GValue value = { 0 };
|
||||
|
||||
key_name = g_strdup_printf ("%s%d", key, i);
|
||||
tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
|
||||
g_free (key_name);
|
||||
|
||||
if (!tmp || !length)
|
||||
break; /* all done */
|
||||
|
||||
if (length != 3) {
|
||||
g_warning ("%s: ignoring invalid IPv6 address item '%s'", __func__, key_name);
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* convert the string array into IPv6 routes */
|
||||
values = g_value_array_new (4); /* NMIP6Route has 4 items */
|
||||
|
||||
/* Split the route and prefix */
|
||||
str_prefix = split_prefix (tmp[0]);
|
||||
|
||||
/* destination address */
|
||||
ret = inet_pton (AF_INET6, tmp[0], &addr);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[0]);
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
address = g_byte_array_new ();
|
||||
g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, address);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* prefix */
|
||||
prefix = 0;
|
||||
if (str_prefix) {
|
||||
if (!get_one_int (str_prefix, 128, key_name, &prefix)) {
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
} else {
|
||||
/* default to 64 if unspecified */
|
||||
prefix = 64;
|
||||
}
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
g_value_set_uint (&value, prefix);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* next hop address */
|
||||
ret = inet_pton (AF_INET6, tmp[1], &addr);
|
||||
if (ret <= 0) {
|
||||
g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[1]);
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
address = g_byte_array_new ();
|
||||
g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
|
||||
g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
|
||||
g_value_take_boxed (&value, address);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
/* metric */
|
||||
metric = 0;
|
||||
if (!get_one_int (tmp[2], G_MAXUINT32, key_name, &metric)) {
|
||||
g_value_array_free (values);
|
||||
goto next;
|
||||
}
|
||||
g_value_init (&value, G_TYPE_UINT);
|
||||
g_value_set_uint (&value, metric);
|
||||
g_value_array_append (values, &value);
|
||||
g_value_unset (&value);
|
||||
|
||||
g_ptr_array_add (routes, values);
|
||||
|
||||
next:
|
||||
g_strfreev (tmp);
|
||||
}
|
||||
|
||||
if (routes->len < 1) {
|
||||
g_ptr_array_unref (routes);
|
||||
routes = NULL;
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
static void
|
||||
ip6_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
GPtrArray *routes;
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
|
||||
routes = read_ip6_routes (keyfile, setting_name, key);
|
||||
|
||||
if (routes) {
|
||||
g_object_set (setting, key, routes, NULL);
|
||||
g_ptr_array_unref (routes);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
|
||||
{
|
||||
|
|
@ -955,26 +837,26 @@ typedef struct {
|
|||
/* A table of keys that require further parsing/conversion because they are
|
||||
* stored in a format that can't be automatically read using the key's type.
|
||||
* i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
|
||||
* stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
|
||||
* stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
|
||||
* in struct in6_addr internally, but as string in keyfiles.
|
||||
*/
|
||||
static KeyParser key_parsers[] = {
|
||||
{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
||||
NM_SETTING_IP4_CONFIG_ADDRESSES,
|
||||
FALSE,
|
||||
ip4_addr_parser },
|
||||
ip_addr_parser },
|
||||
{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
||||
NM_SETTING_IP6_CONFIG_ADDRESSES,
|
||||
FALSE,
|
||||
ip6_addr_parser },
|
||||
ip_addr_parser },
|
||||
{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
||||
NM_SETTING_IP4_CONFIG_ROUTES,
|
||||
FALSE,
|
||||
ip4_route_parser },
|
||||
ip_route_parser },
|
||||
{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
||||
NM_SETTING_IP6_CONFIG_ROUTES,
|
||||
FALSE,
|
||||
ip6_route_parser },
|
||||
ip_route_parser },
|
||||
{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
||||
NM_SETTING_IP4_CONFIG_DNS,
|
||||
FALSE,
|
||||
|
|
@ -1330,6 +1212,8 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
|
|||
g_clear_error (&verify_error);
|
||||
g_object_unref (connection);
|
||||
connection = NULL;
|
||||
g_warning ("Connection failed to verify: %s",
|
||||
verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)");
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -115,18 +115,16 @@ write_ip4_values (GKeyFile *file,
|
|||
guint32 addr1_pos,
|
||||
guint32 addr2_pos)
|
||||
{
|
||||
char **list = NULL;
|
||||
GString *output;
|
||||
int i, j;
|
||||
|
||||
list = g_new (char *, tuple_len);
|
||||
|
||||
for (i = 0, j = 0; i < array->len; i++, j++) {
|
||||
GArray *tuple = g_ptr_array_index (array, i);
|
||||
gboolean success = TRUE;
|
||||
char *key_name;
|
||||
int k;
|
||||
|
||||
memset (list, 0, tuple_len * sizeof (char *));
|
||||
output = g_string_new ("");
|
||||
|
||||
for (k = 0; k < tuple_len; k++) {
|
||||
if (k == addr1_pos || k == addr2_pos) {
|
||||
|
|
@ -141,24 +139,23 @@ write_ip4_values (GKeyFile *file,
|
|||
success = FALSE;
|
||||
break;
|
||||
} else {
|
||||
list[k] = g_strdup (buf);
|
||||
g_string_append_printf (output, "%s%s", k == 0 ? "" : ",", buf);
|
||||
}
|
||||
} else {
|
||||
/* prefix, metric */
|
||||
list[k] = g_strdup_printf ("%d", g_array_index (tuple, guint32, k));
|
||||
g_string_append_printf (output, "%c%d", k == 1 ? '/' : ',', g_array_index (tuple, guint32, k));
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
key_name = g_strdup_printf ("%s%d", key, j + 1);
|
||||
g_key_file_set_string_list (file, setting_name, key_name, (const char **) list, tuple_len);
|
||||
g_key_file_set_string (file, setting_name, key_name, output->str);
|
||||
g_free (key_name);
|
||||
}
|
||||
|
||||
for (k = 0; k < tuple_len; k++)
|
||||
g_free (list[k]);
|
||||
g_string_free (output, TRUE);
|
||||
|
||||
}
|
||||
g_free (list);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -357,7 +354,7 @@ ip6_route_writer (GKeyFile *file,
|
|||
{
|
||||
GPtrArray *array;
|
||||
const char *setting_name = nm_setting_get_name (setting);
|
||||
char *list[3];
|
||||
GString *output;
|
||||
int i, j;
|
||||
|
||||
g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE));
|
||||
|
|
@ -370,36 +367,23 @@ ip6_route_writer (GKeyFile *file,
|
|||
GValueArray *values = g_ptr_array_index (array, i);
|
||||
char *key_name;
|
||||
guint32 int_val;
|
||||
char buf[INET6_ADDRSTRLEN + 1];
|
||||
gboolean is_unspec = FALSE;
|
||||
|
||||
memset (list, 0, sizeof (list));
|
||||
output = g_string_new ("");
|
||||
|
||||
/* Address and prefix */
|
||||
list[0] = ip6_array_to_addr_prefix (values);
|
||||
if (!list[0])
|
||||
continue;
|
||||
|
||||
/* Next Hop */
|
||||
if (!ip6_array_to_addr (values, 2, buf, sizeof (buf), &is_unspec))
|
||||
continue;
|
||||
if (is_unspec)
|
||||
continue;
|
||||
list[1] = g_strdup (buf);
|
||||
/* Address, prefix and next hop*/
|
||||
g_string_append (output, ip6_array_to_addr_prefix (values));
|
||||
|
||||
/* Metric */
|
||||
value = g_value_array_get_nth (values, 3);
|
||||
int_val = g_value_get_uint (value);
|
||||
list[2] = g_strdup_printf ("%d", int_val);
|
||||
g_string_append_printf (output, ",%d", int_val);
|
||||
|
||||
/* Write it out */
|
||||
key_name = g_strdup_printf ("%s%d", key, j++);
|
||||
g_key_file_set_string_list (file, setting_name, key_name, (const char **) list, 3);
|
||||
g_key_file_set_string (file, setting_name, key_name, output->str);
|
||||
g_free (key_name);
|
||||
|
||||
g_free (list[0]);
|
||||
g_free (list[1]);
|
||||
g_free (list[2]);
|
||||
g_string_free (output, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue