rdisc: obey rtr_solicitations and rtr_solicitation_interval

The IPv6 spec say that when performing SLAAC, you should sent at most
3 RSes, at least 4 seconds apart. We were previously continuing to
send RSes forever if we didn't get back a response. Fix that.

(Since the fix involves making nm-lndp-rdisc use NMPlatform, it was
necessary to rewrite the rdisc test program a bit, to not try to
include <net/if.h>, which is incompatible with <linux/if.h>.)
This commit is contained in:
Dan Winship 2014-03-03 13:10:45 -05:00
parent a10bd9991f
commit ba75ad2d95
7 changed files with 85 additions and 37 deletions

View file

@ -445,13 +445,6 @@ restore_ip6_properties (NMDevice *self)
nm_device_ipv6_sysctl_set (self, key, value);
}
static gint32
sysctl_get_ipv6_max_addresses (NMDevice *self)
{
return nm_platform_sysctl_get_int32 (ip6_property_path (self, "max_addresses"), 16);
}
/*
* Get driver info from SIOCETHTOOL ioctl() for 'iface'
* Returns driver and firmware versions to 'driver_version and' 'firmware_version'
@ -3498,8 +3491,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
priv->ac_ip6_config = NULL;
}
priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface,
sysctl_get_ipv6_max_addresses (self));
priv->rdisc = nm_lndp_rdisc_new (nm_device_get_ip_ifindex (self), ip_iface);
if (!priv->rdisc) {
nm_log_err (LOGD_IP6, "(%s): failed to start router discovery.", ip_iface);
return FALSE;

View file

@ -36,15 +36,15 @@ G_DEFINE_TYPE (NMFakeRDisc, nm_fake_rdisc, NM_TYPE_RDISC)
/******************************************************************/
NMRDisc *
nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses)
nm_fake_rdisc_new (int ifindex, const char *ifname)
{
NMRDisc *rdisc = g_object_new (NM_TYPE_FAKE_RDISC, NULL);
g_assert (rdisc);
rdisc->ifindex = ifindex;
rdisc->ifname = g_strdup (ifname);
rdisc->max_addresses = max_addresses;
rdisc->max_addresses = NM_RDISC_MAX_ADDRESSES_DEFAULT;
rdisc->rtr_solicitations = NM_RDISC_RTR_SOLICITATIONS_DEFAULT;
rdisc->rtr_solicitation_interval = NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT;
return rdisc;
}

View file

@ -44,6 +44,6 @@ typedef struct {
GType nm_fake_rdisc_get_type (void);
NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname, gint32 max_addressses);
NMRDisc *nm_fake_rdisc_new (int ifindex, const char *ifname);
#endif /* NM_FAKE_RDISC_H */

View file

@ -40,6 +40,8 @@ typedef struct {
GIOChannel *event_channel;
guint event_id;
guint timeout_id;
int solicitations_left;
} NMLNDPRDiscPrivate;
#define NM_LNDP_RDISC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_LNDP_RDISC, NMLNDPRDiscPrivate))
@ -48,19 +50,30 @@ G_DEFINE_TYPE (NMLNDPRDisc, nm_lndp_rdisc, NM_TYPE_RDISC)
/******************************************************************/
static inline gint32
ipv6_sysctl_get (const char *ifname, const char *property, gint32 defval)
{
return nm_platform_sysctl_get_int32 (nm_utils_ip6_property_path (ifname, property), defval);
}
NMRDisc *
nm_lndp_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses)
nm_lndp_rdisc_new (int ifindex, const char *ifname)
{
NMRDisc *rdisc;
NMLNDPRDiscPrivate *priv;
int error;
rdisc = g_object_new (NM_TYPE_LNDP_RDISC, NULL);
g_assert (rdisc);
rdisc->ifindex = ifindex;
rdisc->ifname = g_strdup (ifname);
rdisc->max_addresses = max_addresses;
rdisc->max_addresses = ipv6_sysctl_get (ifname, "max_addresses",
NM_RDISC_MAX_ADDRESSES_DEFAULT);
rdisc->rtr_solicitations = ipv6_sysctl_get (ifname, "router_solicitations",
NM_RDISC_RTR_SOLICITATIONS_DEFAULT);
rdisc->rtr_solicitation_interval = ipv6_sysctl_get (ifname, "router_solicitation_interval",
NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT);
priv = NM_LNDP_RDISC_GET_PRIVATE (rdisc);
error = ndp_open (&priv->ndp);
@ -210,8 +223,6 @@ add_dns_domain (NMRDisc *rdisc, const NMRDiscDNSDomain *new)
return TRUE;
}
#define RETRY 10
static gboolean
send_rs (NMRDisc *rdisc)
{
@ -228,13 +239,23 @@ send_rs (NMRDisc *rdisc)
error = ndp_msg_send (priv->ndp, msg);
if (error)
error ("(%s): cannot send router solicitation: %d.", rdisc->ifname, error);
else
priv->solicitations_left--;
ndp_msg_destroy (msg);
debug ("(%s): scheduling router solicitation retry in %d seconds.", rdisc->ifname, RETRY);
priv->send_rs_id = g_timeout_add_seconds (RETRY, (GSourceFunc) send_rs, rdisc);
if (priv->solicitations_left > 0) {
debug ("(%s): scheduling router solicitation retry in %d seconds.",
rdisc->ifname, rdisc->rtr_solicitation_interval);
priv->send_rs_id = g_timeout_add_seconds (rdisc->rtr_solicitation_interval,
(GSourceFunc) send_rs, rdisc);
} else {
debug ("(%s): did not receive a router advertisement after %d solicitations.",
rdisc->ifname, rdisc->rtr_solicitations);
priv->send_rs_id = 0;
}
return FALSE;
return G_SOURCE_REMOVE;
}
static void
@ -245,6 +266,7 @@ solicit (NMRDisc *rdisc)
if (!priv->send_rs_id) {
debug ("(%s): scheduling router solicitation.", rdisc->ifname);
priv->send_rs_id = g_idle_add ((GSourceFunc) send_rs, rdisc);
priv->solicitations_left = rdisc->rtr_solicitations;
}
}

