mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-05 11:30:15 +01:00
rules: merge branch 'th/routing-rule-suppress-prefixlength'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/202
This commit is contained in:
commit
1bee1f5530
10 changed files with 327 additions and 128 deletions
|
|
@ -661,25 +661,26 @@ 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"
|
||||
#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_SUPPRESS_PREFIXLENGTH "suppress-prefixlength"
|
||||
#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,
|
||||
|
|
|
|||
|
|
@ -1392,6 +1392,7 @@ struct NMIPRoutingRule {
|
|||
guint ref_count;
|
||||
guint32 priority;
|
||||
guint32 table;
|
||||
gint32 suppress_prefixlength;
|
||||
guint32 fwmark;
|
||||
guint32 fwmask;
|
||||
guint16 sport_start;
|
||||
|
|
@ -1472,10 +1473,11 @@ nm_ip_routing_rule_new (int addr_family)
|
|||
|
||||
self = g_slice_new (NMIPRoutingRule);
|
||||
*self = (NMIPRoutingRule) {
|
||||
.ref_count = 1,
|
||||
.is_v4 = (addr_family == AF_INET),
|
||||
.action = FR_ACT_TO_TBL,
|
||||
.table = RT_TABLE_MAIN,
|
||||
.ref_count = 1,
|
||||
.is_v4 = (addr_family == AF_INET),
|
||||
.action = FR_ACT_TO_TBL,
|
||||
.table = RT_TABLE_MAIN,
|
||||
.suppress_prefixlength = -1,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
|
@ -1499,50 +1501,52 @@ nm_ip_routing_rule_new_clone (const NMIPRoutingRule *rule)
|
|||
|
||||
self = g_slice_new (NMIPRoutingRule);
|
||||
*self = (NMIPRoutingRule) {
|
||||
.ref_count = 1,
|
||||
.sealed = FALSE,
|
||||
.is_v4 = rule->is_v4,
|
||||
.ref_count = 1,
|
||||
.sealed = FALSE,
|
||||
.is_v4 = rule->is_v4,
|
||||
|
||||
.priority = rule->priority,
|
||||
.priority_has = rule->priority_has,
|
||||
.priority = rule->priority,
|
||||
.priority_has = rule->priority_has,
|
||||
|
||||
.invert = rule->invert,
|
||||
.invert = rule->invert,
|
||||
|
||||
.tos = rule->tos,
|
||||
.tos = rule->tos,
|
||||
|
||||
.fwmark = rule->fwmark,
|
||||
.fwmask = rule->fwmask,
|
||||
.fwmark = rule->fwmark,
|
||||
.fwmask = rule->fwmask,
|
||||
|
||||
.sport_start = rule->sport_start,
|
||||
.sport_end = rule->sport_end,
|
||||
.dport_start = rule->dport_start,
|
||||
.dport_end = rule->dport_end,
|
||||
.sport_start = rule->sport_start,
|
||||
.sport_end = rule->sport_end,
|
||||
.dport_start = rule->dport_start,
|
||||
.dport_end = rule->dport_end,
|
||||
|
||||
.ipproto = rule->ipproto,
|
||||
.ipproto = rule->ipproto,
|
||||
|
||||
.from_len = rule->from_len,
|
||||
.from_bin = rule->from_bin,
|
||||
.from_str = ( rule->from_has
|
||||
&& !rule->from_valid)
|
||||
? g_strdup (rule->from_str)
|
||||
: NULL,
|
||||
.from_has = rule->from_has,
|
||||
.from_valid = rule->from_valid,
|
||||
.from_len = rule->from_len,
|
||||
.from_bin = rule->from_bin,
|
||||
.from_str = ( rule->from_has
|
||||
&& !rule->from_valid)
|
||||
? g_strdup (rule->from_str)
|
||||
: NULL,
|
||||
.from_has = rule->from_has,
|
||||
.from_valid = rule->from_valid,
|
||||
|
||||
.to_len = rule->to_len,
|
||||
.to_bin = rule->to_bin,
|
||||
.to_str = ( rule->to_has
|
||||
&& !rule->to_valid)
|
||||
? g_strdup (rule->to_str)
|
||||
: NULL,
|
||||
.to_has = rule->to_has,
|
||||
.to_valid = rule->to_valid,
|
||||
.to_len = rule->to_len,
|
||||
.to_bin = rule->to_bin,
|
||||
.to_str = ( rule->to_has
|
||||
&& !rule->to_valid)
|
||||
? g_strdup (rule->to_str)
|
||||
: NULL,
|
||||
.to_has = rule->to_has,
|
||||
.to_valid = rule->to_valid,
|
||||
|
||||
.iifname = g_strdup (rule->iifname),
|
||||
.oifname = g_strdup (rule->oifname),
|
||||
.iifname = g_strdup (rule->iifname),
|
||||
.oifname = g_strdup (rule->oifname),
|
||||
|
||||
.action = rule->action,
|
||||
.table = rule->table,
|
||||
.action = rule->action,
|
||||
.table = rule->table,
|
||||
|
||||
.suppress_prefixlength = rule->suppress_prefixlength,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
|
@ -2325,6 +2329,38 @@ nm_ip_routing_rule_set_table (NMIPRoutingRule *self, guint32 table)
|
|||
self->table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_ip_routing_rule_get_suppress_prefixlength:
|
||||
* @self: the #NMIPRoutingRule instance
|
||||
*
|
||||
* Returns: the suppress_prefixlength of the rule. -1 means that the value is unset.
|
||||
*
|
||||
* Since: 1.20
|
||||
*/
|
||||
gint32
|
||||
nm_ip_routing_rule_get_suppress_prefixlength (const NMIPRoutingRule *self)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_IP_ROUTING_RULE (self, TRUE), -1);
|
||||
|
||||
return self->suppress_prefixlength;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_ip_routing_rule_set_suppress_prefixlength:
|
||||
* @self: the #NMIPRoutingRule instance
|
||||
* @suppress_prefixlength: the suppress_prefixlength to set. The value -1 means
|
||||
* unset.
|
||||
*
|
||||
* Since: 1.20
|
||||
*/
|
||||
void
|
||||
nm_ip_routing_rule_set_suppress_prefixlength (NMIPRoutingRule *self, gint32 suppress_prefixlength)
|
||||
{
|
||||
g_return_if_fail (NM_IS_IP_ROUTING_RULE (self, FALSE));
|
||||
|
||||
self->suppress_prefixlength = suppress_prefixlength;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_ip_routing_rule_cmp:
|
||||
* @rule: (allow-none): the #NMIPRoutingRule instance to compare
|
||||
|
|
@ -2361,6 +2397,8 @@ nm_ip_routing_rule_cmp (const NMIPRoutingRule *rule,
|
|||
|
||||
NM_CMP_FIELD (rule, other, table);
|
||||
|
||||
NM_CMP_FIELD (rule, other, suppress_prefixlength);
|
||||
|
||||
NM_CMP_FIELD (rule, other, sport_start);
|
||||
NM_CMP_FIELD (rule, other, sport_end);
|
||||
NM_CMP_FIELD (rule, other, dport_start);
|
||||
|
|
@ -2571,6 +2609,20 @@ nm_ip_routing_rule_validate (const NMIPRoutingRule *self,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (self->suppress_prefixlength != -1) {
|
||||
if ( self->suppress_prefixlength < -1
|
||||
|| self->suppress_prefixlength > (self->is_v4 ? 32 : 128)) {
|
||||
g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("suppress_prefixlength out of range"));
|
||||
return FALSE;
|
||||
}
|
||||
if (self->action != FR_ACT_TO_TBL) {
|
||||
g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("suppress_prefixlength is only allowed with the to-table action"));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -2593,6 +2645,7 @@ typedef enum {
|
|||
RR_DBUS_ATTR_SPORT_END,
|
||||
RR_DBUS_ATTR_SPORT_START,
|
||||
RR_DBUS_ATTR_TABLE,
|
||||
RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH,
|
||||
RR_DBUS_ATTR_TO,
|
||||
RR_DBUS_ATTR_TOS,
|
||||
RR_DBUS_ATTR_TO_LEN,
|
||||
|
|
@ -2607,25 +2660,26 @@ typedef struct {
|
|||
|
||||
static const RRDbusData rr_dbus_data[_RR_DBUS_ATTR_NUM] = {
|
||||
#define _D(attr, _name, type) [attr] = { .name = _name, .dbus_type = type, }
|
||||
_D (RR_DBUS_ATTR_ACTION, NM_IP_ROUTING_RULE_ATTR_ACTION, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_DPORT_END, NM_IP_ROUTING_RULE_ATTR_DPORT_END, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_DPORT_START, NM_IP_ROUTING_RULE_ATTR_DPORT_START, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_FAMILY, NM_IP_ROUTING_RULE_ATTR_FAMILY, G_VARIANT_TYPE_INT32),
|
||||
_D (RR_DBUS_ATTR_FROM, NM_IP_ROUTING_RULE_ATTR_FROM, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_FROM_LEN, NM_IP_ROUTING_RULE_ATTR_FROM_LEN, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_FWMARK, NM_IP_ROUTING_RULE_ATTR_FWMARK, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_FWMASK, NM_IP_ROUTING_RULE_ATTR_FWMASK, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_IIFNAME, NM_IP_ROUTING_RULE_ATTR_IIFNAME, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_INVERT, NM_IP_ROUTING_RULE_ATTR_INVERT, G_VARIANT_TYPE_BOOLEAN),
|
||||
_D (RR_DBUS_ATTR_IPPROTO, NM_IP_ROUTING_RULE_ATTR_IPPROTO, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_OIFNAME, NM_IP_ROUTING_RULE_ATTR_OIFNAME, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_PRIORITY, NM_IP_ROUTING_RULE_ATTR_PRIORITY, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_SPORT_END, NM_IP_ROUTING_RULE_ATTR_SPORT_END, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_SPORT_START, NM_IP_ROUTING_RULE_ATTR_SPORT_START, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_TABLE, NM_IP_ROUTING_RULE_ATTR_TABLE, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_TO, NM_IP_ROUTING_RULE_ATTR_TO, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_TOS, NM_IP_ROUTING_RULE_ATTR_TOS, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_TO_LEN, NM_IP_ROUTING_RULE_ATTR_TO_LEN, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_ACTION, NM_IP_ROUTING_RULE_ATTR_ACTION, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_DPORT_END, NM_IP_ROUTING_RULE_ATTR_DPORT_END, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_DPORT_START, NM_IP_ROUTING_RULE_ATTR_DPORT_START, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_FAMILY, NM_IP_ROUTING_RULE_ATTR_FAMILY, G_VARIANT_TYPE_INT32),
|
||||
_D (RR_DBUS_ATTR_FROM, NM_IP_ROUTING_RULE_ATTR_FROM, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_FROM_LEN, NM_IP_ROUTING_RULE_ATTR_FROM_LEN, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_FWMARK, NM_IP_ROUTING_RULE_ATTR_FWMARK, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_FWMASK, NM_IP_ROUTING_RULE_ATTR_FWMASK, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_IIFNAME, NM_IP_ROUTING_RULE_ATTR_IIFNAME, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_INVERT, NM_IP_ROUTING_RULE_ATTR_INVERT, G_VARIANT_TYPE_BOOLEAN),
|
||||
_D (RR_DBUS_ATTR_IPPROTO, NM_IP_ROUTING_RULE_ATTR_IPPROTO, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_OIFNAME, NM_IP_ROUTING_RULE_ATTR_OIFNAME, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_PRIORITY, NM_IP_ROUTING_RULE_ATTR_PRIORITY, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_SPORT_END, NM_IP_ROUTING_RULE_ATTR_SPORT_END, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_SPORT_START, NM_IP_ROUTING_RULE_ATTR_SPORT_START, G_VARIANT_TYPE_UINT16),
|
||||
_D (RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH, NM_IP_ROUTING_RULE_ATTR_SUPPRESS_PREFIXLENGTH, G_VARIANT_TYPE_INT32),
|
||||
_D (RR_DBUS_ATTR_TABLE, NM_IP_ROUTING_RULE_ATTR_TABLE, G_VARIANT_TYPE_UINT32),
|
||||
_D (RR_DBUS_ATTR_TO, NM_IP_ROUTING_RULE_ATTR_TO, G_VARIANT_TYPE_STRING),
|
||||
_D (RR_DBUS_ATTR_TOS, NM_IP_ROUTING_RULE_ATTR_TOS, G_VARIANT_TYPE_BYTE),
|
||||
_D (RR_DBUS_ATTR_TO_LEN, NM_IP_ROUTING_RULE_ATTR_TO_LEN, G_VARIANT_TYPE_BYTE),
|
||||
#undef _D
|
||||
};
|
||||
|
||||
|
|
@ -2788,6 +2842,9 @@ nm_ip_routing_rule_from_dbus (GVariant *variant,
|
|||
if (variants[RR_DBUS_ATTR_TABLE])
|
||||
nm_ip_routing_rule_set_table (self, g_variant_get_uint32 (variants[RR_DBUS_ATTR_TABLE]));
|
||||
|
||||
if (variants[RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH])
|
||||
nm_ip_routing_rule_set_suppress_prefixlength (self, g_variant_get_int32 (variants[RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH]));
|
||||
|
||||
if ( strict
|
||||
&& !nm_ip_routing_rule_validate (self, error))
|
||||
return NULL;
|
||||
|
|
@ -2889,6 +2946,9 @@ nm_ip_routing_rule_to_dbus (const NMIPRoutingRule *self)
|
|||
if (self->table != 0)
|
||||
_rr_to_dbus_add (&builder, RR_DBUS_ATTR_TABLE, g_variant_new_uint32 (self->table));
|
||||
|
||||
if (self->suppress_prefixlength != -1)
|
||||
_rr_to_dbus_add (&builder, RR_DBUS_ATTR_SUPPRESS_PREFIXLENGTH, g_variant_new_int32 (self->suppress_prefixlength));
|
||||
|
||||
return g_variant_builder_end (&builder);;
|
||||
}
|
||||
|
||||
|
|
@ -2965,6 +3025,7 @@ nm_ip_routing_rule_from_string (const char *str,
|
|||
gint64 i64_fwmask = -1;
|
||||
gint64 i64_sport_start = -1;
|
||||
gint64 i64_ipproto = -1;
|
||||
gint64 i64_suppress_prefixlength = -1;
|
||||
guint16 sport_end = 0;
|
||||
gint64 i64_dport_start = -1;
|
||||
guint16 dport_end = 0;
|
||||
|
|
@ -2992,7 +3053,8 @@ nm_ip_routing_rule_from_string (const char *str,
|
|||
* Of course, valid rules can be converted to string and read back the same (round-trip).
|
||||
*
|
||||
* - iproute2 in may regards is flexible about the command lines. For example
|
||||
* - for tables it accepts table names from /etc/iproute2/rt_tables
|
||||
* - for tables it accepts table names from /etc/iproute2/rt_tables. We only
|
||||
* accept the special aliases "main", "local", and "default".
|
||||
* - key names like "preference" can be abbreviated to "prefe", we don't do that.
|
||||
* - the "preference"/"priority" may be unspecified, in which kernel automatically
|
||||
* chooses an unused priority (during `ip rule add`). We don't allow for that, the
|
||||
|
|
@ -3072,8 +3134,16 @@ nm_ip_routing_rule_from_string (const char *str,
|
|||
if (i64_table != -1)
|
||||
goto next_fail_word0_duplicate_key;
|
||||
i64_table = _nm_utils_ascii_str_to_int64 (word1, 0, 1, G_MAXUINT32, -1);
|
||||
if (i64_table == -1)
|
||||
goto next_fail_word1_invalid_value;
|
||||
if (i64_table == -1) {
|
||||
if (nm_streq (word1, "main"))
|
||||
i64_table = RT_TABLE_MAIN;
|
||||
else if (nm_streq (word1, "local"))
|
||||
i64_table = RT_TABLE_LOCAL;
|
||||
else if (nm_streq (word1, "default"))
|
||||
i64_table = RT_TABLE_DEFAULT;
|
||||
else
|
||||
goto next_fail_word1_invalid_value;
|
||||
}
|
||||
goto next_words_consumed;
|
||||
}
|
||||
if (NM_IN_STRSET (word0, "tos",
|
||||
|
|
@ -3151,6 +3221,17 @@ nm_ip_routing_rule_from_string (const char *str,
|
|||
word_oifname = word1;
|
||||
goto next_words_consumed;
|
||||
}
|
||||
if (NM_IN_STRSET (word0, "suppress_prefixlength",
|
||||
"sup_pl")) {
|
||||
if (!word1)
|
||||
continue;
|
||||
if (i64_suppress_prefixlength != -1)
|
||||
goto next_fail_word0_duplicate_key;
|
||||
i64_suppress_prefixlength = _nm_utils_ascii_str_to_int64 (word1, 0, 0, G_MAXINT32, -1);;
|
||||
if (i64_suppress_prefixlength == -1)
|
||||
goto next_fail_word1_invalid_value;
|
||||
goto next_words_consumed;
|
||||
}
|
||||
|
||||
/* also the action is still unsupported. For the moment, we only support
|
||||
* FR_ACT_TO_TBL, which is the default (by not expressing it on the command
|
||||
|
|
@ -3244,6 +3325,9 @@ next_words_consumed:
|
|||
if (i64_dport_start != -1)
|
||||
nm_ip_routing_rule_set_destination_port (self, i64_dport_start, dport_end);
|
||||
|
||||
if (i64_suppress_prefixlength != -1)
|
||||
nm_ip_routing_rule_set_suppress_prefixlength (self, i64_suppress_prefixlength);
|
||||
|
||||
if ( val_from_len > 0
|
||||
|| ( val_from_len == 0
|
||||
&& !nm_ip_addr_is_null (addr_family, &val_from))) {
|
||||
|
|
@ -3480,6 +3564,12 @@ nm_ip_routing_rule_to_string (const NMIPRoutingRule *self,
|
|||
(guint) self->table);
|
||||
}
|
||||
|
||||
if (self->suppress_prefixlength != -1) {
|
||||
g_string_append_printf (nm_gstring_add_space_delimiter (str),
|
||||
"suppress_prefixlength %d",
|
||||
(int) self->suppress_prefixlength);
|
||||
}
|
||||
|
||||
return g_string_free (g_steal_pointer (&str), FALSE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,6 +262,11 @@ 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_20
|
||||
gint32 nm_ip_routing_rule_get_suppress_prefixlength (const NMIPRoutingRule *self);
|
||||
NM_AVAILABLE_IN_1_20
|
||||
void nm_ip_routing_rule_set_suppress_prefixlength (NMIPRoutingRule *self, gint32 suppress_prefixlength);
|
||||
|
||||
NM_AVAILABLE_IN_1_18
|
||||
int nm_ip_routing_rule_cmp (const NMIPRoutingRule *rule,
|
||||
const NMIPRoutingRule *other);
|
||||
|
|
|
|||
|
|
@ -1614,6 +1614,8 @@ global:
|
|||
nm_device_modem_get_apn;
|
||||
nm_device_modem_get_device_id;
|
||||
nm_device_modem_get_operator_code;
|
||||
nm_ip_routing_rule_get_suppress_prefixlength;
|
||||
nm_ip_routing_rule_set_suppress_prefixlength;
|
||||
nm_setting_connection_get_wait_device_timeout;
|
||||
nm_setting_ethtool_get_optnames;
|
||||
nm_setting_ovs_dpdk_get_devargs;
|
||||
|
|
|
|||
|
|
@ -923,29 +923,30 @@ nm_ip_routing_rule_to_platform (const NMIPRoutingRule *rule,
|
|||
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),
|
||||
.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),
|
||||
.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),
|
||||
.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),
|
||||
.suppress_prefixlen_inverse = ~((guint32) nm_ip_routing_rule_get_suppress_prefixlength (rule)),
|
||||
};
|
||||
|
||||
nm_ip_routing_rule_get_xifname_bin (rule, TRUE, out_pl->iifname);
|
||||
|
|
|
|||
|
|
@ -6618,10 +6618,15 @@ _routing_rules_sync (NMDevice *self,
|
|||
|
||||
rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
|
||||
nm_ip_routing_rule_to_platform (rule, &plrule);
|
||||
|
||||
/* We track this rule, but we also make it explicitly not weakly-tracked
|
||||
* (meaning to untrack NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG at
|
||||
* the same time). */
|
||||
nmp_rules_manager_track (rules_manager,
|
||||
&plrule,
|
||||
10,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,17 +127,27 @@ constructed (GObject *object)
|
|||
|
||||
priv->rules_manager = nmp_rules_manager_new (priv->platform);
|
||||
|
||||
/* Weakly track the default rules and rules that were added
|
||||
* outside of NetworkManager. */
|
||||
/* Weakly track the default rules with a dummy user-tag. These
|
||||
* rules are always weekly tracked... */
|
||||
nmp_rules_manager_track_default (priv->rules_manager,
|
||||
AF_UNSPEC,
|
||||
0,
|
||||
nm_netns_parent_class /* static dummy user-tag */);
|
||||
|
||||
/* Also weakly track all existing rules. These were added before NetworkManager
|
||||
* starts, so they are probably none of NetworkManager's business.
|
||||
*
|
||||
* However note that during service restart, devices may stay up and rules kept.
|
||||
* That means, after restart such rules may have been added by a previous run
|
||||
* of NetworkManager, we just don't know.
|
||||
*
|
||||
* For that reason, whenever we will touch such rules later one, we make them
|
||||
* fully owned and no longer weekly tracked. See %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. */
|
||||
nmp_rules_manager_track_from_platform (priv->rules_manager,
|
||||
NULL,
|
||||
AF_UNSPEC,
|
||||
0,
|
||||
nm_netns_parent_class /* static dummy user-tag */);
|
||||
NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG);
|
||||
|
||||
G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,17 @@ typedef enum {
|
|||
CONFIG_STATE_NONE = 0,
|
||||
CONFIG_STATE_ADDED_BY_US = 1,
|
||||
CONFIG_STATE_REMOVED_BY_US = 2,
|
||||
|
||||
/* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE).
|
||||
*
|
||||
* Maybe we would only need to track whether we touched the rule at all. But we
|
||||
* track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US)
|
||||
* or did we remove it (CONFIG_STATE_REMOVED_BY_US)?
|
||||
* Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively
|
||||
* add/remove it, but whenever we are about to undo the add/remove, we need to do it.
|
||||
* In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally
|
||||
* force the state next time when necessary. */
|
||||
CONFIG_STATE_OWNED_BY_US = 3,
|
||||
} ConfigState;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -111,8 +122,10 @@ typedef struct {
|
|||
* This makes NMPRulesManager stateful (beyond the configuration that indicates
|
||||
* which rules are tracked).
|
||||
* After a restart, NetworkManager would no longer remember which rules were added
|
||||
* by us. That would need to be fixed by persisting the state and reloading it after
|
||||
* restart. */
|
||||
* by us.
|
||||
*
|
||||
* That is partially fixed by NetworkManager taking over the rules that it
|
||||
* actively configures (see %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */
|
||||
ConfigState config_state;
|
||||
} RulesObjData;
|
||||
|
||||
|
|
@ -121,6 +134,15 @@ typedef struct {
|
|||
CList user_tag_lst_head;
|
||||
} RulesUserTagData;
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void _rules_data_untrack (NMPRulesManager *self,
|
||||
RulesData *rules_data,
|
||||
gboolean remove_user_tag_data,
|
||||
gboolean make_owned_by_us);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
_rules_data_assert (const RulesData *rules_data, gboolean linked)
|
||||
{
|
||||
|
|
@ -278,11 +300,31 @@ _rules_data_lookup (GHashTable *by_data,
|
|||
return g_hash_table_lookup (by_data, &rules_data_needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* nmp_rules_manager_track:
|
||||
* @self: the #NMPRulesManager instance
|
||||
* @routing_rule: the #NMPlatformRoutingRule to track or untrack
|
||||
* @track_priority: the priority for tracking the rule. Note that
|
||||
* negative values indicate a forced absence of the rule. Priorities
|
||||
* are compared with their absolute values (with higher absolute
|
||||
* value being more important). For example, if you track the same
|
||||
* rule twice, once with priority -5 and +10, then the rule is
|
||||
* present (because the positive number is more important).
|
||||
* The special value 0 indicates weakly-tracked rules.
|
||||
* @user_tag: the tag associated with tracking this rule. The same tag
|
||||
* must be used to untrack the rule later.
|
||||
* @user_tag_untrack: if not %NULL, at the same time untrack this user-tag
|
||||
* for the same rule. Note that this is different from a plain nmp_rules_manager_untrack(),
|
||||
* because it enforces ownership of the now tracked rule. On the other hand,
|
||||
* a plain nmp_rules_manager_untrack() merely forgets about the tracking.
|
||||
* The purpose here is to set this to %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG.
|
||||
*/
|
||||
void
|
||||
nmp_rules_manager_track (NMPRulesManager *self,
|
||||
const NMPlatformRoutingRule *routing_rule,
|
||||
gint32 track_priority,
|
||||
gconstpointer user_tag)
|
||||
gconstpointer user_tag,
|
||||
gconstpointer user_tag_untrack)
|
||||
{
|
||||
NMPObject obj_stack;
|
||||
const NMPObject *p_obj_stack;
|
||||
|
|
@ -359,6 +401,17 @@ nmp_rules_manager_track (NMPRulesManager *self,
|
|||
}
|
||||
}
|
||||
|
||||
if (user_tag_untrack) {
|
||||
if (user_tag != user_tag_untrack) {
|
||||
RulesData *rules_data_untrack;
|
||||
|
||||
rules_data_untrack = _rules_data_lookup (self->by_data, p_obj_stack, user_tag_untrack);
|
||||
if (rules_data_untrack)
|
||||
_rules_data_untrack (self, rules_data_untrack, FALSE, TRUE);
|
||||
} else
|
||||
nm_assert_not_reached ();
|
||||
}
|
||||
|
||||
_rules_data_assert (rules_data, TRUE);
|
||||
|
||||
if (changed) {
|
||||
|
|
@ -377,7 +430,8 @@ nmp_rules_manager_track (NMPRulesManager *self,
|
|||
static void
|
||||
_rules_data_untrack (NMPRulesManager *self,
|
||||
RulesData *rules_data,
|
||||
gboolean remove_user_tag_data)
|
||||
gboolean remove_user_tag_data,
|
||||
gboolean make_owned_by_us)
|
||||
{
|
||||
RulesObjData *obj_data;
|
||||
|
||||
|
|
@ -401,15 +455,22 @@ _rules_data_untrack (NMPRulesManager *self,
|
|||
#endif
|
||||
|
||||
nm_assert (!c_list_is_empty (&rules_data->user_tag_lst));
|
||||
if ( remove_user_tag_data
|
||||
&& c_list_length_is (&rules_data->user_tag_lst, 1))
|
||||
g_hash_table_remove (self->by_user_tag, &rules_data->user_tag);
|
||||
|
||||
obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj);
|
||||
nm_assert (obj_data);
|
||||
nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst));
|
||||
nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj));
|
||||
|
||||
if (make_owned_by_us) {
|
||||
if (obj_data->config_state == CONFIG_STATE_NONE) {
|
||||
/* we need to mark this entry that it requires a touch on the next
|
||||
* sync. */
|
||||
obj_data->config_state = CONFIG_STATE_OWNED_BY_US;
|
||||
}
|
||||
} else if ( remove_user_tag_data
|
||||
&& c_list_length_is (&rules_data->user_tag_lst, 1))
|
||||
g_hash_table_remove (self->by_user_tag, &rules_data->user_tag);
|
||||
|
||||
/* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry
|
||||
* around for the next sync -- so that we can undo what we did earlier. */
|
||||
if ( obj_data->config_state == CONFIG_STATE_NONE
|
||||
|
|
@ -440,7 +501,7 @@ nmp_rules_manager_untrack (NMPRulesManager *self,
|
|||
|
||||
rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag);
|
||||
if (rules_data)
|
||||
_rules_data_untrack (self, rules_data, TRUE);
|
||||
_rules_data_untrack (self, rules_data, TRUE, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -486,7 +547,7 @@ nmp_rules_manager_untrack_all (NMPRulesManager *self,
|
|||
c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) {
|
||||
if ( all
|
||||
|| rules_data->dirty)
|
||||
_rules_data_untrack (self, rules_data, FALSE);
|
||||
_rules_data_untrack (self, rules_data, FALSE, FALSE);
|
||||
}
|
||||
if (c_list_is_empty (&user_tag_data->user_tag_lst_head))
|
||||
g_hash_table_remove (self->by_user_tag, user_tag_data);
|
||||
|
|
@ -525,11 +586,17 @@ nmp_rules_manager_sync (NMPRulesManager *self,
|
|||
|
||||
rd_best = _rules_obj_get_best_data (obj_data);
|
||||
if (rd_best) {
|
||||
if (rd_best->track_priority_present)
|
||||
if (rd_best->track_priority_present) {
|
||||
if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US)
|
||||
obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
|
||||
continue;
|
||||
}
|
||||
if (rd_best->track_priority_val == 0) {
|
||||
if (obj_data->config_state != CONFIG_STATE_ADDED_BY_US)
|
||||
if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_ADDED_BY_US,
|
||||
CONFIG_STATE_OWNED_BY_US)) {
|
||||
obj_data->config_state = CONFIG_STATE_NONE;
|
||||
continue;
|
||||
}
|
||||
obj_data->config_state = CONFIG_STATE_NONE;
|
||||
}
|
||||
}
|
||||
|
|
@ -563,11 +630,17 @@ nmp_rules_manager_sync (NMPRulesManager *self,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!rd_best->track_priority_present)
|
||||
if (!rd_best->track_priority_present) {
|
||||
if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US)
|
||||
obj_data->config_state = CONFIG_STATE_REMOVED_BY_US;
|
||||
continue;
|
||||
}
|
||||
if (rd_best->track_priority_val == 0) {
|
||||
if (obj_data->config_state != CONFIG_STATE_REMOVED_BY_US)
|
||||
if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_REMOVED_BY_US,
|
||||
CONFIG_STATE_OWNED_BY_US)) {
|
||||
obj_data->config_state = CONFIG_STATE_NONE;
|
||||
continue;
|
||||
}
|
||||
obj_data->config_state = CONFIG_STATE_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -610,7 +683,7 @@ nmp_rules_manager_track_from_platform (NMPRulesManager *self,
|
|||
&& rr->addr_family != addr_family)
|
||||
continue;
|
||||
|
||||
nmp_rules_manager_track (self, rr, tracking_priority, user_tag);
|
||||
nmp_rules_manager_track (self, rr, tracking_priority, user_tag, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -638,7 +711,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
|
|||
.protocol = RTPROT_KERNEL,
|
||||
}),
|
||||
track_priority,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NULL);
|
||||
nmp_rules_manager_track (self,
|
||||
&((NMPlatformRoutingRule) {
|
||||
.addr_family = AF_INET,
|
||||
|
|
@ -648,7 +722,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
|
|||
.protocol = RTPROT_KERNEL,
|
||||
}),
|
||||
track_priority,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NULL);
|
||||
nmp_rules_manager_track (self,
|
||||
&((NMPlatformRoutingRule) {
|
||||
.addr_family = AF_INET,
|
||||
|
|
@ -658,7 +733,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
|
|||
.protocol = RTPROT_KERNEL,
|
||||
}),
|
||||
track_priority,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NULL);
|
||||
}
|
||||
if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) {
|
||||
nmp_rules_manager_track (self,
|
||||
|
|
@ -670,7 +746,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
|
|||
.protocol = RTPROT_KERNEL,
|
||||
}),
|
||||
track_priority,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NULL);
|
||||
nmp_rules_manager_track (self,
|
||||
&((NMPlatformRoutingRule) {
|
||||
.addr_family = AF_INET6,
|
||||
|
|
@ -680,7 +757,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self,
|
|||
.protocol = RTPROT_KERNEL,
|
||||
}),
|
||||
track_priority,
|
||||
user_tag);
|
||||
user_tag,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_rules_manager_new)
|
||||
|
||||
typedef struct _NMPRulesManager NMPRulesManager;
|
||||
|
||||
NMPRulesManager *nmp_rules_manager_new (NMPlatform *platform);
|
||||
|
|
@ -35,7 +37,8 @@ NM_AUTO_DEFINE_FCN0 (NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_mana
|
|||
void nmp_rules_manager_track (NMPRulesManager *self,
|
||||
const NMPlatformRoutingRule *routing_rule,
|
||||
gint32 track_priority,
|
||||
gconstpointer user_tag);
|
||||
gconstpointer user_tag,
|
||||
gconstpointer user_tag_untrack);
|
||||
|
||||
void nmp_rules_manager_track_default (NMPRulesManager *self,
|
||||
int addr_family,
|
||||
|
|
|
|||
|
|
@ -1534,14 +1534,16 @@ again:
|
|||
nmp_rules_manager_track (rules_manager,
|
||||
NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]),
|
||||
1,
|
||||
USER_TAG_1);
|
||||
USER_TAG_1,
|
||||
NULL);
|
||||
if (nmtst_get_rand_bool ()) {
|
||||
/* this has no effect, because a negative priority (of same absolute value)
|
||||
* has lower priority than the positive priority above. */
|
||||
nmp_rules_manager_track (rules_manager,
|
||||
NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]),
|
||||
-1,
|
||||
USER_TAG_2);
|
||||
USER_TAG_2,
|
||||
NULL);
|
||||
}
|
||||
if (nmtst_get_rand_uint32 () % objs_sync->len == 0) {
|
||||
nmp_rules_manager_sync (rules_manager, FALSE);
|
||||
|
|
@ -1566,13 +1568,15 @@ again:
|
|||
nmp_rules_manager_track (rules_manager,
|
||||
NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]),
|
||||
-1,
|
||||
USER_TAG_1);
|
||||
USER_TAG_1,
|
||||
NULL);
|
||||
break;
|
||||
case 2:
|
||||
nmp_rules_manager_track (rules_manager,
|
||||
NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]),
|
||||
-2,
|
||||
USER_TAG_2);
|
||||
USER_TAG_2,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
if (nmtst_get_rand_uint32 () % objs_sync->len == 0) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue