l3cfg: merge branch 'th/l3cfg-14'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/660
This commit is contained in:
Thomas Haller 2020-10-28 12:10:40 +01:00
commit 817ea086ee
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
12 changed files with 494 additions and 258 deletions

View file

@ -190,11 +190,11 @@ typedef uint64_t _nm_bitwise nm_be64_t;
/*****************************************************************************/
static inline uint32_t
nm_add_u32_clamped(uint32_t a, uint32_t b)
nm_add_clamped_u32(uint32_t a, uint32_t b)
{
uint32_t c;
/* returns the sum of a+b, or UINT32_MAX if the result would overflow. */
/* returns a+b, or UINT32_MAX if the result would overflow. */
c = a + b;
if (c < a)
@ -202,6 +202,24 @@ nm_add_u32_clamped(uint32_t a, uint32_t b)
return c;
}
static inline unsigned
nm_mult_clamped_u(unsigned a, unsigned b)
{
unsigned c;
/* returns a*b, or UINT_MAX if the result would overflow. */
if (b == 0)
return 0;
c = a * b;
if (c / b != a)
return (unsigned) -1;
return c;
}
/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate
* the argument possibly twice.
*

View file

@ -1636,3 +1636,203 @@ nm_utils_ip_routes_to_dbus(int addr_family,
NM_SET_OUT(out_route_data, g_variant_builder_end(&builder_data));
NM_SET_OUT(out_routes, g_variant_builder_end(&builder_legacy));
}
/*****************************************************************************/
typedef struct {
char *table;
char *rule;
} ShareRule;
struct _NMUtilsShareRules {
GArray *rules;
};
static void
_share_rule_clear(gpointer data)
{
ShareRule *rule = data;
g_free(rule->table);
g_free(rule->rule);
}
NMUtilsShareRules *
nm_utils_share_rules_new(void)
{
NMUtilsShareRules *self;
self = g_slice_new(NMUtilsShareRules);
*self = (NMUtilsShareRules){
.rules = g_array_sized_new(FALSE, FALSE, sizeof(ShareRule), 10),
};
g_array_set_clear_func(self->rules, _share_rule_clear);
return self;
}
void
nm_utils_share_rules_free(NMUtilsShareRules *self)
{
if (!self)
return;
g_array_unref(self->rules);
nm_g_slice_free(self);
}
void
nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take)
{
ShareRule *rule;
g_return_if_fail(self);
g_return_if_fail(table);
g_return_if_fail(rule_take);
rule = nm_g_array_append_new(self->rules, ShareRule);
*rule = (ShareRule){
.table = g_strdup(table),
.rule = g_steal_pointer(&rule_take),
};
}
void
nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared)
{
guint i;
g_return_if_fail(self);
if (self->rules->len == 0)
return;
/* depending on whether we share or unshare, we add/remote the rules
* in opposite order. */
if (shared)
i = self->rules->len - 1;
else
i = 0;
for (;;) {
gs_free_error GError *error = NULL;
ShareRule * rule;
gs_free const char ** argv = NULL;
gs_free char * cmd = NULL;
int status;
rule = &g_array_index(self->rules, ShareRule, i);
cmd = g_strdup_printf("%s --table %s %s %s",
IPTABLES_PATH,
rule->table,
shared ? "--insert" : "--delete",
rule->rule);
argv = nm_utils_strsplit_set(cmd, " ");
nm_log_info(LOGD_SHARING, "Executing: %s", cmd);
if (!g_spawn_sync("/",
(char **) argv,
(char **) NM_PTRARRAY_EMPTY(const char *),
G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
NULL,
NULL,
&status,
&error)) {
nm_log_warn(LOGD_SHARING, "Error executing command: %s", error->message);
goto next;
}
if (WEXITSTATUS(status)) {
nm_log_warn(LOGD_SHARING, "** Command returned exit status %d.", WEXITSTATUS(status));
}
next:
if (shared) {
if (i == 0)
break;
i--;
} else {
i++;
if (i >= self->rules->len)
break;
}
}
}
void
nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
const char * ip_iface,
in_addr_t addr,
guint plen)
{
in_addr_t netmask;
in_addr_t network;
char str_mask[NM_UTILS_INET_ADDRSTRLEN];
char str_addr[NM_UTILS_INET_ADDRSTRLEN];
nm_assert(self);
netmask = _nm_utils_ip4_prefix_to_netmask(plen);
_nm_utils_inet4_ntop(netmask, str_mask);
network = addr & netmask;
_nm_utils_inet4_ntop(network, str_addr);
nm_utils_share_rules_add_rule_v(
self,
"nat",
"POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE",
str_addr,
str_mask,
str_addr,
str_mask);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"FORWARD --destination %s/%s --out-interface %s --match state --state "
"ESTABLISHED,RELATED --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --source %s/%s --in-interface %s --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --in-interface %s --out-interface %s --jump ACCEPT",
ip_iface,
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --out-interface %s --jump REJECT",
ip_iface);
nm_utils_share_rules_add_rule_v(self,
"filter",
"FORWARD --in-interface %s --jump REJECT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_add_rule_v(
self,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT",
ip_iface);
}

