libnm,core: merge branch 'th/routing-rule-pt2'

https://github.com/NetworkManager/NetworkManager/pull/321
This commit is contained in:
Thomas Haller 2019-03-27 16:32:39 +01:00
commit 7680014732
30 changed files with 5004 additions and 821 deletions

View file

@ -2729,6 +2729,7 @@ EXTRA_DIST += \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected \

View file

@ -543,13 +543,18 @@ nmc_setting_set_property (NMClient *client,
g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (NM_IN_SET (modifier, '\0', '-', '+'), FALSE);
g_return_val_if_fail (value || modifier == '\0', FALSE);
if (!(property_info = nm_meta_property_info_find_by_setting (setting, prop)))
goto out_fail_read_only;
if (!property_info->property_type->set_fcn)
goto out_fail_read_only;
if ( NM_IN_SET (modifier, '+', '-')
&& !value) {
/* nothing to do. */
return TRUE;
}
if ( modifier == '-'
&& !property_info->property_type->set_supports_remove) {
/* The property is a plain property. It does not support '-'.

View file

@ -170,6 +170,7 @@ _value_str_as_index_list (const char *value, gsize *out_len)
typedef enum {
VALUE_STRSPLIT_MODE_STRIPPED,
VALUE_STRSPLIT_MODE_OBJLIST,
VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE,
VALUE_STRSPLIT_MODE_MULTILIST,
VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE,
} ValueStrsplitMode;
@ -201,6 +202,9 @@ _value_strsplit (const char *value,
case VALUE_STRSPLIT_MODE_OBJLIST:
strv = nm_utils_strsplit_set (value, ",", FALSE);
break;
case VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE:
strv = nm_utils_strsplit_set (value, ",", TRUE);
break;
case VALUE_STRSPLIT_MODE_MULTILIST:
strv = nm_utils_strsplit_set (value, " \t,", FALSE);
break;
@ -227,6 +231,8 @@ _value_strsplit (const char *value,
if (split_mode == VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE)
_nm_utils_unescape_plain ((char *) s, MULTILIST_WITH_ESCAPE_CHARS, TRUE);
else if (split_mode == VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE)
_nm_utils_unescape_plain ((char *) s, ",", TRUE);
else
g_strchomp ((char *) s);
@ -1876,7 +1882,7 @@ _set_fcn_multilist (ARGS_SET_FCN)
}
strv = _value_strsplit (value,
property_info->property_typ_data->subtype.multilist.with_escaped_spaces
property_info->property_typ_data->subtype.multilist.strsplit_with_escape
? VALUE_STRSPLIT_MODE_MULTILIST_WITH_ESCAPE
: VALUE_STRSPLIT_MODE_MULTILIST,
&nstrv);
@ -3049,6 +3055,10 @@ _get_fcn_objlist (ARGS_GET_FCN)
num = property_info->property_typ_data->subtype.objlist.get_num_fcn (setting);
for (idx = 0; idx < num; idx++) {
#if NM_MORE_ASSERTS
gsize start_offset;
#endif
if (!str)
str = g_string_new (NULL);
else if (str->len > 0) {
@ -3059,10 +3069,32 @@ _get_fcn_objlist (ARGS_GET_FCN)
g_string_append (str, ", ");
}
#if NM_MORE_ASSERTS
start_offset = str->len;
#endif
property_info->property_typ_data->subtype.objlist.obj_to_str_fcn (get_type,
setting,
idx,
str);
#if NM_MORE_ASSERTS
nm_assert (start_offset < str->len);
if ( property_info->property_typ_data->subtype.objlist.strsplit_with_escape
&& get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) {
/* if the strsplit is done with VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE, then the appended
* value must have no unescaped ','. */
for (; start_offset < str->len; ) {
if (str->str[start_offset] == '\\') {
start_offset++;
nm_assert (start_offset < str->len);
nm_assert (!NM_IN_SET (str->str[start_offset], '\0'));
} else
nm_assert (!NM_IN_SET (str->str[start_offset], '\0', ','));
start_offset++;
}
}
#endif
}
NM_SET_OUT (out_is_default, num == 0);
@ -3237,7 +3269,9 @@ _set_fcn_objlist (ARGS_SET_FCN)
}
strv = _value_strsplit (value,
VALUE_STRSPLIT_MODE_OBJLIST,
property_info->property_typ_data->subtype.objlist.strsplit_with_escape
? VALUE_STRSPLIT_MODE_OBJLIST_WITH_ESCAPE
: VALUE_STRSPLIT_MODE_OBJLIST,
&nstrv);
if (_SET_FCN_DO_SET_ALL (modifier, value)) {
@ -3335,6 +3369,65 @@ _is_default_func_ip_config_dns_options (NMSetting *setting)
&& !nm_setting_ip_config_get_num_dns_options (NM_SETTING_IP_CONFIG (setting));
}
static void
_objlist_obj_to_str_fcn_ip_config_routing_rules (NMMetaAccessorGetType get_type,
NMSetting *setting,
guint idx,
GString *str)
{
NMIPRoutingRule *rule;
gs_free char *s = NULL;
rule = nm_setting_ip_config_get_routing_rule (NM_SETTING_IP_CONFIG (setting), idx);
s = nm_ip_routing_rule_to_string (rule,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE,
NULL,
NULL);
if (s)
g_string_append (str, s);
}
static gboolean
_objlist_set_fcn_ip_config_routing_rules (NMSetting *setting,
gboolean do_add,
const char *str,
GError **error)
{
NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
guint i, n;
rule = nm_ip_routing_rule_from_string (str,
( NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| ( NM_IS_SETTING_IP4_CONFIG (setting)
? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET
: NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6)),
NULL,
error);
if (!rule)
return FALSE;
/* also for @do_add, we first always search whether such a rule already exist
* and remove the first occurance.
*
* The effect is, that we don't add multiple times the same rule,
* and that if the rule already exists, it gets moved to the end (append).
*/
n = nm_setting_ip_config_get_num_routing_rules (s_ip);
for (i = 0; i < n; i++) {
NMIPRoutingRule *rr;
rr = nm_setting_ip_config_get_routing_rule (s_ip, i);
if (nm_ip_routing_rule_cmp (rule, rr) == 0) {
nm_setting_ip_config_remove_routing_rule (s_ip, i);
break;
}
}
if (do_add)
nm_setting_ip_config_add_routing_rule (s_ip, rule);
return TRUE;
}
static gconstpointer
_get_fcn_match_interface_name (ARGS_GET_FCN)
{
@ -5549,6 +5642,23 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
),
),
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTING_RULES, NULL,
.describe_message =
N_("Enter a list of IPv4 routing rules formatted as:\n"
" priority [prio] [from [src]] [to [dst]], ,...\n"
"\n"),
.property_type = &_pt_objlist,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
PROPERTY_TYP_DATA_SUBTYPE (objlist,
.get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingIPConfig, nm_setting_ip_config_get_num_routing_rules),
.clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingIPConfig, nm_setting_ip_config_clear_routing_rules),
.obj_to_str_fcn = _objlist_obj_to_str_fcn_ip_config_routing_rules,
.set_fcn = _objlist_set_fcn_ip_config_routing_rules,
.remove_by_idx_fcn_u = OBJLIST_REMOVE_BY_IDX_FCN_U (NMSettingIPConfig, nm_setting_ip_config_remove_routing_rule),
.strsplit_with_escape = TRUE,
),
),
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_IGNORE_AUTO_ROUTES,
.property_type = &_pt_gobject_bool,
),
@ -5740,6 +5850,23 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
),
),
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_ROUTING_RULES, NULL,
.describe_message =
N_("Enter a list of IPv6 routing rules formatted as:\n"
" priority [prio] [from [src]] [to [dst]], ,...\n"
"\n"),
.property_type = &_pt_objlist,
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
PROPERTY_TYP_DATA_SUBTYPE (objlist,
.get_num_fcn = OBJLIST_GET_NUM_FCN (NMSettingIPConfig, nm_setting_ip_config_get_num_routing_rules),
.clear_all_fcn = OBJLIST_CLEAR_ALL_FCN (NMSettingIPConfig, nm_setting_ip_config_clear_routing_rules),
.obj_to_str_fcn = _objlist_obj_to_str_fcn_ip_config_routing_rules,
.set_fcn = _objlist_set_fcn_ip_config_routing_rules,
.remove_by_idx_fcn_u = OBJLIST_REMOVE_BY_IDX_FCN_U (NMSettingIPConfig, nm_setting_ip_config_remove_routing_rule),
.strsplit_with_escape = TRUE,
),
),
),
PROPERTY_INFO (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
.property_type = &_pt_gobject_bool,
),
@ -5983,7 +6110,7 @@ static const NMMetaPropertyInfo *const property_infos_MATCH[] = {
.add2_fcn = MULTILIST_ADD2_FCN (NMSettingMatch, nm_setting_match_add_interface_name),
.remove_by_idx_fcn_s = MULTILIST_REMOVE_BY_IDX_FCN_S (NMSettingMatch, nm_setting_match_remove_interface_name),
.remove_by_value_fcn = MULTILIST_REMOVE_BY_VALUE_FCN (NMSettingMatch, nm_setting_match_remove_interface_name_by_value),
.with_escaped_spaces = TRUE,
.strsplit_with_escape = TRUE,
),
),
),

View file

