Skip to content
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a33baee
Add a dynamic_prefix key to the user-context so we know which subnet6…
Monviech Apr 30, 2026
de70085
Also add dynamic_prefix to subnet6 dialog
Monviech Apr 30, 2026
59cd5ec
Add prefix source interface and resolve current prefix via Autoconf::…
Monviech Apr 30, 2026
e590582
model bump not needed anymore
Monviech Apr 30, 2026
21fa7c6
Add validations that disallow users to configure subnet value, pool v…
Monviech May 4, 2026
0064a48
Since the prefix_source is verbatim to a subnet, we only allow its us…
Monviech May 4, 2026
e911328
Add a mvp for the dynamic pd_pool, the pool is auto generated from th…
Monviech May 4, 2026
7ec936a
Make prefix pool validation stricter, if only a /64 prefix exists the…
Monviech May 4, 2026
baafa85
Remove config instantiation inside loops
Monviech May 4, 2026
082ee3d
Fix typo in previous
Monviech May 4, 2026
315d8ca
Add comment about possible overlap between identity association and p…
Monviech May 4, 2026
41d9295
Hide fields used for static prefix configuration if the subnet and pd…
Monviech May 4, 2026
13464f1
Add grid formatter to mark values as dynamic if they are in a dynamic…
Monviech May 4, 2026
623c06e
Add helper utilities for idassoc owned prefixes, and a helper in fire…
Monviech May 6, 2026
2e51733
Change all plumbing in the KEA model to use the new idassoc and util …
Monviech May 6, 2026
2db3180
Small typo in previous, type should be checkbox now for the dynamic p…
Monviech May 6, 2026
918837e
Add more validations to prevent multiple dynamic subnets and pd_pools…
Monviech May 7, 2026
bcb8576
Merge remote-tracking branch 'origin/master' into kea-dynamic-poc
Monviech May 7, 2026
d441b9f
Add hook script that can regenerate and reload the running kea-dhcpv6…
Monviech May 7, 2026
a3c9c7d
Fix merge conflicts
Monviech May 7, 2026
a903b30
plist-fix
Monviech May 7, 2026
bc10a18
Do not call kea_generage_dhcpv6 helper directly
Monviech May 7, 2026
6d45f52
Remove some unused cruft in KeaDhcpv6.php
Monviech May 7, 2026
d5a6489
Update src/etc/inc/plugins.inc.d/kea.inc
Monviech May 7, 2026
cfc150e
Update src/etc/inc/plugins.inc.d/kea.inc
Monviech May 7, 2026
8ed46cd
use nested ifs inside kea_newwanip
Monviech May 7, 2026
67c172a
Use mwexecf instead
Monviech May 7, 2026
01b79c6
Always pass model into kea_generate_dhcpv6()
Monviech May 7, 2026
89d15a5
Add a small guard to the pd_pool generation so it doesnt pass null in…
Monviech May 7, 2026
50a8276
Add a temporary placeholder prefix for all idassoc interfaces that fo…
Monviech May 7, 2026
2c0c5dc
Emit the prefix status and prefix source into the user context for tr…
Monviech May 7, 2026
9df3f2e
Add a new client-class that prevents any client in a subnet from gett…
Monviech May 7, 2026
a834609
Since we always have a prefix, this validation can be relaxed
Monviech May 7, 2026
72a60e3
Only evaluate the client class inside a subnet scope if its required.
Monviech May 7, 2026
c95009a
DisableCache in the subnet model relation fields so interfaces have t…
Monviech May 7, 2026
726a260
Always run dhcpv6 newwanip hook script when dhcpv6 is enabled since i…
Monviech May 8, 2026
b00a50a
Since KEA logs all commands issued to its socket anyway, we can reduc…
Monviech May 8, 2026
e382f17
DHCPSRV_ONLY_IF_REQUIRED_DEPRECATED The parameter 'only-if-required' …
Monviech May 8, 2026
daed0e3
Fix the NO_LEASES_PLEASE client-classes test
Monviech May 10, 2026
f4a0759
Add comment about issue if interfaces vanish or become deconfigured b…
Monviech May 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plist
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@
/usr/local/opnsense/mvc/app/library/OPNsense/Firewall/SNatRule.php
/usr/local/opnsense/mvc/app/library/OPNsense/Firewall/Util.php
/usr/local/opnsense/mvc/app/library/OPNsense/Interface/Autoconf.php
/usr/local/opnsense/mvc/app/library/OPNsense/Interface/Idassoc.php
/usr/local/opnsense/mvc/app/library/OPNsense/Mvc/Controller.php
/usr/local/opnsense/mvc/app/library/OPNsense/Mvc/Dispatcher.php
/usr/local/opnsense/mvc/app/library/OPNsense/Mvc/Exceptions/ClassNotFoundException.php
Expand Down Expand Up @@ -1307,6 +1308,7 @@
/usr/local/opnsense/scripts/kea/del_kea_leases.py
/usr/local/opnsense/scripts/kea/get_kea_leases.py
/usr/local/opnsense/scripts/kea/kea_dhcp_options.py
/usr/local/opnsense/scripts/kea/kea_prefix_renew.py
/usr/local/opnsense/scripts/kea/kea_prefix_watcher.py
/usr/local/opnsense/scripts/kea/lib/__init__.py
/usr/local/opnsense/scripts/kea/lib/kea_ctrl.py
Expand Down
37 changes: 32 additions & 5 deletions src/etc/inc/plugins.inc.d/kea.inc
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,26 @@ function kea_staticmap($proto = null, $valid_addresses = true, $ifconfig_details
function kea_configure()
{
return [
'kea_sync' => ['kea_configure_do']
'kea_generate_dhcpv6' => ['kea_generate_dhcpv6_do'],
'kea_sync' => ['kea_configure_do'],
'newwanip' => ['kea_newwanip'],
];
}

// used by kea_prefix_renew hook script to regenerate config when dynamic IPv6 prefix changes
function kea_generate_dhcpv6_do($verbose = false)
{
kea_generate_dhcpv6(new \OPNsense\Kea\KeaDhcpv6());
}

function kea_generate_dhcpv6($keaDhcpv6)
{
if ($keaDhcpv6->isEnabled() && $keaDhcpv6->general->manual_config->isEmpty()) {
/* skip kea-dhcp6.conf when configured manually */
$keaDhcpv6->generateConfig();
}
}

function kea_configure_do($verbose = false)
{
$keaDhcpv4 = new \OPNsense\Kea\KeaDhcpv4();
Expand All @@ -164,10 +180,7 @@ function kea_configure_do($verbose = false)
/* skip kea-dhcp4.conf when configured manually */
$keaDhcpv4->generateConfig();
}
if ($keaDhcpv6->isEnabled() && $keaDhcpv6->general->manual_config->isEmpty()) {
/* skip kea-dhcp6.conf when configured manually */
$keaDhcpv6->generateConfig();
}
kea_generate_dhcpv6($keaDhcpv6);
if ($keaDdns->isEnabled() && $keaDdns->general->manual_config->isEmpty()) {
/* skip kea-dhcp-ddns.conf when configured manually */
$keaDdns->generateConfig();
Expand All @@ -187,6 +200,20 @@ function kea_configure_do($verbose = false)
}
}