View file

@ -224,4 +224,31 @@ NM_AUTO_DEFINE_FCN(NMDhcpLease *, _nm_auto_unref_dhcplease, nm_dhcp_lease_unref)
/*****************************************************************************/
typedef struct _NMUtilsShareRules NMUtilsShareRules;
NMUtilsShareRules *nm_utils_share_rules_new(void);
void nm_utils_share_rules_free(NMUtilsShareRules *self);
void
nm_utils_share_rules_add_rule_take(NMUtilsShareRules *self, const char *table, char *rule_take);
static inline void
nm_utils_share_rules_add_rule(NMUtilsShareRules *self, const char *table, const char *rule)
{
nm_utils_share_rules_add_rule_take(self, table, g_strdup(rule));
}
#define nm_utils_share_rules_add_rule_v(self, table, ...) \
nm_utils_share_rules_add_rule_take((self), (table), g_strdup_printf(__VA_ARGS__))
void nm_utils_share_rules_add_all_rules(NMUtilsShareRules *self,
const char * ip_iface,
in_addr_t addr,
guint plen);
void nm_utils_share_rules_apply(NMUtilsShareRules *self, gboolean shared);
/*****************************************************************************/
#endif /* __NETWORKMANAGER_UTILS_H__ */

View file

@ -11536,17 +11536,16 @@ activate_stage4_ip_config_timeout_6(NMDevice *self)
static gboolean
share_init(NMDevice *self, GError **error)
{
char * modules[] = {"ip_tables",
"iptable_nat",
"nf_nat_ftp",
"nf_nat_irc",
"nf_nat_sip",
"nf_nat_tftp",
"nf_nat_pptp",
"nf_nat_h323",
NULL};
char **iter;
int errsv;
const char *const modules[] = {"ip_tables",
"iptable_nat",
"nf_nat_ftp",
"nf_nat_irc",
"nf_nat_sip",
"nf_nat_tftp",
"nf_nat_pptp",
"nf_nat_h323"};
guint i;
int errsv;
if (nm_platform_sysctl_get_int32(nm_device_get_platform(self),
NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/ipv4/ip_forward"),
@ -11584,35 +11583,24 @@ share_init(NMDevice *self, GError **error)
nm_strerror_native(errsv));
}
for (iter = modules; *iter; iter++)
nm_utils_modprobe(NULL, FALSE, *iter, NULL);
for (i = 0; i < G_N_ELEMENTS(modules); i++)
nm_utils_modprobe(NULL, FALSE, modules[i], NULL);
return TRUE;
}
#define add_share_rule(req, table, ...) \
G_STMT_START \
{ \
char *_cmd = g_strdup_printf(__VA_ARGS__); \
nm_act_request_add_share_rule(req, table, _cmd); \
g_free(_cmd); \
} \
G_STMT_END
static gboolean
start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
{
NMDevicePrivate * priv = NM_DEVICE_GET_PRIVATE(self);
NMActRequest * req;
char str_addr[INET_ADDRSTRLEN];
char str_mask[INET_ADDRSTRLEN];
guint32 netmask, network;
const NMPlatformIP4Address *ip4_addr = NULL;
const char * ip_iface;
GError * local = NULL;
NMConnection * conn;
NMSettingConnection * s_con;
gboolean announce_android_metered;
NMUtilsShareRules * share_rules;
g_return_val_if_fail(config, FALSE);
@ -11637,57 +11625,13 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
req = nm_device_get_act_request(self);
g_return_val_if_fail(req, FALSE);
netmask = _nm_utils_ip4_prefix_to_netmask(ip4_addr->plen);
_nm_utils_inet4_ntop(netmask, str_mask);
share_rules = nm_utils_share_rules_new();
network = ip4_addr->address & netmask;
_nm_utils_inet4_ntop(network, str_addr);
nm_utils_share_rules_add_all_rules(share_rules, ip_iface, ip4_addr->address, ip4_addr->plen);
add_share_rule(req,
"nat",
"POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE",
str_addr,
str_mask,
str_addr,
str_mask);
add_share_rule(req,
"filter",
"FORWARD --destination %s/%s --out-interface %s --match state --state "
"ESTABLISHED,RELATED --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
add_share_rule(req,
"filter",
"FORWARD --source %s/%s --in-interface %s --jump ACCEPT",
str_addr,
str_mask,
ip_iface);
add_share_rule(req,
"filter",
"FORWARD --in-interface %s --out-interface %s --jump ACCEPT",
ip_iface,
ip_iface);
add_share_rule(req, "filter", "FORWARD --out-interface %s --jump REJECT", ip_iface);
add_share_rule(req, "filter", "FORWARD --in-interface %s --jump REJECT", ip_iface);
add_share_rule(req,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 67 --jump ACCEPT",
ip_iface);
add_share_rule(req,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 67 --jump ACCEPT",
ip_iface);
add_share_rule(req,
"filter",
"INPUT --in-interface %s --protocol udp --destination-port 53 --jump ACCEPT",
ip_iface);
add_share_rule(req,
"filter",
"INPUT --in-interface %s --protocol tcp --destination-port 53 --jump ACCEPT",
ip_iface);
nm_utils_share_rules_apply(share_rules, TRUE);
nm_act_request_set_shared(req, TRUE);
nm_act_request_set_shared(req, share_rules);
conn = nm_act_request_get_applied_connection(req);
s_con = nm_connection_get_setting_connection(conn);
@ -11722,7 +11666,7 @@ start_sharing(NMDevice *self, NMIP4Config *config, GError **error)
"could not start dnsmasq due to %s",
local->message);
g_error_free(local);
nm_act_request_set_shared(req, FALSE);
nm_act_request_set_shared(req, NULL);
return FALSE;
}

