ndisc/lndp: add ability to announce the managed IPv6 configuration
Announce the prefixes and DNS configuration.
This commit is contained in:
@@ -6455,6 +6455,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
|
||||
stable_type,
|
||||
stable_id,
|
||||
nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
|
||||
NM_NDISC_NODE_TYPE_HOST,
|
||||
&error);
|
||||
}
|
||||
if (!priv->ndisc) {
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/icmp6.h>
|
||||
/* stdarg.h included because of a bug in ndp.h */
|
||||
#include <stdarg.h>
|
||||
#include <ndp.h>
|
||||
@@ -302,6 +303,176 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
_ndp_msg_add_option (struct ndp_msg *msg, int len)
|
||||
{
|
||||
void *ret = (uint8_t *)msg + ndp_msg_payload_len (msg);
|
||||
|
||||
len += ndp_msg_payload_len (msg);
|
||||
if (len > ndp_msg_payload_maxlen (msg))
|
||||
return NULL;
|
||||
|
||||
ndp_msg_payload_len_set (msg, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define NM_ND_OPT_RDNSS 25
|
||||
typedef struct {
|
||||
struct nd_opt_hdr header;
|
||||
uint16_t reserved;
|
||||
uint32_t lifetime;;
|
||||
struct in6_addr addrs[0];
|
||||
} NMLndpRdnssOption;
|
||||
|
||||
#define NM_ND_OPT_DNSSL 31
|
||||
typedef struct {
|
||||
struct nd_opt_hdr header;
|
||||
uint16_t reserved;
|
||||
uint32_t lifetime;
|
||||
char search_list[0];
|
||||
} NMLndpDnsslOption;
|
||||
|
||||
static gboolean
|
||||
send_ra (NMNDisc *ndisc, GError **error)
|
||||
{
|
||||
NMLndpNDiscPrivate *priv = NM_LNDP_NDISC_GET_PRIVATE ((NMLndpNDisc *) ndisc);
|
||||
NMNDiscDataInternal *rdata = ndisc->rdata;
|
||||
guint32 now = nm_utils_get_monotonic_timestamp_s ();
|
||||
int errsv;
|
||||
struct in6_addr *addr;
|
||||
struct ndp_msg *msg;
|
||||
struct nd_opt_prefix_info *prefix;
|
||||
int i;
|
||||
|
||||
errsv = ndp_msg_new (&msg, NDP_MSG_RA);
|
||||
if (errsv) {
|
||||
errsv = errsv > 0 ? errsv : -errsv;
|
||||
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"cannot create a router advertisement");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ndp_msg_ifindex_set (msg, nm_ndisc_get_ifindex (ndisc));
|
||||
|
||||
/* Multicast to all nodes. */
|
||||
addr = ndp_msg_addrto (msg);
|
||||
addr->s6_addr32[0] = htonl(0xff020000);
|
||||
addr->s6_addr32[1] = 0;
|
||||
addr->s6_addr32[2] = 0;
|
||||
addr->s6_addr32[3] = htonl(0x1);
|
||||
|
||||
ndp_msgra_router_lifetime_set (ndp_msgra (msg), NM_NDISC_ROUTER_LIFETIME);
|
||||
|
||||
/* The device let us know about all addresses that the device got
|
||||
* whose prefixes are suitable for delegating. Let's announce them. */
|
||||
for (i = 0; i < rdata->addresses->len; i++) {
|
||||
NMNDiscAddress *address = &g_array_index (rdata->addresses, NMNDiscAddress, i);
|
||||
guint32 age = now - address->timestamp;
|
||||
guint32 lifetime = address->lifetime;
|
||||
guint32 preferred = address->preferred;
|
||||
|
||||
/* Clamp the life times if they're not forever. */
|
||||
if (lifetime != 0xffffffff)
|
||||
lifetime = lifetime > age ? lifetime - age : 0;
|
||||
if (preferred != 0xffffffff)
|
||||
preferred = preferred > age ? preferred - age : 0;
|
||||
|
||||
prefix = _ndp_msg_add_option (msg, sizeof(*prefix));
|
||||
if (!prefix) {
|
||||
/* Maybe we could sent separate RAs, but why bother... */
|
||||
_LOGW ("The RA is too big, had to omit some some prefixes.");
|
||||
break;
|
||||
}
|
||||
|
||||
prefix->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
|
||||
prefix->nd_opt_pi_len = 4;
|
||||
prefix->nd_opt_pi_prefix_len = 64;
|
||||
prefix->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK;
|
||||
prefix->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
|
||||
prefix->nd_opt_pi_valid_time = htonl(lifetime);
|
||||
prefix->nd_opt_pi_preferred_time = htonl(preferred);
|
||||
prefix->nd_opt_pi_prefix.s6_addr32[0] = address->address.s6_addr32[0];
|
||||
prefix->nd_opt_pi_prefix.s6_addr32[1] = address->address.s6_addr32[1];
|
||||
prefix->nd_opt_pi_prefix.s6_addr32[2] = 0;
|
||||
prefix->nd_opt_pi_prefix.s6_addr32[3] = 0;
|
||||
}
|
||||
|
||||
if (rdata->dns_servers->len) {
|
||||
NMLndpRdnssOption *option;
|
||||
int len = sizeof(*option) + sizeof(option->addrs[0]) * rdata->dns_servers->len;
|
||||
|
||||
option = _ndp_msg_add_option (msg, len);
|
||||
if (option) {
|
||||
option->header.nd_opt_type = NM_ND_OPT_RDNSS;
|
||||
option->header.nd_opt_len = len / 8;
|
||||
option->lifetime = htonl (900);
|
||||
|
||||
for (i = 0; i < rdata->dns_servers->len; i++) {
|
||||
NMNDiscDNSServer *dns_server = &g_array_index (rdata->dns_servers, NMNDiscDNSServer, i);
|
||||
option->addrs[i] = dns_server->address;
|
||||
}
|
||||
} else {
|
||||
_LOGW ("The RA is too big, had to omit DNS information.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (rdata->dns_domains->len) {
|
||||
NMLndpDnsslOption *option;
|
||||
NMNDiscDNSDomain *dns_server;
|
||||
int len = sizeof(*option);
|
||||
char *search_list;
|
||||
|
||||
for (i = 0; i < rdata->dns_domains->len; i++) {
|
||||
dns_server = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i);
|
||||
len += strlen (dns_server->domain) + 2;
|
||||
}
|
||||
len = (len + 8) & ~0x7;
|
||||
|
||||
option = _ndp_msg_add_option (msg, len);
|
||||
if (option) {
|
||||
option->header.nd_opt_type = NM_ND_OPT_DNSSL;
|
||||
option->header.nd_opt_len = len / 8;
|
||||
option->lifetime = htonl (900);
|
||||
|
||||
search_list = option->search_list;
|
||||
for (i = 0; i < rdata->dns_domains->len; i++) {
|
||||
NMNDiscDNSDomain *dns_domain = &g_array_index (rdata->dns_domains, NMNDiscDNSDomain, i);
|
||||
uint8_t domain_len = strlen (dns_domain->domain);
|
||||
|
||||
*search_list++ = domain_len;
|
||||
memcpy (search_list, dns_domain->domain, domain_len);
|
||||
search_list += domain_len;
|
||||
*search_list++ = '\0';
|
||||
}
|
||||
} else {
|
||||
_LOGW ("The RA is too big, had to omit DNS search list.");
|
||||
}
|
||||
}
|
||||
|
||||
errsv = ndp_msg_send (priv->ndp, msg);
|
||||
|
||||
ndp_msg_destroy (msg);
|
||||
if (errsv) {
|
||||
errsv = errsv > 0 ? errsv : -errsv;
|
||||
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
||||
"%s (%d)",
|
||||
g_strerror (errsv), errsv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
receive_rs (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
|
||||
{
|
||||
NMNDisc *ndisc = user_data;
|
||||
|
||||
nm_ndisc_rs_received (ndisc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
event_ready (GIOChannel *source, GIOCondition condition, NMNDisc *ndisc)
|
||||
{
|
||||
@@ -332,7 +503,16 @@ start (NMNDisc *ndisc)
|
||||
/* Flush any pending messages to avoid using obsolete information */
|
||||
event_ready (priv->event_channel, 0, ndisc);
|
||||
|
||||
ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
switch (nm_ndisc_get_node_type (ndisc)) {
|
||||
case NM_NDISC_NODE_TYPE_HOST:
|
||||
ndp_msgrcv_handler_register (priv->ndp, receive_ra, NDP_MSG_RA, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
break;
|
||||
case NM_NDISC_NODE_TYPE_ROUTER:
|
||||
ndp_msgrcv_handler_register (priv->ndp, receive_rs, NDP_MSG_RS, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -360,6 +540,7 @@ nm_lndp_ndisc_new (NMPlatform *platform,
|
||||
NMUtilsStableType stable_type,
|
||||
const char *network_id,
|
||||
NMSettingIP6ConfigAddrGenMode addr_gen_mode,
|
||||
NMNDiscNodeType node_type,
|
||||
GError **error)
|
||||
{
|
||||
nm_auto_pop_netns NMPNetns *netns = NULL;
|
||||
@@ -380,7 +561,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_NODE_TYPE, (int) node_type,
|
||||
NM_NDISC_MAX_ADDRESSES, ipv6_sysctl_get (platform, ifname,
|
||||
"max_addresses",
|
||||
0, G_MAXINT32, NM_NDISC_MAX_ADDRESSES_DEFAULT),
|
||||
@@ -417,7 +598,16 @@ dispose (GObject *object)
|
||||
g_clear_pointer (&priv->event_channel, g_io_channel_unref);
|
||||
|
||||
if (priv->ndp) {
|
||||
ndp_msgrcv_handler_unregister (priv->ndp, receive_ra, NDP_MSG_RA, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
switch (nm_ndisc_get_node_type (ndisc)) {
|
||||
case NM_NDISC_NODE_TYPE_HOST:
|
||||
ndp_msgrcv_handler_unregister (priv->ndp, receive_rs, NDP_MSG_RA, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
break;
|
||||
case NM_NDISC_NODE_TYPE_ROUTER:
|
||||
ndp_msgrcv_handler_unregister (priv->ndp, receive_ra, NDP_MSG_RA, nm_ndisc_get_ifindex (ndisc), ndisc);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
ndp_close (priv->ndp);
|
||||
priv->ndp = NULL;
|
||||
}
|
||||
@@ -434,4 +624,5 @@ nm_lndp_ndisc_class_init (NMLndpNDiscClass *klass)
|
||||
object_class->dispose = dispose;
|
||||
ndisc_class->start = start;
|
||||
ndisc_class->send_rs = send_rs;
|
||||
ndisc_class->send_ra = send_ra;
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ NMNDisc *nm_lndp_ndisc_new (NMPlatform *platform,
|
||||
NMUtilsStableType stable_type,
|
||||
const char *network_id,
|
||||
NMSettingIP6ConfigAddrGenMode addr_gen_mode,
|
||||
NMNDiscNodeType node_type,
|
||||
GError **error);
|
||||
|
||||
#endif /* __NETWORKMANAGER_LNDP_NDISC_H__ */
|
||||
|
@@ -537,7 +537,8 @@ announce_router (NMNDisc *ndisc)
|
||||
_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),
|
||||
priv->send_ra_id = g_timeout_add_seconds (g_random_int_range (NM_NDISC_ROUTER_ADVERT_DELAY,
|
||||
NM_NDISC_ROUTER_ADVERT_INITIAL_INTERVAL),
|
||||
(GSourceFunc) announce_router, ndisc);
|
||||
} else {
|
||||
_LOGD ("will send an unsolicited router advertisement");
|
||||
@@ -586,7 +587,7 @@ announce_router_solicited (NMNDisc *ndisc)
|
||||
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),
|
||||
priv->send_ra_id = g_timeout_add (g_random_int_range (0, NM_NDISC_ROUTER_ADVERT_DELAY_MS),
|
||||
(GSourceFunc) announce_router, ndisc);
|
||||
}
|
||||
}
|
||||
|
@@ -121,7 +121,9 @@ typedef enum {
|
||||
#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_DELAY_MS 500 /* RFC4861 MAX_RA_DELAY_TIME */
|
||||
#define NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL 600 /* RFC4861 MaxRtrAdvInterval default */
|
||||
#define NM_NDISC_ROUTER_LIFETIME 900 /* 1.5 * NM_NDISC_ROUTER_ADVERT_MAX_INTERVAL */
|
||||
|
||||
struct _NMNDiscPrivate;
|
||||
struct _NMNDiscDataInternal;
|
||||
|
@@ -67,6 +67,7 @@ main (int argc, char **argv)
|
||||
NM_UTILS_STABLE_TYPE_UUID,
|
||||
"8ce666e8-d34d-4fb1-b858-f15a7al28086",
|
||||
NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
|
||||
NM_NDISC_NODE_TYPE_HOST,
|
||||
&error);
|
||||
if (!ndisc) {
|
||||
g_print ("Failed to create NMNDisc instance: %s\n", error->message);
|
||||
|
@@ -504,7 +504,9 @@ main (int argc, char *argv[])
|
||||
}
|
||||
ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET, gl.ifindex, global_opt.ifname,
|
||||
stable_type, stable_id,
|
||||
global_opt.addr_gen_mode, NULL);
|
||||
global_opt.addr_gen_mode,
|
||||
NM_NDISC_NODE_TYPE_HOST,
|
||||
NULL);
|
||||
g_assert (ndisc);
|
||||
|
||||
if (iid)
|
||||
|
Reference in New Issue
Block a user