View file

@ -44,6 +44,6 @@ typedef struct {
GType nm_lndp_rdisc_get_type (void);
NMRDisc *nm_lndp_rdisc_new (int ifindex, const char *ifname, gint32 max_addresses);
NMRDisc *nm_lndp_rdisc_new (int ifindex, const char *ifname);
#endif /* NM_LNDP_RDISC_H */

View file

@ -94,6 +94,10 @@ typedef enum {
NM_RDISC_CONFIG_HOP_LIMIT = 1 << 6,
} NMRDiscConfigMap;
#define NM_RDISC_MAX_ADDRESSES_DEFAULT 16
#define NM_RDISC_RTR_SOLICITATIONS_DEFAULT 3
#define NM_RDISC_RTR_SOLICITATION_INTERVAL_DEFAULT 4
/**
* NMRDisc:
* @ifindex: Interface index
@ -108,6 +112,8 @@ typedef struct {
char *ifname;
GBytes *lladdr;
gint32 max_addresses;
gint32 rtr_solicitations;
gint32 rtr_solicitation_interval;
NMRDiscDHCPLevel dhcp_level;
GArray *gateways;

View file

@ -1,40 +1,68 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* rdisc.c - test program
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2013 Red Hat, Inc.
*/
#include <string.h>
#include <syslog.h>
#include <net/if.h>
#include "nm-rdisc.h"
#include "nm-fake-rdisc.h"
#include "nm-lndp-rdisc.h"
#include "nm-logging.h"
#include "nm-fake-platform.h"
#include "nm-linux-platform.h"
int
main (int argc, char **argv)
{
GMainLoop *loop;
NMRDisc *rdisc;
NMRDisc *(*new) (int ifindex, const char *ifname, gint32 max_addresses) = nm_lndp_rdisc_new;
NMRDisc *(*new) (int ifindex, const char *ifname);
int ifindex = 1;
char ifname[IF_NAMESIZE];
const char *ifname;
char mac[6] = { 0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
if_indextoname (ifindex, ifname);
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
nm_logging_setup ("debug", NULL, NULL, NULL);
nm_logging_setup ("debug", "ip6", NULL, NULL);
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON);
argv++;
for (; *argv; argv++) {
if (!g_strcmp0 (*argv, "--fake"))
new = nm_fake_rdisc_new;
else {
strncpy (ifname, *argv, IF_NAMESIZE);
ifindex = if_nametoindex (ifname);
}
if (!g_strcmp0 (argv[0], "--fake")) {
new = nm_fake_rdisc_new;
nm_fake_platform_setup ();
argv++;
} else {
new = nm_lndp_rdisc_new;
nm_linux_platform_setup ();
}
rdisc = new (ifindex, ifname, 0);
if (argv[0]) {
ifname = argv[0];
ifindex = nm_platform_link_get_ifindex (ifname);
} else {
ifindex = 1;
ifname = nm_platform_link_get_name (ifindex);
}
rdisc = new (ifindex, ifname);
if (!rdisc)
return EXIT_FAILURE;