@ -280,9 +280,7 @@ struct _NMMetaPropertyTypData {
void (*remove_by_idx_fcn_u) (NMSetting *setting, guint idx);
void (*remove_by_idx_fcn_s) (NMSetting *setting, int idx);
gboolean (*remove_by_value_fcn) (NMSetting *setting, const char *item);
/* if true, separate the list by space and allow backslash escaping. */
bool with_escaped_spaces:1;
bool strsplit_with_escape:1;
} multilist;
struct {
guint (*get_num_fcn) (NMSetting *setting);
@ -298,6 +296,7 @@ struct _NMMetaPropertyTypData {
void (*remove_by_idx_fcn_u) (NMSetting *setting, guint idx);
void (*remove_by_idx_fcn_s) (NMSetting *setting, int idx);
bool delimit_pretty_with_semicolon:1;
bool strsplit_with_escape:1;
} objlist;
struct {
gboolean (*set_fcn) (NMSetting *setting,

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -250,6 +250,8 @@ guint nm_setting_ethtool_init_features (NMSettingEthtool *setting,
guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length);
const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_case, char *buf, gsize buf_len);
gboolean nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name);
GSList * _nm_utils_hash_values_to_slist (GHashTable *hash);
GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
@ -611,6 +613,47 @@ void _nm_wireguard_peer_set_public_key_bin (NMWireGuardPeer *self,
/*****************************************************************************/
const NMIPAddr *nm_ip_routing_rule_get_from_bin (const NMIPRoutingRule *self);
void nm_ip_routing_rule_set_from_bin (NMIPRoutingRule *self,
gconstpointer from,
guint8 len);
const NMIPAddr *nm_ip_routing_rule_get_to_bin (const NMIPRoutingRule *self);
void nm_ip_routing_rule_set_to_bin (NMIPRoutingRule *self,
gconstpointer to,
guint8 len);
gboolean nm_ip_routing_rule_get_xifname_bin (const NMIPRoutingRule *self,
gboolean iif /* or else oif */,
char out_xifname[static 16]);
#define NM_IP_ROUTING_RULE_ATTR_ACTION "action"
#define NM_IP_ROUTING_RULE_ATTR_DPORT_END "dport-end"
#define NM_IP_ROUTING_RULE_ATTR_DPORT_START "dport-start"
#define NM_IP_ROUTING_RULE_ATTR_FAMILY "family"
#define NM_IP_ROUTING_RULE_ATTR_FROM "from"
#define NM_IP_ROUTING_RULE_ATTR_FROM_LEN "from-len"
#define NM_IP_ROUTING_RULE_ATTR_FWMARK "fwmark"
#define NM_IP_ROUTING_RULE_ATTR_FWMASK "fwmask"
#define NM_IP_ROUTING_RULE_ATTR_IIFNAME "iifname"
#define NM_IP_ROUTING_RULE_ATTR_INVERT "invert"
#define NM_IP_ROUTING_RULE_ATTR_IPPROTO "ipproto"
#define NM_IP_ROUTING_RULE_ATTR_OIFNAME "oifname"
#define NM_IP_ROUTING_RULE_ATTR_PRIORITY "priority"
#define NM_IP_ROUTING_RULE_ATTR_SPORT_END "sport-end"
#define NM_IP_ROUTING_RULE_ATTR_SPORT_START "sport-start"
#define NM_IP_ROUTING_RULE_ATTR_TABLE "table"
#define NM_IP_ROUTING_RULE_ATTR_TO "to"
#define NM_IP_ROUTING_RULE_ATTR_TOS "tos"
#define NM_IP_ROUTING_RULE_ATTR_TO_LEN "to-len"
NMIPRoutingRule *nm_ip_routing_rule_from_dbus (GVariant *variant,
gboolean strict,
GError **error);
GVariant *nm_ip_routing_rule_to_dbus (const NMIPRoutingRule *self);
/*****************************************************************************/
typedef struct _NMSettInfoSetting NMSettInfoSetting;
typedef struct _NMSettInfoProperty NMSettInfoProperty;

View file

@ -41,6 +41,8 @@
/*****************************************************************************/
typedef struct _ParseInfoProperty ParseInfoProperty;
typedef struct {
NMConnection *connection;
GKeyFile *keyfile;
@ -534,13 +536,19 @@ typedef struct {
const char *s_key;
gint32 key_idx;
gint8 key_type;
} IPAddrRouteBuildListData;
} BuildListData;
typedef enum {
BUILD_LIST_TYPE_ADDRESSES,
BUILD_LIST_TYPE_ROUTES,
BUILD_LIST_TYPE_ROUTING_RULES,
} BuildListType;
static int
_ip_addrroute_build_lst_data_cmp (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
_build_list_data_cmp (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
{
const IPAddrRouteBuildListData *a = p_a;
const IPAddrRouteBuildListData *b = p_b;
const BuildListData *a = p_a;
const BuildListData *b = p_b;
NM_CMP_FIELD (a, b, key_idx);
NM_CMP_FIELD (a, b, key_type);
@ -549,10 +557,25 @@ _ip_addrroute_build_lst_data_cmp (gconstpointer p_a, gconstpointer p_b, gpointer
}
static gboolean
ip_addrroute_match_key_w_name_ (const char *key,
const char *base_name,
gsize base_name_l,
gint32 *out_key_idx)
_build_list_data_is_shadowed (const BuildListData *build_list,
gsize build_list_len,
gsize idx)
{
/* the keyfile contains duplicate keys, which are both returned
* by g_key_file_get_keys() (WHY??).
*
* Skip the earlier one. */
return idx + 1 < build_list_len
&& build_list[idx].key_idx == build_list[idx + 1].key_idx
&& build_list[idx].key_type == build_list[idx + 1].key_type
&& nm_streq (build_list[idx].s_key, build_list[idx + 1].s_key);
}
static gboolean
_build_list_match_key_w_name_impl (const char *key,
const char *base_name,
gsize base_name_l,
gint32 *out_key_idx)
{
gint64 v;
@ -595,31 +618,85 @@ ip_addrroute_match_key_w_name_ (const char *key,
return TRUE;
}
static gboolean
ip_addrroute_match_key (const char *key,
gboolean is_routes,
gint32 *out_key_idx,
gint8 *out_key_type)
{
#define ip_addrroute_match_key_w_name(key, base_name, out_key_idx) \
ip_addrroute_match_key_w_name_ (key, base_name, NM_STRLEN (base_name), out_key_idx)
#define _build_list_match_key_w_name(key, base_name, out_key_idx) \
_build_list_match_key_w_name_impl (key, base_name, NM_STRLEN (base_name), out_key_idx)
if (is_routes) {
if (ip_addrroute_match_key_w_name (key, "route", out_key_idx))
NM_SET_OUT (out_key_type, 0);
else if (ip_addrroute_match_key_w_name (key, "routes", out_key_idx))
NM_SET_OUT (out_key_type, 1);
else
return FALSE;
} else {
if (ip_addrroute_match_key_w_name (key, "address", out_key_idx))
NM_SET_OUT (out_key_type, 0);
else if (ip_addrroute_match_key_w_name (key, "addresses", out_key_idx))
NM_SET_OUT (out_key_type, 1);
else
return FALSE;
static BuildListData *
_build_list_create (GKeyFile *keyfile,
const char *group_name,
BuildListType build_list_type,
gsize *out_build_list_len,
char ***out_keys_strv)
{
gs_strfreev char **keys = NULL;
gsize i_keys, n_keys;
gs_free BuildListData *build_list = NULL;
gsize build_list_len = 0;
nm_assert (out_build_list_len && *out_build_list_len == 0);
nm_assert (out_keys_strv && !*out_keys_strv);
keys = nm_keyfile_plugin_kf_get_keys (keyfile, group_name, &n_keys, NULL);
if (n_keys == 0)
return NULL;
for (i_keys = 0; i_keys < n_keys; i_keys++) {
const char *s_key = keys[i_keys];
gint32 key_idx;
gint8 key_type;
switch (build_list_type) {
case BUILD_LIST_TYPE_ROUTES:
if (_build_list_match_key_w_name (s_key, "route", &key_idx))
key_type = 0;
else if (_build_list_match_key_w_name (s_key, "routes", &key_idx))
key_type = 1;
else
continue;
break;
case BUILD_LIST_TYPE_ADDRESSES:
if (_build_list_match_key_w_name (s_key, "address", &key_idx))
key_type = 0;
else if (_build_list_match_key_w_name (s_key, "addresses", &key_idx))
key_type = 1;
else
continue;
break;
case BUILD_LIST_TYPE_ROUTING_RULES:
if (_build_list_match_key_w_name (s_key, "routing-rule", &key_idx))
key_type = 0;
else
continue;
break;
default:
nm_assert_not_reached ();
break;
}
if (G_UNLIKELY (!build_list))
build_list = g_new (BuildListData, n_keys - i_keys);
build_list[build_list_len++] = (BuildListData) {
.s_key = s_key,
.key_idx = key_idx,
.key_type = key_type,
};
}
return TRUE;
if (build_list_len == 0)
return NULL;
if (build_list_len > 1) {
g_qsort_with_data (build_list,
build_list_len,
sizeof (BuildListData),
_build_list_data_cmp,
NULL);
}
*out_build_list_len = build_list_len;
*out_keys_strv = g_steal_pointer (&keys);
return g_steal_pointer (&build_list);
}
static void
@ -631,64 +708,35 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c
gs_free char *gateway = NULL;
gs_unref_ptrarray GPtrArray *list = NULL;
gs_strfreev char **keys = NULL;
gsize i_keys, n_keys;
gs_free IPAddrRouteBuildListData *build_list = NULL;
gs_free BuildListData *build_list = NULL;
gsize i_build_list, build_list_len = 0;
keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
if (n_keys == 0)
build_list = _build_list_create (info->keyfile,
setting_name,
is_routes
? BUILD_LIST_TYPE_ROUTES
: BUILD_LIST_TYPE_ADDRESSES,
&build_list_len,
&keys);
if (!build_list)
return;
/* first create a list of all relevant keys, and sort them. */
for (i_keys = 0; i_keys < n_keys; i_keys++) {
const char *s_key = keys[i_keys];
gint32 key_idx;
gint8 key_type;
if (!ip_addrroute_match_key (s_key, is_routes, &key_idx, &key_type))
continue;
if (G_UNLIKELY (!build_list))
build_list = g_new (IPAddrRouteBuildListData, n_keys - i_keys);
build_list[build_list_len].s_key = s_key;
build_list[build_list_len].key_idx = key_idx;
build_list[build_list_len].key_type = key_type;
build_list_len++;
}
if (build_list_len == 0)
return;
g_qsort_with_data (build_list,
build_list_len,
sizeof (IPAddrRouteBuildListData),
_ip_addrroute_build_lst_data_cmp,
NULL);
list = g_ptr_array_new_with_free_func (is_routes
? (GDestroyNotify) nm_ip_route_unref
: (GDestroyNotify) nm_ip_address_unref);
for (i_build_list = 0; i_build_list < build_list_len; i_build_list++) {
const IPAddrRouteBuildListData *build_data = &build_list[i_build_list];
const char *s_key;
gpointer item;
if ( i_build_list + 1 < build_list_len
&& build_data->key_idx == build_data[1].key_idx
&& build_data->key_type == build_data[1].key_type
&& nm_streq (build_data->s_key, build_data[1].s_key)) {
/* the keyfile contains duplicate keys, which are both returned
* by g_key_file_get_keys() (WHY??).
*
* Skip the earlier one. */
if (_build_list_data_is_shadowed (build_list, build_list_len, i_build_list))
continue;
}
s_key = build_list[i_build_list].s_key;
item = read_one_ip_address_or_route (info,
setting_key,
setting_name,
build_data->s_key,
s_key,
is_ipv6,
is_routes,
gateway ? NULL : &gateway,
@ -696,7 +744,7 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c
if (item && is_routes) {
char options_key[128];
nm_sprintf_buf (options_key, "%s_options", build_data->s_key);
nm_sprintf_buf (options_key, "%s_options", s_key);
fill_route_attributes (info->keyfile,
item,
setting_name,
@ -718,6 +766,63 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c
g_object_set (setting, "gateway", gateway, NULL);
}
static void
ip_routing_rule_parser_full (KeyfileReaderInfo *info,
const NMMetaSettingInfo *setting_info,
const NMSettInfoProperty *property_info,
const ParseInfoProperty *pip,
NMSetting *setting)
{
const char *setting_name = nm_setting_get_name (setting);
gboolean is_ipv6 = nm_streq (setting_name, "ipv6");
gs_strfreev char **keys = NULL;
gs_free BuildListData *build_list = NULL;
gsize i_build_list, build_list_len = 0;
build_list = _build_list_create (info->keyfile,
setting_name,
BUILD_LIST_TYPE_ROUTING_RULES,
&build_list_len,
&keys);
if (!build_list)
return;
for (i_build_list = 0; i_build_list < build_list_len; i_build_list++) {
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
gs_free char *value = NULL;
gs_free_error GError *local = NULL;
if (_build_list_data_is_shadowed (build_list, build_list_len, i_build_list))
continue;
value = nm_keyfile_plugin_kf_get_string (info->keyfile,
setting_name,
build_list[i_build_list].s_key,
NULL);
if (!value)
continue;
rule = nm_ip_routing_rule_from_string (value,
( NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| ( is_ipv6
? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6
: NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET)),
NULL,
&local);
if (!rule) {
handle_warn (info, property_info->name, NM_KEYFILE_WARN_SEVERITY_WARN,
_("invalid value for \"%s\": %s"),
build_list[i_build_list].s_key,
local->message);
if (info->error)
return;
continue;
}
nm_setting_ip_config_add_routing_rule (NM_SETTING_IP_CONFIG (setting), rule);
}
}
static void
ip_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
{
@ -1921,6 +2026,40 @@ bridge_vlan_writer (KeyfileWriterInfo *info,
}
}
static void
ip_routing_rule_writer_full (KeyfileWriterInfo *info,
const NMMetaSettingInfo *setting_info,
const NMSettInfoProperty *property_info,
const ParseInfoProperty *pip,
NMSetting *setting)
{
const char *setting_name = nm_setting_get_name (setting);
NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
guint i, j, n;
char key_name_full[100] = "routing-rule";
char *key_name_num = &key_name_full[NM_STRLEN ("routing-rule")];
n = nm_setting_ip_config_get_num_routing_rules (s_ip);
j = 0;
for (i = 0; i < n; i++) {
NMIPRoutingRule *rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
gs_free char *str = NULL;
str = nm_ip_routing_rule_to_string (rule,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE,
NULL,
NULL);
if (!str)
continue;
sprintf (key_name_num, "%u", ++j);
nm_keyfile_plugin_kf_set_string (info->keyfile,
setting_name,
key_name_full,
str);
}
}
static void
qdisc_writer (KeyfileWriterInfo *info,
NMSetting *setting,
@ -2232,24 +2371,40 @@ cert_writer (KeyfileWriterInfo *info,
/*****************************************************************************/
typedef struct {
struct _ParseInfoProperty {
const char *property_name;
void (*parser) (KeyfileReaderInfo *info,
NMSetting *setting,
const char *key);
void (*writer) (KeyfileWriterInfo *info,
NMSetting *setting,
const char *key,
const GValue *value);
union {
void (*parser) (KeyfileReaderInfo *info,
NMSetting *setting,
const char *key);
void (*parser_full) (KeyfileReaderInfo *info,
const NMMetaSettingInfo *setting_info,
const NMSettInfoProperty *property_info,
const ParseInfoProperty *pip,
NMSetting *setting);
};
union {
void (*writer) (KeyfileWriterInfo *info,
NMSetting *setting,
const char *key,
const GValue *value);
void (*writer_full) (KeyfileWriterInfo *info,
const NMMetaSettingInfo *setting_info,
const NMSettInfoProperty *property_info,
const ParseInfoProperty *pip,
NMSetting *setting);
};
bool parser_skip;
bool parser_no_check_key:1;
bool writer_skip:1;
bool has_writer_full:1;
bool has_parser_full:1;
/* usually, we skip to write values that have their
* default value. By setting this flag to TRUE, also
* default values are written. */
bool writer_persist_default:1;
} ParseInfoProperty;
};
#define PARSE_INFO_PROPERTY(_property_name, ...) \
(&((const ParseInfoProperty) { \
@ -2406,6 +2561,13 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
.parser = ip_address_or_route_parser,
.writer = route_writer,
),
PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTING_RULES,
.parser_no_check_key = TRUE,
.parser_full = ip_routing_rule_parser_full,
.writer_full = ip_routing_rule_writer_full,
.has_parser_full = TRUE,
.has_writer_full = TRUE,
),
),
),
PARSE_INFO_SETTING (NM_META_SETTING_TYPE_IP6_CONFIG,
@ -2434,6 +2596,13 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
.parser = ip_address_or_route_parser,
.writer = route_writer,
),
PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTING_RULES,
.parser_no_check_key = TRUE,
.parser_full = ip_routing_rule_parser_full,
.writer_full = ip_routing_rule_writer_full,
.has_parser_full = TRUE,
.has_writer_full = TRUE,
),
),
),
PARSE_INFO_SETTING (NM_META_SETTING_TYPE_SERIAL,
@ -2612,7 +2781,7 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = {
static const ParseInfoProperty *
_parse_info_find (NMSetting *setting,
const char *property_name,
const char **out_setting_name)
const NMMetaSettingInfo **out_setting_info)
{
const NMMetaSettingInfo *setting_info;
const ParseInfoSetting *pis;
@ -2651,11 +2820,13 @@ _parse_info_find (NMSetting *setting,
if ( !NM_IS_SETTING (setting)
|| !(setting_info = NM_SETTING_GET_CLASS (setting)->setting_info)) {
/* handle invalid setting objects gracefully. */
*out_setting_name = NULL;
*out_setting_info = NULL;
return NULL;
}
*out_setting_name = setting_info->setting_name;
nm_assert (setting_info->setting_name);
*out_setting_info = setting_info;
if ((pis = parse_infos[setting_info->meta_type])) {
G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoProperty, property_name) == 0);
@ -2682,32 +2853,42 @@ read_one_setting_value (KeyfileReaderInfo *info,
{
GKeyFile *keyfile = info->keyfile;
gs_free_error GError *err = NULL;
const NMMetaSettingInfo *setting_info;
const ParseInfoProperty *pip;
gs_free char *tmp_str = NULL;
const char *setting_name;
const char *key;
GType type;
guint64 u64;
gint64 i64;
nm_assert (!info->error);
nm_assert ( !property_info->param_spec
|| nm_streq (property_info->param_spec->name, property_info->name));
key = property_info->name;
pip = _parse_info_find (setting, key, &setting_info);
nm_assert (setting_info);
if (!pip) {
if (nm_streq (key, NM_SETTING_NAME))
return;
if (!property_info->param_spec)
return;
if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)
return;
} else {
if (pip->parser_skip)
return;
if (pip->has_parser_full) {
pip->parser_full (info, setting_info, property_info, pip, setting);
return;
}
}
nm_assert (property_info->param_spec);
if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)
return;
key = property_info->param_spec->name;
pip = _parse_info_find (setting, key, &setting_name);
nm_assert (setting_name);
if ( !pip
&& nm_streq (key, NM_SETTING_NAME))
return;
if (pip && pip->parser_skip)
return;
nm_assert ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE);
/* Check for the exact key in the GKeyFile if required. Most setting
* properties map 1:1 to a key in the GKeyFile, but for those properties
@ -2715,7 +2896,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
* encoded by the setting property, this won't be true.
*/
if ( (!pip || !pip->parser_no_check_key)
&& !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) {
&& !nm_keyfile_plugin_kf_has_key (keyfile, setting_info->setting_name, key, &err)) {
/* Key doesn't exist or an error occurred, thus nothing to do. */
if (err) {
if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
@ -2726,7 +2907,8 @@ read_one_setting_value (KeyfileReaderInfo *info,
return;
}
if (pip && pip->parser) {
if ( pip
&& pip->parser) {
pip->parser (info, setting, key);
return;
}
@ -2736,11 +2918,11 @@ read_one_setting_value (KeyfileReaderInfo *info,
if (type == G_TYPE_STRING) {
gs_free char *str_val = NULL;
str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, &err);
str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_info->setting_name, key, &err);
if (!err)
nm_g_object_set_property_string_take (G_OBJECT (setting), key, g_steal_pointer (&str_val), &err);
} else if (type == G_TYPE_UINT) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64);
if ( u64 == G_MAXUINT64
@ -2751,7 +2933,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
nm_g_object_set_property_uint (G_OBJECT (setting), key, u64, &err);
}
} else if (type == G_TYPE_INT) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64);
if ( i64 == G_MININT64
@ -2764,11 +2946,11 @@ read_one_setting_value (KeyfileReaderInfo *info,
} else if (type == G_TYPE_BOOLEAN) {
gboolean bool_val;
bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, &err);
bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_info->setting_name, key, &err);
if (!err)
nm_g_object_set_property_boolean (G_OBJECT (setting), key, bool_val, &err);
} else if (type == G_TYPE_CHAR) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
/* As documented by glib, G_TYPE_CHAR is really a (signed!) gint8. */
i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT8, G_MAXINT8, G_MININT64);
@ -2780,7 +2962,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
nm_g_object_set_property_char (G_OBJECT (setting), key, i64, &err);
}
} else if (type == G_TYPE_UINT64) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT64, G_MAXUINT64);
if ( u64 == G_MAXUINT64
@ -2791,7 +2973,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
nm_g_object_set_property_uint64 (G_OBJECT (setting), key, u64, &err);
}
} else if (type == G_TYPE_INT64) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT64, G_MAXINT64, G_MAXINT64);
if ( i64 == G_MAXINT64
@ -2809,7 +2991,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
int i;
gboolean already_warned = FALSE;
tmp = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
tmp = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_info->setting_name, key, &length, NULL);
array = g_byte_array_sized_new (length);
for (i = 0; i < length; i++) {
@ -2836,14 +3018,14 @@ read_one_setting_value (KeyfileReaderInfo *info,
gs_strfreev char **sa = NULL;
gsize length;
sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_info->setting_name, key, &length, NULL);
g_object_set (setting, key, sa, NULL);
} else if (type == G_TYPE_HASH_TABLE) {
read_hash_of_string (keyfile, setting, key);
} else if (type == G_TYPE_ARRAY) {
read_array_of_uint (keyfile, setting, key);
} else if (G_TYPE_IS_FLAGS (type)) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64);
if ( u64 == G_MAXUINT64
@ -2854,7 +3036,7 @@ read_one_setting_value (KeyfileReaderInfo *info,
nm_g_object_set_property_flags (G_OBJECT (setting), key, type, u64, &err);
}
} else if (G_TYPE_IS_ENUM (type)) {
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_info->setting_name, key, &err);
if (!err) {
i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MAXINT64);
if ( i64 == G_MAXINT64
@ -2976,13 +3158,11 @@ _read_setting (KeyfileReaderInfo *info)
}
for (i = 0; i < sett_info->property_infos_len; i++) {
const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
if (property_info->param_spec) {
read_one_setting_value (info, setting, property_info);
if (info->error)
goto out;
}
read_one_setting_value (info,
setting,
&sett_info->property_infos[i]);
if (info->error)
goto out;
}
out:
@ -3291,40 +3471,47 @@ write_setting_value (KeyfileWriterInfo *info,
NMSetting *setting,
const NMSettInfoProperty *property_info)
{
const NMMetaSettingInfo *setting_info;
const ParseInfoProperty *pip;
const char *setting_name;
const char *key;
char numstr[64];
GValue value;
GType type;
nm_assert (!info->error);
nm_assert ( !property_info->param_spec
|| nm_streq (property_info->param_spec->name, property_info->name));
if (!property_info->param_spec)
return;
key = property_info->name;
key = property_info->param_spec->name;
pip = _parse_info_find (setting, key, &setting_info);
pip = _parse_info_find (setting, key, &setting_name);
if (!setting_name) {
/* the setting type is unknown. That is highly unexpected
* (and as this is currently only called from NetworkManager
* daemon, not possible).
*
* Still, handle it gracefully, because later keyfile writer will become
* public API of libnm, where @setting is (untrusted) user input.
*
* Gracefully here just means: ignore the setting. */
return;
if (!pip) {
if (!setting_info) {
/* the setting type is unknown. That is highly unexpected
* (and as this is currently only called from NetworkManager
* daemon, not possible).
*
* Still, handle it gracefully, because later keyfile writer will become
* public API of libnm, where @setting is (untrusted) user input.
*
* Gracefully here just means: ignore the setting. */
return;
}
if (!property_info->param_spec)
return;
if (nm_streq (key, NM_SETTING_NAME))
return;
} else {
if (pip->has_writer_full) {
pip->writer_full (info, setting_info, property_info, pip, setting);
return;
}
if (pip->writer_skip)
return;
}
if ( !pip
&& nm_streq (key, NM_SETTING_NAME))
return;
if (pip && pip->writer_skip)
return;
nm_assert (property_info->param_spec);
/* Don't write secrets that are owned by user secret agents or aren't
* supposed to be saved. VPN secrets are handled specially though since
@ -3348,11 +3535,12 @@ write_setting_value (KeyfileWriterInfo *info,
if ( (!pip || !pip->writer_persist_default)
&& g_param_value_defaults (property_info->param_spec, &value)) {
nm_assert (!g_key_file_has_key (info->keyfile, setting_name, key, NULL));
nm_assert (!g_key_file_has_key (info->keyfile, setting_info->setting_name, key, NULL));
goto out_unset_value;
}
if (pip && pip->writer) {
if ( pip
&& pip->writer) {
pip->writer (info, setting, key, &value);
goto out_unset_value;
}
@ -3363,27 +3551,27 @@ write_setting_value (KeyfileWriterInfo *info,
str = g_value_get_string (&value);
if (str)
nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
nm_keyfile_plugin_kf_set_string (info->keyfile, setting_info->setting_name, key, str);
} else if (type == G_TYPE_UINT) {
nm_sprintf_buf (numstr, "%u", g_value_get_uint (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (type == G_TYPE_INT) {
nm_sprintf_buf (numstr, "%d", g_value_get_int (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (type == G_TYPE_UINT64) {
nm_sprintf_buf (numstr, "%" G_GUINT64_FORMAT, g_value_get_uint64 (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (type == G_TYPE_INT64) {
nm_sprintf_buf (numstr, "%" G_GINT64_FORMAT, g_value_get_int64 (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (type == G_TYPE_BOOLEAN) {
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key,
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key,
g_value_get_boolean (&value)
? "true"
: "false");
} else if (type == G_TYPE_CHAR) {
nm_sprintf_buf (numstr, "%d", (int) g_value_get_schar (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (type == G_TYPE_BYTES) {
GBytes *bytes;
const guint8 *data;
@ -3393,22 +3581,22 @@ write_setting_value (KeyfileWriterInfo *info,
data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
if (data != NULL && len > 0)
nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len);
nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_info->setting_name, key, data, len);
} else if (type == G_TYPE_STRV) {
char **array;
array = (char **) g_value_get_boxed (&value);
nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const char **const) array, g_strv_length (array));
nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_info->setting_name, key, (const char **const) array, g_strv_length (array));
} else if (type == G_TYPE_HASH_TABLE) {
write_hash_of_string (info->keyfile, setting, key, &value);
} else if (type == G_TYPE_ARRAY) {
write_array_of_uint (info->keyfile, setting, key, &value);
} else if (G_VALUE_HOLDS_FLAGS (&value)) {
nm_sprintf_buf (numstr, "%u", g_value_get_flags (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else if (G_VALUE_HOLDS_ENUM (&value)) {
nm_sprintf_buf (numstr, "%d", g_value_get_enum (&value));
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
nm_keyfile_plugin_kf_set_value (info->keyfile, setting_info->setting_name, key, numstr);
} else
g_return_if_reached ();

File diff suppressed because it is too large Load diff

View file

@ -159,6 +159,153 @@ gboolean nm_ip_route_attribute_validate (const char *name,
#define NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND "lock-initrwnd"
#define NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU "lock-mtu"
/*****************************************************************************/
typedef struct NMIPRoutingRule NMIPRoutingRule;
NM_AVAILABLE_IN_1_18
GType nm_ip_routing_rule_get_type (void);
NM_AVAILABLE_IN_1_18
NMIPRoutingRule *nm_ip_routing_rule_new (int addr_family);
NM_AVAILABLE_IN_1_18
NMIPRoutingRule *nm_ip_routing_rule_new_clone (const NMIPRoutingRule *rule);
NM_AVAILABLE_IN_1_18
NMIPRoutingRule *nm_ip_routing_rule_ref (NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_unref (NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
gboolean nm_ip_routing_rule_is_sealed (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_seal (NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
int nm_ip_routing_rule_get_addr_family (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
gboolean nm_ip_routing_rule_get_invert (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_invert (NMIPRoutingRule *self, gboolean invert);
NM_AVAILABLE_IN_1_18
gint64 nm_ip_routing_rule_get_priority (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_priority (NMIPRoutingRule *self, gint64 priority);
NM_AVAILABLE_IN_1_18
guint8 nm_ip_routing_rule_get_tos (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_tos (NMIPRoutingRule *self, guint8 tos);
NM_AVAILABLE_IN_1_18
guint8 nm_ip_routing_rule_get_ipproto (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_ipproto (NMIPRoutingRule *self, guint8 ipproto);
NM_AVAILABLE_IN_1_18
guint16 nm_ip_routing_rule_get_source_port_start (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
guint16 nm_ip_routing_rule_get_source_port_end (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_source_port (NMIPRoutingRule *self, guint16 start, guint16 end);
NM_AVAILABLE_IN_1_18
guint16 nm_ip_routing_rule_get_destination_port_start (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
guint16 nm_ip_routing_rule_get_destination_port_end (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_destination_port (NMIPRoutingRule *self, guint16 start, guint16 end);
NM_AVAILABLE_IN_1_18
guint32 nm_ip_routing_rule_get_fwmark (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
guint32 nm_ip_routing_rule_get_fwmask (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_fwmark (NMIPRoutingRule *self, guint32 fwmark, guint32 fwmask);
NM_AVAILABLE_IN_1_18
guint8 nm_ip_routing_rule_get_from_len (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
const char *nm_ip_routing_rule_get_from (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_from (NMIPRoutingRule *self,
const char *from,
guint8 len);
NM_AVAILABLE_IN_1_18
guint8 nm_ip_routing_rule_get_to_len (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
const char *nm_ip_routing_rule_get_to (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_to (NMIPRoutingRule *self,
const char *to,
guint8 len);
NM_AVAILABLE_IN_1_18
const char *nm_ip_routing_rule_get_iifname (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_iifname (NMIPRoutingRule *self, const char *iifname);
NM_AVAILABLE_IN_1_18
const char *nm_ip_routing_rule_get_oifname (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_oifname (NMIPRoutingRule *self, const char *oifname);
NM_AVAILABLE_IN_1_18
guint8 nm_ip_routing_rule_get_action (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_action (NMIPRoutingRule *self, guint8 action);
NM_AVAILABLE_IN_1_18
guint32 nm_ip_routing_rule_get_table (const NMIPRoutingRule *self);
NM_AVAILABLE_IN_1_18
void nm_ip_routing_rule_set_table (NMIPRoutingRule *self, guint32 table);
NM_AVAILABLE_IN_1_18
int nm_ip_routing_rule_cmp (const NMIPRoutingRule *rule,
const NMIPRoutingRule *other);
NM_AVAILABLE_IN_1_18
gboolean nm_ip_routing_rule_validate (const NMIPRoutingRule *self,
GError **error);
/**
* NMIPRoutingRuleAsStringFlags:
* @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE: no flags selected.
* @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET: whether to allow parsing
* IPv4 addresses.
* @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6: whether to allow parsing
* IPv6 addresses. If both @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET and
* @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6 are unset, it's the same
* as setting them both.
* @NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE: if set, ensure that the
* rule verfies or fail.
*
* Since: 1.18
*/
typedef enum { /*< flags >*/
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE = 0,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET = 0x1,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6 = 0x2,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE = 0x4,
} NMIPRoutingRuleAsStringFlags;
NMIPRoutingRule *nm_ip_routing_rule_from_string (const char *str,
NMIPRoutingRuleAsStringFlags to_string_flags,
GHashTable *extra_args,
GError **error);
char *nm_ip_routing_rule_to_string (const NMIPRoutingRule *self,
NMIPRoutingRuleAsStringFlags to_string_flags,
GHashTable *extra_args,
GError **error);
/*****************************************************************************/
#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))
#define NM_SETTING_IP_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_IPCONFIG, NMSettingIPConfigClass))
@ -187,6 +334,9 @@ gboolean nm_ip_route_attribute_validate (const char *name,
#define NM_SETTING_IP_CONFIG_DAD_TIMEOUT "dad-timeout"
#define NM_SETTING_IP_CONFIG_DHCP_TIMEOUT "dhcp-timeout"
/* these are not real GObject properties. */
#define NM_SETTING_IP_CONFIG_ROUTING_RULES "routing-rules"
#define NM_SETTING_DNS_OPTION_DEBUG "debug"
#define NM_SETTING_DNS_OPTION_NDOTS "ndots"
#define NM_SETTING_DNS_OPTION_TIMEOUT "timeout"
@ -289,6 +439,20 @@ gint64 nm_setting_ip_config_get_route_metric (NMSettingIPConfig
NM_AVAILABLE_IN_1_10
guint32 nm_setting_ip_config_get_route_table (NMSettingIPConfig *setting);
NM_AVAILABLE_IN_1_18
guint nm_setting_ip_config_get_num_routing_rules (NMSettingIPConfig *setting);
NM_AVAILABLE_IN_1_18
NMIPRoutingRule *nm_setting_ip_config_get_routing_rule (NMSettingIPConfig *setting,
guint idx);
NM_AVAILABLE_IN_1_18
void nm_setting_ip_config_add_routing_rule (NMSettingIPConfig *setting,
NMIPRoutingRule *routing_rule);
NM_AVAILABLE_IN_1_18
void nm_setting_ip_config_remove_routing_rule (NMSettingIPConfig *setting,
guint idx);
NM_AVAILABLE_IN_1_18
void nm_setting_ip_config_clear_routing_rules (NMSettingIPConfig *setting);
gboolean nm_setting_ip_config_get_ignore_auto_routes (NMSettingIPConfig *setting);
gboolean nm_setting_ip_config_get_ignore_auto_dns (NMSettingIPConfig *setting);

View file

@ -4541,6 +4541,29 @@ _nm_utils_generate_mac_address_mask_parse (const char *value,
/*****************************************************************************/
gboolean
nm_utils_is_valid_iface_name_utf8safe (const char *utf8safe_name)
{
gs_free gpointer bin_to_free = NULL;
gconstpointer bin;
gsize len;
g_return_val_if_fail (utf8safe_name, FALSE);
bin = nm_utils_buf_utf8safe_unescape (utf8safe_name, &len, &bin_to_free);
if (bin_to_free) {
/* some unescaping happened... */
if (len != strlen (bin)) {
/* there are embedded NUL chars. Invalid. */
return FALSE;
}
}
return nm_utils_is_valid_iface_name (bin, NULL);
}
/**
* nm_utils_is_valid_iface_name:
* @name: Name of interface

View file

@ -2710,6 +2710,7 @@ test_connection_diff_a_only (void)
{ NM_SETTING_IP_CONFIG_ROUTES, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_ROUTE_METRIC, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_ROUTE_TABLE, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_ROUTING_RULES, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, NM_SETTING_DIFF_RESULT_IN_A },

View file

@ -20,6 +20,7 @@
#include "nm-default.h"
#include <linux/pkt_sched.h>
#include <net/if.h>
#include "nm-utils.h"
#include "nm-utils-private.h"
@ -2361,7 +2362,16 @@ test_roundtrip_conversion (gconstpointer test_data)
NMSettingConnection *s_con = NULL;
NMSettingWired *s_eth = NULL;
NMSettingWireGuard *s_wg = NULL;
union {
struct {
NMSettingIPConfig *s_6;
NMSettingIPConfig *s_4;
};
NMSettingIPConfig *s_x[2];
} s_ip;
int is_ipv4;
guint i;
gboolean success;
switch (MODE) {
case 0:
@ -2548,6 +2558,89 @@ test_roundtrip_conversion (gconstpointer test_data)
_rndt_wg_peers_assert_equal (s_wg, wg_peers, TRUE, TRUE, FALSE);
break;
case 3:
con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED));
g_assert (NM_IS_SETTING_WIRED (s_eth));
g_object_set (s_eth,
NM_SETTING_WIRED_MTU,
ETH_MTU,
NULL);
s_ip.s_4 = NM_SETTING_IP_CONFIG (nm_connection_get_setting (con, NM_TYPE_SETTING_IP4_CONFIG));
g_assert (NM_IS_SETTING_IP4_CONFIG (s_ip.s_4));
s_ip.s_6 = NM_SETTING_IP_CONFIG (nm_connection_get_setting (con, NM_TYPE_SETTING_IP6_CONFIG));
g_assert (NM_IS_SETTING_IP6_CONFIG (s_ip.s_6));
for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) {
g_assert (NM_IS_SETTING_IP_CONFIG (s_ip.s_x[is_ipv4]));
for (i = 0; i < 3; i++) {
char addrstr[NM_UTILS_INET_ADDRSTRLEN];
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL;
rr = nm_ip_routing_rule_new (is_ipv4 ? AF_INET : AF_INET6);
nm_ip_routing_rule_set_priority (rr, i + 1);
if (i > 0) {
if (is_ipv4)
nm_sprintf_buf (addrstr, "192.168.%u.0", i);
else
nm_sprintf_buf (addrstr, "1:2:3:%x::", 10 + i);
nm_ip_routing_rule_set_from (rr, addrstr, is_ipv4 ? 24 + i : 64 + i);
}
nm_ip_routing_rule_set_table (rr, 1000 + i);
success = nm_ip_routing_rule_validate (rr, &error);
nmtst_assert_success (success, error);
nm_setting_ip_config_add_routing_rule (s_ip.s_x[is_ipv4], rr);
}
}
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=ethernet\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ethernet]\n"
"mac-address-blacklist=\n"
"%s" /* mtu */
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=auto\n"
"routing-rule1=priority 1 from 0.0.0.0/0 table 1000\n"
"routing-rule2=priority 2 from 192.168.1.0/25 table 1001\n"
"routing-rule3=priority 3 from 192.168.2.0/26 table 1002\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=auto\n"
"routing-rule1=priority 1 from ::/0 table 1000\n"
"routing-rule2=priority 2 from 1:2:3:b::/65 table 1001\n"
"routing-rule3=priority 3 from 1:2:3:c::/66 table 1002\n"
"",
ID,
UUID,
INTERFACE_NAME,
(ETH_MTU != 0)
? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU)
: ""));
break;
default:
g_assert_not_reached ();
}
@ -2675,6 +2768,203 @@ test_roundtrip_conversion (gconstpointer test_data)
/*****************************************************************************/
static NMIPRoutingRule *
_rr_from_str_get_impl (const char *str, const char *const*aliases)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL;
gs_free_error GError *error = NULL;
gboolean vbool;
int addr_family;
int i;
NMIPRoutingRuleAsStringFlags to_string_flags;
rr = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE,
NULL,
&error);
nmtst_assert_success (rr, error);
addr_family = nm_ip_routing_rule_get_addr_family (rr);
g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
if (addr_family == AF_INET)
to_string_flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET;
else
to_string_flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6;
for (i = 0; TRUE; i++) {
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr2 = NULL;
gs_free char *str1 = NULL;
gs_unref_variant GVariant *variant1 = NULL;
const char *cstr1;
switch (i) {
case 0:
rr2 = nm_ip_routing_rule_ref (rr);
break;
case 1:
rr2 = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
case 2:
str1 = nm_ip_routing_rule_to_string (rr,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (str1 && str1[0], error);
g_assert_cmpstr (str, ==, str1);
rr2 = nm_ip_routing_rule_from_string (str1,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
case 3:
variant1 = nm_ip_routing_rule_to_dbus (rr);
g_assert (variant1);
g_assert (g_variant_is_floating (variant1));
g_assert (g_variant_is_of_type (variant1, G_VARIANT_TYPE_VARDICT));
rr2 = nm_ip_routing_rule_from_dbus (variant1,
TRUE,
&error);
nmtst_assert_success (rr, error);
break;
default:
if (!aliases || !aliases[0])
goto done;
cstr1 = (aliases++)[0];
rr2 = nm_ip_routing_rule_from_string (cstr1,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
}
g_assert (rr2);
vbool = nm_ip_routing_rule_validate (rr, &error);
nmtst_assert_success (vbool, error);
vbool = nm_ip_routing_rule_validate (rr2, &error);
nmtst_assert_success (vbool, error);
g_assert_cmpint (nm_ip_routing_rule_cmp (rr, rr2), ==, 0);
g_assert_cmpint (nm_ip_routing_rule_cmp (rr2, rr), ==, 0);
}
done:
return g_steal_pointer (&rr);
}
#define _rr_from_str_get(a, ...) _rr_from_str_get_impl (a, &(NM_MAKE_STRV (NULL, ##__VA_ARGS__))[1])
#define _rr_from_str(...) \
G_STMT_START { \
nm_auto_unref_ip_routing_rule NMIPRoutingRule *_rr = NULL; \
\
_rr = _rr_from_str_get (__VA_ARGS__); \
g_assert (_rr); \
} G_STMT_END
static void
test_routing_rule (gconstpointer test_data)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr1 = NULL;
gboolean success;
char ifname_buf[16];
_rr_from_str ("priority 5 from 0.0.0.0 table 1",
" from 0.0.0\\.0 \\priority 5 lookup 1 ");
_rr_from_str ("priority 5 from 0.0.0.0/0 table 4");
_rr_from_str ("priority 5 to 0.0.0.0 table 6");
_rr_from_str ("priority 5 to 0.0.0.0 table 254",
"priority 5 to 0.0.0.0/32");
_rr_from_str ("priority 5 from 1.2.3.4 table 15",
"priority 5 from 1.2.3.4/32 table 0xF ",
"priority 5 from 1.2.3.4/32 to 0.0.0.0/0 lookup 15 ");
_rr_from_str ("priority 5 from 1.2.3.4 to 0.0.0.0 table 8");
_rr_from_str ("priority 5 to a:b:c:: tos 0x16 table 25",
"priority 5 to a:b:c::/128 table 0x19 tos 16",
"priority 5 to a:b:c::/128 lookup 0x19 dsfield 16",
"priority 5 to a:b:c::/128 lookup 0x19 dsfield 16 fwmark 0/0x00",
"priority 5 to a:b:c:: from all lookup 0x19 dsfield 16 fwmark 0x0/0");
_rr_from_str ("priority 5 from :: fwmark 0 table 25",
"priority 5 from ::/128 to all table 0x19 fwmark 0/0xFFFFFFFF",
"priority 5 from :: to ::/0 table 0x19 fwmark 0x00/4294967295");
_rr_from_str ("priority 5 from :: iif aab table 25");
_rr_from_str ("priority 5 from :: iif aab oif er table 25",
"priority 5 from :: table 0x19 dev \\a\\a\\b oif er");
_rr_from_str ("priority 5 from :: iif a\\\\303b table 25");
_rr_from_str ("priority 5 to 0.0.0.0 sport 10 table 6",
"priority 5 to 0.0.0.0 sport 10-10 table 6");
_rr_from_str ("not priority 5 to 0.0.0.0 dport 10-133 table 6",
"priority 5 to 0.0.0.0 not dport 10-133 not table 6",
"priority 5 to 0.0.0.0 not dport 10-\\ 133 not table 6");
_rr_from_str ("priority 5 to 0.0.0.0 ipproto 10 sport 10 table 6");
rr1 = _rr_from_str_get ("priority 5 from :: iif aab table 25");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "aab");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "aab");
g_assert (success);
rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261xb table 254");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "a\\303\\261xb");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "a\303\261xb");
g_assert (success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 from :: oif \\\\101=\\\\303\\\\261xb table 7");
g_assert_cmpstr (nm_ip_routing_rule_get_oifname (rr1), ==, "\\101=\\303\\261xb");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "A=\303\261xb");
g_assert (success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert (!success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 to 0.0.0.0 tos 0x10 table 7");
g_assert_cmpstr (NULL, ==, nm_ip_routing_rule_get_from (rr1));
g_assert (!nm_ip_routing_rule_get_from_bin (rr1));
g_assert_cmpint (0, ==, nm_ip_routing_rule_get_from_len (rr1));
g_assert_cmpstr ("0.0.0.0", ==, nm_ip_routing_rule_get_to (rr1));
g_assert (nm_ip_addr_is_null (AF_INET, nm_ip_routing_rule_get_to_bin (rr1)));
g_assert_cmpint (32, ==, nm_ip_routing_rule_get_to_len (rr1));
g_assert_cmpint (7, ==, nm_ip_routing_rule_get_table (rr1));
g_assert_cmpint (0x10, ==, nm_ip_routing_rule_get_tos (rr1));
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261\\,x\\;b table 254",
"priority 5 from :: iif a\\\\303\\\\261,x;b table 254");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "a\\303\\261,x;b");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "a\303\261,x;b");
g_assert (success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
}
/*****************************************************************************/
NMTST_DEFINE ();
int
@ -2753,9 +3043,12 @@ main (int argc, char **argv)
g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config);
#endif
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/1", GINT_TO_POINTER (1), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/2", GINT_TO_POINTER (2), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/3", GINT_TO_POINTER (3), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/routing-rule/1", GINT_TO_POINTER (0), test_routing_rule);
return g_test_run ();
}

View file

@ -1536,13 +1536,57 @@ global:
nm_bridge_vlan_set_untagged;
nm_bridge_vlan_to_str;
nm_bridge_vlan_unref;
nm_ip_routing_rule_as_string_flags_get_type;
nm_ip_routing_rule_cmp;
nm_ip_routing_rule_from_string;
nm_ip_routing_rule_get_action;
nm_ip_routing_rule_get_addr_family;
nm_ip_routing_rule_get_destination_port_end;
nm_ip_routing_rule_get_destination_port_start;
nm_ip_routing_rule_get_from;
nm_ip_routing_rule_get_from_len;
nm_ip_routing_rule_get_fwmark;
nm_ip_routing_rule_get_fwmask;
nm_ip_routing_rule_get_iifname;
nm_ip_routing_rule_get_invert;
nm_ip_routing_rule_get_ipproto;
nm_ip_routing_rule_get_oifname;
nm_ip_routing_rule_get_priority;
nm_ip_routing_rule_get_source_port_end;
nm_ip_routing_rule_get_source_port_start;
nm_ip_routing_rule_get_table;
nm_ip_routing_rule_get_to;
nm_ip_routing_rule_get_to_len;
nm_ip_routing_rule_get_tos;
nm_ip_routing_rule_get_type;
nm_ip_routing_rule_is_sealed;
nm_ip_routing_rule_new;
nm_ip_routing_rule_new_clone;
nm_ip_routing_rule_ref;
nm_ip_routing_rule_seal;
nm_ip_routing_rule_set_action;
nm_ip_routing_rule_set_destination_port;
nm_ip_routing_rule_set_from;
nm_ip_routing_rule_set_fwmark;
nm_ip_routing_rule_set_iifname;
nm_ip_routing_rule_set_invert;
nm_ip_routing_rule_set_ipproto;
nm_ip_routing_rule_set_oifname;
nm_ip_routing_rule_set_priority;
nm_ip_routing_rule_set_source_port;
nm_ip_routing_rule_set_table;
nm_ip_routing_rule_set_to;
nm_ip_routing_rule_set_tos;
nm_ip_routing_rule_to_string;
nm_ip_routing_rule_unref;
nm_ip_routing_rule_validate;
nm_lldp_neighbor_get_attr_value;
nm_setting_bridge_add_vlan;
nm_setting_bridge_clear_vlans;
nm_setting_bridge_get_num_vlans;
nm_setting_bridge_get_vlan;
nm_setting_bridge_get_vlan_filtering;
nm_setting_bridge_get_vlan_default_pvid;
nm_setting_bridge_get_vlan_filtering;
nm_setting_bridge_port_add_vlan;
nm_setting_bridge_port_clear_vlans;
nm_setting_bridge_port_get_num_vlans;
@ -1551,4 +1595,9 @@ global:
nm_setting_bridge_port_remove_vlan_by_vid;
nm_setting_bridge_remove_vlan;
nm_setting_bridge_remove_vlan_by_vid;
nm_setting_ip_config_add_routing_rule;
nm_setting_ip_config_clear_routing_rules;
nm_setting_ip_config_get_num_routing_rules;
nm_setting_ip_config_get_routing_rule;
nm_setting_ip_config_remove_routing_rule;
} libnm_1_16_0;

View file

@ -38,6 +38,9 @@ NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref)
#define nm_auto_unref_ip_route nm_auto (_nm_auto_unref_ip_route)
NM_AUTO_DEFINE_FCN0 (NMIPRoute *, _nm_auto_unref_ip_route, nm_ip_route_unref)
#define nm_auto_unref_ip_routing_rule nm_auto(_nm_auto_unref_ip_routing_rule)
NM_AUTO_DEFINE_FCN0 (NMIPRoutingRule *, _nm_auto_unref_ip_routing_rule, nm_ip_routing_rule_unref)
#define nm_auto_unref_sriov_vf nm_auto (_nm_auto_unref_sriov_vf)
NM_AUTO_DEFINE_FCN0 (NMSriovVF *, _nm_auto_unref_sriov_vf, nm_sriov_vf_unref)

View file

@ -2505,6 +2505,59 @@ _nm_utils_unescape_plain (char *str, const char *candidates, gboolean do_strip)
return str;
}
char *
nm_utils_str_simpletokens_extract_next (char **p_line_start)
{
char *s_next;
char *s_start;
gsize j;
s_start = *p_line_start;
if (!s_start)
return NULL;
s_start = nm_str_skip_leading_spaces (s_start);
if (s_start[0] == '\0') {
*p_line_start = s_start;
return NULL;
}
s_next = s_start;
j = 0;
while (TRUE) {
if (s_next[0] == '\0') {
s_start[j] = '\0';
*p_line_start = s_next;
return s_start;
}
if (s_next[0] == '\\') {
s_next++;
if (s_next[0] == '\0') {
/* trailing backslash at end of word. That's an error,
* but we silently drop the backslash and signal success. */
*p_line_start = s_next;
if (j == 0)
return NULL;
s_start[j] = '\0';
return s_start;
}
s_start[j++] = (s_next++)[0];
continue;
}
if (!g_ascii_isspace (s_next[0])) {
s_start[j++] = (s_next++)[0];
continue;
}
nm_assert (j > 0);
s_start[j] = '\0';
*p_line_start = nm_str_skip_leading_spaces (&s_next[1]);
return s_start;
}
}
/*****************************************************************************/
typedef struct {

View file

@ -126,6 +126,16 @@ typedef struct {
extern const NMIPAddr nm_ip_addr_zero;
static inline gboolean
nm_ip_addr_is_null (int addr_family, gconstpointer addr)
{
nm_assert (addr);
if (addr_family == AF_INET6)
return IN6_IS_ADDR_UNSPECIFIED ((const struct in6_addr *) addr);
nm_assert (addr_family == AF_INET);
return ((const struct in_addr *) addr)->s_addr == 0;
}
static inline void
nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src)
{
@ -436,6 +446,8 @@ int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping);
char *nm_utils_str_simpletokens_extract_next (char **p_line_start);
gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
char **_nm_utils_strv_cleanup (char **strv,

View file

@ -23,6 +23,8 @@
#include "NetworkManagerUtils.h"
#include <linux/fib_rules.h>
#include "nm-utils/nm-c-list.h"
#include "nm-common-macros.h"
@ -908,6 +910,48 @@ nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
/*****************************************************************************/
NMPlatformRoutingRule *
nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
NMPlatformRoutingRule *out_pl)
{
nm_assert (rule);
nm_assert (nm_ip_routing_rule_validate (rule, NULL));
nm_assert (out_pl);
*out_pl = (NMPlatformRoutingRule) {
.addr_family = nm_ip_routing_rule_get_addr_family (rule),
.flags = ( nm_ip_routing_rule_get_invert (rule)
? FIB_RULE_INVERT
: 0),
.priority = nm_ip_routing_rule_get_priority (rule),
.tos = nm_ip_routing_rule_get_tos (rule),
.ip_proto = nm_ip_routing_rule_get_ipproto (rule),
.fwmark = nm_ip_routing_rule_get_fwmark (rule),
.fwmask = nm_ip_routing_rule_get_fwmask (rule),
.sport_range = {
.start = nm_ip_routing_rule_get_source_port_start (rule),
.end = nm_ip_routing_rule_get_source_port_end (rule),
},
.dport_range = {
.start = nm_ip_routing_rule_get_destination_port_start (rule),
.end = nm_ip_routing_rule_get_destination_port_end (rule),
},
.src = *(nm_ip_routing_rule_get_from_bin (rule) ?: &nm_ip_addr_zero),
.dst = *(nm_ip_routing_rule_get_to_bin (rule) ?: &nm_ip_addr_zero),
.src_len = nm_ip_routing_rule_get_from_len (rule),
.dst_len = nm_ip_routing_rule_get_to_len (rule),
.action = nm_ip_routing_rule_get_action (rule),
.table = nm_ip_routing_rule_get_table (rule),
};
nm_ip_routing_rule_get_xifname_bin (rule, TRUE, out_pl->iifname);
nm_ip_routing_rule_get_xifname_bin (rule, FALSE, out_pl->oifname);
return out_pl;
}
/*****************************************************************************/
struct _NMShutdownWaitObjHandle {
CList lst;
GObject *watched_obj;

View file

@ -24,6 +24,9 @@
#include "nm-core-utils.h"
#include "nm-setting-ip-config.h"
#include "platform/nm-platform.h"
/*****************************************************************************/
const char *nm_utils_get_ip_config_method (NMConnection *connection,
@ -60,6 +63,11 @@ int nm_match_spec_device_by_pllink (const NMPlatformLink *pllink,
/*****************************************************************************/
NMPlatformRoutingRule *nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
NMPlatformRoutingRule *out_pl);
/*****************************************************************************/
/* during shutdown, there are two relevant timeouts. One is
* NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all
* actions to complete. Of course, during shutdown components should hurry

View file

@ -47,6 +47,7 @@
#include "nm-manager.h"
#include "platform/nm-platform.h"
#include "platform/nmp-object.h"
#include "platform/nmp-rules-manager.h"
#include "ndisc/nm-ndisc.h"
#include "ndisc/nm-lndp-ndisc.h"
#include "dhcp/nm-dhcp-manager.h"
@ -6408,6 +6409,84 @@ lldp_init (NMDevice *self, gboolean restart)
}
}
/* set-mode can be:
* - TRUE: sync with new rules.
* - FALSE: sync, but remove all rules (== flush)
* - DEFAULT: forget about all the rules that we previously tracked,
* but don't actually remove them. This is when quitting NM
* we want to keep the rules.
* The problem is, after restart of NM, the rule manager will
* no longer remember that NM added these rules and treat them
* as externally added ones. Don't restart NetworkManager if
* you care about that.
*/
static void
_routing_rules_sync (NMDevice *self,
NMTernary set_mode)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self));
gboolean untrack_only_dirty = FALSE;
gboolean keep_deleted_rules;
gpointer user_tag;
user_tag = priv;
if (set_mode == NM_TERNARY_TRUE) {
NMConnection *applied_connection;
NMSettingIPConfig *s_ip;
guint i, num;
int is_ipv4;
untrack_only_dirty = TRUE;
nmp_rules_manager_set_dirty (rules_manager, user_tag);
applied_connection = nm_device_get_applied_connection (self);
for (is_ipv4 = 0; applied_connection && is_ipv4 < 2; is_ipv4++) {
int addr_family = is_ipv4 ? AF_INET : AF_INET6;
s_ip = nm_connection_get_setting_ip_config (applied_connection, addr_family);
if (!s_ip)
continue;
num = nm_setting_ip_config_get_num_routing_rules (s_ip);
for (i = 0; i < num; i++) {
NMPlatformRoutingRule plrule;
NMIPRoutingRule *rule;
rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
nm_ip_routing_rule_to_platform (rule, &plrule);
nmp_rules_manager_track (rules_manager,
&plrule,
10,
user_tag);
}
}
}
nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty);
keep_deleted_rules = FALSE;
if (set_mode == NM_TERNARY_DEFAULT) {
/* when exiting NM, we leave the device up and the rules configured.
* We just all nmp_rules_manager_sync() to forget about the synced rules,
* but we don't actually delete them.
*
* FIXME: that is a problem after restart of NetworkManager, because these
* rules will look like externally added, and NM will no longer remove
* them.
* To fix that, we could during "assume" mark the rules of the profile
* as owned (and "added" by the device). The problem with that is that it
* wouldn't cover rules that devices add by internal decision (not because
* of a setting in the profile, e.g. WireGuard could setup policy routing).
* Maybe it would be better to remember these orphaned rules at exit in a
* file and track them after restart again. */
keep_deleted_rules = TRUE;
}
nmp_rules_manager_sync (rules_manager, keep_deleted_rules);
}
static gboolean
tc_commit (NMDevice *self)
{
@ -6519,6 +6598,8 @@ activate_stage2_device_config (NMDevice *self)
}
}
_routing_rules_sync (self, NM_TERNARY_TRUE);
if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
if (no_firmware)
@ -14350,6 +14431,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
}
}
_routing_rules_sync (self,
cleanup_type == CLEANUP_TYPE_KEEP
? NM_TERNARY_DEFAULT
: NM_TERNARY_FALSE);
if (ifindex > 0)
nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL);

View file

@ -4144,7 +4144,7 @@ _nl_msg_new_routing_rule (int nlmsg_type,
.src_len = routing_rule->src_len,
.dst_len = routing_rule->dst_len,
.tos = routing_rule->tos,
.table = table,
.table = table < 0x100u ? (guint8) table : (guint8) RT_TABLE_UNSPEC,
.action = routing_rule->action,
/* we only allow setting the "not" flag. */

View file

@ -471,7 +471,8 @@ nmp_rules_manager_untrack_all (NMPRulesManager *self,
}
void
nmp_rules_manager_sync (NMPRulesManager *self)
nmp_rules_manager_sync (NMPRulesManager *self,
gboolean keep_deleted_rules)
{
const NMDedupMultiHeadEntry *pl_head_entry;
NMDedupMultiIter pl_iter;
@ -486,7 +487,7 @@ nmp_rules_manager_sync (NMPRulesManager *self)
if (!self->by_data)
return;
_LOGD ("sync");
_LOGD ("sync%s", keep_deleted_rules ? " (don't remove any rules)" : "");
pl_head_entry = nm_platform_lookup_obj_type (self->platform, NMP_OBJECT_TYPE_ROUTING_RULE);
if (pl_head_entry) {
@ -508,6 +509,11 @@ nmp_rules_manager_sync (NMPRulesManager *self)
obj_data->added_by_us = FALSE;
}
if (keep_deleted_rules) {
_LOGD ("forget/leak rule added by us: %s", nmp_object_to_string (plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
continue;
}
if (!rules_to_delete)
rules_to_delete = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref);
@ -558,6 +564,7 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
.priority = 0,
.table = RT_TABLE_LOCAL,
.action = FR_ACT_TO_TBL,
.protocol = RTPROT_KERNEL,
}),
priority,
user_tag);
@ -567,6 +574,7 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
.priority = 32766,
.table = RT_TABLE_MAIN,
.action = FR_ACT_TO_TBL,
.protocol = RTPROT_KERNEL,
}),
priority,
user_tag);
@ -576,6 +584,7 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
.priority = 32767,
.table = RT_TABLE_DEFAULT,
.action = FR_ACT_TO_TBL,
.protocol = RTPROT_KERNEL,
}),
priority,
user_tag);
@ -587,6 +596,7 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
.priority = 0,
.table = RT_TABLE_LOCAL,
.action = FR_ACT_TO_TBL,
.protocol = RTPROT_KERNEL,
}),
priority,
user_tag);
@ -596,6 +606,7 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
.priority = 32766,
.table = RT_TABLE_MAIN,
.action = FR_ACT_TO_TBL,
.protocol = RTPROT_KERNEL,
}),
priority,
user_tag);

