netns: allow defining a ip reservation that wraps around

The current implementation returns IP addresses obtained by adding a
counter to a base address. For CLAT we want to return all the 8
addresses in the 192.0.0.0/29 range, but not starting from 192.0.0.0
because that looks more like a network address. Slightly tweak the
algorithm so that addresses can wrap around.
This commit is contained in:
Beniamino Galvani 2026-01-08 19:09:41 +01:00
parent a2fe2f6300
commit 72f0600cf3

View file

@ -574,8 +574,8 @@ notify_watcher:
typedef struct {
const char *name;
guint32 start_addr; /* host byte order */
guint prefix_len;
guint num_addrs;
guint range_plen;
guint addr_plen;
gboolean allow_reuse;
} IPReservationTypeDesc;
@ -583,9 +583,9 @@ static const IPReservationTypeDesc ip_reservation_types[_NM_NETNS_IP_RESERVATION
[NM_NETNS_IP_RESERVATION_TYPE_SHARED4] =
{
.name = "shared-ip4",
.start_addr = 0x0a2a0001, /* 10.42.0.1 */
.prefix_len = 24,
.num_addrs = 256,
.start_addr = 0x0a2a0001, /* 10.42.{0-255}.1/24 */
.range_plen = 16,
.addr_plen = 24,
.allow_reuse = TRUE,
},
};
@ -613,13 +613,23 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
g_object_ref(self);
} else {
guint32 count;
guint32 base_network;
guint32 host_mask;
guint32 increment;
nm_assert(g_hash_table_size(*table) > 0);
nm_assert(desc->prefix_len > 0 && desc->prefix_len <= 32);
nm_assert(desc->range_plen < 32);
nm_assert(desc->addr_plen > 0 && desc->addr_plen <= 32);
nm_assert(desc->addr_plen > desc->range_plen);
base_network = desc->start_addr & ~(0xFFFFFFFFu >> desc->range_plen);
host_mask = 0xFFFFFFFFu >> desc->range_plen;
increment = 1 << (32 - desc->addr_plen);
count = 0u;
for (;;) {
addr = htonl(desc->start_addr + (count << (32 - desc->prefix_len)));
addr = htonl(base_network
+ ((base_network + (desc->start_addr + count * increment)) & host_mask));
res = g_hash_table_lookup(*table, &addr);
if (!res)
@ -627,7 +637,7 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
count++;
if (count >= desc->num_addrs) {
if (count >= 1 << (desc->addr_plen - desc->range_plen)) {
if (!desc->allow_reuse) {
_LOGE("%s: ran out of IP addresses", desc->name);
return NULL;
@ -637,12 +647,12 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
_LOGE("%s: ran out of IP addresses. Reuse %s/%u",
desc->name,
nm_inet4_ntop(res->addr, buf),
desc->prefix_len);
desc->addr_plen);
} else {
_LOGD("%s: reserved IP address %s/%u (duplicate)",
desc->name,
nm_inet4_ntop(res->addr, buf),
desc->prefix_len);
desc->addr_plen);
}
res->_ref_count++;
return res;
@ -663,7 +673,7 @@ nm_netns_ip_reservation_get(NMNetns *self, NMNetnsIPReservationType type)
_LOGD("%s: reserved IP address %s/%u",
desc->name,
nm_inet4_ntop(res->addr, buf),
desc->prefix_len);
desc->addr_plen);
return res;
}
@ -695,7 +705,7 @@ nm_netns_ip_reservation_release(NMNetnsIPReservation *res)
_LOGD("%s: release IP address reservation %s/%u (%d more references held)",
desc->name,
nm_inet4_ntop(res->addr, buf),
desc->prefix_len,
desc->addr_plen,
res->_ref_count);
return;
}
@ -706,7 +716,7 @@ nm_netns_ip_reservation_release(NMNetnsIPReservation *res)
_LOGD("%s: release IP address reservation %s/%u",
desc->name,
nm_inet4_ntop(res->addr, buf),
desc->prefix_len);
desc->addr_plen);
if (g_hash_table_size(*table) == 0) {
nm_clear_pointer(table, g_hash_table_unref);