View file

@ -22,14 +22,8 @@
#include "nm-libnm-core-intern/nm-auth-subject.h"
typedef struct {
char *table;
char *rule;
} ShareRule;
typedef struct {
CList call_ids_lst_head;
gboolean shared;
GSList * share_rules;
CList call_ids_lst_head;
NMUtilsShareRules *share_rules;
} NMActRequestPrivate;
struct _NMActRequest {
@ -254,109 +248,32 @@ nm_act_request_clear_secrets(NMActRequest *self)
/*****************************************************************************/
static void
clear_share_rules(NMActRequest *req)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
GSList * iter;
for (iter = priv->share_rules; iter; iter = g_slist_next(iter)) {
ShareRule *rule = (ShareRule *) iter->data;
g_free(rule->table);
g_free(rule->rule);
g_free(rule);
}
g_slist_free(priv->share_rules);
priv->share_rules = NULL;
}
void
nm_act_request_set_shared(NMActRequest *req, gboolean shared)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
GSList * list, *iter;
g_return_if_fail(NM_IS_ACT_REQUEST(req));
NM_ACT_REQUEST_GET_PRIVATE(req)->shared = shared;
/* Tear the rules down in reverse order when sharing is stopped */
list = g_slist_copy(priv->share_rules);
if (!shared)
list = g_slist_reverse(list);
/* Send the rules to iptables */
for (iter = list; iter; iter = g_slist_next(iter)) {
ShareRule * rule = (ShareRule *) iter->data;
char * envp[1] = {NULL};
gs_strfreev char **argv = NULL;
gs_free char * cmd = NULL;
cmd = g_strdup_printf("%s --table %s %s %s",
IPTABLES_PATH,
rule->table,
shared ? "--insert" : "--delete",
rule->rule);
if (!cmd)
continue;
argv = g_strsplit(cmd, " ", 0);
if (argv && argv[0]) {
int status;
GError *error = NULL;
nm_log_info(LOGD_SHARING, "Executing: %s", cmd);
if (!g_spawn_sync("/",
argv,
envp,
G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
NULL,
NULL,
NULL,
NULL,
&status,
&error)) {
nm_log_warn(LOGD_SHARING, "Error executing command: %s", error->message);
g_clear_error(&error);
} else if (WEXITSTATUS(status)) {
nm_log_warn(LOGD_SHARING,
"** Command returned exit status %d.",
WEXITSTATUS(status));
}
}
}
g_slist_free(list);
/* Clear the share rule list when sharing is stopped */
if (!shared)
clear_share_rules(req);
}
gboolean
NMUtilsShareRules *
nm_act_request_get_shared(NMActRequest *req)
{
g_return_val_if_fail(NM_IS_ACT_REQUEST(req), FALSE);
return NM_ACT_REQUEST_GET_PRIVATE(req)->shared;
return NM_ACT_REQUEST_GET_PRIVATE(req)->share_rules;
}
void
nm_act_request_add_share_rule(NMActRequest *req, const char *table, const char *table_rule)
nm_act_request_set_shared(NMActRequest *req, NMUtilsShareRules *rules)
{
NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE(req);
ShareRule * rule;
g_return_if_fail(NM_IS_ACT_REQUEST(req));
g_return_if_fail(table != NULL);
g_return_if_fail(table_rule != NULL);
rule = g_malloc0(sizeof(ShareRule));
rule->table = g_strdup(table);
rule->rule = g_strdup(table_rule);
priv->share_rules = g_slist_prepend(priv->share_rules, rule);
if (priv->share_rules == rules)
return;
if (priv->share_rules) {
nm_utils_share_rules_apply(priv->share_rules, FALSE);
priv->share_rules = NULL;
}
if (rules) {
priv->share_rules = rules;
nm_utils_share_rules_apply(priv->share_rules, TRUE);
}
}
/*****************************************************************************/
@ -589,10 +506,9 @@ dispose(GObject *object)
c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
_do_cancel_secrets(self, call_id, TRUE);
/* Clear any share rules */
if (priv->share_rules) {
nm_act_request_set_shared(NM_ACT_REQUEST(object), FALSE);
clear_share_rules(NM_ACT_REQUEST(object));
nm_utils_share_rules_apply(priv->share_rules, FALSE);
nm_clear_pointer(&priv->share_rules, nm_utils_share_rules_free);
}
G_OBJECT_CLASS(nm_act_request_parent_class)->dispose(object);