View file

@ -54,7 +54,8 @@ void nmp_rules_manager_untrack_all (NMPRulesManager *self,
gconstpointer user_tag,
gboolean all /* or only dirty */);
void nmp_rules_manager_sync (NMPRulesManager *self);
void nmp_rules_manager_sync (NMPRulesManager *self,
gboolean keep_deleted_rules);
/*****************************************************************************/

View file

@ -1546,12 +1546,12 @@ again:
USER_TAG_2);
}
if (nmtst_get_rand_int () % objs_sync->len == 0) {
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1);
}
}
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len);
for (i = 0; i < objs_sync->len; i++) {
@ -1578,12 +1578,12 @@ again:
break;
}
if (nmtst_get_rand_int () % objs_sync->len == 0) {
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1);
}
}
nmp_rules_manager_sync (rules_manager);
nmp_rules_manager_sync (rules_manager, FALSE);
} else {
for (i = 0; i < objs->len;) {

View file

@ -2083,7 +2083,7 @@ make_ip6_setting (shvarFile *ifcfg,
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL);
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
/* Read static IP addresses.
* Read them even for AUTO and DHCP methods - in this case the addresses are
@ -4316,6 +4316,84 @@ parse_ethtool_option (const char *value,
}
}
static GPtrArray *
read_routing_rules_parse (shvarFile *ifcfg,
gboolean routes_read)
{
gs_unref_ptrarray GPtrArray *arr = NULL;
gs_free const char **keys = NULL;
guint i, len;
keys = svGetKeysSorted (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6, &len);
if (len == 0)
return NULL;
if (!routes_read) {
PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing rules (ROUTING_RULE*) settings are ignored");
return NULL;
}
arr = g_ptr_array_new_full (len, (GDestroyNotify) nm_ip_routing_rule_unref);
for (i = 0; i < len; i++) {
const char *key = keys[i];
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
gs_free_error GError *local = NULL;
gs_free char *value_to_free = NULL;
const char *value;
gboolean key_is_ipv4;
key_is_ipv4 = (key[NM_STRLEN ("ROUTING_RULE")] == '_');
nm_assert ( key_is_ipv4 == NM_STR_HAS_PREFIX (key, "ROUTING_RULE_"));
nm_assert (!key_is_ipv4 == NM_STR_HAS_PREFIX (key, "ROUTING_RULE6_"));
value = svGetValueStr (ifcfg, key, &value_to_free);
if (!value)
continue;
rule = nm_ip_routing_rule_from_string (value,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (key_is_ipv4
? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET
: NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6),
NULL,
&local);
if (!rule) {
PARSE_WARNING ("invalid routing rule %s=\"%s\": %s", key, value, local->message);
continue;
}
g_ptr_array_add (arr, g_steal_pointer (&rule));
}
if (arr->len == 0)
return NULL;
return g_steal_pointer (&arr);
}
static void
read_routing_rules (shvarFile *ifcfg,
gboolean routes_read,
NMSettingIPConfig *s_ip4,
NMSettingIPConfig *s_ip6)
{
gs_unref_ptrarray GPtrArray *routing_rules = NULL;
guint i;
routing_rules = read_routing_rules_parse (ifcfg, routes_read);
if (!routing_rules)
return;
for (i = 0; i < routing_rules->len; i++) {
NMIPRoutingRule *rule = routing_rules->pdata[i];
nm_setting_ip_config_add_routing_rule ( (nm_ip_routing_rule_get_addr_family (rule) == AF_INET)
? s_ip4
: s_ip6,
rule);
}
}
static void
parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection)
{
@ -5817,8 +5895,7 @@ connection_from_file_full (const char *filename,
error);
if (!s_ip6)
return NULL;
else
nm_connection_add_setting (connection, s_ip6);
nm_connection_add_setting (connection, s_ip6);
s_ip4 = make_ip4_setting (main_ifcfg,
network_ifcfg,
@ -5827,12 +5904,15 @@ connection_from_file_full (const char *filename,
error);
if (!s_ip4)
return NULL;
else {
read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
!has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
filename);
nm_connection_add_setting (connection, s_ip4);
}
read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
!has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
filename);
nm_connection_add_setting (connection, s_ip4);
read_routing_rules (main_ifcfg,
!has_complex_routes_v4 && !has_complex_routes_v6,
NM_SETTING_IP_CONFIG (s_ip4),
NM_SETTING_IP_CONFIG (s_ip6));
s_sriov = make_sriov_setting (main_ifcfg);
if (s_sriov)

View file

@ -2954,6 +2954,52 @@ write_ip6_setting (NMConnection *connection,
return TRUE;
}
static void
write_ip_routing_rules (NMConnection *connection,
shvarFile *ifcfg,
gboolean route_ignore)
{
gsize idx;
int is_ipv4;
svUnsetAll (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6);
if (route_ignore)
return;
idx = 0;
for (is_ipv4 = 1; is_ipv4 >= 0; is_ipv4--) {
const int addr_family = is_ipv4 ? AF_INET : AF_INET6;
NMSettingIPConfig *s_ip;
guint i, num;
s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
if (!s_ip)
continue;
num = nm_setting_ip_config_get_num_routing_rules (s_ip);
for (i = 0; i < num; i++) {
NMIPRoutingRule *rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
gs_free const char *s = NULL;
char key[64];
s = nm_ip_routing_rule_to_string (rule,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE,
NULL,
NULL);
if (!s)
continue;
if (is_ipv4)
numbered_tag (key, "ROUTING_RULE_", ++idx);
else
numbered_tag (key, "ROUTING_RULE6_", ++idx);
svSetValueStr (ifcfg, key, s);
}
}
}
static char *
escape_id (const char *id)
{
@ -3176,6 +3222,15 @@ do_write_construct (NMConnection *connection,
has_complex_routes_v4 ? "" : "6");
return FALSE;
}
if ( ( s_ip4
&& nm_setting_ip_config_get_num_routing_rules (s_ip4) > 0)
|| ( s_ip6
&& nm_setting_ip_config_get_num_routing_rules (s_ip6) > 0)) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"Cannot configure routing rules on a connection that has an associated 'rule%s-' file",
has_complex_routes_v4 ? "" : "6");
return FALSE;
}
route_ignore = TRUE;
} else
route_ignore = FALSE;
@ -3193,6 +3248,10 @@ do_write_construct (NMConnection *connection,
error))
return FALSE;
write_ip_routing_rules (connection,
ifcfg,
route_ignore);
write_connection_setting (s_con, ifcfg);
NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg));

View file

@ -879,10 +879,25 @@ _is_all_digits (const char *str)
#define IS_NUMBERED_TAG(key, tab_name) \
({ \
const char *_key = (key); \
const char *_key2 = (key); \
\
( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
&& _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
( (strncmp (_key2, tab_name, NM_STRLEN (tab_name)) == 0) \
&& _is_all_digits (&_key2[NM_STRLEN (tab_name)])); \
})
#define IS_NUMBERED_TAG_PARSE(key, tab_name, out_idx) \
({ \
const char *_key = (key); \
gint64 _idx; \
gboolean _good = FALSE; \
gint64 *_out_idx = (out_idx); \
\
if ( IS_NUMBERED_TAG (_key, ""tab_name"") \
&& (_idx = _nm_utils_ascii_str_to_int64 (&_key[NM_STRLEN (tab_name)], 10, 0, G_MAXINT64, -1)) != -1) { \
NM_SET_OUT (_out_idx, _idx); \
_good = TRUE; \
} \
_good; \
})
static gboolean
@ -919,10 +934,30 @@ _svKeyMatchesType (const char *key, SvKeyType match_key_type)
if (IS_NUMBERED_TAG (key, "SRIOV_VF"))
return TRUE;
}
if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) {
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", NULL))
return TRUE;
}
if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) {
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", NULL))
return TRUE;
}
return FALSE;
}
gint64
svNumberedParseKey (const char *key)
{
gint64 idx;
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", &idx))
return idx;
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", &idx))
return idx;
return -1;
}
GHashTable *
svGetKeys (shvarFile *s, SvKeyType match_key_type)
{
@ -947,6 +982,42 @@ svGetKeys (shvarFile *s, SvKeyType match_key_type)
return keys;
}
static int
_get_keys_sorted_cmp (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const char *k_a = *((const char *const*) a);
const char *k_b = *((const char *const*) b);
gint64 n_a;
gint64 n_b;
n_a = svNumberedParseKey (k_a);
n_b = svNumberedParseKey (k_b);
NM_CMP_DIRECT (n_a, n_b);
NM_CMP_RETURN (strcmp (k_a, k_b));
nm_assert_not_reached ();
return 0;
}
const char **
svGetKeysSorted (shvarFile *s,
SvKeyType match_key_type,
guint *out_len)
{
gs_unref_hashtable GHashTable *keys_hash = NULL;
keys_hash = svGetKeys (s, match_key_type);
if (!keys_hash) {
NM_SET_OUT (out_len, 0);
return NULL;
}
return (const char **) nm_utils_hash_keys_to_array (keys_hash,
_get_keys_sorted_cmp,
NULL,
out_len);
}
/*****************************************************************************/
const char *

View file

@ -40,6 +40,8 @@ typedef enum {
SV_KEY_TYPE_TC = (1LL << 3),
SV_KEY_TYPE_USER = (1LL << 4),
SV_KEY_TYPE_SRIOV_VF = (1LL << 5),
SV_KEY_TYPE_ROUTING_RULE4 = (1LL << 6),
SV_KEY_TYPE_ROUTING_RULE6 = (1LL << 7),
} SvKeyType;
const char *svFileGetName (const shvarFile *s);
@ -67,8 +69,14 @@ char *svGetValueStr_cp (shvarFile *s, const char *key);
int svParseBoolean (const char *value, int def);
gint64 svNumberedParseKey (const char *key);
GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type);
const char **svGetKeysSorted (shvarFile *s,
SvKeyType match_key_type,
guint *out_len);
/* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
* return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
* return <def> otherwise

View file

@ -0,0 +1,19 @@
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
ROUTING_RULE_1="priority 10 from 0.0.0.0/0 table 1"
ROUTING_RULE_2="priority 10 to 192.167.8.0/24 table 2"
ROUTING_RULE6_3="priority 10 from ::/0 table 10"
ROUTING_RULE6_4="priority 10 to 1:2:3::5/24 table 22"
ROUTING_RULE6_5="priority 10 to 1:3:3::5 table 55"
NAME="Test Write Routing Rules"
UUID=${UUID}
ONBOOT=yes

View file

@ -4465,6 +4465,101 @@ test_write_wired_dhcp (void)
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
}
static NMIPRoutingRule *
_ip_routing_rule_new (int addr_family,
const char *str)
{
NMIPRoutingRuleAsStringFlags flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE;
gs_free_error GError *local = NULL;
NMIPRoutingRule *rule;
if (addr_family != AF_UNSPEC) {
if (addr_family == AF_INET)
flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET;
else {
g_assert (addr_family == AF_INET6);
flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6;
}
}
rule = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| flags,
NULL,
nmtst_get_rand_bool () ? &local : NULL);
nmtst_assert_success (rule, local);
if (addr_family != AF_UNSPEC)
g_assert_cmpint (nm_ip_routing_rule_get_addr_family (rule), ==, addr_family);
return rule;
}
static void
_ip_routing_rule_add_to_setting (NMSettingIPConfig *s_ip,
const char *str)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
rule = _ip_routing_rule_new (nm_setting_ip_config_get_addr_family (s_ip), str);
nm_setting_ip_config_add_routing_rule (s_ip, rule);
}
static void
test_write_routing_rules (void)
{
nmtst_auto_unlinkfile char *testfile = NULL;
gs_unref_object NMConnection *connection = NULL;
gs_unref_object NMConnection *reread = NULL;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingIPConfig *s_ip4;
NMSettingIPConfig *s_ip6;
connection = nm_simple_connection_new ();
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_connection_add_setting (connection, NM_SETTING (s_con));
g_object_set (s_con,
NM_SETTING_CONNECTION_ID, "Test Write Routing Rules",
NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
NULL);
s_wired = (NMSettingWired *) nm_setting_wired_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wired));
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip4));
g_object_set (s_ip4,
NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
NULL);
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip6));
g_object_set (s_ip6,
NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
NULL);
_ip_routing_rule_add_to_setting (s_ip4, "pref 10 from 0.0.0.0/0 table 1");
_ip_routing_rule_add_to_setting (s_ip4, "priority 10 to 192.167.8.0/24 table 2");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 table 10");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:2:3::5/24 table 22");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:3:3::5/128 table 55");
nmtst_assert_connection_verifies (connection);
_writer_new_connec_exp (connection,
TEST_SCRATCH_DIR,
TEST_IFCFG_DIR"/ifcfg-Test_Write_Routing_Rules.cexpected",
&testfile);
reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
}
static void
test_write_wired_match (void)
{
@ -10200,6 +10295,7 @@ int main (int argc, char **argv)
g_test_add_func (TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip);
g_test_add_func (TPATH "wired/write/dhcp-8021x-peap-mschapv2", test_write_wired_dhcp_8021x_peap_mschapv2);
g_test_add_func (TPATH "wired/write/match", test_write_wired_match);
g_test_add_func (TPATH "wired/write/routing-rules", test_write_routing_rules);
#define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \
nmtst_add_test_func (testpath, test_write_wired_8021x_tls, GINT_TO_POINTER (scheme), GINT_TO_POINTER (flags))