rdisc: add RA wait timeout

Add an advisory timeout when waiting for router advertisements so
we can fail IPv6 addressing attempts when no router advertisement
has been received.
This commit is contained in:
Dan Williams 2014-08-13 09:40:08 -05:00
parent bc18992dd8
commit 7c9d4e8f5a
4 changed files with 113 additions and 32 deletions

View file

@ -270,7 +270,8 @@ typedef struct {
NMIP6Config * ext_ip6_config; /* Stuff added outside NM */
NMRDisc * rdisc;
gulong rdisc_config_changed_sigid;
gulong rdisc_changed_id;
gulong rdisc_timeout_id;
NMSettingIP6ConfigPrivacy rdisc_use_tempaddr;
/* IP6 config from autoconf */
NMIP6Config * ac_ip6_config;
@ -3591,6 +3592,21 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self)
nm_device_activate_schedule_ip6_config_result (self);
}
static void
rdisc_ra_timeout (NMRDisc *rdisc, NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
/* We don't want to stop listening for router advertisements completely,
* but instead let device activation continue activating. If an RA
* shows up later, we'll use it as long as the device is not disconnected.
*/
_LOGD (LOGD_IP6, "timed out waiting for IPv6 router advertisement");
if (priv->ip6_state == IP_CONF)
nm_device_activate_schedule_ip6_config_timeout (self);
}
static gboolean
addrconf6_start_with_link_ready (NMDevice *self)
{
@ -3610,8 +3626,14 @@ addrconf6_start_with_link_ready (NMDevice *self)
nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0");
nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0");
priv->rdisc_config_changed_sigid = g_signal_connect (priv->rdisc, NM_RDISC_CONFIG_CHANGED,
G_CALLBACK (rdisc_config_changed), self);
priv->rdisc_changed_id = g_signal_connect (priv->rdisc,
NM_RDISC_CONFIG_CHANGED,
G_CALLBACK (rdisc_config_changed),
self);
priv->rdisc_timeout_id = g_signal_connect (priv->rdisc,
NM_RDISC_RA_TIMEOUT,
G_CALLBACK (rdisc_ra_timeout),
self);
nm_rdisc_start (priv->rdisc);
return TRUE;
}
@ -3662,10 +3684,14 @@ addrconf6_cleanup (NMDevice *self)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->rdisc_config_changed_sigid) {
g_signal_handler_disconnect (priv->rdisc,
priv->rdisc_config_changed_sigid);
priv->rdisc_config_changed_sigid = 0;
if (priv->rdisc_changed_id) {
g_signal_handler_disconnect (priv->rdisc, priv->rdisc_changed_id);
priv->rdisc_changed_id = 0;
}
if (priv->rdisc_timeout_id) {
g_signal_handler_disconnect (priv->rdisc, priv->rdisc_timeout_id);
priv->rdisc_timeout_id = 0;
}
nm_device_remove_pending_action (self, PENDING_ACTION_AUTOCONF6, FALSE);
@ -4647,6 +4673,12 @@ nm_device_activate_schedule_ip6_config_result (NMDevice *self)
g_return_if_fail (NM_IS_DEVICE (self));
/* If IP had previously failed, move it back to IP_CONF since we
* clearly now have configuration.
*/
if (priv->ip6_state == IP_FAIL)
priv->ip6_state = IP_CONF;
activation_source_schedule (self, nm_device_activate_ip6_config_commit, AF_INET6);
_LOG (level, LOGD_DEVICE | LOGD_IP6,

View file

@ -40,7 +40,8 @@ typedef struct {
guint send_rs_id;
GIOChannel *event_channel;
guint event_id;
guint timeout_id;
guint timeout_id; /* prefix/dns/etc lifetime timeout */
guint ra_timeout_id; /* first RA timeout */
int solicitations_left;
} NMLNDPRDiscPrivate;
@ -433,11 +434,32 @@ translate_preference (enum ndp_route_preference preference)
}
}
static void
clear_rs_timeout (NMLNDPRDisc *rdisc)
{
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
if (priv->send_rs_id) {
g_source_remove (priv->send_rs_id);
priv->send_rs_id = 0;
}
}
static void
clear_ra_timeout (NMLNDPRDisc *rdisc)
{
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
if (priv->ra_timeout_id) {
g_source_remove (priv->ra_timeout_id);
priv->ra_timeout_id = 0;
}
}
static int
receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
{
NMRDisc *rdisc = (NMRDisc *) user_data;
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
NMRDiscConfigMap changed = 0;
struct ndp_msgra *msgra = ndp_msgra (msg);
NMRDiscGateway gateway;
@ -458,10 +480,8 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
*/
debug ("(%s): received router advertisement at %u", rdisc->ifname, now);
if (priv->send_rs_id) {
g_source_remove (priv->send_rs_id);
priv->send_rs_id = 0;
}
clear_ra_timeout (NM_LNDP_RDISC (rdisc));
clear_rs_timeout (NM_LNDP_RDISC (rdisc));
/* DHCP level:
*
@ -606,21 +626,25 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
return 0;
}
static void
process_events (NMRDisc *rdisc)
static gboolean
event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
{
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
debug ("(%s): processing libndp events.", rdisc->ifname);
ndp_callall_eventfd_handler (priv->ndp);
return G_SOURCE_CONTINUE;
}
static gboolean
event_ready (GIOChannel *source, GIOCondition condition, NMRDisc *rdisc)
rdisc_ra_timeout_cb (gpointer user_data)
{
process_events (rdisc);
NMLNDPRDisc *rdisc = NM_LNDP_RDISC (user_data);
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
return TRUE;
priv->ra_timeout_id = 0;
g_signal_emit_by_name (rdisc, NM_RDISC_RA_TIMEOUT);
return G_SOURCE_REMOVE;
}
static void
@ -628,14 +652,20 @@ start (NMRDisc *rdisc)
{
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
int fd = ndp_get_eventfd (priv->ndp);
guint ra_wait_secs;
priv->event_channel = g_io_channel_unix_new (fd);
priv->event_id = g_io_add_watch (priv->event_channel, G_IO_IN, (GIOFunc) event_ready, rdisc);
/* Flush any pending messages to avoid using obsolete information */
process_events (rdisc);
clear_ra_timeout (NM_LNDP_RDISC (rdisc));
ra_wait_secs = CLAMP (rdisc->rtr_solicitations * rdisc->rtr_solicitation_interval, 30, 120);
priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, rdisc_ra_timeout_cb, rdisc);
debug ("(%s): scheduling RA timeout in %d seconds", rdisc->ifname, ra_wait_secs);
ndp_msgrcv_handler_register (priv->ndp, &receive_ra, NDP_MSG_RA, rdisc->ifindex, rdisc);
/* Flush any pending messages to avoid using obsolete information */
event_ready (priv->event_channel, 0, rdisc);
ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, rdisc->ifindex, rdisc);
solicit (rdisc);
}
@ -647,21 +677,29 @@ nm_lndp_rdisc_init (NMLNDPRDisc *lndp_rdisc)
}
static void
nm_lndp_rdisc_finalize (GObject *object)
dispose (GObject *object)
{
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (object);
NMLNDPRDisc *rdisc = NM_LNDP_RDISC (object);
NMLNDPRDiscPrivate *priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
if (priv->send_rs_id)
g_source_remove (priv->send_rs_id);
if (priv->timeout_id)
clear_rs_timeout (rdisc);
clear_ra_timeout (rdisc);
if (priv->timeout_id) {
g_source_remove (priv->timeout_id);
if (priv->event_channel)
g_io_channel_unref (priv->event_channel);
if (priv->event_id)
g_source_remove (priv->event_id);
priv->timeout_id = 0;
}
if (priv->ndp)
if (priv->event_id) {
g_source_remove (priv->event_id);
priv->event_id = 0;
}
g_clear_pointer (&priv->event_channel, g_io_channel_unref);
if (priv->ndp) {
ndp_close (priv->ndp);
priv->ndp = NULL;
}
}
static void
@ -672,6 +710,6 @@ nm_lndp_rdisc_class_init (NMLNDPRDiscClass *klass)
g_type_class_add_private (klass, sizeof (NMLNDPRDiscPrivate));
object_class->finalize = nm_lndp_rdisc_finalize;
object_class->dispose = dispose;
rdisc_class->start = start;
}

View file

@ -32,6 +32,7 @@ G_DEFINE_TYPE (NMRDisc, nm_rdisc, G_TYPE_OBJECT)
enum {
CONFIG_CHANGED,
RA_TIMEOUT,
LAST_SIGNAL
};
@ -184,4 +185,12 @@ nm_rdisc_class_init (NMRDiscClass *klass)
G_STRUCT_OFFSET (NMRDiscClass, config_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_INT);
signals[RA_TIMEOUT] = g_signal_new (
NM_RDISC_RA_TIMEOUT,
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMRDiscClass, ra_timeout),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}

View file

@ -36,6 +36,7 @@
#define NM_RDISC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_RDISC, NMRDiscClass))
#define NM_RDISC_CONFIG_CHANGED "config-changed"
#define NM_RDISC_RA_TIMEOUT "ra-timeout"
typedef enum {
NM_RDISC_DHCP_LEVEL_UNKNOWN,
@ -131,6 +132,7 @@ typedef struct {
void (*start) (NMRDisc *rdisc);
void (*config_changed) (NMRDisc *rdisc, NMRDiscConfigMap changed);
void (*ra_timeout) (NMRDisc *rdisc);
} NMRDiscClass;
GType nm_rdisc_get_type (void);