View file

@ -36,11 +36,15 @@ NMSettingsConnection *nm_act_request_get_settings_connection(NMActRequest *req);
NMConnection *nm_act_request_get_applied_connection(NMActRequest *req);
gboolean nm_act_request_get_shared(NMActRequest *req);
/*****************************************************************************/
void nm_act_request_set_shared(NMActRequest *req, gboolean shared);
struct _NMUtilsShareRules;
void nm_act_request_add_share_rule(NMActRequest *req, const char *table, const char *rule);
struct _NMUtilsShareRules *nm_act_request_get_shared(NMActRequest *req);
void nm_act_request_set_shared(NMActRequest *req, struct _NMUtilsShareRules *rules);
/*****************************************************************************/
/* Secrets handling */

View file

@ -2600,7 +2600,7 @@ nm_l3_config_data_merge(NML3ConfigData * self,
if (r_src->metric_any) {
_ensure_r();
r.rx.metric_any = FALSE;
r.rx.metric = nm_add_u32_clamped(r.rx.metric, default_route_metric_x[IS_IPv4]);
r.rx.metric = nm_add_clamped_u32(r.rx.metric, default_route_metric_x[IS_IPv4]);
}
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT(r_src)) {

View file

@ -11,8 +11,16 @@
#define ADDR_IPV4LL_PREFIX_LEN 16
#define TIMED_OUT_TIME_FACTOR 5u
/*****************************************************************************/
typedef enum {
TIMED_OUT_STATE_IS_NOT_TIMED_OUT,
TIMED_OUT_STATE_IS_TIMED_OUT,
TIMED_OUT_STATE_HAVE_TIMER_RUNNING,
} TimedOutState;
struct _NML3IPv4LLRegistration {
NML3IPv4LL *self;
CList reg_lst;
@ -29,24 +37,26 @@ struct _NML3IPv4LL {
CList reg_lst_head;
NML3CfgCommitTypeHandle *l3cfg_commit_handle;
GSource * state_change_on_idle_source;
GSource * timed_out_source;
const NML3ConfigData * l3cd;
const NMPObject * plobj;
struct {
nm_le64_t value;
nm_le64_t generation;
} seed;
gint64 timed_out_expiry_msec;
gulong l3cfg_signal_notify_id;
gint64 last_good_at_msec : 1;
NML3IPv4LLState state;
NMEtherAddr seed_mac;
NMEtherAddr mac;
bool seed_set : 1;
bool seed_reset_generation : 1;
bool mac_set : 1;
bool link_seen_not_ready : 1;
bool notify_on_idle : 1;
bool reg_changed : 1;
bool l3cd_timeout_msec_changed : 1;
/* not yet used. */
bool seed_reset_generation : 1;
};
G_STATIC_ASSERT(G_STRUCT_OFFSET(NML3IPv4LL, ref_count) == sizeof(gpointer));
@ -77,6 +87,8 @@ static void _ipv4ll_state_change_on_idle(NML3IPv4LL *self);
static void _ipv4ll_state_change(NML3IPv4LL *self, gboolean is_on_idle_handler);
static void _ipv4ll_set_timed_out_update(NML3IPv4LL *self, TimedOutState new_state);
/*****************************************************************************/
NM_UTILS_ENUM2STR_DEFINE(nm_l3_ipv4ll_state_to_string,
@ -140,6 +152,22 @@ nm_l3_ipv4ll_get_state(NML3IPv4LL *self)
return self->state;
}
static gboolean
_ipv4ll_is_timed_out(NML3IPv4LL *self)
{
_ASSERT(self);
return self->timed_out_expiry_msec != 0 && !self->timed_out_source;
}
gboolean
nm_l3_ipv4ll_is_timed_out(NML3IPv4LL *self)
{
nm_assert(NM_IS_L3_IPV4LL(self));
return _ipv4ll_is_timed_out(self);
}
in_addr_t
nm_l3_ipv4ll_get_addr(NML3IPv4LL *self)
{
@ -156,6 +184,20 @@ nm_l3_ipv4ll_get_l3cd(NML3IPv4LL *self)
return self->l3cd;
}
static void
_ipv4ll_emit_signal_notify(NML3IPv4LL *self)
{
NML3ConfigNotifyData notify_data;
self->notify_on_idle = FALSE;
notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT;
notify_data.ipv4ll_event = (typeof(notify_data.ipv4ll_event)){
.ipv4ll = self,
};
_nm_l3cfg_emit_signal_notify(self->l3cfg, &notify_data);
}
/*****************************************************************************/
static NML3IPv4LLRegistration *
@ -272,23 +314,6 @@ _acd_info_is_good(const NML3AcdAddrInfo *acd_info)
return FALSE;
}
static gboolean
_plobj_link_is_ready(const NMPObject *plobj)
{
const NMPlatformLink *pllink;
if (!plobj)
return FALSE;
pllink = NMP_OBJECT_CAST_LINK(plobj);
if (!NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP))
return FALSE;
if (pllink->l_address.len != ETH_ALEN)
return FALSE;
return TRUE;
}
/*****************************************************************************/
static NMPlatformIP4Address *
@ -693,6 +718,67 @@ _ipv4ll_platform_find_addr(NML3IPv4LL *self, const NML3AcdAddrInfo **out_acd_inf
/*****************************************************************************/
static gboolean
_ipv4ll_set_timed_out_timeout_cb(gpointer user_data)
{
NML3IPv4LL *self = user_data;
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_IS_TIMED_OUT);
if (self->notify_on_idle)
_ipv4ll_emit_signal_notify(self);
return G_SOURCE_REMOVE;
}
static void
_ipv4ll_set_timed_out_update(NML3IPv4LL *self, TimedOutState new_state)
{
gboolean before;
before = _ipv4ll_is_timed_out(self);
switch (new_state) {
case TIMED_OUT_STATE_IS_TIMED_OUT:
if (self->timed_out_expiry_msec == 0) {
nm_assert(!self->timed_out_source);
self->timed_out_expiry_msec = 1;
}
nm_clear_g_source_inst(&self->timed_out_source);
break;
case TIMED_OUT_STATE_IS_NOT_TIMED_OUT:
self->timed_out_expiry_msec = 0;
nm_clear_g_source_inst(&self->timed_out_source);
break;
case TIMED_OUT_STATE_HAVE_TIMER_RUNNING:
{
gint64 now_msec = nm_utils_get_monotonic_timestamp_msec();
guint timeout_msec;
gint64 expiry_msec;
nm_assert(self->reg_timeout_msec > 0u);
timeout_msec = nm_mult_clamped_u(TIMED_OUT_TIME_FACTOR, self->reg_timeout_msec);
expiry_msec = now_msec + timeout_msec;
if (self->timed_out_expiry_msec == 0 || self->timed_out_expiry_msec < expiry_msec) {
self->timed_out_expiry_msec = expiry_msec;
nm_clear_g_source_inst(&self->timed_out_source);
self->timed_out_source = nm_g_timeout_source_new(timeout_msec,
G_PRIORITY_DEFAULT,
_ipv4ll_set_timed_out_timeout_cb,
self,
NULL);
g_source_attach(self->timed_out_source, NULL);
}
break;
}
}
if (before != _ipv4ll_is_timed_out(self)) {
self->notify_on_idle = TRUE;
_LOGT("state: set timed-out-is-bad=%d", (!before));
}
}
static gboolean
_ipv4ll_set_state(NML3IPv4LL *self, NML3IPv4LLState state)
{
@ -731,6 +817,8 @@ _ipv4ll_state_change(NML3IPv4LL *self, gboolean is_on_idle_handler)
if (self->reg_changed) {
guint timeout_msec = self->reg_timeout_msec;
self->reg_changed = FALSE;
if (c_list_is_empty(&self->reg_lst_head))
timeout_msec = 0;
else {
@ -749,12 +837,14 @@ _ipv4ll_state_change(NML3IPv4LL *self, gboolean is_on_idle_handler)
}
if (self->reg_timeout_msec == 0) {
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_IS_NOT_TIMED_OUT);
if (_ipv4ll_set_state(self, NM_L3_IPV4LL_STATE_DISABLED))
_l3cd_config_remove(self);
goto out_notify;
}
if (!self->mac_set) {
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_HAVE_TIMER_RUNNING);
if (_ipv4ll_set_state(self, NM_L3_IPV4LL_STATE_WAIT_FOR_LINK))
_l3cd_config_remove(self);
else
@ -773,7 +863,12 @@ _ipv4ll_state_change(NML3IPv4LL *self, gboolean is_on_idle_handler)
if (pladdr) {
/* we have an externally configured address. Check whether we can use it. */
goto out_set_external_pladdr;
self->addr = pladdr->address;
self->notify_on_idle = TRUE;
_ipv4ll_set_state(self, NM_L3_IPV4LL_STATE_EXTERNAL);
_l3cd_config_add(self);
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_IS_NOT_TIMED_OUT);
goto out_notify;
}
}
@ -782,29 +877,24 @@ _ipv4ll_state_change(NML3IPv4LL *self, gboolean is_on_idle_handler)
_ipv4ll_addrgen(self, generate_new_addr);
acd_info = _ipv4ll_l3cfg_get_acd_addr_info(self, self->addr);
if (_acd_info_is_good(acd_info))
goto out_is_good;
break;
generate_new_addr = TRUE;
}
out_set_external_pladdr:
self->addr = pladdr->address;
_ipv4ll_set_state(self, NM_L3_IPV4LL_STATE_EXTERNAL);
_l3cd_config_add(self);
self->notify_on_idle = TRUE;
goto out_notify;
out_is_good:
nm_assert(_acd_info_is_good(acd_info));
switch (acd_info ? acd_info->state : NM_L3_ACD_ADDR_STATE_INIT) {
case NM_L3_ACD_ADDR_STATE_INIT:
case NM_L3_ACD_ADDR_STATE_PROBING:
new_state = NM_L3_IPV4LL_STATE_PROBING;
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_HAVE_TIMER_RUNNING);
goto out_is_good_1;
case NM_L3_ACD_ADDR_STATE_READY:
new_state = NM_L3_IPV4LL_STATE_READY;
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_HAVE_TIMER_RUNNING);
goto out_is_good_1;
case NM_L3_ACD_ADDR_STATE_DEFENDING:
new_state = NM_L3_IPV4LL_STATE_DEFENDING;
_ipv4ll_set_timed_out_update(self, TIMED_OUT_STATE_IS_NOT_TIMED_OUT);
goto out_is_good_1;
case NM_L3_ACD_ADDR_STATE_EXTERNAL_REMOVED:
case NM_L3_ACD_ADDR_STATE_USED:
@ -823,17 +913,9 @@ out_is_good_1:
out_notify:
if (self->notify_on_idle) {
if (is_on_idle_handler) {
NML3ConfigNotifyData notify_data;
self->notify_on_idle = FALSE;
notify_data.notify_type = NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT;
notify_data.ipv4ll_event = (typeof(notify_data.ipv4ll_event)){
.ipv4ll = self,
};
_nm_l3cfg_emit_signal_notify(self->l3cfg, &notify_data);
} else
if (is_on_idle_handler)
_ipv4ll_emit_signal_notify(self);
else
_ipv4ll_state_change_on_idle(self);
}
}
@ -861,36 +943,9 @@ _ipv4ll_state_change_on_idle(NML3IPv4LL *self)
/*****************************************************************************/
void
nm_l3_ipv4ll_restart(NML3IPv4LL *self)
{
nm_assert(NM_IS_L3_IPV4LL(self));
self->seed_reset_generation = TRUE;
_ipv4ll_state_change(self, FALSE);
}
/*****************************************************************************/
static void
_l3cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NML3IPv4LL *self)
{
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE) {
const NMPObject *obj = notify_data->platform_change.obj;
/* we only process the link changes on the idle handler. That means, we may miss
* events. If we saw the link down for a moment, remember it. Note that netlink
* anyway can loose signals, so we might still miss to see the link down. This
* is as good as we get it. */
if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK) {
if (notify_data->platform_change.change_type == NM_PLATFORM_SIGNAL_REMOVED)
self->link_seen_not_ready = TRUE;
else if (!_plobj_link_is_ready(obj))
self->link_seen_not_ready = TRUE;
}
return;
}
if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE) {
/* NMl3Cfg only reloads the platform link during the idle handler. Pick it up now. */
_ipv4ll_update_link(self, nm_l3cfg_get_plobj(l3cfg, FALSE));
@ -938,8 +993,6 @@ nm_l3_ipv4ll_new(NML3Cfg *l3cfg)
.notify_on_idle = TRUE,
.l3cfg_signal_notify_id =
g_signal_connect(l3cfg, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_l3cfg_notify_cb), self),
.last_good_at_msec = 0,
.link_seen_not_ready = FALSE,
.seed_set = FALSE,
.seed_reset_generation = FALSE,
};
@ -975,6 +1028,9 @@ nm_l3_ipv4ll_unref(NML3IPv4LL *self)
if (--self->ref_count > 0)
return;
if (nm_l3cfg_get_ipv4ll(self->l3cfg) == self)
_nm_l3cfg_unregister_ipv4ll(self->l3cfg);
_LOGT("finalize");
nm_assert(c_list_is_empty(&self->reg_lst_head));
@ -992,6 +1048,7 @@ nm_l3_ipv4ll_unref(NML3IPv4LL *self)
nm_assert(!self->l3cfg_commit_handle);
nm_clear_g_source_inst(&self->state_change_on_idle_source);
nm_clear_g_source_inst(&self->timed_out_source);
nm_clear_g_signal_handler(self->l3cfg, &self->l3cfg_signal_notify_id);

