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) {
|
switch (notify_data->notify_type) {
|
||||||
case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED:
|
case NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED:
|
||||||
nm_assert(!IS_IPv4);
|
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
|
/* Just re-emit. The device just contributes the prefix to the
|
||||||
* pool in NMPolicy, which decides about subnet allocation
|
* pool in NMPolicy, which decides about subnet allocation
|
||||||
* on the shared devices. */
|
* on the shared devices. */
|
||||||
|
@@ -155,17 +155,15 @@ static gboolean hostname_retry_cb(gpointer user_data);
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
NMPlatformIP6Address prefix;
|
NMPlatformIP6Address prefix;
|
||||||
NMDevice *device; /* The requesting ("uplink") device */
|
NMDevice *device; /* The requesting ("uplink") device */
|
||||||
guint64 next_subnet; /* Cache of the next subnet number to be
|
GHashTable *map_subnet_id_to_ifindex; /* (guint64 *) subnet_id -> int ifindex */
|
||||||
* assigned from this prefix */
|
GHashTable *map_ifindex_to_subnet; /* int ifindex -> (NMPlatformIP6Address *) prefix */
|
||||||
GHashTable *subnets; /* ifindex -> NMPlatformIP6Address */
|
|
||||||
} IP6PrefixDelegation;
|
} IP6PrefixDelegation;
|
||||||
|
|
||||||
static void
|
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, ifindex);
|
||||||
NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(key));
|
|
||||||
|
|
||||||
if (device) {
|
if (device) {
|
||||||
/* We can not remove a subnet we already started announcing.
|
/* 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);
|
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
|
static void
|
||||||
clear_ip6_prefix_delegation(gpointer data)
|
clear_ip6_prefix_delegation(gpointer data)
|
||||||
{
|
{
|
||||||
@@ -187,8 +191,9 @@ clear_ip6_prefix_delegation(gpointer data)
|
|||||||
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
||||||
delegation->prefix.plen);
|
delegation->prefix.plen);
|
||||||
|
|
||||||
g_hash_table_foreach(delegation->subnets, _clear_ip6_subnet, NULL);
|
g_hash_table_foreach(delegation->map_ifindex_to_subnet, clear_ip6_subnet_entry, NULL);
|
||||||
g_hash_table_destroy(delegation->subnets);
|
g_hash_table_destroy(delegation->map_ifindex_to_subnet);
|
||||||
|
g_hash_table_destroy(delegation->map_subnet_id_to_ifindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -215,46 +220,112 @@ expire_ip6_delegations(NMPolicy *self)
|
|||||||
static gboolean
|
static gboolean
|
||||||
ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device)
|
ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device)
|
||||||
{
|
{
|
||||||
NMPlatformIP6Address *subnet;
|
NMPlatformIP6Address *subnet;
|
||||||
int ifindex = nm_device_get_ifindex(device);
|
int ifindex = nm_device_get_ifindex(device);
|
||||||
char sbuf[NM_INET_ADDRSTRLEN];
|
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));
|
nm_assert(delegation->prefix.plen > 0 && delegation->prefix.plen <= 64);
|
||||||
if (!subnet) {
|
|
||||||
/* Check for out-of-prefixes condition. */
|
|
||||||
if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) {
|
|
||||||
_LOGD(LOGD_IP6,
|
|
||||||
"ipv6-pd: no more prefixes in %s/%d",
|
|
||||||
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
|
||||||
delegation->prefix.plen);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate a new subnet. */
|
s_pd = nm_device_get_applied_setting(device, NM_TYPE_SETTING_PREFIX_DELEGATION);
|
||||||
subnet = g_slice_new0(NMPlatformIP6Address);
|
if (s_pd) {
|
||||||
g_hash_table_insert(delegation->subnets, GINT_TO_POINTER(ifindex), subnet);
|
wanted_subnet_id = nm_setting_prefix_delegation_get_subnet_id(s_pd);
|
||||||
|
|
||||||
subnet->plen = 64;
|
|
||||||
subnet->address.s6_addr32[0] =
|
|
||||||
delegation->prefix.address.s6_addr32[0] | htonl(delegation->next_subnet >> 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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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/%u",
|
||||||
|
nm_inet6_ntop(&delegation->prefix.address, sbuf),
|
||||||
|
delegation->prefix.plen);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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->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(wanted_subnet_id >> 32);
|
||||||
|
subnet->address.s6_addr32[1] =
|
||||||
|
delegation->prefix.address.s6_addr32[1] | htonl(wanted_subnet_id);
|
||||||
|
|
||||||
|
subnet_found:
|
||||||
subnet->timestamp = delegation->prefix.timestamp;
|
subnet->timestamp = delegation->prefix.timestamp;
|
||||||
subnet->lifetime = delegation->prefix.lifetime;
|
subnet->lifetime = delegation->prefix.lifetime;
|
||||||
subnet->preferred = delegation->prefix.preferred;
|
subnet->preferred = delegation->prefix.preferred;
|
||||||
|
|
||||||
_LOGD(LOGD_IP6,
|
_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),
|
nm_inet6_ntop(&subnet->address, sbuf),
|
||||||
|
(guint64) wanted_subnet_id,
|
||||||
delegation->prefix.plen,
|
delegation->prefix.plen,
|
||||||
nm_device_get_iface(device));
|
nm_device_get_iface(device));
|
||||||
|
|
||||||
@@ -345,8 +416,9 @@ device_ip6_prefix_delegated(NMDevice *device,
|
|||||||
if (i == priv->ip6_prefix_delegations->len) {
|
if (i == priv->ip6_prefix_delegations->len) {
|
||||||
/* Allocate a delegation for new prefix. */
|
/* Allocate a delegation for new prefix. */
|
||||||
delegation = nm_g_array_append_new(priv->ip6_prefix_delegations, IP6PrefixDelegation);
|
delegation = nm_g_array_append_new(priv->ip6_prefix_delegations, IP6PrefixDelegation);
|
||||||
delegation->subnets = g_hash_table_new(nm_direct_hash, NULL);
|
delegation->map_subnet_id_to_ifindex =
|
||||||
delegation->next_subnet = 0;
|
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;
|
delegation->device = device;
|
||||||
|
Reference in New Issue
Block a user