diff --git a/clients/cli/common.c b/clients/cli/common.c index 56f28ad7f1..c46f6c27e6 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -395,26 +395,20 @@ finish: /* * nmc_parse_and_build_route: * @family: AF_INET or AF_INET6 - * @first: the route destination in the form of "address/prefix" - (/prefix is optional) - * @second: (allow-none): next hop address, if third is not NULL. Otherwise it could be - either next hop address or metric. (It can be NULL when @third is NULL). - * @third: (allow-none): route metric + * @str: route string to be parsed * @error: location to store GError * - * Parse route from strings and return an #NMIPRoute + * Parse route from string and return an #NMIPRoute * - * Returns: %TRUE on success, %FALSE on failure + * Returns: a new #NMIPRoute or %NULL on error */ NMIPRoute * nmc_parse_and_build_route (int family, - const char *first, - const char *second, - const char *third, + const char *str, GError **error) { int max_prefix = (family == AF_INET) ? 32 : 128; - char *dest = NULL, *plen = NULL; + char *plen = NULL; const char *next_hop = NULL; const char *canon_dest; long int prefix = max_prefix; @@ -423,13 +417,27 @@ nmc_parse_and_build_route (int family, gboolean success = FALSE; GError *local = NULL; gint64 metric = -1; + guint i, len; + gs_strfreev char **routev = NULL; + gs_free char *value = NULL; + gs_free char *dest = NULL; + gs_unref_hashtable GHashTable *attrs = NULL; + GHashTable *tmp_attrs; g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); - g_return_val_if_fail (first != NULL, FALSE); - g_return_val_if_fail (second || !third, FALSE); + g_return_val_if_fail (str, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - dest = g_strdup (first); + value = g_strdup (str); + routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); + len = g_strv_length (routev); + if (len < 1) { + g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric] [attr=val] [attr=val])"), + str); + goto finish; + } + + dest = g_strdup (routev[0]); plen = strchr (dest, '/'); /* prefix delimiter */ if (plen) *plen++ = '\0'; @@ -443,26 +451,50 @@ nmc_parse_and_build_route (int family, } } - if (second) { - if (third || nm_utils_ipaddr_valid (family, second)) - next_hop = second; - else { - /* 'second' can be a metric */ - if (!nmc_string_to_uint (second, TRUE, 0, G_MAXUINT32, &tmp_ulong)) { - g_set_error (error, 1, 0, _("the second component of route ('%s') is neither " - "a next hop address nor a metric"), second); + for (i = 1; i < len; i++) { + if (nm_utils_ipaddr_valid (family, routev[i])) { + if (metric != -1 || attrs) { + g_set_error (error, 1, 0, _("the next hop ('%s') must be first"), routev[i]); + goto finish; + } + next_hop = routev[i]; + } else if (nmc_string_to_uint (routev[i], TRUE, 0, G_MAXUINT32, &tmp_ulong)) { + if (attrs) { + g_set_error (error, 1, 0, _("the metric ('%s') must be before attributes"), routev[i]); goto finish; } metric = tmp_ulong; - } - } + } else if (strchr (routev[i], '=')) { + GHashTableIter iter; + char *iter_key; + GVariant *iter_value; - if (third) { - if (!nmc_string_to_uint (third, TRUE, 0, G_MAXUINT32, &tmp_ulong)) { - g_set_error (error, 1, 0, _("invalid metric '%s'"), third); + tmp_attrs = nm_utils_parse_variant_attributes (routev[i], ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec(), + error); + if (!tmp_attrs) { + g_prefix_error (error, "invalid option '%s': ", routev[i]); + goto finish; + } + + if (!attrs) + attrs = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_iter_init (&iter, tmp_attrs); + while (g_hash_table_iter_next (&iter, (gpointer *) &iter_key, (gpointer *) &iter_value)) { + if (!nm_ip_route_attribute_validate (iter_key, iter_value, family, NULL, error)) { + g_prefix_error (error, "%s: ", iter_key); + g_hash_table_unref (tmp_attrs); + goto finish; + } + g_hash_table_insert (attrs, iter_key, iter_value); + g_hash_table_iter_steal (&iter); + } + g_hash_table_unref (tmp_attrs); + } else { + g_set_error (error, 1, 0, _("unrecognized option '%s'"), routev[i]); goto finish; } - metric = tmp_ulong; } route = nm_ip_route_new (family, dest, prefix, next_hop, metric, &local); @@ -485,10 +517,19 @@ nmc_parse_and_build_route (int family, goto finish; } + if (attrs) { + GHashTableIter iter; + char *name; + GVariant *variant; + + g_hash_table_iter_init (&iter, attrs); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) + nm_ip_route_set_attribute (route, name, variant); + } + success = TRUE; finish: - g_free (dest); return route; } diff --git a/clients/cli/common.h b/clients/cli/common.h index 4209136350..3a598f631c 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -31,7 +31,7 @@ gboolean print_dhcp4_config (NMDhcpConfig *dhcp4, NmCli *nmc, const char *group_ gboolean print_dhcp6_config (NMDhcpConfig *dhcp6, NmCli *nmc, const char *group_prefix, const char *one_field); NMIPAddress *nmc_parse_and_build_address (int family, const char *ip_str, GError **error); -NMIPRoute *nmc_parse_and_build_route (int family, const char *first, const char *second, const char *third, GError **error); +NMIPRoute *nmc_parse_and_build_route (int family, const char *str, GError **error); const char * nmc_device_state_to_string (NMDeviceState state); const char * nmc_device_reason_to_string (NMDeviceStateReason reason); diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 047e50f616..ffa3e89c20 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -3470,29 +3470,6 @@ _parse_ip_address (int family, const char *address, GError **error) return ipaddr; } -static NMIPRoute * -_parse_ip_route (int family, const char *route, GError **error) -{ - char *value = g_strdup (route); - char **routev; - guint len; - NMIPRoute *iproute = NULL; - - routev = nmc_strsplit_set (g_strstrip (value), " \t", 0); - len = g_strv_length (routev); - if (len < 1 || len > 3) { - g_set_error (error, 1, 0, _("'%s' is not valid (the format is: ip[/prefix] [next-hop] [metric])"), - route); - goto finish; - } - iproute = nmc_parse_and_build_route (family, routev[0], routev[1], len >= 2 ? routev[2] : NULL, error); - -finish: - g_free (value); - g_strfreev (routev); - return iproute; -} - DEFINE_GETTER (nmc_property_ipv4_get_method, NM_SETTING_IP_CONFIG_METHOD) DEFINE_GETTER (nmc_property_ipv4_get_dns, NM_SETTING_IP_CONFIG_DNS) DEFINE_GETTER (nmc_property_ipv4_get_dns_search, NM_SETTING_IP_CONFIG_DNS_SEARCH) @@ -3536,8 +3513,21 @@ nmc_property_ipvx_get_routes (NMSetting *setting, NmcPropertyGetType get_type) num_routes = nm_setting_ip_config_get_num_routes (s_ip); for (i = 0; i < num_routes; i++) { + gs_free char *attr_str = NULL; + gs_strfreev char **attr_names = NULL; + gs_unref_hashtable GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); + int j; + route = nm_setting_ip_config_get_route (s_ip, i); + attr_names = nm_ip_route_get_attribute_names (route); + for (j = 0; attr_names && attr_names[j]; j++) { + g_hash_table_insert (hash, attr_names[j], + nm_ip_route_get_attribute (route, attr_names[j])); + } + + attr_str = nm_utils_format_variant_attributes (hash, ' ', '='); + if (get_type == NMC_PROPERTY_GET_PARSABLE) { if (printable->len > 0) g_string_append (printable, ", "); @@ -3550,7 +3540,10 @@ nmc_property_ipvx_get_routes (NMSetting *setting, NmcPropertyGetType get_type) g_string_append_printf (printable, " %s", nm_ip_route_get_next_hop (route)); if (nm_ip_route_get_metric (route) != -1) g_string_append_printf (printable, " %u", (guint32) nm_ip_route_get_metric (route)); + if (attr_str) + g_string_append_printf (printable, " %s", attr_str); } else { + if (printable->len > 0) g_string_append (printable, "; "); @@ -3567,6 +3560,8 @@ nmc_property_ipvx_get_routes (NMSetting *setting, NmcPropertyGetType get_type) if (nm_ip_route_get_metric (route) != -1) g_string_append_printf (printable, ", mt = %u", (guint32) nm_ip_route_get_metric (route)); + if (attr_str) + g_string_append_printf (printable, " %s", attr_str); g_string_append (printable, " }"); } @@ -3858,7 +3853,7 @@ nmc_property_ipv4_set_gateway (NMSetting *setting, const char *prop, const char static NMIPRoute * _parse_ipv4_route (const char *route, GError **error) { - return _parse_ip_route (AF_INET, route, error); + return nmc_parse_and_build_route (AF_INET, route, error); } static gboolean @@ -4201,7 +4196,7 @@ nmc_property_ipv6_set_gateway (NMSetting *setting, const char *prop, const char static NMIPRoute * _parse_ipv6_route (const char *route, GError **error) { - return _parse_ip_route (AF_INET6, route, error); + return nmc_parse_and_build_route (AF_INET6, route, error); } static gboolean diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index c071264d32..b955dbf617 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -428,6 +428,31 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info, return result; } +static void +fill_route_attributes (GKeyFile *kf, NMIPRoute *route, const char *setting, const char *key, int family) +{ + gs_free char *value = NULL; + gs_unref_hashtable GHashTable *hash = NULL; + GHashTableIter iter; + char *name; + GVariant *variant; + + value = nm_keyfile_plugin_kf_get_string (kf, setting, key, NULL); + if (!value || !value[0]) + return; + + hash = nm_utils_parse_variant_attributes (value, ',', '=', TRUE, + nm_ip_route_get_variant_attribute_spec (), + NULL); + if (hash) { + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) { + if (nm_ip_route_attribute_validate (name, variant, family, NULL, NULL)) + nm_ip_route_set_attribute (route, name, g_variant_ref (variant)); + } + } +} + static void ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { @@ -454,6 +479,7 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c for (key_basename = key_names; *key_basename; key_basename++) { char *key_name; gpointer item; + char options_key[128]; /* -1 means no suffix */ if (i >= 0) @@ -463,6 +489,11 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c item = read_one_ip_address_or_route (info, key, setting_name, key_name, ipv6, routes, gateway ? NULL : &gateway, setting); + if (item && routes) { + nm_sprintf_buf (options_key, "%s_options", key_name); + fill_route_attributes (info->keyfile, item, setting_name, options_key, ipv6 ? AF_INET6 : AF_INET); + } + g_free (key_name); if (info->error) { diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index 21aeca2fa8..13a29ccdcc 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -138,7 +138,7 @@ write_ip_values (GKeyFile *file, int family, i; const char *addr, *gw; guint32 plen, metric; - char key_name[30], *key_name_idx; + char key_name[64], *key_name_idx; if (!array->len) return; @@ -188,6 +188,23 @@ write_ip_values (GKeyFile *file, sprintf (key_name_idx, "%d", i + 1); nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str); + + if (is_route) { + gs_free char *attributes = NULL; + gs_strfreev char **names = NULL; + gs_unref_hashtable GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); + int j; + + names = nm_ip_route_get_attribute_names (array->pdata[i]); + for (j = 0; names && names[j]; j++) + g_hash_table_insert (hash, names[j], nm_ip_route_get_attribute (array->pdata[i], names[j])); + + attributes = nm_utils_format_variant_attributes (hash, ',', '='); + if (attributes) { + g_strlcat (key_name, "_options", sizeof (key_name)); + nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, attributes); + } + } } g_string_free (output, TRUE); } diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 09c5798a25..6a42133c1e 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -1182,6 +1182,157 @@ nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value) g_hash_table_remove (route->attributes, name); } +#define ATTR_SPEC_PTR(name, type, v4, v6, str_type) \ + &(NMVariantAttributeSpec) { name, type, v4, v6, str_type } + +static const NMVariantAttributeSpec * const ip_route_attribute_spec[] = { + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a'), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_SRC, G_VARIANT_TYPE_STRING, FALSE, TRUE, 'p'), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS, G_VARIANT_TYPE_BYTE, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_CWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_MTU, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + NULL, +}; + +/** + * nm_ip_route_get_variant_attribute_spec: + * + * Returns: the specifiers for route attributes + * + * Since: 1.8 + */ +const NMVariantAttributeSpec *const * +nm_ip_route_get_variant_attribute_spec (void) +{ + return ip_route_attribute_spec; +} + +/** + * nm_ip_route_attribute_validate: + * @name: the attribute name + * @value: the attribute value + * @family: IP address family of the route + * @known: (out): on return, whether the attribute name is a known one + * @error: (allow-none): return location for a #GError, or %NULL + * + * Validates a route attribute, i.e. checks that the attribute is a known one + * and the value is of the correct type and well-formed. + * + * Returns: %TRUE if the attribute is valid, %FALSE otherwise + * + * Since: 1.8 + */ +gboolean +nm_ip_route_attribute_validate (const char *name, + GVariant *value, + int family, + gboolean *known, + GError **error) +{ + const NMVariantAttributeSpec *const *iter; + const NMVariantAttributeSpec *spec = NULL; + + g_return_val_if_fail (name, FALSE); + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + for (iter = ip_route_attribute_spec; *iter; iter++) { + if (nm_streq (name, (*iter)->name)) { + spec = *iter; + break; + } + } + + if (!spec) { + NM_SET_OUT (known, FALSE); + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("unknown attribute")); + return FALSE; + } + + NM_SET_OUT (known, TRUE); + + if (!g_variant_is_of_type (value, spec->type)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid attribute type'%s'"), + g_variant_get_type_string (value)); + return FALSE; + } + + if ( (family == AF_INET && !spec->v4) + || (family == AF_INET6 && !spec->v6)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("attribute is not valid for a IPv4 route") : + _("attribute is not valid for a IPv6 route")); + return FALSE; + } + + if (spec->type == G_VARIANT_TYPE_STRING) { + const char *string = g_variant_get_string (value, NULL); + gs_free char *string_free = NULL; + char *sep; + + switch (spec->str_type) { + case 'a': /* IP address */ + if (!nm_utils_ipaddr_valid (family, string)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("'%s' is not a valid IPv4 address") : + _("'%s' is not a valid IPv6 address"), + string); + return FALSE; + } + break; + case 'p': /* IP address + optional prefix */ + string_free = g_strdup (string); + sep = strchr (string_free, '/'); + if (sep) { + *sep = 0; + if (_nm_utils_ascii_str_to_int64 (sep + 1, 10, 1, family == AF_INET ? 32 : 128, -1) < 0) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid prefix %s"), sep + 1); + return FALSE; + } + } + if (!nm_utils_ipaddr_valid (family, string_free)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("'%s' is not a valid IPv4 address") : + _("'%s' is not a valid IPv6 address"), + string_free); + return FALSE; + } + break; + default: + break; + } + } + + return TRUE; +} + /*****************************************************************************/ G_DEFINE_ABSTRACT_TYPE (NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING) diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h index b18d6a9439..a4ebdc3268 100644 --- a/libnm-core/nm-setting-ip-config.h +++ b/libnm-core/nm-setting-ip-config.h @@ -28,6 +28,7 @@ #endif #include "nm-setting.h" +#include "nm-utils.h" G_BEGIN_DECLS @@ -121,7 +122,28 @@ GVariant *nm_ip_route_get_attribute (NMIPRoute *route, void nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value); +NM_AVAILABLE_IN_1_8 +const NMVariantAttributeSpec *const *nm_ip_route_get_variant_attribute_spec (void); +NM_AVAILABLE_IN_1_8 +gboolean nm_ip_route_attribute_validate (const char *name, + GVariant *value, + int family, + gboolean *known, + GError **error); +#define NM_IP_ROUTE_ATTRIBUTE_PREF_SRC "pref-src" +#define NM_IP_ROUTE_ATTRIBUTE_SRC "src" +#define NM_IP_ROUTE_ATTRIBUTE_TOS "tos" +#define NM_IP_ROUTE_ATTRIBUTE_WINDOW "window" +#define NM_IP_ROUTE_ATTRIBUTE_CWND "cwnd" +#define NM_IP_ROUTE_ATTRIBUTE_INITCWND "initcwnd" +#define NM_IP_ROUTE_ATTRIBUTE_INITRWND "initrwnd" +#define NM_IP_ROUTE_ATTRIBUTE_MTU "mtu" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW "lock-window" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND "lock-cwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND "lock-initcwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND "lock-initrwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU "lock-mtu" #define NM_TYPE_SETTING_IP_CONFIG (nm_setting_ip_config_get_type ()) #define NM_SETTING_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfig)) diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index 91a4a3f50c..3a3661613f 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -615,7 +615,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class) * ---end--- * ---ifcfg-rh--- * property: routes - * variable: ADDRESS1, NETMASK1, GATEWAY1, METRIC1, ... + * variable: ADDRESS1, NETMASK1, GATEWAY1, METRIC1, OPTIONS1, ... * description: List of static routes. They are not stored in ifcfg-* file, * but in route-* file instead. * ---end--- diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index fab3803808..03c8790764 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -28,6 +28,14 @@ #include "nm-setting-private.h" #include "nm-setting-ip-config.h" +struct _NMVariantAttributeSpec { + char *name; + const GVariantType *type; + bool v4:1; + bool v6:1; + char str_type; +}; + gboolean _nm_utils_string_slist_validate (GSList *list, const char **valid_values); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 9fc66c8753..f57e2bb154 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -4697,6 +4697,261 @@ _nm_utils_team_config_equal (const char *conf1, } #endif +static char * +attribute_escape (const char *src, char c1, char c2) +{ + char *ret, *dest; + + dest = ret = malloc (strlen (src) * 2 + 1); + + while (*src) { + if (*src == c1 || *src == c2 || *src == '\\') + *dest++ = '\\'; + *dest++ = *src++; + } + *dest++ = '\0'; + + return ret; +} + +static char * +attribute_unescape (const char *start, const char *end) +{ + char *ret, *dest; + + nm_assert (start <= end); + dest = ret = g_malloc (end - start + 1); + + for (; start < end && *start; start++) { + if (*start == '\\') { + start++; + if (!*start) + break; + } + *dest++ = *start; + } + *dest = '\0'; + + return ret; +} + +/** + * nm_utils_parse_variant_attributes: + * @string: the input string + * @attr_separator: the attribute separator character + * @key_value_separator: character separating key and values + * @ignore_unknown: whether unknown attributes should be ignored + * @spec: the attribute format specifiers + * @error: (out) (allow-none): location to store the error on failure + * + * Parse attributes from a string. + * + * Returns: (transfer full): a #GHashTable mapping attribute names to #GVariant values. + * + * Since: 1.8 + */ +GHashTable * +nm_utils_parse_variant_attributes (const char *string, + char attr_separator, + char key_value_separator, + gboolean ignore_unknown, + const NMVariantAttributeSpec *const *spec, + GError **error) +{ + gs_unref_hashtable GHashTable *ht = NULL; + const char *ptr = string, *start = NULL, *sep; + GVariant *variant; + const NMVariantAttributeSpec * const *s; + + g_return_val_if_fail (string, NULL); + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref); + + while (TRUE) { + gs_free char *name = NULL, *value = NULL; + + if (!start) + start = ptr; + if (*ptr == '\\') { + ptr++; + if (!*ptr) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("unterminated escape sequence")); + return NULL; + } + goto next; + } + if (*ptr == attr_separator || *ptr == '\0') { + if (ptr == start) { + /* multiple separators */ + start = NULL; + goto next; + } + + /* Find the key-value separator */ + for (sep = start; sep != ptr; sep++) { + if (*sep == '\\') { + sep++; + if (!*sep) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("unterminated escape sequence")); + return NULL; + } + } + if (*sep == key_value_separator) + break; + } + + if (*sep != key_value_separator) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("missing key-value separator '%c'"), key_value_separator); + return NULL; + } + + name = attribute_unescape (start, sep); + value = attribute_unescape (sep + 1, ptr); + + for (s = spec; *s; s++) { + if (nm_streq (name, (*s)->name)) + break; + } + + if (!*s) { + if (ignore_unknown) + goto next; + else { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("unknown attribute '%s'"), name); + return NULL; + } + } + + if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_UINT32)) { + gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); + + if (num == -1) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("invalid uint32 value '%s' for attribute '%s'"), value, name); + return NULL; + } + variant = g_variant_new_uint32 (num); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BYTE)) { + gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT8, -1); + + if (num == -1) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("invalid uint8 value '%s' for attribute '%s'"), value, name); + return NULL; + } + variant = g_variant_new_byte ((guchar) num); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BOOLEAN)) { + gboolean b; + + if (nm_streq (value, "true")) + b = TRUE; + else if (nm_streq (value, "false")) + b = FALSE; + else { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("invalid boolean value '%s' for attribute '%s'"), value, name); + return NULL; + } + variant = g_variant_new_boolean (b); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_STRING)) { + variant = g_variant_new_take_string (g_steal_pointer (&value)); + } else { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("unsupported attribute '%s' of type '%s'"), name, + (char *) (*s)->type); + return NULL; + } + + g_hash_table_insert (ht, g_steal_pointer (&name), variant); + start = NULL; + } +next: + if (*ptr == '\0') + break; + ptr++; + } + + return g_steal_pointer (&ht); +} + +/* + * nm_utils_format_variant_attributes: + * @attributes: a #GHashTable mapping attribute names to #GVariant values + * @attr_separator: the attribute separator character + * @key_value_separator: character separating key and values + * + * Format attributes to a string. + * + * Returns: (transfer full): the string representing attributes, or %NULL + * in case there are no attributes + * + * Since: 1.8 + */ +char * +nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator) +{ + GString *str = NULL; + GVariant *variant; + char sep = 0; + const char *name, *value; + char *escaped; + char buf[64]; + gs_free_list GList *keys = NULL; + GList *iter; + + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + + if (!attributes || !g_hash_table_size (attributes)) + return NULL; + + keys = g_list_sort (g_hash_table_get_keys (attributes), (GCompareFunc) g_strcmp0); + str = g_string_new (""); + + for (iter = keys; iter; iter = g_list_next (iter)) { + name = iter->data; + variant = g_hash_table_lookup (attributes, name); + value = NULL; + + if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)) + value = nm_sprintf_buf (buf, "%u", g_variant_get_uint32 (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) + value = nm_sprintf_buf (buf, "%hhu", g_variant_get_byte (variant)); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) + value = g_variant_get_boolean (variant) ? "true" : "false"; + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) + value = g_variant_get_string (variant, NULL); + else + continue; + + if (sep) + g_string_append_c (str, sep); + + escaped = attribute_escape (name, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + g_string_append_c (str, key_value_separator); + + escaped = attribute_escape (value, attr_separator, key_value_separator); + g_string_append (str, escaped); + g_free (escaped); + + sep = attr_separator; + } + + return g_string_free (str, FALSE); +} + /*****************************************************************************/ /** diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h index caf2e62e02..77fe18a906 100644 --- a/libnm-core/nm-utils.h +++ b/libnm-core/nm-utils.h @@ -38,6 +38,8 @@ G_BEGIN_DECLS +typedef struct _NMVariantAttributeSpec NMVariantAttributeSpec; + /* SSID helpers */ gboolean nm_utils_is_empty_ssid (const guint8 *ssid, gsize len); const char *nm_utils_escape_ssid (const guint8 *ssid, gsize len); @@ -215,6 +217,19 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to); NM_AVAILABLE_IN_1_6 guint nm_utils_version (void); +NM_AVAILABLE_IN_1_8 +GHashTable * nm_utils_parse_variant_attributes (const char *string, + char attr_separator, + char key_value_separator, + gboolean ignore_unknown, + const NMVariantAttributeSpec *const *spec, + GError **error); + +NM_AVAILABLE_IN_1_8 +char * nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 073850b4e5..9cdbf2626a 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -642,6 +642,47 @@ test_setting_ip4_config_address_data (void) g_object_unref (conn); } +static void +test_setting_ip_route_attributes (void) +{ + GVariant *variant; + gboolean res, known; + +#define TEST_ATTR(name, type, value, family, exp_res, exp_known) \ + variant = g_variant_new_ ## type (value); \ + res = nm_ip_route_attribute_validate (name, variant, family, &known, NULL); \ + g_assert (res == exp_res); \ + g_assert (known == exp_known); \ + g_variant_unref (variant); + + TEST_ATTR ("foo", uint32, 12, AF_INET, FALSE, FALSE); + + TEST_ATTR ("tos", byte, 127, AF_INET, TRUE, TRUE); + TEST_ATTR ("tos", string, "0x28", AF_INET, FALSE, TRUE); + + TEST_ATTR ("cwnd", uint32, 10, AF_INET, TRUE, TRUE); + TEST_ATTR ("cwnd", string, "11", AF_INET, FALSE, TRUE); + + TEST_ATTR ("lock-mtu", boolean, TRUE, AF_INET, TRUE, TRUE); + TEST_ATTR ("lock-mtu", uint32, 1, AF_INET, FALSE, TRUE); + + TEST_ATTR ("src", string, "fd01::1", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/64", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/128", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/129", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "fd01::1/a", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "abc/64", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "1.2.3.4", AF_INET, FALSE, TRUE); + TEST_ATTR ("src", string, "1.2.3.4", AF_INET6, FALSE, TRUE); + + TEST_ATTR ("pref-src", string, "1.2.3.4", AF_INET, TRUE, TRUE); + TEST_ATTR ("pref-src", string, "1.2.3.4", AF_INET6, FALSE, TRUE); + TEST_ATTR ("pref-src", string, "1.2.3.0/24", AF_INET, FALSE, TRUE); + TEST_ATTR ("pref-src", string, "fd01::12", AF_INET6, TRUE, TRUE); + +#undef TEST_ATTR +} + static void test_setting_gsm_apn_spaces (void) { @@ -5412,6 +5453,97 @@ test_nm_in_strset (void) #undef _ASSERT } +static void +test_route_attributes_parse (void) +{ + GHashTable *ht; + GError *error = NULL; + GVariant *variant; + + ht = nm_utils_parse_variant_attributes ("mtu=1400 pref-src=1.2.3.4 cwnd=14", + ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert_no_error (error); + g_assert (ht); + g_hash_table_unref (ht); + + ht = nm_utils_parse_variant_attributes ("mtu=1400 pref-src=1.2.3.4 cwnd=14 \\", + ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED); + g_assert (!ht); + g_clear_error (&error); + + ht = nm_utils_parse_variant_attributes ("mtu.1400 pref-src.1\\.2\\.3\\.4 ", + ' ', '.', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert (ht); + g_assert_no_error (error); + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_MTU); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpuint (g_variant_get_uint32 (variant), ==, 1400); + + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "1.2.3.4"); + g_hash_table_unref (ht); + + ht = nm_utils_parse_variant_attributes ("src:fd01\\:\\:42\\/64/initrwnd:21", + '/', ':', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert (ht); + g_assert_no_error (error); + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_INITRWND); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpuint (g_variant_get_uint32 (variant), ==, 21); + + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_SRC); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "fd01::42/64"); + g_hash_table_unref (ht); +} + +static void +test_route_attributes_format (void) +{ + gs_unref_hashtable GHashTable *ht = NULL; + char *str; + + ht = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) g_variant_unref); + + str = nm_utils_format_variant_attributes (NULL, ' ', '='); + g_assert_cmpstr (str, ==, NULL); + + str = nm_utils_format_variant_attributes (ht, ' ', '='); + g_assert_cmpstr (str, ==, NULL); + + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (5000)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_INITRWND, g_variant_new_uint32 (20)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, g_variant_new_boolean (TRUE)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, g_variant_new_string ("aaaa:bbbb::1")); + str = nm_utils_format_variant_attributes (ht, ' ', '='); + g_assert_cmpstr (str, ==, "initrwnd=20 lock-mtu=true mtu=5000 pref-src=aaaa:bbbb::1"); + g_hash_table_remove_all (ht); + g_free (str); + + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (30000)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_INITCWND, g_variant_new_uint32 (21)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("aaaa:bbbb:cccc:dddd::/64")); + str = nm_utils_format_variant_attributes (ht, '/', ':'); + g_assert_cmpstr (str, ==, "initcwnd:21/src:aaaa\\:bbbb\\:cccc\\:dddd\\:\\:\\/64/window:30000"); + g_hash_table_remove_all (ht); + g_free (str); +} + /*****************************************************************************/ static gboolean @@ -5467,6 +5599,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_vpn_modify_during_foreach", test_setting_vpn_modify_during_foreach); g_test_add_func ("/core/general/test_setting_ip4_config_labels", test_setting_ip4_config_labels); g_test_add_func ("/core/general/test_setting_ip4_config_address_data", test_setting_ip4_config_address_data); + g_test_add_func ("/core/general/test_setting_ip_route_attributes", test_setting_ip_route_attributes); g_test_add_func ("/core/general/test_setting_gsm_apn_spaces", test_setting_gsm_apn_spaces); g_test_add_func ("/core/general/test_setting_gsm_apn_bad_chars", test_setting_gsm_apn_bad_chars); g_test_add_func ("/core/general/test_setting_gsm_apn_underscore", test_setting_gsm_apn_underscore); @@ -5576,6 +5709,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_nm_utils_team_config_equal", test_nm_utils_team_config_equal); g_test_add_func ("/core/general/test_nm_utils_enum", test_nm_utils_enum); g_test_add_func ("/core/general/nm-set-out", test_nm_set_out); + g_test_add_func ("/core/general/route_attributes/parse", test_route_attributes_parse); + g_test_add_func ("/core/general/route_attributes/format", test_route_attributes_format); return g_test_run (); } diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 2d6f739e6e..4b3d02fdfa 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1148,6 +1148,8 @@ libnm_1_8_0 { global: nm_connection_get_setting_dummy; nm_device_dummy_get_type; + nm_ip_route_get_variant_attribute_spec; + nm_ip_route_attribute_validate; nm_setting_802_1x_auth_flags_get_type; nm_setting_802_1x_get_auth_timeout; nm_setting_802_1x_get_ca_cert_password; @@ -1163,4 +1165,6 @@ global: nm_setting_dummy_get_type; nm_setting_dummy_new; nm_setting_gsm_get_mtu; + nm_utils_format_variant_attributes; + nm_utils_parse_variant_attributes; } libnm_1_6_0; diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index 579504dcb7..b7d45b262c 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1291,6 +1291,50 @@ nmtst_setting_ip_config_add_route (NMSettingIPConfig *s_ip, g_assert (nm_setting_ip_config_add_route (s_ip, route)); nm_ip_route_unref (route); } + +inline static void +nmtst_assert_route_attribute_string (NMIPRoute *route, const char *name, const char *value) +{ + GVariant *variant; + + variant = nm_ip_route_get_attribute (route, name); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, value); +} + +inline static void +nmtst_assert_route_attribute_byte (NMIPRoute *route, const char *name, guchar value) +{ + GVariant *variant; + + variant = nm_ip_route_get_attribute (route, name); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)); + g_assert_cmpint (g_variant_get_byte (variant), ==, value); +} + +inline static void +nmtst_assert_route_attribute_uint32 (NMIPRoute *route, const char *name, guint32 value) +{ + GVariant *variant; + + variant = nm_ip_route_get_attribute (route, name); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpint (g_variant_get_uint32 (variant), ==, value); +} + +inline static void +nmtst_assert_route_attribute_boolean (NMIPRoute *route, const char *name, gboolean value) +{ + GVariant *variant; + + variant = nm_ip_route_get_attribute (route, name); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)); + g_assert_cmpint (g_variant_get_boolean (variant), ==, value); +} #endif /* NM_SETTING_IP_CONFIG_H */ #if (defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__)) || (defined(NM_CONNECTION_H)) @@ -1661,6 +1705,7 @@ nmtst_assert_hwaddr_equals (gconstpointer hwaddr1, gssize hwaddr1_len, const cha nmtst_assert_hwaddr_equals (hwaddr1, hwaddr1_len, expected, __FILE__, __LINE__) #endif + #if defined(__NM_SIMPLE_CONNECTION_H__) && defined(__NM_SETTING_CONNECTION_H__) && defined(__NM_KEYFILE_INTERNAL_H__) static inline NMConnection * diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 5944654cff..71ca919ec6 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -301,24 +301,21 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g return FALSE; if (vtable->vt->is_ip4) { - success = nm_platform_ip4_route_add (priv->platform, - entry->route.rx.ifindex, - entry->route.rx.rt_source, - 0, - 0, - entry->route.r4.gateway, - 0, - entry->effective_metric, - entry->route.rx.mss); + NMPlatformIP4Route rt = entry->route.r4; + + rt.network = 0; + rt.plen = 0; + rt.metric = entry->effective_metric; + + success = nm_platform_ip4_route_add (priv->platform, &rt); } else { - success = nm_platform_ip6_route_add (priv->platform, - entry->route.rx.ifindex, - entry->route.rx.rt_source, - in6addr_any, - 0, - entry->route.r6.gateway, - entry->effective_metric, - entry->route.rx.mss); + NMPlatformIP6Route rt = entry->route.r6; + + rt.network = in6addr_any; + rt.plen = 0; + rt.metric = entry->effective_metric; + + success = nm_platform_ip6_route_add (priv->platform, &rt); } if (!success) { _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 7497ab914c..b3d8ee478d 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -412,6 +412,38 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu return TRUE; } +static void +merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r) +{ + GVariant *variant; + in_addr_t addr; + +#define GET_ATTR(name, field, variant_type, type) \ + variant = nm_ip_route_get_attribute (s_route, name); \ + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ + r->field = g_variant_get_ ## type (variant); + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, tos, BYTE, byte); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, window, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, cwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, initcwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, initrwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU, mtu, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, lock_window, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, lock_cwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, lock_initcwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, lock_initrwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, lock_mtu, BOOLEAN, boolean); + + if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + if (inet_pton (AF_INET, g_variant_get_string (variant, NULL), &addr) == 1) + r->pref_src = addr; + } + +#undef GET_ATTR +} + void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) { @@ -492,6 +524,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu route.metric = nm_ip_route_get_metric (s_route); route.rt_source = NM_IP_CONFIG_SOURCE_USER; + merge_route_attributes (s_route, &route); nm_ip4_config_add_route (config, &route); } diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index e83e30a131..070ce06937 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -416,6 +416,57 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_fu return success; } +static void +merge_route_attributes (NMIPRoute *s_route, NMPlatformIP6Route *r) +{ + GVariant *variant; + struct in6_addr addr; + +#define GET_ATTR(name, field, variant_type, type) \ + variant = nm_ip_route_get_attribute (s_route, name); \ + if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \ + r->field = g_variant_get_ ## type (variant); + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, tos, BYTE, byte); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, window, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, cwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, initcwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, initrwnd, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU, mtu, UINT32, uint32); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, lock_window, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, lock_cwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, lock_initcwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, lock_initrwnd, BOOLEAN, boolean); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, lock_mtu, BOOLEAN, boolean); + + + if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + if (inet_pton (AF_INET6, g_variant_get_string (variant, NULL), &addr) == 1) + r->pref_src = addr; + } + + if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + gs_free char *string = NULL; + guint8 plen = 128; + char *sep; + + string = g_variant_dup_string (variant, NULL); + sep = strchr (string, '/'); + if (sep) { + *sep = 0; + plen = _nm_utils_ascii_str_to_int64 (sep + 1, 10, 1, 128, 255); + } + if ( plen <= 128 + && inet_pton (AF_INET6, string, &addr) == 1) { + r->src = addr; + r->src_plen = plen; + } + } +#undef GET_ATTR +} + void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric) { @@ -492,6 +543,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu route.metric = nm_ip_route_get_metric (s_route); route.rt_source = NM_IP_CONFIG_SOURCE_USER; + merge_route_attributes (s_route, &route); nm_ip6_config_add_route (config, &route); } diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h index 546e2361f2..7cd08ce57a 100644 --- a/src/nm-test-utils-core.h +++ b/src/nm-test-utils-core.h @@ -162,7 +162,7 @@ nmtst_platform_ip4_route_full (const char *network, guint plen, const char *gate } static inline NMPlatformIP6Route * -nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway) +nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway, const char *pref_src) { static NMPlatformIP6Route route; @@ -172,6 +172,7 @@ nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway) route.network = *nmtst_inet6_from_string (network); route.plen = plen; route.gateway = *nmtst_inet6_from_string (gateway); + route.pref_src = *nmtst_inet6_from_string (pref_src); return &route; } @@ -181,7 +182,7 @@ nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gate int ifindex, NMIPConfigSource source, guint metric, guint mss) { - NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway); + NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway, NULL); route->ifindex = ifindex; route->rt_source = source; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index b92f56ce21..276ebe85e7 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1225,42 +1225,29 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu } static gboolean -ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - in_addr_t network, guint8 plen, in_addr_t gateway, - in_addr_t pref_src, guint32 metric, guint32 mss) +ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP4Route route; + NMPlatformIP4Route rt = *route; guint i; - guint8 scope; - g_assert (plen <= 32); + rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); + rt.network = nm_utils_ip4_address_clear_host_address (rt.network, rt.plen); + rt.scope_inv = nm_platform_route_scope_inv (rt.gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); - scope = gateway == 0 ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; - - memset (&route, 0, sizeof (route)); - route.ifindex = ifindex; - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source); - route.network = nm_utils_ip4_address_clear_host_address (network, plen); - route.plen = plen; - route.gateway = gateway; - route.metric = metric; - route.mss = mss; - route.scope_inv = nm_platform_route_scope_inv (scope); - - if (gateway) { + if (rt.gateway) { for (i = 0; i < priv->ip4_routes->len; i++) { NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); guint32 gate = ntohl (item->network) >> (32 - item->plen); - guint32 host = ntohl (gateway) >> (32 - item->plen); + guint32 host = ntohl (rt.gateway) >> (32 - item->plen); - if (ifindex == item->ifindex && gate == host) + if (rt.ifindex == item->ifindex && gate == host) break; } if (i == priv->ip4_routes->len) { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - route.ifindex, nm_utils_inet4_ntop (route.network, NULL), route.plen, route.metric); + rt.ifindex, nm_utils_inet4_ntop (rt.network, NULL), rt.plen, rt.metric); return FALSE; } } @@ -1268,65 +1255,58 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, for (i = 0; i < priv->ip4_routes->len; i++) { NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i); - if (item->network != route.network) + if (item->network != rt.network) continue; - if (item->plen != route.plen) + if (item->plen != rt.plen) continue; - if (item->metric != metric) + if (item->metric != rt.metric) continue; - if (item->ifindex != route.ifindex) { + if (item->ifindex != rt.ifindex) { ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); i--; continue; } - memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_CHANGED); + memcpy (item, &rt, sizeof (rt)); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, + rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } - g_array_append_val (priv->ip4_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_ADDED); + g_array_append_val (priv->ip4_routes, rt); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, + rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); return TRUE; } static gboolean -ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - struct in6_addr network, guint8 plen, struct in6_addr gateway, - guint32 metric, guint32 mss) +ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform); - NMPlatformIP6Route route; + NMPlatformIP6Route rt = *route; guint i; - metric = nm_utils_ip6_route_metric_normalize (metric); + rt.metric = nm_utils_ip6_route_metric_normalize (rt.metric); + rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source); + nm_utils_ip6_address_clear_host_address (&rt.network, &rt.network, rt.plen); - memset (&route, 0, sizeof (route)); - route.ifindex = ifindex; - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source); - nm_utils_ip6_address_clear_host_address (&route.network, &network, plen); - route.plen = plen; - route.gateway = gateway; - route.metric = metric; - route.mss = mss; - - if (!IN6_IS_ADDR_UNSPECIFIED(&gateway)) { + if (!IN6_IS_ADDR_UNSPECIFIED (&rt.gateway)) { for (i = 0; i < priv->ip6_routes->len; i++) { NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - guint8 gate_bits = gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); + guint8 gate_bits = rt.gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8); guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8); - if ( ifindex == item->ifindex - && memcmp (&gateway, &item->network, item->plen / 8) == 0 + if ( rt.ifindex == item->ifindex + && memcmp (&rt.gateway, &item->network, item->plen / 8) == 0 && gate_bits == host_bits) break; } if (i == priv->ip6_routes->len) { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - route.ifindex, nm_utils_inet6_ntop (&route.network, NULL), route.plen, route.metric); + rt.ifindex, nm_utils_inet6_ntop (&rt.network, NULL), rt.plen, rt.metric); return FALSE; } } @@ -1334,26 +1314,28 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, for (i = 0; i < priv->ip6_routes->len; i++) { NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i); - if (!IN6_ARE_ADDR_EQUAL (&item->network, &route.network)) + if (!IN6_ARE_ADDR_EQUAL (&item->network, &rt.network)) continue; - if (item->plen != route.plen) + if (item->plen != rt.plen) continue; - if (item->metric != metric) + if (item->metric != rt.metric) continue; - if (item->ifindex != route.ifindex) { + if (item->ifindex != rt.ifindex) { ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric); i--; continue; } - memcpy (item, &route, sizeof (route)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_CHANGED); + memcpy (item, &rt, sizeof (rt)); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, + rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED); return TRUE; } - g_array_append_val (priv->ip6_routes, route); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_ADDED); + g_array_append_val (priv->ip6_routes, rt); + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, + rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED); return TRUE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 3138b171bc..921a70f2a2 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1833,6 +1833,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) NMIPAddr gateway; } nh; guint32 mss; + guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0; guint32 table; if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) @@ -1846,8 +1847,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6)) goto errout; - if ( rtm->rtm_type != RTN_UNICAST - || rtm->rtm_tos != 0) + if (rtm->rtm_type != RTN_UNICAST) goto errout; err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy); @@ -1941,21 +1941,34 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) mss = 0; if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; - int i; + static struct nla_policy rtax_policy[RTAX_MAX + 1] = { + [RTAX_LOCK] = { .type = NLA_U32 }, + [RTAX_ADVMSS] = { .type = NLA_U32 }, + [RTAX_WINDOW] = { .type = NLA_U32 }, + [RTAX_CWND] = { .type = NLA_U32 }, + [RTAX_INITCWND] = { .type = NLA_U32 }, + [RTAX_INITRWND] = { .type = NLA_U32 }, + [RTAX_MTU] = { .type = NLA_U32 }, + }; - err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + err = nla_parse_nested (mtb, RTAX_MAX, tb[RTA_METRICS], rtax_policy); if (err < 0) goto errout; - for (i = 1; i <= RTAX_MAX; i++) { - if (mtb[i]) { - if (i == RTAX_ADVMSS) { - if (nla_len (mtb[i]) >= sizeof (uint32_t)) - mss = nla_get_u32(mtb[i]); - break; - } - } - } + if (mtb[RTAX_LOCK]) + lock = nla_get_u32 (mtb[RTAX_LOCK]); + if (mtb[RTAX_ADVMSS]) + mss = nla_get_u32 (mtb[RTAX_ADVMSS]); + if (mtb[RTAX_WINDOW]) + window = nla_get_u32 (mtb[RTAX_WINDOW]); + if (mtb[RTAX_CWND]) + cwnd = nla_get_u32 (mtb[RTAX_CWND]); + if (mtb[RTAX_INITCWND]) + initcwnd = nla_get_u32 (mtb[RTAX_INITCWND]); + if (mtb[RTAX_INITRWND]) + initrwnd = nla_get_u32 (mtb[RTAX_INITRWND]); + if (mtb[RTAX_MTU]) + mtu = nla_get_u32 (mtb[RTAX_MTU]); } /*****************************************************************/ @@ -1980,12 +1993,31 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (is_v4) obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope); - if (is_v4) { - if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) + if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) { + if (is_v4) memcpy (&obj->ip4_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); + else + memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); + } + + if (!is_v4 && tb[RTA_SRC]) { + _check_addr_or_errout (tb, RTA_SRC, addr_len); + memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + obj->ip6_route.src_plen = rtm->rtm_src_len; } obj->ip_route.mss = mss; + obj->ip_route.window = window; + obj->ip_route.cwnd = cwnd; + obj->ip_route.initcwnd = initcwnd; + obj->ip_route.initrwnd = initrwnd; + obj->ip_route.mtu = mtu; + obj->ip_route.tos = rtm->rtm_tos; + obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW); + obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND); + obj->ip_route.lock_initcwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITCWND); + obj->ip_route.lock_initrwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITRWND); + obj->ip_route.lock_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) { /* we must not straight way reject cloned routes, because we might have cached @@ -2360,19 +2392,28 @@ _nl_msg_new_route (int nlmsg_type, gconstpointer gateway, guint32 metric, guint32 mss, - gconstpointer pref_src) + gconstpointer pref_src, + gconstpointer src, + guint8 src_plen, + guint8 tos, + guint32 window, + guint32 cwnd, + guint32 initcwnd, + guint32 initrwnd, + guint32 mtu, + guint32 lock) { struct nl_msg *msg; struct rtmsg rtmsg = { .rtm_family = family, - .rtm_tos = 0, + .rtm_tos = tos, .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source), .rtm_scope = scope, .rtm_type = RTN_UNICAST, .rtm_flags = 0, .rtm_dst_len = plen, - .rtm_src_len = 0, + .rtm_src_len = src ? src_plen : 0, }; NMIPAddr network_clean; @@ -2394,19 +2435,35 @@ _nl_msg_new_route (int nlmsg_type, nm_utils_ipx_address_clear_host_address (family, &network_clean, network, plen); NLA_PUT (msg, RTA_DST, addr_len, &network_clean); + if (src) + NLA_PUT (msg, RTA_SRC, addr_len, src); + NLA_PUT_U32 (msg, RTA_PRIORITY, metric); if (pref_src) NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); - if (mss > 0) { + if (mss || window || cwnd || initcwnd || initrwnd || mtu || lock) { struct nlattr *metrics; metrics = nla_nest_start (msg, RTA_METRICS); if (!metrics) goto nla_put_failure; - NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); + if (mss) + NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); + if (window) + NLA_PUT_U32 (msg, RTAX_WINDOW, window); + if (cwnd) + NLA_PUT_U32 (msg, RTAX_CWND, cwnd); + if (initcwnd) + NLA_PUT_U32 (msg, RTAX_INITCWND, initcwnd); + if (initrwnd) + NLA_PUT_U32 (msg, RTAX_INITRWND, initrwnd); + if (mtu) + NLA_PUT_U32 (msg, RTAX_MTU, mtu); + if (lock) + NLA_PUT_U32 (msg, RTAX_LOCK, lock); nla_nest_end(msg, metrics); } @@ -5901,53 +5958,85 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags fl return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); } +static guint32 +ip_route_get_lock_flag (NMPlatformIPRoute *route) +{ + return (((guint32) route->lock_window) << RTAX_WINDOW) + | (((guint32) route->lock_cwnd) << RTAX_CWND) + | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) + | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) + | (((guint32) route->lock_mtu) << RTAX_MTU); +} + static gboolean -ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - in_addr_t network, guint8 plen, in_addr_t gateway, - in_addr_t pref_src, guint32 metric, guint32 mss) +ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) { NMPObject obj_id; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + in_addr_t network; + network = nm_utils_ip4_address_clear_host_address (route->network, route->plen); + + /* FIXME: take the scope from route into account */ nlmsg = _nl_msg_new_route (RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, AF_INET, - ifindex, - source, - gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + route->ifindex, + route->rt_source, + route->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, &network, - plen, - &gateway, - metric, - mss, - pref_src ? &pref_src : NULL); + route->plen, + &route->gateway, + route->metric, + route->mss, + route->pref_src ? &route->pref_src : NULL, + NULL, + 0, + route->tos, + route->window, + route->cwnd, + route->initcwnd, + route->initrwnd, + route->mtu, + ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + nmp_object_stackinit_id_ip4_route (&obj_id, route->ifindex, network, route->plen, route->metric); return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean -ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, - struct in6_addr network, guint8 plen, struct in6_addr gateway, - guint32 metric, guint32 mss) +ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) { NMPObject obj_id; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct in6_addr network; + nm_utils_ip6_address_clear_host_address (&network, &route->network, route->plen); + + /* FIXME: take the scope from route into account */ nlmsg = _nl_msg_new_route (RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, AF_INET6, - ifindex, - source, - !IN6_IS_ADDR_UNSPECIFIED (&gateway) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + route->ifindex, + route->rt_source, + IN6_IS_ADDR_UNSPECIFIED (&route->gateway) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, &network, - plen, - &gateway, - metric, - mss, - NULL); + route->plen, + &route->gateway, + route->metric, + route->mss, + !IN6_IS_ADDR_UNSPECIFIED (&route->pref_src) ? &route->pref_src : NULL, + !IN6_IS_ADDR_UNSPECIFIED (&route->src) ? &route->src : NULL, + route->src_plen, + route->tos, + route->window, + route->cwnd, + route->initcwnd, + route->initrwnd, + route->mtu, + ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + nmp_object_stackinit_id_ip6_route (&obj_id, route->ifindex, &network, route->plen, route->metric); return do_add_addrroute (platform, &obj_id, nlmsg); } @@ -6000,7 +6089,16 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p NULL, metric, 0, - NULL); + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); if (!nlmsg) return FALSE; @@ -6026,7 +6124,16 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu NULL, metric, 0, - NULL); + NULL, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); if (!nlmsg) return FALSE; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index b72534420e..e711f642d6 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3109,14 +3109,7 @@ nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRoute /** * nm_platform_ip4_route_add: * @self: - * @ifindex: - * @source: - * network: - * plen: - * gateway: - * pref_src: - * metric: - * mss: + * @route: * * For kernel, a gateway can be either explicitly set or left * at zero (0.0.0.0). In addition, there is the scope of the IPv4 @@ -3137,57 +3130,29 @@ nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRoute * Returns: %TRUE in case of success. */ gboolean -nm_platform_ip4_route_add (NMPlatform *self, - int ifindex, NMIPConfigSource source, - in_addr_t network, guint8 plen, - in_addr_t gateway, in_addr_t pref_src, - guint32 metric, guint32 mss) +nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route) { _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (plen <= 32, FALSE); + g_return_val_if_fail (route, FALSE); + g_return_val_if_fail (route->plen <= 32, FALSE); - if (_LOGD_ENABLED ()) { - NMPlatformIP4Route route = { 0 }; + _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (route, NULL, 0)); - route.ifindex = ifindex; - route.rt_source = source; - route.network = network; - route.plen = plen; - route.gateway = gateway; - route.metric = metric; - route.mss = mss; - route.pref_src = pref_src; - - _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route, NULL, 0)); - } - return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss); + return klass->ip4_route_add (self, route); } gboolean -nm_platform_ip6_route_add (NMPlatform *self, - int ifindex, NMIPConfigSource source, - struct in6_addr network, guint8 plen, struct in6_addr gateway, - guint32 metric, guint32 mss) +nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route) { _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (plen <= 128, FALSE); + g_return_val_if_fail (route, FALSE); + g_return_val_if_fail (route->plen <= 128, FALSE); - if (_LOGD_ENABLED ()) { - NMPlatformIP6Route route = { 0 }; + _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (route, NULL, 0)); - route.ifindex = ifindex; - route.rt_source = source; - route.network = network; - route.plen = plen; - route.gateway = gateway; - route.metric = metric; - route.mss = mss; - - _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route, NULL, 0)); - } - return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss); + return klass->ip6_route_add (self, route); } gboolean @@ -3900,6 +3865,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; char str_scope[30], s_source[50]; + char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) return buf; @@ -3909,16 +3875,35 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + if (route->tos) + nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); + if (route->window) + nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); + if (route->cwnd) + nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); + if (route->initcwnd) + nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); + if (route->initrwnd) + nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); + if (route->mtu) + nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); + g_snprintf (buf, len, "%s/%d" " via %s" "%s" " metric %"G_GUINT32_FORMAT " mss %"G_GUINT32_FORMAT - " src %s" /* source */ + " rt-src %s" /* protocol */ "%s" /* cloned */ "%s%s" /* scope */ "%s%s" /* pref-src */ + "%s" /* tos */ + "%s" /* window */ + "%s" /* cwnd */ + "%s" /* initcwnd */ + "%s" /* initrwnd */ + "%s" /* mtu */ "", s_network, route->plen, @@ -3931,7 +3916,13 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi route->scope_inv ? " scope " : "", route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "", route->pref_src ? " pref-src " : "", - route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : ""); + route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "", + route->tos ? str_tos : "", + route->window ? str_window : "", + route->cwnd ? str_cwnd : "", + route->initcwnd ? str_initcwnd : "", + route->initrwnd ? str_initrwnd : "", + route->mtu ? str_mtu : ""); return buf; } @@ -3950,25 +3941,54 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi const char * nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { - char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN]; + char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN]; + char s_src[INET6_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50]; + char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; if (!nm_utils_to_string_buffer_init_null (route, &buf, &len)) return buf; - inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network)); - inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); + inet_ntop (AF_INET6, &route->network, s_network, sizeof (s_network)); + inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof (s_gateway)); + inet_ntop (AF_INET6, &route->src, s_src, sizeof (s_src)); + + if (IN6_IS_ADDR_UNSPECIFIED (&route->pref_src)) + s_pref_src[0] = 0; + else + inet_ntop (AF_INET6, &route->pref_src, s_pref_src, sizeof (s_pref_src)); _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); + if (route->tos) + nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos); + if (route->window) + nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window); + if (route->cwnd) + nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd); + if (route->initcwnd) + nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd); + if (route->initrwnd) + nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd); + if (route->mtu) + nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu); + g_snprintf (buf, len, "%s/%d" " via %s" "%s" " metric %"G_GUINT32_FORMAT " mss %"G_GUINT32_FORMAT - " src %s" /* source */ + " rt-src %s" /* protocol */ + " src %s/%u" /* source */ "%s" /* cloned */ + "%s%s" /* pref-src */ + "%s" /* tos */ + "%s" /* window */ + "%s" /* cwnd */ + "%s" /* initcwnd */ + "%s" /* initrwnd */ + "%s" /* mtu */ "", s_network, route->plen, @@ -3977,7 +3997,17 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi route->metric, route->mss, nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)), - route->rt_cloned ? " cloned" : ""); + s_src, route->src_plen, + route->rt_cloned ? " cloned" : "", + s_pref_src[0] ? " pref-src " : "", + s_pref_src[0] ? s_pref_src : "", + route->tos ? str_tos : "", + route->window ? str_window : "", + route->cwnd ? str_cwnd : "", + route->initcwnd ? str_initcwnd : "", + route->initrwnd ? str_initrwnd : "", + route->mtu ? str_mtu : ""); + return buf; } @@ -4266,6 +4296,17 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route _CMP_FIELD (a, b, scope_inv); _CMP_FIELD (a, b, pref_src); _CMP_FIELD (a, b, rt_cloned); + _CMP_FIELD (a, b, tos); + _CMP_FIELD (a, b, lock_window); + _CMP_FIELD (a, b, lock_cwnd); + _CMP_FIELD (a, b, lock_initcwnd); + _CMP_FIELD (a, b, lock_initrwnd); + _CMP_FIELD (a, b, lock_mtu); + _CMP_FIELD (a, b, window); + _CMP_FIELD (a, b, cwnd); + _CMP_FIELD (a, b, initcwnd); + _CMP_FIELD (a, b, initrwnd); + _CMP_FIELD (a, b, mtu); return 0; } @@ -4278,9 +4319,23 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route _CMP_FIELD (a, b, plen); _CMP_FIELD (a, b, metric); _CMP_FIELD_MEMCMP (a, b, gateway); + _CMP_FIELD_MEMCMP (a, b, pref_src); + _CMP_FIELD_MEMCMP (a, b, src); + _CMP_FIELD (a, b, src_plen); _CMP_FIELD (a, b, rt_source); _CMP_FIELD (a, b, mss); _CMP_FIELD (a, b, rt_cloned); + _CMP_FIELD (a, b, tos); + _CMP_FIELD (a, b, lock_window); + _CMP_FIELD (a, b, lock_cwnd); + _CMP_FIELD (a, b, lock_initcwnd); + _CMP_FIELD (a, b, lock_initrwnd); + _CMP_FIELD (a, b, lock_mtu); + _CMP_FIELD (a, b, window); + _CMP_FIELD (a, b, cwnd); + _CMP_FIELD (a, b, initcwnd); + _CMP_FIELD (a, b, initrwnd); + _CMP_FIELD (a, b, mtu); return 0; } @@ -4411,28 +4466,27 @@ nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns) static gboolean _vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip4_route_add (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->rx.rt_source, - route->r4.network, - route->rx.plen, - route->r4.gateway, - route->r4.pref_src, - metric >= 0 ? (guint32) metric : route->rx.metric, - route->rx.mss); + NMPlatformIP4Route rt = route->r4; + + if (ifindex > 0) + rt.ifindex = ifindex; + if (metric >= 0) + rt.metric = metric; + + return nm_platform_ip4_route_add (self, &rt); } static gboolean _vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric) { - return nm_platform_ip6_route_add (self, - ifindex > 0 ? ifindex : route->rx.ifindex, - route->rx.rt_source, - route->r6.network, - route->rx.plen, - route->r6.gateway, - metric >= 0 ? (guint32) metric : route->rx.metric, - route->rx.mss); + NMPlatformIP6Route rt = route->r6; + + if (ifindex > 0) + rt.ifindex = ifindex; + if (metric >= 0) + rt.metric = metric; + + return nm_platform_ip6_route_add (self, &rt); } static gboolean diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 63dbe5a07c..a9d192f0dd 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -324,9 +324,20 @@ typedef union { * of platform users. This flag is internal to track those hidden * routes. Such a route is not alive, according to nmp_object_is_alive(). */ \ bool rt_cloned:1; \ + bool lock_window:1; \ + bool lock_cwnd:1; \ + bool lock_initcwnd:1; \ + bool lock_initrwnd:1; \ + bool lock_mtu:1; \ \ guint32 metric; \ guint32 mss; \ + guint32 tos; \ + guint32 window; \ + guint32 cwnd; \ + guint32 initcwnd; \ + guint32 initrwnd; \ + guint32 mtu; \ ; typedef struct { @@ -358,6 +369,9 @@ struct _NMPlatformIP6Route { __NMPlatformIPRoute_COMMON; struct in6_addr network; struct in6_addr gateway; + struct in6_addr pref_src; + struct in6_addr src; + guint8 src_plen; }; typedef union { @@ -667,12 +681,8 @@ typedef struct { GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags); - gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, - in_addr_t network, guint8 plen, in_addr_t gateway, - in_addr_t pref_src, guint32 metric, guint32 mss); - gboolean (*ip6_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source, - struct in6_addr network, guint8 plen, struct in6_addr gateway, - guint32 metric, guint32 mss); + gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route); + gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route); gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric); @@ -969,12 +979,8 @@ const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifind const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); -gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, - in_addr_t network, guint8 plen, in_addr_t gateway, - in_addr_t pref_src, guint32 metric, guint32 mss); -gboolean nm_platform_ip6_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source, - struct in6_addr network, guint8 plen, struct in6_addr gateway, - guint32 metric, guint32 mss); +gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route); +gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route); gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 4ac4929895..71a92cbfae 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -65,12 +65,12 @@ test_cleanup_internal (void) /* Add routes and addresses */ g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, 0, NULL)); g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags)); - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss)); - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss)); - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, in6addr_any, metric, mss); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, in6addr_any, metric, mss); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, in6addr_any, metric, mss); addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 0e42a727d7..d09fa3bae9 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -783,6 +783,54 @@ nmtstp_ip6_address_add (NMPlatform *platform, NULL); } +void nmtstp_ip4_route_add (NMPlatform *platform, + int ifindex, + NMIPConfigSource source, + in_addr_t network, + guint8 plen, + in_addr_t gateway, + in_addr_t pref_src, + guint32 metric, + guint32 mss) +{ + NMPlatformIP4Route route = { }; + + route.ifindex = ifindex; + route.rt_source = source; + route.network = network; + route.plen = plen; + route.gateway = gateway; + route.pref_src = pref_src; + route.metric = metric; + route.mss = mss; + + g_assert (nm_platform_ip4_route_add (platform, &route)); +} + +void nmtstp_ip6_route_add (NMPlatform *platform, + int ifindex, + NMIPConfigSource source, + struct in6_addr network, + guint8 plen, + struct in6_addr gateway, + struct in6_addr pref_src, + guint32 metric, + guint32 mss) +{ + NMPlatformIP6Route route = { }; + + route.ifindex = ifindex; + route.rt_source = source; + route.network = network; + route.plen = plen; + route.gateway = gateway; + route.pref_src = pref_src; + route.metric = metric; + route.mss = mss; + + g_assert (nm_platform_ip6_route_add (platform, &route)); +} + /*****************************************************************************/ static void diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 48a41de63a..e13afe43a7 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -167,6 +167,26 @@ void nmtstp_ip6_address_del (NMPlatform *platform, struct in6_addr address, int plen); +void nmtstp_ip4_route_add (NMPlatform *platform, + int ifindex, + NMIPConfigSource source, + in_addr_t network, + guint8 plen, + in_addr_t gateway, + in_addr_t pref_src, + guint32 metric, + guint32 mss); + +void nmtstp_ip6_route_add (NMPlatform *platform, + int ifindex, + NMIPConfigSource source, + struct in6_addr network, + guint8 plen, + struct in6_addr gateway, + struct in6_addr pref_src, + guint32 metric, + guint32 mss); + /*****************************************************************************/ const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 59a05bd492..ca5b847c9d 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -94,7 +94,7 @@ test_ip4_route_metric0 (void) nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); /* add the first route */ - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss); accept_signal (route_added); nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); @@ -108,7 +108,7 @@ test_ip4_route_metric0 (void) nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); /* add the second route */ - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss); accept_signal (route_added); nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, 0); @@ -160,27 +160,27 @@ test_ip4_route (void) inet_pton (AF_INET, "198.51.100.1", &gateway); /* Add route to gateway */ - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss); accept_signal (route_added); /* Add route */ nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); accept_signal (route_added); /* Add route again */ - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); accept_signals (route_changed, 0, 1); /* Add default route */ nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, 0, 0, metric); - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss); nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, 0, 0, metric); accept_signal (route_added); /* Add default route again */ - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss)); + nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss); accept_signals (route_changed, 0, 1); /* Test route listing */ @@ -222,6 +222,14 @@ test_ip4_route (void) /* Remove route again */ g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + /* Remove default route */ + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric)); + accept_signal (route_removed); + + /* Remove route to gateway */ + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric)); + accept_signal (route_removed); + free_signal (route_added); free_signal (route_changed); free_signal (route_removed); @@ -238,36 +246,41 @@ test_ip6_route (void) NMPlatformIP6Route rts[3]; struct in6_addr network; guint8 plen = 64; - struct in6_addr gateway; + struct in6_addr gateway, pref_src; /* Choose a high metric so that we hopefully don't conflict. */ int metric = 22987; int mss = 1000; inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network); inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway); + inet_pton (AF_INET6, "::42", &pref_src); + + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, pref_src, 128, in6addr_any, + NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0)); + accept_signals (route_added, 0, 1); /* Add route to gateway */ - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss)); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, in6addr_any, metric, mss); accept_signal (route_added); /* Add route */ g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss); g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_added); /* Add route again */ - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss)); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss); accept_signals (route_changed, 0, 1); /* Add default route */ g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss); g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); accept_signal (route_added); /* Add default route again */ - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss)); + nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss); accept_signals (route_changed, 0, 1); /* Test route listing */ @@ -278,6 +291,7 @@ test_ip6_route (void) rts[0].plen = 128; rts[0].ifindex = ifindex; rts[0].gateway = in6addr_any; + rts[0].pref_src = in6addr_any; rts[0].metric = nm_utils_ip6_route_metric_normalize (metric); rts[0].mss = mss; rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); @@ -285,6 +299,7 @@ test_ip6_route (void) rts[1].plen = plen; rts[1].ifindex = ifindex; rts[1].gateway = gateway; + rts[1].pref_src = pref_src; rts[1].metric = nm_utils_ip6_route_metric_normalize (metric); rts[1].mss = mss; rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); @@ -292,6 +307,7 @@ test_ip6_route (void) rts[2].plen = 0; rts[2].ifindex = ifindex; rts[2].gateway = gateway; + rts[2].pref_src = in6addr_any; rts[2].metric = nm_utils_ip6_route_metric_normalize (metric); rts[2].mss = mss; g_assert_cmpint (routes->len, ==, 3); @@ -306,6 +322,14 @@ test_ip6_route (void) /* Remove route again */ g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); + /* Remove default route */ + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + accept_signal (route_removed); + + /* Remove route to gateway */ + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric)); + accept_signal (route_removed); + free_signal (route_added); free_signal (route_changed); free_signal (route_removed); @@ -334,6 +358,119 @@ test_ip4_zero_gateway (void) nm_platform_process_events (NM_PLATFORM_GET); } +static void +test_ip4_route_options (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + NMPlatformIP4Route route = { }; + in_addr_t network; + GArray *routes; + NMPlatformIP4Route rts[1]; + + inet_pton (AF_INET, "172.16.1.0", &network); + + route.ifindex = ifindex; + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + route.network = network; + route.plen = 24; + route.metric = 20; + route.tos = 0x28; + route.window = 10000; + route.cwnd = 16; + route.initcwnd = 30; + route.initrwnd = 50; + route.mtu = 1350; + route.lock_cwnd = TRUE; + + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route)); + + /* Test route listing */ + routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + memset (rts, 0, sizeof (rts)); + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); + rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK); + rts[0].network = network; + rts[0].plen = 24; + rts[0].ifindex = ifindex; + rts[0].metric = 20; + rts[0].tos = 0x28; + rts[0].window = 10000; + rts[0].cwnd = 16; + rts[0].initcwnd = 30; + rts[0].initrwnd = 50; + rts[0].mtu = 1350; + rts[0].lock_cwnd = TRUE; + + g_assert_cmpint (routes->len, ==, 1); + nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE); + + /* Remove route */ + g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20)); + + g_array_unref (routes); +} + + +static void +test_ip6_route_options (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + NMPlatformIP6Route route = { }; + struct in6_addr network, src; + GArray *routes; + NMPlatformIP6Route rts[3]; + + inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network); + inet_pton (AF_INET6, "2001:db8:e:0:0:0:0:0", &src); + + route.ifindex = ifindex; + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + route.network = network; + route.plen = 64; + route.gateway = in6addr_any; + route.src = src; + route.src_plen = 48; + route.metric = 1024; + route.window = 20000; + route.cwnd = 8; + route.initcwnd = 22; + route.initrwnd = 33; + route.mtu = 1300; + route.lock_mtu = TRUE; + + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &route)); + + /* Test route listing */ + routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | + NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); + memset (rts, 0, sizeof (rts)); + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); + rts[0].network = network; + rts[0].plen = 64; + rts[0].ifindex = ifindex; + rts[0].gateway = in6addr_any; + rts[0].src = src; + rts[0].src_plen = 48; + rts[0].metric = 1024; + rts[0].window = 20000; + rts[0].cwnd = 8; + rts[0].initcwnd = 22; + rts[0].initrwnd = 33; + rts[0].mtu = 1300; + rts[0].lock_mtu = TRUE; + + g_assert_cmpint (routes->len, ==, 1); + nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE); + + /* Remove route */ + g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, 64, 1024)); + + g_array_unref (routes); +} + /*****************************************************************************/ NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; @@ -360,6 +497,8 @@ _nmtstp_setup_tests (void) g_test_add_func ("/route/ip4", test_ip4_route); g_test_add_func ("/route/ip6", test_ip6_route); g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); + g_test_add_func ("/route/ip4_options", test_ip4_route_options); + g_test_add_func ("/route/ip6_options", test_ip6_route_options); if (nmtstp_is_root_test ()) g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 4a128024c7..e29a204a08 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -435,6 +435,134 @@ read_full_ip4_address (shvarFile *ifcfg, return FALSE; } +/* + * Use looser syntax to comprise all the possibilities. + * The validity must be checked after the match. + */ +#define IPV4_ADDR_REGEX "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}" +#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+" + +/* + * NOTE: The regexes below don't describe all variants allowed by 'ip route add', + * namely destination IP without 'to' keyword is recognized just at line start. + */ + +static gboolean +parse_route_options (NMIPRoute *route, int family, const char *line, GError **error) +{ + GRegex *regex = NULL; + GMatchInfo *match_info = NULL; + gboolean success = FALSE; + char *metrics[] = { NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND, + NM_IP_ROUTE_ATTRIBUTE_INITCWND , NM_IP_ROUTE_ATTRIBUTE_INITRWND, + NM_IP_ROUTE_ATTRIBUTE_MTU , NULL }; + char buffer[1024]; + int i; + + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + + for (i = 0; metrics[i]; i++) { + nm_sprintf_buf (buffer, "(?:\\s|^)%s\\s+(lock\\s+)?(\\d+)(?:$|\\s)", metrics[i]); + regex = g_regex_new (buffer, 0, 0, NULL); + g_regex_match (regex, line, 0, &match_info); + if (g_match_info_matches (match_info)) { + gs_free char *lock = g_match_info_fetch (match_info, 1); + gs_free char *str = g_match_info_fetch (match_info, 2); + gint64 num = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1); + + if (num == -1) { + g_match_info_free (match_info); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid route %s '%s'", metrics[i], str); + goto out; + } + + nm_ip_route_set_attribute (route, metrics[i], + g_variant_new_uint32 (num)); + if (lock && lock[0]) { + nm_sprintf_buf (buffer, "lock-%s", metrics[i]); + nm_ip_route_set_attribute (route, buffer, + g_variant_new_boolean (TRUE)); + } + } + g_clear_pointer (®ex, g_regex_unref); + g_clear_pointer (&match_info, g_match_info_free); + } + + /* tos */ + regex = g_regex_new ("(?:\\s|^)tos\\s+(\\S+)(?:$|\\s)", 0, 0, NULL); + g_regex_match (regex, line, 0, &match_info); + if (g_match_info_matches (match_info)) { + gs_free char *str = g_match_info_fetch (match_info, 1); + gs_free_error GError *local_error = NULL; + gint64 num = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT8, -1); + + if (num == -1) { + g_match_info_free (match_info); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid route %s '%s'", "tos", str); + goto out; + } + nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_TOS, + g_variant_new_byte ((guchar) num)); + } + g_clear_pointer (®ex, g_regex_unref); + g_clear_pointer (&match_info, g_match_info_free); + + /* from */ + if (family == AF_INET6) { + regex = g_regex_new ("(?:\\s|^)from\\s+(" IPV6_ADDR_REGEX "(?:/\\d{1,3})?)(?:$|\\s)", 0, 0, NULL); + g_regex_match (regex, line, 0, &match_info); + if (g_match_info_matches (match_info)) { + gs_free char *str = g_match_info_fetch (match_info, 1); + gs_free_error GError *local_error = NULL; + GVariant *variant = g_variant_new_string (str); + + if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_SRC, variant, family, NULL, &local_error)) { + g_match_info_free (match_info); + g_variant_unref (variant); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid route from '%s': %s", str, local_error->message); + goto out; + } + nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_SRC, variant); + } + g_clear_pointer (®ex, g_regex_unref); + g_clear_pointer (&match_info, g_match_info_free); + } + + if (family == AF_INET) + regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV4_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); + else + regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV6_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL); + g_regex_match (regex, line, 0, &match_info); + if (g_match_info_matches (match_info)) { + gs_free char *str = g_match_info_fetch (match_info, 1); + gs_free_error GError *local_error = NULL; + GVariant *variant = g_variant_new_string (str); + + if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, variant, family, + NULL, &local_error)) { + g_match_info_free (match_info); + g_variant_unref (variant); + g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, + "Invalid route src '%s': %s", str, local_error->message); + goto out; + } + + nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, variant); + } + success = TRUE; + +out: + if (regex) + g_regex_unref (regex); + if (match_info) + g_match_info_free (match_info); + + return success; +} + /* Returns TRUE on missing route or valid route */ static gboolean read_one_ip4_route (shvarFile *ifcfg, @@ -507,10 +635,20 @@ read_one_ip4_route (shvarFile *ifcfg, metric = -1; *out_route = nm_ip_route_new_binary (AF_INET, &dest, prefix, &next_hop, metric, error); - if (*out_route) - return TRUE; + if (!*out_route) + return FALSE; - return FALSE; + /* Options */ + nm_clear_g_free (&value); + value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "OPTIONS", which)); + if (value) { + if (!parse_route_options (*out_route, AF_INET, value, error)) { + g_clear_pointer (out_route, nm_ip_route_unref); + return FALSE; + } + } + + return TRUE; } static gboolean @@ -636,6 +774,12 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError * route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error); if (!route) goto error; + + if (!parse_route_options (route, AF_INET, *iter, error)) { + nm_ip_route_unref (route); + goto error; + } + if (!nm_setting_ip_config_add_route (s_ip4, route)) PARSE_WARNING ("duplicate IP4 route"); nm_ip_route_unref (route); @@ -729,13 +873,6 @@ error: return success; } -/* IPv6 address is very complex to describe completely by a regular expression, - * so don't try to, rather use looser syntax to comprise all possibilities - * NOTE: The regexes below don't describe all variants allowed by 'ip route add', - * namely destination IP without 'to' keyword is recognized just at line start. - */ -#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+" - static gboolean read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) { @@ -757,6 +894,7 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")"; /* IPv6 of gateway */ const char *pattern_metric = "metric\\s+(\\d+)"; /* metric */ + g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (s_ip6 != NULL, FALSE); g_return_val_if_fail (!error || !*error, FALSE); @@ -862,6 +1000,12 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error g_free (next_hop); if (!route) goto error; + + if (!parse_route_options (route, AF_INET6, *iter, error)) { + nm_ip_route_unref (route); + goto error; + } + if (!nm_setting_ip_config_add_route (s_ip6, route)) PARSE_WARNING ("duplicate IP6 route"); nm_ip_route_unref (route); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 620e89e4ba..2484799cb6 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -1867,6 +1867,61 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) } } +static char * +get_route_attributes_string (NMIPRoute *route, int family) +{ + gs_strfreev char **names = NULL; + GVariant *attr, *lock; + GString *str; + int i; + + names = nm_ip_route_get_attribute_names (route); + if (!names || !names[0]) + return NULL; + + str = g_string_new (""); + + for (i = 0; names[i]; i++) { + attr = nm_ip_route_get_attribute (route, names[i]); + + if (!nm_ip_route_attribute_validate (names[i], attr, family, NULL, NULL)) + continue; + + if (NM_IN_STRSET (names[i], NM_IP_ROUTE_ATTRIBUTE_WINDOW, + NM_IP_ROUTE_ATTRIBUTE_CWND, + NM_IP_ROUTE_ATTRIBUTE_INITCWND, + NM_IP_ROUTE_ATTRIBUTE_INITRWND, + NM_IP_ROUTE_ATTRIBUTE_MTU)) { + char lock_name[256]; + + nm_sprintf_buf (lock_name, "lock-%s", names[i]); + lock = nm_ip_route_get_attribute (route, lock_name); + + g_string_append_printf (str, + "%s %s%u", + names[i], + (lock && g_variant_get_boolean (lock)) ? "lock " : "", + g_variant_get_uint32 (attr)); + } else if (strstr (names[i], "lock-")) { + /* handled above */ + } else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) { + g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr)); + } else if ( nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_PREF_SRC) + || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC)) { + char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_PREF_SRC) ? "src" : "from"; + + g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL)); + } else { + _LOGW ("unknown route option '%s'", names[i]); + continue; + } + if (names[i + 1]) + g_string_append_c (str, ' '); + } + + return g_string_free (str, FALSE); +} + static gboolean write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error) { @@ -1891,6 +1946,8 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError route_items = g_malloc0 (sizeof (char*) * (num + 1)); for (i = 0; i < num; i++) { + gs_free char *options = NULL; + route = nm_setting_ip_config_get_route (s_ip4, i); dest = nm_ip_route_get_dest (route); @@ -1898,10 +1955,19 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError next_hop = nm_ip_route_get_next_hop (route); metric = nm_ip_route_get_metric (route); - if (metric == -1) - route_items[i] = g_strdup_printf ("%s/%u via %s\n", dest, prefix, next_hop); - else - route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, (guint32) metric); + options = get_route_attributes_string (route, AF_INET); + + if (metric == -1) { + route_items[i] = g_strdup_printf ("%s/%u via %s%s%s\n", + dest, prefix, next_hop, + options ? " " : "", + options ?: ""); + } else { + route_items[i] = g_strdup_printf ("%s/%u via %s metric %u%s%s\n", + dest, prefix, next_hop, (guint32) metric, + options ? " " : "", + options ?: ""); + } } route_items[num] = NULL; route_contents = g_strjoinv (NULL, route_items); @@ -1960,10 +2026,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingIPConfig *s_ip4; const char *value; - char *addr_key, *prefix_key, *netmask_key, *gw_key, *metric_key, *tmp; + char *tmp; + char addr_key[64]; + char prefix_key[64]; + char netmask_key[64]; + char gw_key[64]; char *route_path = NULL; - gint32 j; - guint32 i, n, num; + gint j; + guint i, num, n; gint64 route_metric; gint priority; int timeout; @@ -2003,26 +2073,21 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svUnsetValue (ifcfg, "BOOTPROTO"); for (j = -1; j < 256; j++) { if (j == -1) { - addr_key = g_strdup ("IPADDR"); - prefix_key = g_strdup ("PREFIX"); - netmask_key = g_strdup ("NETMASK"); - gw_key = g_strdup ("GATEWAY"); + nm_sprintf_buf (addr_key, "IPADDR"); + nm_sprintf_buf (prefix_key, "PREFIX"); + nm_sprintf_buf (netmask_key, "NETMASK"); + nm_sprintf_buf (gw_key, "GATEWAY"); } else { - addr_key = g_strdup_printf ("IPADDR%d", j); - prefix_key = g_strdup_printf ("PREFIX%d", j); - netmask_key = g_strdup_printf ("NETMASK%d", j); - gw_key = g_strdup_printf ("GATEWAY%d", j); + nm_sprintf_buf (addr_key, "IPADDR%d", (guint) j); + nm_sprintf_buf (prefix_key, "PREFIX%u", (guint) j); + nm_sprintf_buf (netmask_key, "NETMASK%u", (guint) j); + nm_sprintf_buf (gw_key, "GATEWAY%u", (guint) j); } svUnsetValue (ifcfg, addr_key); svUnsetValue (ifcfg, prefix_key); svUnsetValue (ifcfg, netmask_key); svUnsetValue (ifcfg, gw_key); - - g_free (addr_key); - g_free (prefix_key); - g_free (netmask_key); - g_free (gw_key); } route_path = utils_get_route_path (svFileGetName (ifcfg)); @@ -2074,15 +2139,15 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) * See https://bugzilla.redhat.com/show_bug.cgi?id=771673 * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770 */ - addr_key = g_strdup ("IPADDR"); - prefix_key = g_strdup ("PREFIX"); - netmask_key = g_strdup ("NETMASK"); - gw_key = g_strdup ("GATEWAY"); + nm_sprintf_buf (addr_key, "IPADDR"); + nm_sprintf_buf (prefix_key, "PREFIX"); + nm_sprintf_buf (netmask_key, "NETMASK"); + nm_sprintf_buf (gw_key, "GATEWAY"); } else { - addr_key = g_strdup_printf ("IPADDR%d", n); - prefix_key = g_strdup_printf ("PREFIX%d", n); - netmask_key = g_strdup_printf ("NETMASK%d", n); - gw_key = g_strdup_printf ("GATEWAY%d", n); + nm_sprintf_buf (addr_key, "IPADDR%u", n); + nm_sprintf_buf (prefix_key, "PREFIX%u", n); + nm_sprintf_buf (netmask_key, "NETMASK%u", n); + nm_sprintf_buf (gw_key, "GATEWAY%u", n); } svSetValueStr (ifcfg, addr_key, nm_ip_address_get_address (addr)); @@ -2093,30 +2158,20 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svUnsetValue (ifcfg, netmask_key); svUnsetValue (ifcfg, gw_key); - - g_free (addr_key); - g_free (prefix_key); - g_free (netmask_key); - g_free (gw_key); n++; } /* Clear remaining IPADDR, etc */ - for (; n < 256; n++) { - addr_key = g_strdup_printf ("IPADDR%d", n); - prefix_key = g_strdup_printf ("PREFIX%d", n); - netmask_key = g_strdup_printf ("NETMASK%d", n); - gw_key = g_strdup_printf ("GATEWAY%d", n); + for (i = n; i < 256; i++) { + nm_sprintf_buf (addr_key, "IPADDR%u", i); + nm_sprintf_buf (prefix_key, "PREFIX%u", i); + nm_sprintf_buf (netmask_key, "NETMASK%u", i); + nm_sprintf_buf (gw_key, "GATEWAY%u", i); svUnsetValue (ifcfg, addr_key); svUnsetValue (ifcfg, prefix_key); svUnsetValue (ifcfg, netmask_key); svUnsetValue (ifcfg, gw_key); - - g_free (addr_key); - g_free (prefix_key); - g_free (netmask_key); - g_free (gw_key); } svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4)); @@ -2125,15 +2180,13 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) for (i = 0; i < 254; i++) { const char *dns; - addr_key = g_strdup_printf ("DNS%d", i + 1); - + nm_sprintf_buf (addr_key, "DNS%u", i + 1); if (i >= num) svUnsetValue (ifcfg, addr_key); else { dns = nm_setting_ip_config_get_dns (s_ip4, i); svSetValueStr (ifcfg, addr_key, dns); } - g_free (addr_key); } num = nm_setting_ip_config_get_num_dns_searches (s_ip4); @@ -2212,18 +2265,24 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) NMIPRoute *route; guint32 netmask; gint64 metric; + char metric_key[64]; + char options_key[64]; - addr_key = g_strdup_printf ("ADDRESS%d", i); - netmask_key = g_strdup_printf ("NETMASK%d", i); - gw_key = g_strdup_printf ("GATEWAY%d", i); - metric_key = g_strdup_printf ("METRIC%d", i); + nm_sprintf_buf (addr_key, "ADDRESS%u", i); + nm_sprintf_buf (netmask_key, "NETMASK%u", i); + nm_sprintf_buf (gw_key, "GATEWAY%u", i); + nm_sprintf_buf (metric_key, "METRIC%u", i); + nm_sprintf_buf (options_key, "OPTIONS%u", i); if (i >= num) { svUnsetValue (routefile, addr_key); svUnsetValue (routefile, netmask_key); svUnsetValue (routefile, gw_key); svUnsetValue (routefile, metric_key); + svUnsetValue (routefile, options_key); } else { + gs_free char *options = NULL; + route = nm_setting_ip_config_get_route (s_ip4, i); svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route)); @@ -2244,12 +2303,11 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) svSetValueStr (routefile, metric_key, tmp); g_free (tmp); } - } - g_free (addr_key); - g_free (netmask_key); - g_free (gw_key); - g_free (metric_key); + options = get_route_attributes_string (route, AF_INET); + if (options) + svSetValueStr (routefile, options_key, options); + } } if (!svWriteFile (routefile, 0644, error)) { svCloseFile (routefile); @@ -2371,15 +2429,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path) static gboolean write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error) { - char **route_items; - gs_free char *route_contents = NULL; + nm_auto_free_gstring GString *contents = NULL; NMIPRoute *route; guint32 i, num; - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (s_ip6 != NULL, FALSE); - g_return_val_if_fail (error != NULL, FALSE); - g_return_val_if_fail (*error == NULL, FALSE); + g_return_val_if_fail (filename, FALSE); + g_return_val_if_fail (s_ip6, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); num = nm_setting_ip_config_get_num_routes (s_ip6); if (num == 0) { @@ -2387,28 +2443,33 @@ write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **erro return TRUE; } - route_items = g_malloc0 (sizeof (char*) * (num + 1)); + contents = g_string_new (""); for (i = 0; i < num; i++) { + gs_free char *options = NULL; + route = nm_setting_ip_config_get_route (s_ip6, i); + options = get_route_attributes_string (route, AF_INET6); if (nm_ip_route_get_metric (route) == -1) { - route_items[i] = g_strdup_printf ("%s/%u via %s\n", - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route), - nm_ip_route_get_next_hop (route)); - } else { - route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", + g_string_append_printf (contents, "%s/%u via %s%s%s", nm_ip_route_get_dest (route), nm_ip_route_get_prefix (route), nm_ip_route_get_next_hop (route), - (guint32) nm_ip_route_get_metric (route)); + options ? " " : "", + options ?: ""); + } else { + g_string_append_printf (contents, "%s/%u via %s metric %u%s%s", + nm_ip_route_get_dest (route), + nm_ip_route_get_prefix (route), + nm_ip_route_get_next_hop (route), + (unsigned) nm_ip_route_get_metric (route), + options ? " " : "", + options ?: ""); } + g_string_append (contents, "\n"); } - route_items[num] = NULL; - route_contents = g_strjoinv (NULL, route_items); - g_strfreev (route_items); - if (!g_file_set_contents (filename, route_contents, -1, NULL)) { + if (!g_file_set_contents (filename, contents->str, -1, NULL)) { g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, "Writing route6 file '%s' failed", filename); return FALSE; @@ -2440,9 +2501,8 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) NMSettingIPConfig *s_ip6; NMSettingIPConfig *s_ip4; const char *value; - char *addr_key; char *tmp; - guint32 i, num, num4; + guint i, num, num4; gint priority; NMIPAddress *addr; const char *dns; @@ -2530,7 +2590,9 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */ num = nm_setting_ip_config_get_num_dns (s_ip6); for (i = 0; i < 254; i++) { - addr_key = g_strdup_printf ("DNS%d", i + num4 + 1); + char addr_key[64]; + + nm_sprintf_buf (addr_key, "DNS%u", i + num4 + 1); if (i >= num) svUnsetValue (ifcfg, addr_key); @@ -2538,7 +2600,6 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) dns = nm_setting_ip_config_get_dns (s_ip6, i); svSetValueStr (ifcfg, addr_key, dns); } - g_free (addr_key); } /* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */ diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes index ee2a32d8e2..10a63b674f 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes @@ -6,3 +6,4 @@ ADDRESS1=44.55.66.77 NETMASK1=255.255.255.255 GATEWAY1=192.168.1.7 METRIC1=3 +OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 initcwnd lock 13 initrwnd 14" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy index cb7d42bde2..3f02032ad7 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy @@ -3,5 +3,5 @@ 21.31.41.0/24 via 9.9.9.9 metric 1 via 8.8.8.8 to 32.42.52.62 - 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 + 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000 initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4 diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual index b3259ab7ff..8bdf0acf5c 100644 --- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual @@ -5,3 +5,5 @@ default via dead::beaf # routes without "via" are valid abbe::cafe/64 metric 777 + +aaaa::cccc/64 from 1111::2222/48 via 3333::4444 src 5555::6666 mtu lock 1450 cwnd 13 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 bd6068ce15..2058b6209a 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -1127,6 +1127,15 @@ test_read_wired_static_routes (void) g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32); g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "192.168.1.7"); g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3); + nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); + nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE); + nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, "1.1.1.1"); g_object_unref (connection); } @@ -1188,6 +1197,14 @@ test_read_wired_static_routes_legacy (void) g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 16); g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "7.7.7.7"); g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 10000); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 14); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 42); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 20); + nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000); + nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, TRUE); + nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, "1.2.3.4"); g_object_unref (connection); } @@ -1328,7 +1345,7 @@ test_read_wired_ipv6_manual (void) g_assert_cmpint (nm_ip_address_get_prefix (ip6_addr), ==, 96); /* Routes */ - g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 2); + g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 3); /* Route #1 */ ip6_route = nm_setting_ip_config_get_route (s_ip6, 0); g_assert (ip6_route); @@ -1343,6 +1360,17 @@ test_read_wired_ipv6_manual (void) g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64); g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, NULL); g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, 777); + /* Route #3 */ + ip6_route = nm_setting_ip_config_get_route (s_ip6, 2); + g_assert (ip6_route); + g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "aaaa::cccc"); + g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64); + g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, "3333::4444"); + nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 13); + nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1450); + nmtst_assert_route_attribute_boolean (ip6_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE); + nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1111::2222/48"); + nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, "5555::6666"); /* DNS Addresses */ g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 2); @@ -3867,6 +3895,12 @@ test_write_wired_static (void) route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error); g_assert_no_error (error); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_TOS, g_variant_new_byte (0xb8)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE)); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("2222::bbbb/32")); + nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, g_variant_new_string ("::42")); nm_setting_ip_config_add_route (s_ip6, route6); nm_ip_route_unref (route6); diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection index de8373bef2..e55e229306 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection +++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection @@ -34,6 +34,7 @@ routes8=1.1.1.8/18,0.0.0.0, routes9=1.1.1.9/19,0.0.0.0,0 routes10=1.1.1.10/20,,0 routes11=1.1.1.11/21,,21 +routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,pref-src=7.7.7.7 ignore-auto-routes=false ignore-auto-dns=false @@ -58,5 +59,6 @@ route3=6:7:8:9:0:1:2:3/126,,1 route4=7:8:9:0:1:2:3:4/125/::,5 route5=8:9:0:1:2:3:4:5/124,6 route6=8:9:0:1:2:3:4:6/123,, +route6_options=src=abce::/63 ignore-auto-routes=false ignore-auto-dns=false diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index 1d1f5d8ccb..f43300f2c8 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -224,6 +224,7 @@ test_read_valid_wired_connection (void) NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; + NMIPRoute *route; gs_free_error GError *error = NULL; const char *mac; char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; @@ -318,6 +319,15 @@ test_read_valid_wired_connection (void) check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, -1); check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21); + /* Route attributes */ + route = nm_setting_ip_config_get_route (s_ip4, 11); + g_assert (route); + + nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10); + nmtst_assert_route_attribute_uint32 (route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430); + nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE); + nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, "7.7.7.7"); + /* ===== IPv6 SETTING ===== */ s_ip6 = nm_connection_get_setting_ip6_config (connection); g_assert (s_ip6); @@ -357,6 +367,11 @@ test_read_valid_wired_connection (void) check_ip_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, NULL, 5); check_ip_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, NULL, 6); check_ip_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, NULL, -1); + + /* Route attributes */ + route = nm_setting_ip_config_get_route (s_ip6, 6); + g_assert (route); + nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "abce::/63"); } static void @@ -402,6 +417,7 @@ test_write_wired_connection (void) NMSettingWired *s_wired; NMSettingIPConfig *s_ip4; NMSettingIPConfig *s_ip6; + NMIPRoute *rt; const char *mac = "99:88:77:66:55:44"; const char *dns1 = "4.2.2.1"; const char *dns2 = "4.2.2.2"; @@ -429,6 +445,7 @@ test_write_wired_connection (void) const char *route6_4 = "5:6:7:8:9:0:1:2"; const char *route6_4_nh = "::"; guint64 timestamp = 0x12345678L; + GError *error = NULL; connection = nm_simple_connection_new (); @@ -473,7 +490,14 @@ test_write_wired_connection (void) add_one_ip_route (s_ip4, route1, route1_nh, 24, 3); add_one_ip_route (s_ip4, route2, route2_nh, 8, 1); add_one_ip_route (s_ip4, route3, route3_nh, 7, -1); - add_one_ip_route (s_ip4, route4, route4_nh, 6, 4); + + rt = nm_ip_route_new (AF_INET, route4, 6, route4_nh, 4, &error); + g_assert_no_error (error); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (10)); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1492)); + nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, g_variant_new_string ("1.2.3.4")); + g_assert (nm_setting_ip_config_add_route (s_ip4, rt)); + nm_ip_route_unref (rt); /* DNS servers */ nm_setting_ip_config_add_dns (s_ip4, dns1); diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index 505f9a5d19..7e83625c5b 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -37,8 +37,8 @@ build_test_config (void) config = nm_ip6_config_new (1); nm_ip6_config_add_address (config, nmtst_platform_ip6_address ("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); - nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2")); - nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234")); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL)); + nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234", NULL)); nm_ip6_config_set_gateway (config, nmtst_inet6_from_string ("3001:abba::3234")); @@ -74,7 +74,7 @@ test_subtract (void) /* add a couple more things to the test config */ dst = build_test_config (); nm_ip6_config_add_address (dst, nmtst_platform_ip6_address (expected_addr, NULL, expected_addr_plen)); - nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop)); + nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop, NULL)); expected_ns1 = *nmtst_inet6_from_string ("2222:3333:4444::5555"); nm_ip6_config_add_nameserver (dst, &expected_ns1); @@ -139,7 +139,7 @@ test_compare_with_source (void) nm_ip6_config_add_address (b, &addr); /* Route */ - route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); + route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_route (a, &route); @@ -203,7 +203,7 @@ test_add_route_with_source (void) a = nm_ip6_config_new (1); /* Test that a higher priority source is not overwritten */ - route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"); + route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL); route.rt_source = NM_IP_CONFIG_SOURCE_USER; nm_ip6_config_add_route (a, &route); diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index df43dd987a..ba7834e9ea 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -75,16 +75,15 @@ setup_dev1_ip4 (int ifindex) /* Add some route outside of route manager. The route manager * should get rid of it upon sync. */ - if (!nm_platform_ip4_route_add (NM_PLATFORM_GET, - route.ifindex, - NM_IP_CONFIG_SOURCE_USER, - nmtst_inet4_from_string ("9.0.0.0"), - 8, - INADDR_ANY, - 0, - 10, - route.mss)) - g_assert_not_reached (); + nmtstp_ip4_route_add (NM_PLATFORM_GET, + route.ifindex, + NM_IP_CONFIG_SOURCE_USER, + nmtst_inet4_from_string ("9.0.0.0"), + 8, + INADDR_ANY, + 0, + 10, + route.mss); route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); inet_pton (AF_INET, "6.6.6.0", &route.network); @@ -421,15 +420,15 @@ setup_dev1_ip6 (int ifindex) /* Add some route outside of route manager. The route manager * should get rid of it upon sync. */ - if (!nm_platform_ip6_route_add (NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - *nmtst_inet6_from_string ("2001:db8:8088::"), - 48, - in6addr_any, - 10, - 0)) - g_assert_not_reached (); + nmtstp_ip6_route_add (NM_PLATFORM_GET, + ifindex, + NM_IP_CONFIG_SOURCE_USER, + *nmtst_inet6_from_string ("2001:db8:8088::"), + 48, + in6addr_any, + in6addr_any, + 10, + 0); route = nmtst_platform_ip6_route_full ("2001:db8:8086::", 48,