View file

@ -19,6 +19,23 @@ typedef enum _nm_packed {
const char *nm_l3_ipv4ll_state_to_string(NML3IPv4LLState val, char *buf, gsize len);
static inline gboolean
nm_l3_ipv4ll_state_is_good(NML3IPv4LLState state)
{
switch (state) {
case NM_L3_IPV4LL_STATE_UNKNOWN:
case NM_L3_IPV4LL_STATE_DISABLED:
case NM_L3_IPV4LL_STATE_WAIT_FOR_LINK:
case NM_L3_IPV4LL_STATE_PROBING:
return FALSE;
case NM_L3_IPV4LL_STATE_EXTERNAL:
case NM_L3_IPV4LL_STATE_READY:
case NM_L3_IPV4LL_STATE_DEFENDING:
return TRUE;
}
return nm_assert_unreachable_val(FALSE);
}
/*****************************************************************************/
typedef struct _NML3IPv4LL NML3IPv4LL;
@ -92,12 +109,10 @@ nm_l3_ipv4ll_register_get_instance(NML3IPv4LLRegistration *reg)
NML3IPv4LLState nm_l3_ipv4ll_get_state(NML3IPv4LL *self);
gboolean nm_l3_ipv4ll_is_timed_out(NML3IPv4LL *self);
in_addr_t nm_l3_ipv4ll_get_addr(NML3IPv4LL *self);
const NML3ConfigData *nm_l3_ipv4ll_get_l3cd(NML3IPv4LL *self);
/*****************************************************************************/
void nm_l3_ipv4ll_restart(NML3IPv4LL *self);
#endif /* __NM_L3_IPV4LL_H__ */

View file

@ -16,6 +16,8 @@
/*****************************************************************************/
G_STATIC_ASSERT(NM_ACD_TIMEOUT_RFC5227_MSEC == N_ACD_TIMEOUT_RFC5227);
#define ACD_SUPPORTED_ETH_ALEN ETH_ALEN
#define ACD_ENSURE_RATELIMIT_MSEC ((guint32) 4000u)
#define ACD_WAIT_PROBING_EXTRA_TIME_MSEC ((guint32)(1000u + ACD_ENSURE_RATELIMIT_MSEC))
@ -145,6 +147,8 @@ typedef struct _NML3CfgPrivate {
GArray *property_emit_list;
GArray *l3_config_datas;
NML3IPv4LL *ipv4ll;
const NML3ConfigData *combined_l3cd_merged;
const NML3ConfigData *combined_l3cd_commited;
@ -3606,6 +3610,47 @@ nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self)
/*****************************************************************************/
NML3IPv4LL *
nm_l3cfg_get_ipv4ll(NML3Cfg *self)
{
g_return_val_if_fail(NM_IS_L3CFG(self), NULL);
return self->priv.p->ipv4ll;
}
NML3IPv4LL *
nm_l3cfg_access_ipv4ll(NML3Cfg *self)
{
g_return_val_if_fail(NM_IS_L3CFG(self), NULL);
if (self->priv.p->ipv4ll)
return nm_l3_ipv4ll_ref(self->priv.p->ipv4ll);
/* We return the reference. But the NML3IPv4LL instance
* will call _nm_l3cfg_unregister_ipv4ll() when it gets
* destroyed.
*
* We don't have weak references, but NML3Cfg and NML3IPv4LL
* cooperate to handle this reference. */
self->priv.p->ipv4ll = nm_l3_ipv4ll_new(self);
return self->priv.p->ipv4ll;
}
void
_nm_l3cfg_unregister_ipv4ll(NML3Cfg *self)
{
nm_assert(NM_IS_L3CFG(self));
/* we don't own the refernce to "self->priv.p->ipv4ll", but
* when that instance gets destroyed, we get called back to
* forget about it. Basically, it's like a weak pointer. */
nm_assert(self->priv.p->ipv4ll);
self->priv.p->ipv4ll = NULL;
}
/*****************************************************************************/
static void
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
@ -3673,6 +3718,7 @@ finalize(GObject *object)
NML3Cfg *self = NM_L3CFG(object);
nm_assert(!self->priv.p->l3_config_datas);
nm_assert(!self->priv.p->ipv4ll);
nm_assert(c_list_is_empty(&self->priv.p->commit_type_lst_head));

View file

@ -7,6 +7,7 @@
#include "nm-l3-config-data.h"
#define NM_L3CFG_CONFIG_PRIORITY_IPV4LL 0
#define NM_ACD_TIMEOUT_RFC5227_MSEC 9000u
#define NM_TYPE_L3CFG (nm_l3cfg_get_type())
#define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_L3CFG, NML3Cfg))
@ -344,4 +345,12 @@ gboolean nm_l3cfg_has_commited_ip6_addresses_pending_dad(NML3Cfg *self);
/*****************************************************************************/
struct _NML3IPv4LL *nm_l3cfg_get_ipv4ll(NML3Cfg *self);
struct _NML3IPv4LL *nm_l3cfg_access_ipv4ll(NML3Cfg *self);
void _nm_l3cfg_unregister_ipv4ll(NML3Cfg *self);
/*****************************************************************************/
#endif /* __NM_L3CFG_H__ */

View file

@ -2086,7 +2086,7 @@ nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r)
{
nm_assert(r);
return r->metric_any ? nm_add_u32_clamped(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, r->metric)
return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, r->metric)
: r->metric;
}
@ -2095,7 +2095,7 @@ nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r)
{
nm_assert(r);
return r->metric_any ? nm_add_u32_clamped(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, r->metric)
return r->metric_any ? nm_add_clamped_u32(NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, r->metric)
: r->metric;
}