mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-09-20 08:31:47 +00:00
ndisc: add logic for acting as a router
This commit is contained in:
parent
be6a0c7ce5
commit
87624a6c50
|
@ -373,6 +373,7 @@ nm_fake_ndisc_new (int ifindex, const char *ifname)
|
|||
return g_object_new (NM_TYPE_FAKE_NDISC,
|
||||
NM_NDISC_IFINDEX, ifindex,
|
||||
NM_NDISC_IFNAME, ifname,
|
||||
NM_NDISC_NODE_TYPE, (int) NM_NDISC_NODE_TYPE_HOST,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -380,6 +380,7 @@ nm_lndp_ndisc_new (NMPlatform *platform,
|
|||
NM_NDISC_IFNAME, ifname,
|
||||
NM_NDISC_NETWORK_ID, network_id,
|
||||
NM_NDISC_ADDR_GEN_MODE, (int) addr_gen_mode,
|
||||
NM_NDISC_NODE_TYPE, (int) NM_NDISC_NODE_TYPE_HOST,
|
||||
NM_NDISC_MAX_ADDRESSES, ipv6_sysctl_get (platform, ifname,
|
||||
"max_addresses",
|
||||
0, G_MAXINT32, NM_NDISC_MAX_ADDRESSES_DEFAULT),
|
||||
|
|
|
@ -37,6 +37,7 @@ struct _NMNDiscDataInternal {
|
|||
typedef struct _NMNDiscDataInternal NMNDiscDataInternal;
|
||||
|
||||
void nm_ndisc_ra_received (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed);
|
||||
void nm_ndisc_rs_received (NMNDisc *ndisc);
|
||||
|
||||
gboolean nm_ndisc_add_gateway (NMNDisc *ndisc, const NMNDiscGateway *new);
|
||||
gboolean nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new);
|
||||
|
|
|
@ -41,9 +41,18 @@ struct _NMNDiscPrivate {
|
|||
/* this *must* be the first field. */
|
||||
NMNDiscDataInternal rdata;
|
||||
|
||||
union {
|
||||
gint32 solicitations_left;
|
||||
gint32 announcements_left;
|
||||
};
|
||||
union {
|
||||
guint send_rs_id;
|
||||
guint send_ra_id;
|
||||
};
|
||||
union {
|
||||
gint32 last_rs;
|
||||
gint32 last_ra;
|
||||
};
|
||||
guint ra_timeout_id; /* first RA timeout */
|
||||
guint timeout_id; /* prefix/dns/etc lifetime timeout */
|
||||
char *last_error;
|
||||
|
@ -58,6 +67,7 @@ struct _NMNDiscPrivate {
|
|||
gint32 max_addresses;
|
||||
gint32 router_solicitations;
|
||||
gint32 router_solicitation_interval;
|
||||
NMNDiscNodeType node_type;
|
||||
|
||||
NMPlatform *platform;
|
||||
NMPNetns *netns;
|
||||
|
@ -75,6 +85,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
|
|||
PROP_MAX_ADDRESSES,
|
||||
PROP_ROUTER_SOLICITATIONS,
|
||||
PROP_ROUTER_SOLICITATION_INTERVAL,
|
||||
PROP_NODE_TYPE,
|
||||
);
|
||||
|
||||
enum {
|
||||
|
@ -139,6 +150,14 @@ nm_ndisc_get_ifname (NMNDisc *self)
|
|||
return NM_NDISC_GET_PRIVATE (self)->ifname;
|
||||
}
|
||||
|
||||
NMNDiscNodeType
|
||||
nm_ndisc_get_node_type (NMNDisc *self)
|
||||
{
|
||||
g_return_val_if_fail (NM_IS_NDISC (self), NM_NDISC_NODE_TYPE_INVALID);
|
||||
|
||||
return NM_NDISC_GET_PRIVATE (self)->node_type;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const NMNDiscData *
|
||||
|
@ -259,19 +278,13 @@ complete_address (NMNDisc *ndisc, NMNDiscAddress *addr)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new)
|
||||
static gboolean
|
||||
nm_ndisc_add_address (NMNDisc *ndisc, const NMNDiscAddress *new)
|
||||
{
|
||||
NMNDiscPrivate *priv;
|
||||
NMNDiscDataInternal *rdata;
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
NMNDiscDataInternal *rdata = &priv->rdata;
|
||||
int i;
|
||||
|
||||
if (!complete_address (ndisc, new))
|
||||
return FALSE;
|
||||
|
||||
priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
rdata = &priv->rdata;
|
||||
|
||||
for (i = 0; i < rdata->addresses->len; i++) {
|
||||
NMNDiscAddress *item = &g_array_index (rdata->addresses, NMNDiscAddress, i);
|
||||
|
||||
|
@ -302,6 +315,15 @@ nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new)
|
|||
return !!new->lifetime;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_ndisc_complete_and_add_address (NMNDisc *ndisc, NMNDiscAddress *new)
|
||||
{
|
||||
if (!complete_address (ndisc, new))
|
||||
return FALSE;
|
||||
|
||||
return nm_ndisc_add_address (ndisc, new);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_ndisc_add_route (NMNDisc *ndisc, const NMNDiscRoute *new)
|
||||
{
|
||||
|
@ -473,7 +495,7 @@ send_rs_timeout (NMNDisc *ndisc)
|
|||
}
|
||||
|
||||
static void
|
||||
solicit (NMNDisc *ndisc)
|
||||
solicit_routers (NMNDisc *ndisc)
|
||||
{
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
gint64 next, now;
|
||||
|
@ -482,7 +504,6 @@ solicit (NMNDisc *ndisc)
|
|||
return;
|
||||
|
||||
now = nm_utils_get_monotonic_timestamp_s ();
|
||||
|
||||
priv->solicitations_left = priv->router_solicitations;
|
||||
|
||||
next = (((gint64) priv->last_rs) + priv->router_solicitation_interval) - now;
|
||||
|
@ -492,8 +513,114 @@ solicit (NMNDisc *ndisc)
|
|||
priv->send_rs_id = g_timeout_add_seconds ((guint32) next, (GSourceFunc) send_rs_timeout, ndisc);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
announce_router (NMNDisc *ndisc)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
NMNDiscClass *klass = NM_NDISC_GET_CLASS (ndisc);
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
GError *error = NULL;
|
||||
|
||||
if (!nm_ndisc_netns_push (ndisc, &netns))
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
priv->last_ra = nm_utils_get_monotonic_timestamp_s ();
|
||||
if (klass->send_ra (ndisc, &error)) {
|
||||
_LOGD ("router advertisement sent");
|
||||
g_clear_pointer (&priv->last_error, g_free);
|
||||
} else {
|
||||
_MAYBE_WARN ("failure sending router advertisement: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
if (--priv->announcements_left) {
|
||||
_LOGD ("will resend an initial router advertisement");
|
||||
|
||||
/* Schedule next initial announcement retransmit. */
|
||||
priv->send_ra_id = g_timeout_add_seconds (g_random_int_range (0, NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL),
|
||||
(GSourceFunc) announce_router, ndisc);
|
||||
} else {
|
||||
_LOGD ("will send an unsolicited router advertisement");
|
||||
|
||||
/* Schedule next unsolicited announcement. */
|
||||
priv->announcements_left = 1;
|
||||
priv->send_ra_id = g_timeout_add_seconds (NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL,
|
||||
(GSourceFunc) announce_router,
|
||||
ndisc);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
announce_router_initial (NMNDisc *ndisc)
|
||||
{
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
|
||||
_LOGD ("will send an initial router advertisement");
|
||||
|
||||
/* Retry three more times. */
|
||||
priv->announcements_left = NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT;
|
||||
|
||||
/* Unschedule an unsolicited resend if we are allowed to send now. */
|
||||
if (G_LIKELY (nm_utils_get_monotonic_timestamp_s () - priv->last_ra > NM_NDISC_ROUTER_ADVERT_DELAY))
|
||||
nm_clear_g_source (&priv->send_ra_id);
|
||||
|
||||
/* Schedule the initial send rather early. Clamp the delay by minimal
|
||||
* delay and not the initial advert internal so that we start fast. */
|
||||
if (G_LIKELY (!priv->send_ra_id)) {
|
||||
priv->send_ra_id = g_timeout_add_seconds (g_random_int_range (0, NM_NDISC_ROUTER_ADVERT_DELAY),
|
||||
(GSourceFunc) announce_router, ndisc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
announce_router_solicited (NMNDisc *ndisc)
|
||||
{
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
|
||||
_LOGD ("will send an solicited router advertisement");
|
||||
|
||||
/* Unschedule an unsolicited resend if we are allowed to send now. */
|
||||
if (nm_utils_get_monotonic_timestamp_s () - priv->last_ra > NM_NDISC_ROUTER_ADVERT_DELAY)
|
||||
nm_clear_g_source (&priv->send_ra_id);
|
||||
|
||||
if (!priv->send_ra_id) {
|
||||
priv->send_ra_id = g_timeout_add (g_random_int_range (0, 500),
|
||||
(GSourceFunc) announce_router, ndisc);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
nm_ndisc_set_config (NMNDisc *ndisc,
|
||||
const GArray *addresses,
|
||||
const GArray *dns_servers,
|
||||
const GArray *dns_domains)
|
||||
{
|
||||
int changed = FALSE;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < addresses->len; i++) {
|
||||
if (nm_ndisc_add_address (ndisc, &g_array_index (addresses, NMNDiscAddress, i)))
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < dns_servers->len; i++) {
|
||||
if (nm_ndisc_add_dns_server (ndisc, &g_array_index (dns_servers, NMNDiscDNSServer, i)))
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < dns_domains->len; i++) {
|
||||
if (nm_ndisc_add_dns_domain (ndisc, &g_array_index (dns_domains, NMNDiscDNSDomain, i)))
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
announce_router_initial (ndisc);
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_ndisc_set_iid:
|
||||
* @ndisc: the #NMNDisc
|
||||
|
@ -534,7 +661,7 @@ nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid)
|
|||
_LOGD ("IPv6 interface identifier changed, flushing addresses");
|
||||
g_array_remove_range (rdata->addresses, 0, rdata->addresses->len);
|
||||
_emit_config_change (ndisc, NM_NDISC_CONFIG_ADDRESSES);
|
||||
solicit (ndisc);
|
||||
solicit_routers (ndisc);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -568,14 +695,22 @@ nm_ndisc_start (NMNDisc *ndisc)
|
|||
if (!nm_ndisc_netns_push (ndisc, &netns))
|
||||
return;
|
||||
|
||||
klass->start (ndisc);
|
||||
|
||||
switch (priv->node_type) {
|
||||
case NM_NDISC_NODE_TYPE_HOST:
|
||||
ra_wait_secs = (((gint64) priv->router_solicitations) * priv->router_solicitation_interval) + 1;
|
||||
ra_wait_secs = CLAMP (ra_wait_secs, 30, 120);
|
||||
priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, ndisc_ra_timeout_cb, ndisc);
|
||||
_LOGD ("scheduling RA timeout in %d seconds", (int) ra_wait_secs);
|
||||
|
||||
klass->start (ndisc);
|
||||
|
||||
solicit (ndisc);
|
||||
solicit_routers (ndisc);
|
||||
break;
|
||||
case NM_NDISC_NODE_TYPE_ROUTER:
|
||||
announce_router_initial (ndisc);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -780,7 +915,7 @@ clean_dns_servers (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint
|
|||
g_array_remove_index (rdata->dns_servers, i--);
|
||||
*changed |= NM_NDISC_CONFIG_DNS_SERVERS;
|
||||
} else if (now >= refresh)
|
||||
solicit (ndisc);
|
||||
solicit_routers (ndisc);
|
||||
else if (*nextevent > refresh)
|
||||
*nextevent = refresh;
|
||||
}
|
||||
|
@ -806,7 +941,7 @@ clean_dns_domains (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap *changed, guint
|
|||
g_array_remove_index (rdata->dns_domains, i--);
|
||||
*changed |= NM_NDISC_CONFIG_DNS_DOMAINS;
|
||||
} else if (now >= refresh)
|
||||
solicit (ndisc);
|
||||
solicit_routers (ndisc);
|
||||
else if (*nextevent > refresh)
|
||||
*nextevent = refresh;
|
||||
}
|
||||
|
@ -862,6 +997,15 @@ nm_ndisc_ra_received (NMNDisc *ndisc, guint32 now, NMNDiscConfigMap changed)
|
|||
check_timestamps (ndisc, now, changed);
|
||||
}
|
||||
|
||||
void
|
||||
nm_ndisc_rs_received (NMNDisc *ndisc)
|
||||
{
|
||||
NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
|
||||
|
||||
g_clear_pointer (&priv->last_error, g_free);
|
||||
announce_router_solicited (ndisc);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
|
@ -926,6 +1070,10 @@ set_property (GObject *object, guint prop_id,
|
|||
/* construct-only */
|
||||
priv->router_solicitation_interval = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_NODE_TYPE:
|
||||
/* construct-only */
|
||||
priv->node_type = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -965,6 +1113,7 @@ dispose (GObject *object)
|
|||
|
||||
nm_clear_g_source (&priv->ra_timeout_id);
|
||||
nm_clear_g_source (&priv->send_rs_id);
|
||||
nm_clear_g_source (&priv->send_ra_id);
|
||||
g_clear_pointer (&priv->last_error, g_free);
|
||||
|
||||
nm_clear_g_source (&priv->timeout_id);
|
||||
|
@ -1059,6 +1208,12 @@ nm_ndisc_class_init (NMNDiscClass *klass)
|
|||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
obj_properties[PROP_NODE_TYPE] =
|
||||
g_param_spec_int (NM_NDISC_NODE_TYPE, "", "",
|
||||
NM_NDISC_NODE_TYPE_INVALID, NM_NDISC_NODE_TYPE_ROUTER, NM_NDISC_NODE_TYPE_INVALID,
|
||||
G_PARAM_WRITABLE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_STRINGS);
|
||||
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
||||
|
||||
signals[CONFIG_CHANGED] =
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#define NM_NDISC_NETWORK_ID "network-id"
|
||||
#define NM_NDISC_ADDR_GEN_MODE "addr-gen-mode"
|
||||
#define NM_NDISC_STABLE_TYPE "stable-type"
|
||||
#define NM_NDISC_NODE_TYPE "node-type"
|
||||
#define NM_NDISC_MAX_ADDRESSES "max-addresses"
|
||||
#define NM_NDISC_ROUTER_SOLICITATIONS "router-solicitations"
|
||||
#define NM_NDISC_ROUTER_SOLICITATION_INTERVAL "router-solicitation-interval"
|
||||
|
@ -108,9 +109,19 @@ typedef enum {
|
|||
NM_NDISC_CONFIG_MTU = 1 << 7,
|
||||
} NMNDiscConfigMap;
|
||||
|
||||
typedef enum {
|
||||
NM_NDISC_NODE_TYPE_INVALID,
|
||||
NM_NDISC_NODE_TYPE_HOST,
|
||||
NM_NDISC_NODE_TYPE_ROUTER,
|
||||
} NMNDiscNodeType;
|
||||
|
||||
#define NM_NDISC_MAX_ADDRESSES_DEFAULT 16
|
||||
#define NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT 3 /* RFC4861 MAX_RTR_SOLICITATIONS */
|
||||
#define NM_NDISC_ROUTER_SOLICITATION_INTERVAL_DEFAULT 4 /* RFC4861 RTR_SOLICITATION_INTERVAL */
|
||||
#define NM_NDISC_ROUTER_ADVERTISEMENTS_DEFAULT 3 /* RFC4861 MAX_INITIAL_RTR_ADVERTISEMENTS */
|
||||
#define NM_NDISC_ROUTER_ADVERT_DELAY 3 /* RFC4861 MIN_DELAY_BETWEEN_RAS */
|
||||
#define NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL 16 /* RFC4861 MAX_INITIAL_RTR_ADVERT_INTERVAL */
|
||||
#define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL 600 /* RFC4861 MaxRtrAdvInterval default */
|
||||
|
||||
struct _NMNDiscPrivate;
|
||||
struct _NMNDiscDataInternal;
|
||||
|
@ -152,16 +163,22 @@ typedef struct {
|
|||
|
||||
void (*start) (NMNDisc *ndisc);
|
||||
gboolean (*send_rs) (NMNDisc *ndisc, GError **error);
|
||||
gboolean (*send_ra) (NMNDisc *ndisc, GError **error);
|
||||
} NMNDiscClass;
|
||||
|
||||
GType nm_ndisc_get_type (void);
|
||||
|
||||
int nm_ndisc_get_ifindex (NMNDisc *self);
|
||||
const char *nm_ndisc_get_ifname (NMNDisc *self);
|
||||
NMNDiscNodeType nm_ndisc_get_node_type (NMNDisc *self);
|
||||
|
||||
gboolean nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid);
|
||||
void nm_ndisc_start (NMNDisc *ndisc);
|
||||
void nm_ndisc_dad_failed (NMNDisc *ndisc, struct in6_addr *address);
|
||||
void nm_ndisc_set_config (NMNDisc *ndisc,
|
||||
const GArray *addresses,
|
||||
const GArray *dns_servers,
|
||||
const GArray *dns_domains);
|
||||
|
||||
NMPlatform *nm_ndisc_get_platform (NMNDisc *self);
|
||||
NMPNetns *nm_ndisc_netns_get (NMNDisc *self);
|
||||
|
|
Loading…
Reference in a new issue