core: support prefix-delegation.subnet-id
This commit is contained in:
@@ -11355,6 +11355,13 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d
|
||||
switch (notify_data->notify_type) {
|
||||
case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED:
|
||||
nm_assert(!IS_IPv4);
|
||||
if (notify_data->prefix_delegated.prefix->plen == 0
|
||||
|| notify_data->prefix_delegated.prefix->plen > 64) {
|
||||
_LOGW_ipdhcp(addr_family,
|
||||
"ignoring invalid prefix-delegation with length %u",
|
||||
notify_data->prefix_delegated.prefix->plen);
|
||||
return;
|
||||
}
|
||||
/* Just re-emit. The device just contributes the prefix to the
|
||||
* pool in NMPolicy, which decides about subnet allocation
|
||||
* on the shared devices. */
|
||||
|
@@ -156,16 +156,14 @@ static gboolean hostname_retry_cb(gpointer user_data);
|
||||
typedef struct {
|
||||
NMPlatformIP6Address prefix;
|
||||
NMDevice *device; /* The requesting ("uplink") device */
|
||||
guint64 next_subnet; /* Cache of the next subnet number to be
|
||||
* assigned from this prefix */
|
||||
GHashTable *subnets; /* ifindex -> NMPlatformIP6Address */
|
||||
GHashTable *map_subnet_id_to_ifindex; /* (guint64 *) subnet_id -> int ifindex */
|
||||
GHashTable *map_ifindex_to_subnet; /* int ifindex -> (NMPlatformIP6Address *) prefix */
|
||||
} IP6PrefixDelegation;
|
||||
|
||||
static void
|
||||
_clear_ip6_subnet(gpointer key, gpointer value, gpointer user_data)
|
||||
clear_ip6_subnet(int ifindex, NMPlatformIP6Address *subnet)
|
||||
{
|
||||
NMPlatformIP6Address *subnet = value;
|
||||
NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(key));
|
||||
NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, ifindex);
|
||||
|
||||
if (device) {
|
||||
/* We can not remove a subnet we already started announcing.
|
||||
@@ -176,6 +174,12 @@ _clear_ip6_subnet(gpointer key, gpointer value, gpointer user_data)
|
||||
g_slice_free(NMPlatformIP6Address, subnet);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_ip6_subnet_entry(gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
clear_ip6_subnet(GPOINTER_TO_INT(key), value);
|
||||
}
|
||||
|
||||
static void
|
||||
clear_ip6_prefix_delegation(gpointer data)
|
||||
{
|
||||
@@ -187,8 +191,9 @@ clear_ip6_prefix_delegation(gpointer data)
|
||||
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
||||
delegation->prefix.plen);
|
||||
|
||||
g_hash_table_foreach(delegation->subnets, _clear_ip6_subnet, NULL);
|
||||
g_hash_table_destroy(delegation->subnets);
|
||||
g_hash_table_foreach(delegation->map_ifindex_to_subnet, clear_ip6_subnet_entry, NULL);
|
||||
g_hash_table_destroy(delegation->map_ifindex_to_subnet);
|
||||
g_hash_table_destroy(delegation->map_subnet_id_to_ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -218,43 +223,109 @@ ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device)
|
||||
NMPlatformIP6Address *subnet;
|
||||
int ifindex = nm_device_get_ifindex(device);
|
||||
char sbuf[NM_INET_ADDRSTRLEN];
|
||||
NMSettingPrefixDelegation *s_pd;
|
||||
gint64 wanted_subnet_id = -1;
|
||||
guint64 num_subnets;
|
||||
guint64 old_subnet_id;
|
||||
|
||||
subnet = g_hash_table_lookup(delegation->subnets, GINT_TO_POINTER(ifindex));
|
||||
if (!subnet) {
|
||||
/* Check for out-of-prefixes condition. */
|
||||
if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) {
|
||||
nm_assert(delegation->prefix.plen > 0 && delegation->prefix.plen <= 64);
|
||||
|
||||
s_pd = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PREFIX_DELEGATION);
|
||||
if (s_pd) {
|
||||
wanted_subnet_id = nm_setting_prefix_delegation_get_subnet_id(s_pd);
|
||||
}
|
||||
|
||||
/* Try to use the cached subnet assigned to the interface */
|
||||
subnet = g_hash_table_lookup(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex));
|
||||
if (subnet) {
|
||||
old_subnet_id = nm_ip6_addr_get_subnet_id(&subnet->address, delegation->prefix.plen);
|
||||
if (wanted_subnet_id != -1 && wanted_subnet_id != old_subnet_id) {
|
||||
/* The device had a subnet assigned before, but now wants a
|
||||
* different subnet-id. Release the old subnet and continue below
|
||||
* to get a new one. */
|
||||
clear_ip6_subnet(ifindex, subnet);
|
||||
subnet = NULL;
|
||||
g_hash_table_remove(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex));
|
||||
g_hash_table_remove(delegation->map_subnet_id_to_ifindex, &old_subnet_id);
|
||||
} else {
|
||||
goto subnet_found;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for out-of-prefixes condition */
|
||||
num_subnets = 1 << (64 - delegation->prefix.plen);
|
||||
if (nm_g_hash_table_size(delegation->map_subnet_id_to_ifindex) >= num_subnets) {
|
||||
_LOGD(LOGD_IP6,
|
||||
"ipv6-pd: no more prefixes in %s/%d",
|
||||
"ipv6-pd: no more prefixes in %s/%u",
|
||||
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
||||
delegation->prefix.plen);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Allocate a new subnet. */
|
||||
/* Try to honor the "prefix-delegation.subnet-id" property */
|
||||
if (wanted_subnet_id >= 0) {
|
||||
gpointer value;
|
||||
NMDevice *other_device;
|
||||
|
||||
if (g_hash_table_lookup_extended(delegation->map_subnet_id_to_ifindex,
|
||||
&wanted_subnet_id,
|
||||
NULL,
|
||||
&value)) {
|
||||
other_device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(value));
|
||||
_LOGW(LOGD_IP6,
|
||||
"ipv6-pd: subnet-id 0x%" G_GINT64_MODIFIER
|
||||
"x wanted by device %s is already in use by "
|
||||
"device %s (ifindex %d)",
|
||||
(guint64) wanted_subnet_id,
|
||||
nm_device_get_iface(device),
|
||||
other_device ? nm_device_get_ip_iface(other_device) : NULL,
|
||||
GPOINTER_TO_INT(value));
|
||||
wanted_subnet_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't have a subnet-id yet, find the first one available */
|
||||
if (wanted_subnet_id < 0) {
|
||||
guint64 i;
|
||||
|
||||
for (i = 0; i < num_subnets; i++) {
|
||||
if (!g_hash_table_lookup_extended(delegation->map_subnet_id_to_ifindex,
|
||||
&i,
|
||||
NULL,
|
||||
NULL)) {
|
||||
wanted_subnet_id = (gint64) i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wanted_subnet_id < 0) {
|
||||
/* We already verified that there are available subnets, this should not happen */
|
||||
return nm_assert_unreachable_val(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a new subnet */
|
||||
subnet = g_slice_new0(NMPlatformIP6Address);
|
||||
g_hash_table_insert(delegation->subnets, GINT_TO_POINTER(ifindex), subnet);
|
||||
g_hash_table_insert(delegation->map_ifindex_to_subnet, GINT_TO_POINTER(ifindex), subnet);
|
||||
g_hash_table_insert(delegation->map_subnet_id_to_ifindex,
|
||||
nm_memdup(&wanted_subnet_id, sizeof(guint64)),
|
||||
GINT_TO_POINTER(ifindex));
|
||||
|
||||
subnet->plen = 64;
|
||||
subnet->address.s6_addr32[0] =
|
||||
delegation->prefix.address.s6_addr32[0] | htonl(delegation->next_subnet >> 32);
|
||||
delegation->prefix.address.s6_addr32[0] | htonl(wanted_subnet_id >> 32);
|
||||
subnet->address.s6_addr32[1] =
|
||||
delegation->prefix.address.s6_addr32[1] | htonl(delegation->next_subnet);
|
||||
|
||||
/* Out subnet pool management is pretty unsophisticated. We only add
|
||||
* the subnets and index them by ifindex. That keeps the implementation
|
||||
* simple and the dead entries make it easy to reuse the same subnet on
|
||||
* subsequent activations. On the other hand they may waste the subnet
|
||||
* space. */
|
||||
delegation->next_subnet++;
|
||||
}
|
||||
delegation->prefix.address.s6_addr32[1] | htonl(wanted_subnet_id);
|
||||
|
||||
subnet_found:
|
||||
subnet->timestamp = delegation->prefix.timestamp;
|
||||
subnet->lifetime = delegation->prefix.lifetime;
|
||||
subnet->preferred = delegation->prefix.preferred;
|
||||
|
||||
_LOGD(LOGD_IP6,
|
||||
"ipv6-pd: %s allocated from a /%d prefix on %s",
|
||||
"ipv6-pd: %s/64 (subnet-id 0x%" G_GINT64_MODIFIER "x) allocated from a /%d prefix on %s",
|
||||
nm_inet6_ntop(&subnet->address, sbuf),
|
||||
(guint64) wanted_subnet_id,
|
||||
delegation->prefix.plen,
|
||||
nm_device_get_iface(device));
|
||||
|
||||
@@ -345,8 +416,9 @@ device_ip6_prefix_delegated(NMDevice *device,
|
||||
if (i == priv->ip6_prefix_delegations->len) {
|
||||
/* Allocate a delegation for new prefix. */
|
||||
delegation = nm_g_array_append_new(priv->ip6_prefix_delegations, IP6PrefixDelegation);
|
||||
delegation->subnets = g_hash_table_new(nm_direct_hash, NULL);
|
||||
delegation->next_subnet = 0;
|
||||
delegation->map_subnet_id_to_ifindex =
|
||||
g_hash_table_new_full(nm_puint64_hash, nm_puint64_equal, g_free, NULL);
|
||||
delegation->map_ifindex_to_subnet = g_hash_table_new(nm_direct_hash, NULL);
|
||||
}
|
||||
|
||||
delegation->device = device;
|
||||
|
Reference in New Issue
Block a user