function kea_newwanip($interfaces, $family)
{
if ($family === 'inet6') {
$keaDhcpv6 = new \OPNsense\Kea\KeaDhcpv6();
if (
$keaDhcpv6->isEnabled() &&
$keaDhcpv6->general->manual_config->isEmpty() &&
$keaDhcpv6->pd_pools->pd_pool->count() > 0
Comment thread
Monviech marked this conversation as resolved.
Outdated
) {
mwexecf('/usr/local/opnsense/scripts/kea/kea_prefix_renew.py');
}
}
}

function kea_syslog()
{
return ['kea' => ['facility' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@
<id>pd_pool.prefix</id>
<label>Prefix</label>
<type>text</type>
<style>static_prefix</style>
<help></help>
<grid_view>
<formatter>pd_pool</formatter>
</grid_view>
</field>
<field>
<id>pd_pool.prefix_len</id>
<label>Prefix length</label>
<type>text</type>
<style>static_prefix</style>
<help></help>
<grid_view>
<formatter>pd_pool</formatter>
</grid_view>
</field>
<field>
<id>pd_pool.delegated_len</id>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,58 @@
<id>subnet6.subnet</id>
<label>Subnet</label>
<type>text</type>
<style>static_prefix</style>
<help>Subnet to use, should be large enough to hold the specified pools and reservations</help>
<grid_view>
<formatter>subnet</formatter>
</grid_view>
</field>
<field>
<id>subnet6.interface</id>
<label>Interface</label>
<type>dropdown</type>
<help>Select which interface this subnet belongs too.</help>
</field>
<field>
<id>subnet6.dynamic_prefix</id>
<label>Dynamic Prefix</label>
<type>checkbox</type>
<help>Use the identity association prefix allocated to this interface and generate subnet and pools automatically. DHCP options or DDNS settings that use IPv6 addresses are unaffected by prefix changes, they remain static.</help>
<grid_view>
<type>boolean</type>
<formatter>boolean</formatter>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>subnet6.allocator</id>
<label>Allocator</label>
<type>dropdown</type>
<advanced>true</advanced>
<help>Select allocator method to use when offering leases to clients.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>subnet6.pd-allocator</id>
<label>PD Allocator</label>
<type>dropdown</type>
<advanced>true</advanced>
<help>Select allocator method to use when offering prefix delegations to clients</help>
</field>
<field>
<id>subnet6.description</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed).</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
<field>
<id>subnet6.pools</id>
<label>Pools</label>
<type>textbox</type>
<style>static_prefix</style>
<help>List of pools, one per line in range or subnet format (e.g. 2001:db8:1::-2001:db8:1::100, 2001:db8:1::/80</help>
<grid_view>
<formatter>subnet</formatter>
</grid_view>
</field>
<field>
<id>subnet6.valid_lifetime</id>
Expand All @@ -46,6 +65,12 @@
<visible>false</visible>
</grid_view>
</field>
<field>
<id>subnet6.description</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed).</help>
</field>
<field>
<type>header</type>
<label>DHCP option data</label>
Expand Down
34 changes: 34 additions & 0 deletions src/opnsense/mvc/app/library/OPNsense/Firewall/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,40 @@ public static function isIPv6PrefixInPrefix(string $childPrefix, string $parentP
return self::isIPInCIDR($childAddress, $parentPrefix);
}

/**
* Split an IPv6 parent prefix (e.g., /56) into two child prefixes (e.g., 2x /57).
*
* @param string $prefix IPv6 CIDR prefix
* @return array two child prefixes or empty array
*/
public static function splitIPv6Prefix($prefix): array
{
if (!self::isSubnetStrict($prefix)) {
return [];
}

[$address, $prefix_len] = explode('/', $prefix, 2);
if (!self::isIpv6Address($address)) {
return [];
}

$child_prefix_len = (int)$prefix_len + 1;
if ($child_prefix_len > 128) {
return [];
}

$bytes = array_values(unpack('C*', inet_pton($address)));
$second = $bytes;

$bit = $child_prefix_len - 1;
$second[intdiv($bit, 8)] |= 1 << (7 - ($bit % 8));

return [
inet_ntop(pack('C*', ...$bytes)) . '/' . $child_prefix_len,
inet_ntop(pack('C*', ...$second)) . '/' . $child_prefix_len,
];
}

/**
* convert ipv4 cidr to netmask e.g. 24 --> 255.255.255.0
* @param int $bits ipv4 bits
Expand Down
Loading