mirror of
https://github.com/lucaspalomodevelop/opnsense-core.git
synced 2026-03-13 00:07:27 +00:00
Services: Kea DHCP: Kea DHCPv6 - add new option based on v4 (#8571)
This contains roughly the same configuration items as our current isc-dhcp6 alternative, with the exception of not trying to implement dynamic ranges based on data received from dhclient6. In terms of target audience, dynamic environments (receiving their "wan" type addressess via dhcp), should logically use dnsmasq for client configuration. Large (enterprise) setups usually are static by nature and may require prefix deligation to routers behind the primary one. In these cases Kea will be the tool of choice. Both v4 and v6 share the same rc scripts underneath, which means reconfiguration happens per package (eventhough two services are registered). Existing hooks for v4 have been extended with v6 data (firewall rules and staticmaps). Advanced configurations can still opt out of config file generation and supply their own json config, same as implemented for v4. The lease view still needs to be implemented, but that's likely a minor addition.
This commit is contained in:
parent
25e5341dd4
commit
9c50cbfcb8
9
plist
9
plist
@ -391,14 +391,20 @@
|
|||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVxlan.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVxlan.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/CtrlAgentController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/CtrlAgentController.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Dhcpv4Controller.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Dhcpv4Controller.php
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Dhcpv6Controller.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Leases4Controller.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Leases4Controller.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/ServiceController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/ServiceController.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/DhcpController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/DhcpController.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/agentSettings.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/agentSettings.xml
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogPDPool6.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogPeer4.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogPeer4.xml
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogPeer6.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogReservation4.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogReservation4.xml
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogReservation6.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogSubnet4.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogSubnet4.xml
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogSubnet6.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/generalSettings4.xml
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/generalSettings4.xml
|
||||||
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/generalSettings6.xml
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php
|
||||||
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
|
/usr/local/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php
|
||||||
@ -789,6 +795,8 @@
|
|||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaCtrlAgent.xml
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaCtrlAgent.xml
|
||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv4.php
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv4.php
|
||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv4.xml
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv4.xml
|
||||||
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.php
|
||||||
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.xml
|
||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/Menu/Menu.xml
|
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/Menu/Menu.xml
|
||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
|
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml
|
||||||
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
|
/usr/local/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml
|
||||||
@ -933,6 +941,7 @@
|
|||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vxlan.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vxlan.volt
|
||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/ctrl_agent.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/ctrl_agent.volt
|
||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/dhcpv4.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/dhcpv4.volt
|
||||||
|
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/dhcpv6.volt
|
||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/leases4.volt
|
||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/index.volt
|
||||||
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt
|
/usr/local/opnsense/mvc/app/views/OPNsense/Monit/status.volt
|
||||||
|
|||||||
@ -42,6 +42,23 @@ function kea_services()
|
|||||||
'name' => 'kea-dhcpv4',
|
'name' => 'kea-dhcpv4',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
if (!empty((string)(new \OPNsense\Kea\KeaDhcpv6())->general->enabled)) {
|
||||||
|
/**
|
||||||
|
* Although kea's backend operation is a single package, various services do offer their own pid files.
|
||||||
|
* Ideally we should have a list of pids to check for the services and only return a single item,
|
||||||
|
* but showing both with the same action below is the second best we can achieve.
|
||||||
|
*/
|
||||||
|
$services[] = [
|
||||||
|
'description' => gettext('KEA DHCPv6 server'),
|
||||||
|
'pidfile' => '/var/run/kea/kea-dhcp6.kea-dhcp6.pid',
|
||||||
|
'configd' => [
|
||||||
|
'restart' => ['kea restart'],
|
||||||
|
'start' => ['kea start'],
|
||||||
|
'stop' => ['kea stop'],
|
||||||
|
],
|
||||||
|
'name' => 'kea-dhcpv6',
|
||||||
|
];
|
||||||
|
}
|
||||||
return $services;
|
return $services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,14 +72,18 @@ function kea_run()
|
|||||||
function kea_staticmap($proto = null, $valid_addresses = true, $ifconfig_details = null)
|
function kea_staticmap($proto = null, $valid_addresses = true, $ifconfig_details = null)
|
||||||
{
|
{
|
||||||
$staticmap = [];
|
$staticmap = [];
|
||||||
$keav4 = new \OPNsense\Kea\KeaDhcpv4();
|
if ($proto == 6) {
|
||||||
|
$keamdl = new \OPNsense\Kea\KeaDhcpv6();
|
||||||
|
} else {
|
||||||
|
$keamdl = new \OPNsense\Kea\KeaDhcpv4();
|
||||||
|
}
|
||||||
|
|
||||||
if ($proto == 6 || empty((string)$keav4->general->enabled)) {
|
if (empty((string)$keamdl->general->enabled)) {
|
||||||
/* unsupported protocol or not enabled */
|
/* not enabled */
|
||||||
return $staticmap;
|
return $staticmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($keav4->reservations->reservation->iterateItems() as $reservation) {
|
foreach ($keamdl->reservations->reservation->iterateItems() as $reservation) {
|
||||||
$hostname = !empty((string)$reservation->hostname) ? (string)$reservation->hostname : null;
|
$hostname = !empty((string)$reservation->hostname) ? (string)$reservation->hostname : null;
|
||||||
$ip_address = (string)$reservation->ip_address;
|
$ip_address = (string)$reservation->ip_address;
|
||||||
if ($valid_addresses) {
|
if ($valid_addresses) {
|
||||||
@ -81,11 +102,13 @@ function kea_staticmap($proto = null, $valid_addresses = true, $ifconfig_details
|
|||||||
|
|
||||||
$description = !empty((string)$reservation->description) ? (string)$reservation->description : null;
|
$description = !empty((string)$reservation->description) ? (string)$reservation->description : null;
|
||||||
|
|
||||||
$subnet_node = $keav4->getNodeByReference("subnets.subnet4.{$reservation->subnet}");
|
|
||||||
$domain = null;
|
$domain = null;
|
||||||
if ($subnet_node) {
|
if ($proto == 4) {
|
||||||
if (!empty((string)$subnet_node->option_data->domain_name)) {
|
$subnet_node = $keamdl->getNodeByReference("subnets.subnet4.{$reservation->subnet}");
|
||||||
$domain = (string)$subnet_node->option_data->domain_name;
|
if ($subnet_node) {
|
||||||
|
if (!empty((string)$subnet_node->option_data->domain_name)) {
|
||||||
|
$domain = (string)$subnet_node->option_data->domain_name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,12 +136,17 @@ function kea_configure()
|
|||||||
function kea_configure_do($verbose = false)
|
function kea_configure_do($verbose = false)
|
||||||
{
|
{
|
||||||
$keaDhcpv4 = new \OPNsense\Kea\KeaDhcpv4();
|
$keaDhcpv4 = new \OPNsense\Kea\KeaDhcpv4();
|
||||||
if ($keaDhcpv4->isEnabled()) {
|
$keaDhcpv6 = new \OPNsense\Kea\KeaDhcpv6();
|
||||||
|
if ($keaDhcpv4->isEnabled() || $keaDhcpv6->isEnabled()) {
|
||||||
service_log('Sync KEA DHCP config...', $verbose);
|
service_log('Sync KEA DHCP config...', $verbose);
|
||||||
if ($keaDhcpv4->general->manual_config->isEmpty()) {
|
if ($keaDhcpv4->isEnabled() && $keaDhcpv4->general->manual_config->isEmpty()) {
|
||||||
/* skip kea-dhcp4.conf when configured manually */
|
/* skip kea-dhcp4.conf when configured manually */
|
||||||
$keaDhcpv4->generateConfig();
|
$keaDhcpv4->generateConfig();
|
||||||
}
|
}
|
||||||
|
if ($keaDhcpv6->isEnabled() && $keaDhcpv6->general->manual_config->isEmpty()) {
|
||||||
|
/* skip kea-dhcp6.conf when configured manually */
|
||||||
|
$keaDhcpv6->generateConfig();
|
||||||
|
}
|
||||||
(new \OPNsense\Kea\KeaCtrlAgent())->generateConfig();
|
(new \OPNsense\Kea\KeaCtrlAgent())->generateConfig();
|
||||||
service_log("done.\n", $verbose);
|
service_log("done.\n", $verbose);
|
||||||
}
|
}
|
||||||
@ -136,6 +164,7 @@ function kea_firewall($fw)
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$keav4 = new \OPNsense\Kea\KeaDhcpv4();
|
$keav4 = new \OPNsense\Kea\KeaDhcpv4();
|
||||||
|
$keav6 = new \OPNsense\Kea\KeaDhcpv6();
|
||||||
if ($keav4->fwrulesEnabled()) {
|
if ($keav4->fwrulesEnabled()) {
|
||||||
// automatic (IPv4) rules enabled
|
// automatic (IPv4) rules enabled
|
||||||
foreach (explode(',', $keav4->general->interfaces) as $intf) {
|
foreach (explode(',', $keav4->general->interfaces) as $intf) {
|
||||||
@ -169,6 +198,65 @@ function kea_firewall($fw)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($keav6->fwrulesEnabled()) {
|
||||||
|
foreach (explode(',', $keav6->general->interfaces) as $intf) {
|
||||||
|
$default_opts = [
|
||||||
|
'protocol' => 'udp',
|
||||||
|
'ipprotocol' => 'inet6',
|
||||||
|
'interface' => $intf,
|
||||||
|
'#ref' => 'ui/kea/dhcp/v6',
|
||||||
|
'descr' => 'allow access to DHCPv6 server',
|
||||||
|
'log' => !isset($config['syslog']['nologdefaultpass'])
|
||||||
|
];
|
||||||
|
$fw->registerFilterRule(
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
'from' => 'fe80::/10',
|
||||||
|
'to' => 'fe80::/10,ff02::/16',
|
||||||
|
'to_port' => 546
|
||||||
|
],
|
||||||
|
$default_opts
|
||||||
|
);
|
||||||
|
$fw->registerFilterRule(
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
'from' => 'fe80::/10',
|
||||||
|
'to' => 'ff02::/16',
|
||||||
|
'to_port' => 547
|
||||||
|
],
|
||||||
|
$default_opts
|
||||||
|
);
|
||||||
|
$fw->registerFilterRule(
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
'from' => 'ff02::/16',
|
||||||
|
'to' => 'fe80::/10',
|
||||||
|
'to_port' => 547
|
||||||
|
],
|
||||||
|
$default_opts
|
||||||
|
);
|
||||||
|
$fw->registerFilterRule(
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
'from' => 'fe80::/10',
|
||||||
|
'to' => '(self)',
|
||||||
|
'to_port' => 546
|
||||||
|
],
|
||||||
|
$default_opts
|
||||||
|
);
|
||||||
|
$fw->registerFilterRule(
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
'from' => '(self)',
|
||||||
|
'to' => 'fe80::/10',
|
||||||
|
'from_port' => 547,
|
||||||
|
'direction' => 'out'
|
||||||
|
],
|
||||||
|
$default_opts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function kea_xmlrpc_sync()
|
function kea_xmlrpc_sync()
|
||||||
@ -179,7 +267,7 @@ function kea_xmlrpc_sync()
|
|||||||
'description' => gettext('Kea DHCP'),
|
'description' => gettext('Kea DHCP'),
|
||||||
'section' => 'OPNsense.Kea',
|
'section' => 'OPNsense.Kea',
|
||||||
'id' => 'kea',
|
'id' => 'kea',
|
||||||
'services' => ["kea-dhcpv4"],
|
'services' => ["kea-dhcpv4", "kea-dhcpv6"],
|
||||||
];
|
];
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|||||||
@ -0,0 +1,188 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Deciso B.V.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OPNsense\Kea\Api;
|
||||||
|
|
||||||
|
use OPNsense\Base\ApiMutableModelControllerBase;
|
||||||
|
use OPNsense\Core\Config;
|
||||||
|
use OPNsense\Firewall\Util;
|
||||||
|
|
||||||
|
class Dhcpv6Controller extends ApiMutableModelControllerBase
|
||||||
|
{
|
||||||
|
protected static $internalModelName = 'dhcpv6';
|
||||||
|
protected static $internalModelClass = 'OPNsense\Kea\KeaDhcpv6';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getAction()
|
||||||
|
{
|
||||||
|
$data = parent::getAction();
|
||||||
|
return [
|
||||||
|
self::$internalModelName => [
|
||||||
|
'general' => $data[self::$internalModelName]['general'],
|
||||||
|
'ha' => $data[self::$internalModelName]['ha'],
|
||||||
|
'this_hostname' => (string)Config::getInstance()->object()->system->hostname
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchSubnetAction()
|
||||||
|
{
|
||||||
|
return $this->searchBase("subnets.subnet6", null, "subnet");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubnetAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->setBase("subnet6", "subnets.subnet6", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSubnetAction()
|
||||||
|
{
|
||||||
|
return $this->addBase("subnet6", "subnets.subnet6");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubnetAction($uuid = null)
|
||||||
|
{
|
||||||
|
return $this->getBase("subnet6", "subnets.subnet6", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delSubnetAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->delBase("subnets.subnet6", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchReservationAction()
|
||||||
|
{
|
||||||
|
return $this->searchBase("reservations.reservation", null, "duid");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReservationAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->setBase("reservation", "reservations.reservation", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addReservationAction()
|
||||||
|
{
|
||||||
|
return $this->addBase("reservation", "reservations.reservation");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReservationAction($uuid = null)
|
||||||
|
{
|
||||||
|
return $this->getBase("reservation", "reservations.reservation", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delReservationAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->delBase("reservations.reservation", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadReservationsAction()
|
||||||
|
{
|
||||||
|
if ($this->request->isGet()) {
|
||||||
|
$this->exportCsv($this->getModel()->reservations->reservation->asRecordSet(false, ['subnet']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadReservationsAction()
|
||||||
|
{
|
||||||
|
if ($this->request->isPost() && $this->request->hasPost('payload')) {
|
||||||
|
$subnets = [];
|
||||||
|
foreach ($this->getModel()->subnets->subnet6->iterateItems() as $key => $node) {
|
||||||
|
$subnets[(string)$node->subnet] = $key;
|
||||||
|
}
|
||||||
|
return $this->importCsv(
|
||||||
|
'reservations.reservation',
|
||||||
|
$this->request->getPost('payload'),
|
||||||
|
['duid', 'subnet'],
|
||||||
|
function (&$record) use ($subnets) {
|
||||||
|
/* seek matching subnet */
|
||||||
|
if (!empty($record['ip_address'])) {
|
||||||
|
foreach ($subnets as $subnet => $uuid) {
|
||||||
|
if (Util::isIPInCIDR($record['ip_address'], $subnet)) {
|
||||||
|
$record['subnet'] = $uuid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ['status' => 'failed'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPdPoolAction()
|
||||||
|
{
|
||||||
|
return $this->searchBase("pd_pools.pd_pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPdPoolAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->setBase("pd_pool", "pd_pools.pd_pool", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addPdPoolAction()
|
||||||
|
{
|
||||||
|
return $this->addBase("pd_pool", "pd_pools.pd_pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPdPoolAction($uuid = null)
|
||||||
|
{
|
||||||
|
return $this->getBase("pd_pool", "pd_pools.pd_pool", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delPdPoolAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->delBase("pd_pools.pd_pool", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchPeerAction()
|
||||||
|
{
|
||||||
|
return $this->searchBase("ha_peers.peer", null, "name");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPeerAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->setBase("peer", "ha_peers.peer", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addPeerAction()
|
||||||
|
{
|
||||||
|
return $this->addBase("peer", "ha_peers.peer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPeerAction($uuid = null)
|
||||||
|
{
|
||||||
|
return $this->getBase("peer", "ha_peers.peer", $uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delPeerAction($uuid)
|
||||||
|
{
|
||||||
|
return $this->delBase("ha_peers.peer", $uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,8 @@ namespace OPNsense\Kea\Api;
|
|||||||
|
|
||||||
use OPNsense\Base\ApiMutableServiceControllerBase;
|
use OPNsense\Base\ApiMutableServiceControllerBase;
|
||||||
use OPNsense\Core\Backend;
|
use OPNsense\Core\Backend;
|
||||||
|
use OPNsense\Kea\KeaDhcpv4;
|
||||||
|
use OPNsense\Kea\KeaDhcpv6;
|
||||||
|
|
||||||
class ServiceController extends ApiMutableServiceControllerBase
|
class ServiceController extends ApiMutableServiceControllerBase
|
||||||
{
|
{
|
||||||
@ -38,8 +40,8 @@ class ServiceController extends ApiMutableServiceControllerBase
|
|||||||
protected static $internalServiceEnabled = 'general.enabled';
|
protected static $internalServiceEnabled = 'general.enabled';
|
||||||
protected static $internalServiceName = 'kea';
|
protected static $internalServiceName = 'kea';
|
||||||
|
|
||||||
/**
|
protected function serviceEnabled()
|
||||||
* TODO: overwrite when implementing KeaDhcpv6 as well. Both services share the same rc script
|
{
|
||||||
* protected function serviceEnabled() {}
|
return (new KeaDhcpv4())->general->enabled == '1' || (new KeaDhcpv6())->general->enabled == '1';
|
||||||
*/
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,24 @@ class DhcpController extends \OPNsense\Base\IndexController
|
|||||||
$this->view->formGridPeer = $this->getFormGrid("dialogPeer4");
|
$this->view->formGridPeer = $this->getFormGrid("dialogPeer4");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function v6Action()
|
||||||
|
{
|
||||||
|
$this->view->pick('OPNsense/Kea/dhcpv6');
|
||||||
|
$this->view->formGeneralSettings = $this->getForm("generalSettings6");
|
||||||
|
|
||||||
|
$this->view->formDialogSubnet = $this->getForm("dialogSubnet6");
|
||||||
|
$this->view->formGridSubnet = $this->getFormGrid("dialogSubnet6");
|
||||||
|
|
||||||
|
$this->view->formDialogReservation = $this->getForm("dialogReservation6");
|
||||||
|
$this->view->formGridReservation = $this->getFormGrid("dialogReservation6");
|
||||||
|
|
||||||
|
$this->view->formDialogPDPool = $this->getForm("dialogPDPool6");
|
||||||
|
$this->view->formGridPDPool = $this->getFormGrid("dialogPDPool6");
|
||||||
|
|
||||||
|
$this->view->formDialogPeer = $this->getForm("dialogPeer6");
|
||||||
|
$this->view->formGridPeer = $this->getFormGrid("dialogPeer6");
|
||||||
|
}
|
||||||
|
|
||||||
public function leases4Action()
|
public function leases4Action()
|
||||||
{
|
{
|
||||||
$this->view->pick('OPNsense/Kea/leases4');
|
$this->view->pick('OPNsense/Kea/leases4');
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
<form>
|
||||||
|
<field>
|
||||||
|
<id>pd_pool.subnet</id>
|
||||||
|
<label>Subnet</label>
|
||||||
|
<type>dropdown</type>
|
||||||
|
<help>Subnet this reservation belongs to</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>pd_pool.prefix</id>
|
||||||
|
<label>Prefix</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help></help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>pd_pool.prefix_len</id>
|
||||||
|
<label>Prefix length</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help></help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>pd_pool.delegated_len</id>
|
||||||
|
<label>Delegated Length</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help></help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>pd_pool.description</id>
|
||||||
|
<label>Description</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>You may enter a description here for your reference (not parsed).</help>
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
<form>
|
||||||
|
<field>
|
||||||
|
<id>peer.name</id>
|
||||||
|
<label>Name</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>Peer name, there should be one entry matching this machines "This server name"</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>peer.role</id>
|
||||||
|
<label>Role</label>
|
||||||
|
<type>dropdown</type>
|
||||||
|
<help>This peers role</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>peer.url</id>
|
||||||
|
<label>Url</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>This specifies the URL of our server instance, which should use a different port than the control agent.
|
||||||
|
For example http://my.host:8001/</help>
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<form>
|
||||||
|
<field>
|
||||||
|
<id>reservation.subnet</id>
|
||||||
|
<label>Subnet</label>
|
||||||
|
<type>dropdown</type>
|
||||||
|
<help>Subnet this reservation belongs to</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>reservation.ip_address</id>
|
||||||
|
<label>IP address</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>IP address to offer to the client</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>reservation.duid</id>
|
||||||
|
<label>DUID</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>duid of the client in question</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>reservation.hostname</id>
|
||||||
|
<label>Hostname</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>Offer a hostname to the client</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>reservation.domain_search</id>
|
||||||
|
<label>Domain search</label>
|
||||||
|
<type>select_multiple</type>
|
||||||
|
<style>tokenize</style>
|
||||||
|
<allownew>true</allownew>
|
||||||
|
<separator>,</separator>
|
||||||
|
<help>Specifies a ´search list´ of Domain Names to be used by the client to locate not-fully-qualified domain names.</help>
|
||||||
|
<grid_view>
|
||||||
|
<visible>false</visible>
|
||||||
|
</grid_view>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>reservation.description</id>
|
||||||
|
<label>Description</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>You may enter a description here for your reference (not parsed).</help>
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
<form>
|
||||||
|
<field>
|
||||||
|
<id>subnet6.subnet</id>
|
||||||
|
<label>Subnet</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>Subnet to use, should be large enough to hold the specified pools and reservations</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>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>subnet6.pools</id>
|
||||||
|
<label>Pools</label>
|
||||||
|
<type>textbox</type>
|
||||||
|
<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>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<type>header</type>
|
||||||
|
<label>DHCP option data</label>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>subnet6.option_data.dns_servers</id>
|
||||||
|
<label>DNS servers</label>
|
||||||
|
<type>select_multiple</type>
|
||||||
|
<style>tokenize</style>
|
||||||
|
<allownew>true</allownew>
|
||||||
|
<help>DNS servers to offer to the clients</help>
|
||||||
|
<grid_view>
|
||||||
|
<visible>false</visible>
|
||||||
|
</grid_view>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>subnet6.option_data.domain_search</id>
|
||||||
|
<label>Domain search</label>
|
||||||
|
<type>select_multiple</type>
|
||||||
|
<style>tokenize</style>
|
||||||
|
<allownew>true</allownew>
|
||||||
|
<separator>,</separator>
|
||||||
|
<help>Specifies a ´search list´ of Domain Names to be used by the client to locate not-fully-qualified domain names.</help>
|
||||||
|
<grid_view>
|
||||||
|
<visible>false</visible>
|
||||||
|
</grid_view>
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
<form>
|
||||||
|
<field>
|
||||||
|
<type>header</type>
|
||||||
|
<label>Service</label>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.general.enabled</id>
|
||||||
|
<label>Enabled</label>
|
||||||
|
<type>checkbox</type>
|
||||||
|
<help>Enable DHCPv6 server.</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.general.manual_config</id>
|
||||||
|
<label>Manual config</label>
|
||||||
|
<type>checkbox</type>
|
||||||
|
<advanced>true</advanced>
|
||||||
|
<help>Disable configuration file generation and manage the file (/usr/local/etc/kea/kea-dhcp4.conf) manually.</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<type>header</type>
|
||||||
|
<label>General settings</label>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.general.interfaces</id>
|
||||||
|
<label>Interfaces</label>
|
||||||
|
<type>select_multiple</type>
|
||||||
|
<help>Select interfaces to listen on.</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.general.valid_lifetime</id>
|
||||||
|
<label>Valid lifetime</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>Defines how long the addresses (leases) given out by the server are valid (in seconds)</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.general.fwrules</id>
|
||||||
|
<label>Firewall rules</label>
|
||||||
|
<type>checkbox</type>
|
||||||
|
<help>Automatically add a basic set of firewall rules to allow dhcp traffic, more fine grained controls can be offered manually when disabling this option.</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<type>header</type>
|
||||||
|
<label>High Availability</label>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.ha.enabled</id>
|
||||||
|
<label>Enabled</label>
|
||||||
|
<type>checkbox</type>
|
||||||
|
<help>Enable High availability hook, requires the Control Agent to be enabled as well.</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.ha.this_server_name</id>
|
||||||
|
<label>This server name</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>The name of this server, should match with one of the entries in the HA peers.
|
||||||
|
Leave empty to use this machines hostname
|
||||||
|
</help>
|
||||||
|
</field>
|
||||||
|
<field>
|
||||||
|
<id>dhcpv6.ha.max_unacked_clients</id>
|
||||||
|
<label>Max Unacked clients</label>
|
||||||
|
<type>text</type>
|
||||||
|
<help>
|
||||||
|
This specifies the number of clients which send messages to the partner but appear to not receive any response.
|
||||||
|
A higher value needs a busier environment in order to consider a member down, when set to 0,
|
||||||
|
any network disruption will cause a failover to happen.
|
||||||
|
</help>
|
||||||
|
</field>
|
||||||
|
</form>
|
||||||
@ -10,6 +10,17 @@
|
|||||||
<pattern>api/kea/service/*</pattern>
|
<pattern>api/kea/service/*</pattern>
|
||||||
</patterns>
|
</patterns>
|
||||||
</page-dhcp-kea-v4>
|
</page-dhcp-kea-v4>
|
||||||
|
<page-dhcp-kea-v6>
|
||||||
|
<name>Services: DHCP: Kea(v6)</name>
|
||||||
|
<description>Allow access to the KEA dhcp6 server</description>
|
||||||
|
<patterns>
|
||||||
|
<pattern>ui/kea/dhcp/v6</pattern>
|
||||||
|
<pattern>ui/kea/dhcp/leases6</pattern>
|
||||||
|
<pattern>api/kea/dhcpv6/*</pattern>
|
||||||
|
<pattern>api/kea/leases6/*</pattern>
|
||||||
|
<pattern>api/kea/service/*</pattern>
|
||||||
|
</patterns>
|
||||||
|
</page-dhcp-kea-v6>
|
||||||
<page-dhcp-kea-ctrl-agent>
|
<page-dhcp-kea-ctrl-agent>
|
||||||
<name>Services: DHCP: Kea Ctrl Agent</name>
|
<name>Services: DHCP: Kea Ctrl Agent</name>
|
||||||
<description>Allow access to the KEA Ctrl Agent</description>
|
<description>Allow access to the KEA Ctrl Agent</description>
|
||||||
|
|||||||
239
src/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.php
Normal file
239
src/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.php
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Deciso B.V.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OPNsense\Kea;
|
||||||
|
|
||||||
|
use OPNsense\Base\Messages\Message;
|
||||||
|
use OPNsense\Base\BaseModel;
|
||||||
|
use OPNsense\Core\Config;
|
||||||
|
use OPNsense\Core\Backend;
|
||||||
|
use OPNsense\Core\File;
|
||||||
|
use OPNsense\Firewall\Util;
|
||||||
|
|
||||||
|
class KeaDhcpv6 extends BaseModel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function performValidation($validateFullModel = false)
|
||||||
|
{
|
||||||
|
$messages = parent::performValidation($validateFullModel);
|
||||||
|
// validate changed reservations
|
||||||
|
foreach ($this->reservations->reservation->iterateItems() as $reservation) {
|
||||||
|
if (!$validateFullModel && !$reservation->isFieldChanged()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$key = $reservation->__reference;
|
||||||
|
$subnet = "";
|
||||||
|
$subnet_node = $this->getNodeByReference("subnets.subnet6.{$reservation->subnet}");
|
||||||
|
if ($subnet_node) {
|
||||||
|
$subnet = (string)$subnet_node->subnet;
|
||||||
|
}
|
||||||
|
if (!Util::isIPInCIDR((string)$reservation->ip_address, $subnet)) {
|
||||||
|
$messages->appendMessage(new Message(gettext("Address not in specified subnet"), $key . ".ip_address"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEnabled()
|
||||||
|
{
|
||||||
|
return (string)$this->general->enabled == '1' && !empty((string)(string)$this->general->interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* should filter rules be enabled
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function fwrulesEnabled()
|
||||||
|
{
|
||||||
|
return (string)$this->general->enabled == '1' &&
|
||||||
|
(string)$this->general->fwrules == '1' &&
|
||||||
|
!empty((string)$this->general->interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function getConfigPhysicalInterfaces()
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$cfg = Config::getInstance()->object();
|
||||||
|
foreach (explode(',', $this->general->interfaces) as $if) {
|
||||||
|
if (isset($cfg->interfaces->$if) && !empty($cfg->interfaces->$if->if)) {
|
||||||
|
$result[] = (string)$cfg->interfaces->$if->if;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConfigThisServerHostname()
|
||||||
|
{
|
||||||
|
$hostname = (string)$this->ha->this_server_name;
|
||||||
|
if (empty($hostname)) {
|
||||||
|
$hostname = (string)Config::getInstance()->object()->system->hostname;
|
||||||
|
}
|
||||||
|
return $hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConfigSubnets()
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$subnet_id = 1;
|
||||||
|
foreach ($this->subnets->subnet6->iterateItems() as $subnet_uuid => $subnet) {
|
||||||
|
$record = [
|
||||||
|
'id' => $subnet_id++,
|
||||||
|
'subnet' => (string)$subnet->subnet,
|
||||||
|
'option-data' => [],
|
||||||
|
'pools' => [],
|
||||||
|
'pd-pools' => [],
|
||||||
|
'reservations' => []
|
||||||
|
];
|
||||||
|
/* standard option-data elements */
|
||||||
|
foreach ($subnet->option_data->iterateItems() as $key => $value) {
|
||||||
|
$target_fieldname = str_replace('_', '-', $key);
|
||||||
|
if ((string)$value != '') {
|
||||||
|
$record['option-data'][] = [
|
||||||
|
'name' => $target_fieldname,
|
||||||
|
'data' => (string)$value
|
||||||
|
];
|
||||||
|
} elseif ($key == 'domain_name') {
|
||||||
|
$record['option-data'][] = [
|
||||||
|
'name' => $target_fieldname,
|
||||||
|
'data' => (string)Config::getInstance()->object()->system->domain
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* add pools */
|
||||||
|
foreach (array_filter(explode("\n", $subnet->pools)) as $pool) {
|
||||||
|
$record['pools'][] = ['pool' => $pool];
|
||||||
|
}
|
||||||
|
/* add pd-pools */
|
||||||
|
foreach ($this->pd_pools->pd_pool->iterateItems() as $key => $pdpool) {
|
||||||
|
if ($pdpool->subnet != $subnet_uuid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$record['pd-pools'][] = [
|
||||||
|
'prefix' => (string)$pdpool->prefix,
|
||||||
|
'prefix-len' => (int)$pdpool->prefix_len->getCurrentValue(),
|
||||||
|
'delegated-len' => (int)$pdpool->delegated_len->getCurrentValue()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
/* static reservations */
|
||||||
|
foreach ($this->reservations->reservation->iterateItems() as $key => $reservation) {
|
||||||
|
if ($reservation->subnet != $subnet_uuid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$res = ['option-data' => []];
|
||||||
|
foreach (['duid', 'hostname'] as $key) {
|
||||||
|
if (!empty((string)$reservation->$key)) {
|
||||||
|
$res[str_replace('_', '-', $key)] = (string)$reservation->$key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$res['ip-addresses'] = explode(',', (string)$reservation->ip_address);
|
||||||
|
if (!$reservation->domain_search->isEmpty()) {
|
||||||
|
$res['option-data'][] = [
|
||||||
|
'name' => 'domain-search',
|
||||||
|
'data' => (string)$reservation->domain_search
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$record['reservations'][] = $res;
|
||||||
|
}
|
||||||
|
$result[] = $record;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateConfig($target = '/usr/local/etc/kea/kea-dhcp6.conf')
|
||||||
|
{
|
||||||
|
$cnf = [
|
||||||
|
'Dhcp6' => [
|
||||||
|
'valid-lifetime' => (int)$this->general->valid_lifetime->__toString(),
|
||||||
|
'interfaces-config' => [
|
||||||
|
'interfaces' => $this->getConfigPhysicalInterfaces()
|
||||||
|
],
|
||||||
|
'lease-database' => [
|
||||||
|
'type' => 'memfile',
|
||||||
|
'persist' => true,
|
||||||
|
],
|
||||||
|
'control-socket' => [
|
||||||
|
'socket-type' => 'unix',
|
||||||
|
'socket-name' => '/var/run/kea6-ctrl-socket'
|
||||||
|
],
|
||||||
|
'loggers' => [
|
||||||
|
[
|
||||||
|
'name' => 'kea-dhcp6',
|
||||||
|
'output_options' => [
|
||||||
|
[
|
||||||
|
'output' => 'syslog'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'severity' => 'INFO',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'subnet6' => $this->getConfigSubnets(),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (!empty((string)(new KeaCtrlAgent())->general->enabled)) {
|
||||||
|
$cnf['Dhcp6']['hooks-libraries'] = [];
|
||||||
|
$cnf['Dhcp6']['hooks-libraries'][] = [
|
||||||
|
'library' => '/usr/local/lib/kea/hooks/libdhcp_lease_cmds.so'
|
||||||
|
];
|
||||||
|
if (!empty((string)$this->ha->enabled)) {
|
||||||
|
$record = [
|
||||||
|
'library' => '/usr/local/lib/kea/hooks/libdhcp_ha.so',
|
||||||
|
'parameters' => [
|
||||||
|
'high-availability' => [
|
||||||
|
[
|
||||||
|
'this-server-name' => $this->getConfigThisServerHostname(),
|
||||||
|
'mode' => 'hot-standby',
|
||||||
|
'heartbeat-delay' => 10000,
|
||||||
|
'max-response-delay' => 60000,
|
||||||
|
'max-ack-delay' => 5000,
|
||||||
|
'max-unacked-clients' => (int)((string)$this->ha->max_unacked_clients),
|
||||||
|
'sync-timeout' => 60000,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
foreach ($this->ha_peers->peer->iterateItems() as $peer) {
|
||||||
|
if (!isset($record['parameters']['high-availability'][0]['peers'])) {
|
||||||
|
$record['parameters']['high-availability'][0]['peers'] = [];
|
||||||
|
}
|
||||||
|
$record['parameters']['high-availability'][0]['peers'][] = array_map(
|
||||||
|
fn($x) => (string)$x,
|
||||||
|
iterator_to_array($peer->iterateItems())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$cnf['Dhcp6']['hooks-libraries'][] = $record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File::file_put_contents($target, json_encode($cnf, JSON_PRETTY_PRINT), 0600);
|
||||||
|
}
|
||||||
|
}
|
||||||
181
src/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.xml
Normal file
181
src/opnsense/mvc/app/models/OPNsense/Kea/KeaDhcpv6.xml
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<model>
|
||||||
|
<mount>//OPNsense/Kea/dhcp6</mount>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<description>Kea DHCPv6 configuration</description>
|
||||||
|
<items>
|
||||||
|
<general>
|
||||||
|
<enabled type="BooleanField">
|
||||||
|
<Default>0</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</enabled>
|
||||||
|
<manual_config type="BooleanField"/>
|
||||||
|
<interfaces type="InterfaceField">
|
||||||
|
<Multiple>Y</Multiple>
|
||||||
|
</interfaces>
|
||||||
|
<valid_lifetime type="IntegerField">
|
||||||
|
<Default>4000</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</valid_lifetime>
|
||||||
|
<fwrules type="BooleanField">
|
||||||
|
<Required>Y</Required>
|
||||||
|
<Default>1</Default>
|
||||||
|
</fwrules>
|
||||||
|
</general>
|
||||||
|
<ha>
|
||||||
|
<enabled type="BooleanField">
|
||||||
|
<Default>0</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</enabled>
|
||||||
|
<this_server_name type="TextField">
|
||||||
|
<Mask>/^([0-9a-zA-Z.\:\-,_]){0,1024}$/u</Mask>
|
||||||
|
</this_server_name>
|
||||||
|
<max_unacked_clients type="IntegerField">
|
||||||
|
<MinimumValue>0</MinimumValue>
|
||||||
|
<MaximumValue>65535</MaximumValue>
|
||||||
|
<Default>2</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</max_unacked_clients>
|
||||||
|
</ha>
|
||||||
|
<subnets>
|
||||||
|
<subnet6 type="ArrayField">
|
||||||
|
<subnet type="NetworkField">
|
||||||
|
<NetMaskRequired>Y</NetMaskRequired>
|
||||||
|
<AddressFamily>ipv6</AddressFamily>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</subnet>
|
||||||
|
<option_data>
|
||||||
|
<dns_servers type="NetworkField">
|
||||||
|
<NetMaskAllowed>N</NetMaskAllowed>
|
||||||
|
<AddressFamily>ipv6</AddressFamily>
|
||||||
|
<AsList>Y</AsList>
|
||||||
|
<FieldSeparator>,</FieldSeparator>
|
||||||
|
</dns_servers>
|
||||||
|
<domain_search type="HostnameField">
|
||||||
|
<IpAllowed>N</IpAllowed>
|
||||||
|
<FieldSeparator>,</FieldSeparator>
|
||||||
|
<AsList>Y</AsList>
|
||||||
|
<ValidationMessage>Please specify a valid list of domains</ValidationMessage>
|
||||||
|
</domain_search>
|
||||||
|
</option_data>
|
||||||
|
<pools type=".\KeaPoolsField">
|
||||||
|
</pools>
|
||||||
|
<description type="DescriptionField"/>
|
||||||
|
</subnet6>
|
||||||
|
</subnets>
|
||||||
|
<reservations>
|
||||||
|
<reservation type="ArrayField">
|
||||||
|
<subnet type="ModelRelationField">
|
||||||
|
<Model>
|
||||||
|
<subnets>
|
||||||
|
<source>OPNsense.Kea.KeaDhcpv6</source>
|
||||||
|
<items>subnets.subnet6</items>
|
||||||
|
<display>subnet</display>
|
||||||
|
</subnets>
|
||||||
|
</Model>
|
||||||
|
<ValidationMessage>Related subnet not found</ValidationMessage>
|
||||||
|
<Constraints>
|
||||||
|
<check001>
|
||||||
|
<reference>duid.check001</reference>
|
||||||
|
</check001>
|
||||||
|
</Constraints>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</subnet>
|
||||||
|
<ip_address type="NetworkField">
|
||||||
|
<NetMaskAllowed>N</NetMaskAllowed>
|
||||||
|
<AddressFamily>ipv6</AddressFamily>
|
||||||
|
<Constraints>
|
||||||
|
<check001>
|
||||||
|
<ValidationMessage>Duplicate entry exists</ValidationMessage>
|
||||||
|
<type>UniqueConstraint</type>
|
||||||
|
</check001>
|
||||||
|
</Constraints>
|
||||||
|
</ip_address>
|
||||||
|
<duid type="TextField">
|
||||||
|
<Required>Y</Required>
|
||||||
|
<Mask>/^(?:[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2})+)$/</Mask>
|
||||||
|
<ValidationMessage>Value must be a colon-separated hexadecimal sequence (e.g., 01:02:f3).</ValidationMessage>
|
||||||
|
<Constraints>
|
||||||
|
<check001>
|
||||||
|
<ValidationMessage>Duplicate entry exists</ValidationMessage>
|
||||||
|
<type>UniqueConstraint</type>
|
||||||
|
<addFields>
|
||||||
|
<field1>subnet</field1>
|
||||||
|
</addFields>
|
||||||
|
</check001>
|
||||||
|
</Constraints>
|
||||||
|
</duid>
|
||||||
|
<hostname type="HostnameField">
|
||||||
|
<IsDNSName>Y</IsDNSName>
|
||||||
|
</hostname>
|
||||||
|
<domain_search type="HostnameField">
|
||||||
|
<IpAllowed>N</IpAllowed>
|
||||||
|
<FieldSeparator>,</FieldSeparator>
|
||||||
|
<AsList>Y</AsList>
|
||||||
|
<ValidationMessage>Please specify a valid list of domains</ValidationMessage>
|
||||||
|
</domain_search>
|
||||||
|
<description type="DescriptionField"/>
|
||||||
|
</reservation>
|
||||||
|
</reservations>
|
||||||
|
<pd_pools>
|
||||||
|
<pd_pool type="ArrayField">
|
||||||
|
<subnet type="ModelRelationField">
|
||||||
|
<Model>
|
||||||
|
<subnets>
|
||||||
|
<source>OPNsense.Kea.KeaDhcpv6</source>
|
||||||
|
<items>subnets.subnet6</items>
|
||||||
|
<display>subnet</display>
|
||||||
|
</subnets>
|
||||||
|
</Model>
|
||||||
|
<ValidationMessage>Related subnet not found</ValidationMessage>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</subnet>
|
||||||
|
<prefix type="NetworkField">
|
||||||
|
<AddressFamily>ipv6</AddressFamily>
|
||||||
|
</prefix>
|
||||||
|
<prefix_len type="IntegerField">
|
||||||
|
<MinimumValue>1</MinimumValue>
|
||||||
|
<MaximumValue>128</MaximumValue>
|
||||||
|
<Default>56</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
</prefix_len>
|
||||||
|
<delegated_len type="IntegerField">
|
||||||
|
<MinimumValue>1</MinimumValue>
|
||||||
|
<MaximumValue>128</MaximumValue>
|
||||||
|
<Required>Y</Required>
|
||||||
|
<Default>64</Default>
|
||||||
|
</delegated_len>
|
||||||
|
<description type="DescriptionField"/>
|
||||||
|
</pd_pool>
|
||||||
|
</pd_pools>
|
||||||
|
<ha_peers>
|
||||||
|
<peer type="ArrayField">
|
||||||
|
<name type="TextField">
|
||||||
|
<Required>Y</Required>
|
||||||
|
<Constraints>
|
||||||
|
<check001>
|
||||||
|
<ValidationMessage>Duplicate entry exists</ValidationMessage>
|
||||||
|
<type>UniqueConstraint</type>
|
||||||
|
</check001>
|
||||||
|
</Constraints>
|
||||||
|
</name>
|
||||||
|
<role type="OptionField">
|
||||||
|
<Default>primary</Default>
|
||||||
|
<Required>Y</Required>
|
||||||
|
<OptionValues>
|
||||||
|
<primary>primary</primary>
|
||||||
|
<standby>standby</standby>
|
||||||
|
</OptionValues>
|
||||||
|
</role>
|
||||||
|
<url type="UrlField">
|
||||||
|
<Required>Y</Required>
|
||||||
|
<Constraints>
|
||||||
|
<check001>
|
||||||
|
<ValidationMessage>Duplicate entry exists</ValidationMessage>
|
||||||
|
<type>UniqueConstraint</type>
|
||||||
|
</check001>
|
||||||
|
</Constraints>
|
||||||
|
</url>
|
||||||
|
</peer>
|
||||||
|
</ha_peers>
|
||||||
|
</items>
|
||||||
|
</model>
|
||||||
@ -3,6 +3,7 @@
|
|||||||
<KeaDHCP VisibleName="Kea DHCP" cssClass="fa fa-bullseye fa-fw">
|
<KeaDHCP VisibleName="Kea DHCP" cssClass="fa fa-bullseye fa-fw">
|
||||||
<ControlAgent order="5" VisibleName="Control Agent" url="/ui/kea/dhcp/ctrl_agent"/>
|
<ControlAgent order="5" VisibleName="Control Agent" url="/ui/kea/dhcp/ctrl_agent"/>
|
||||||
<Keav4 order="10" VisibleName="Kea DHCPv4" url="/ui/kea/dhcp/v4"/>
|
<Keav4 order="10" VisibleName="Kea DHCPv4" url="/ui/kea/dhcp/v4"/>
|
||||||
|
<Keav6 order="20" VisibleName="Kea DHCPv6" url="/ui/kea/dhcp/v6"/>
|
||||||
<Leases4 order="50" VisibleName="Leases DHCPv4" url="/ui/kea/dhcp/leases4"/>
|
<Leases4 order="50" VisibleName="Leases DHCPv4" url="/ui/kea/dhcp/leases4"/>
|
||||||
<LogFile order="100" VisibleName="Log File" url="/ui/diagnostics/log/core/kea"/>
|
<LogFile order="100" VisibleName="Log File" url="/ui/diagnostics/log/core/kea"/>
|
||||||
</KeaDHCP>
|
</KeaDHCP>
|
||||||
|
|||||||
201
src/opnsense/mvc/app/views/OPNsense/Kea/dhcpv6.volt
Normal file
201
src/opnsense/mvc/app/views/OPNsense/Kea/dhcpv6.volt
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
{#
|
||||||
|
# Copyright (c) 2025 Deciso B.V.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||||
|
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
const data_get_map = {'frm_generalsettings':"/api/kea/dhcpv6/get"};
|
||||||
|
mapDataToFormUI(data_get_map).done(function(data){
|
||||||
|
try {
|
||||||
|
$("#dhcpv6\\.ha\\.this_server_name").attr(
|
||||||
|
"placeholder",
|
||||||
|
data.frm_generalsettings.dhcpv6.this_hostname
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
formatTokenizersUI();
|
||||||
|
$('.selectpicker').selectpicker('refresh');
|
||||||
|
updateServiceControlUI('kea');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("#{{formGridSubnet['table_id']}}").UIBootgrid(
|
||||||
|
{ search:'/api/kea/dhcpv6/search_subnet',
|
||||||
|
get:'/api/kea/dhcpv6/get_subnet/',
|
||||||
|
set:'/api/kea/dhcpv6/set_subnet/',
|
||||||
|
add:'/api/kea/dhcpv6/add_subnet/',
|
||||||
|
del:'/api/kea/dhcpv6/del_subnet/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$("#{{formGridPDPool['table_id']}}").UIBootgrid(
|
||||||
|
{ search:'/api/kea/dhcpv6/search_pd_pool',
|
||||||
|
get:'/api/kea/dhcpv6/get_pd_pool/',
|
||||||
|
set:'/api/kea/dhcpv6/set_pd_pool/',
|
||||||
|
add:'/api/kea/dhcpv6/add_pd_pool/',
|
||||||
|
del:'/api/kea/dhcpv6/del_pd_pool/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const grid_reservations = $("#{{formGridReservation['table_id']}}").UIBootgrid(
|
||||||
|
{ search:'/api/kea/dhcpv6/search_reservation',
|
||||||
|
get:'/api/kea/dhcpv6/get_reservation/',
|
||||||
|
set:'/api/kea/dhcpv6/set_reservation/',
|
||||||
|
add:'/api/kea/dhcpv6/add_reservation/',
|
||||||
|
del:'/api/kea/dhcpv6/del_reservation/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$("#{{formGridPeer['table_id']}}").UIBootgrid(
|
||||||
|
{ search:'/api/kea/dhcpv6/search_peer',
|
||||||
|
get:'/api/kea/dhcpv6/get_peer/',
|
||||||
|
set:'/api/kea/dhcpv6/set_peer/',
|
||||||
|
add:'/api/kea/dhcpv6/add_peer/',
|
||||||
|
del:'/api/kea/dhcpv6/del_peer/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$("#reconfigureAct").SimpleActionButton({
|
||||||
|
onPreAction: function() {
|
||||||
|
const dfObj = new $.Deferred();
|
||||||
|
saveFormToEndpoint("/api/kea/dhcpv6/set", 'frm_generalsettings', function () { dfObj.resolve(); }, true, function () { dfObj.reject(); });
|
||||||
|
return dfObj;
|
||||||
|
},
|
||||||
|
onAction: function(data, status) {
|
||||||
|
updateServiceControlUI('kea');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reservations csv download and upload
|
||||||
|
*/
|
||||||
|
const $tfoot = grid_reservations.find("tfoot td:last");
|
||||||
|
$tfoot.append(`
|
||||||
|
<button
|
||||||
|
id="upload_reservations"
|
||||||
|
type="button"
|
||||||
|
data-title="{{ lang._('Import reservations') }}"
|
||||||
|
data-endpoint='/api/kea/dhcpv6/upload_reservations'
|
||||||
|
title="{{ lang._('Import csv') }}"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
class="btn btn-xs"
|
||||||
|
>
|
||||||
|
<span class="fa fa-fw fa-upload"></span>
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
$tfoot.append(`
|
||||||
|
<button
|
||||||
|
id="download_reservations"
|
||||||
|
type="button"
|
||||||
|
title="{{ lang._('Export as csv') }}"
|
||||||
|
data-toggle="tooltip"
|
||||||
|
class="btn btn-xs"
|
||||||
|
>
|
||||||
|
<span class="fa fa-fw fa-table"></span>
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
|
||||||
|
$("#download_reservations").click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
window.open("/api/kea/dhcpv6/download_reservations");
|
||||||
|
});
|
||||||
|
$("#upload_reservations").SimpleFileUploadDlg({
|
||||||
|
onAction: function(){
|
||||||
|
grid_reservations.bootgrid('reload');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
$("#subnet4\\.option_data_autocollect").change(function(){
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
$(".option_data_autocollect").closest('tr').hide();
|
||||||
|
} else {
|
||||||
|
$(".option_data_autocollect").closest('tr').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Manual configuration, hide all config elements except the service section*/
|
||||||
|
$("#dhcpv6\\.general\\.manual_config").change(function(){
|
||||||
|
let manual_config = $(this).is(':checked');
|
||||||
|
if (manual_config) {
|
||||||
|
if (!$("#show_advanced_frm_generalsettings").hasClass('fa-toggle-on')) {
|
||||||
|
/* enforce advanced mode so the user notices the checkbox */
|
||||||
|
$("#show_advanced_frm_generalsettings").click();
|
||||||
|
}
|
||||||
|
$(".is_managed").hide();
|
||||||
|
} else {
|
||||||
|
$(".is_managed").show();
|
||||||
|
}
|
||||||
|
$("#settings").find('table').each(function(){
|
||||||
|
if (manual_config && $(this).find('#dhcpv6\\.general\\.manual_config').length == 0) {
|
||||||
|
$(this).hide();
|
||||||
|
} else {
|
||||||
|
$(this).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||||
|
<li class="active"><a data-toggle="tab" href="#settings" id="tab_settings">{{ lang._('Settings') }}</a></li>
|
||||||
|
<li><a data-toggle="tab" href="#subnets" id="tab_pools" class="is_managed"> {{ lang._('Subnets') }} </a></li>
|
||||||
|
<li><a data-toggle="tab" href="#pdpools" id="tab_reservations" class="is_managed"> {{ lang._('PD Pools') }} </a></li>
|
||||||
|
<li><a data-toggle="tab" href="#reservations" id="tab_reservations" class="is_managed"> {{ lang._('Reservations') }} </a></li>
|
||||||
|
<li><a data-toggle="tab" href="#ha-peers" id="tab_ha-peers" class="is_managed"> {{ lang._('HA Peers') }} </a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content content-box">
|
||||||
|
<!-- general settings -->
|
||||||
|
<div id="settings" class="tab-pane fade in active">
|
||||||
|
{{ partial("layout_partials/base_form",['fields':formGeneralSettings,'id':'frm_generalsettings'])}}
|
||||||
|
</div>
|
||||||
|
<!-- subnets / pools -->
|
||||||
|
<div id="subnets" class="tab-pane fade in">
|
||||||
|
{{ partial('layout_partials/base_bootgrid_table', formGridSubnet)}}
|
||||||
|
</div>
|
||||||
|
<!-- prefix delegation pools -->
|
||||||
|
<div id="pdpools" class="tab-pane fade in">
|
||||||
|
{{ partial('layout_partials/base_bootgrid_table', formGridPDPool)}}
|
||||||
|
</div>
|
||||||
|
<!-- reservations -->
|
||||||
|
<div id="reservations" class="tab-pane fade in">
|
||||||
|
{{ partial('layout_partials/base_bootgrid_table', formGridReservation + {'command_width': '8em'} )}}
|
||||||
|
</div>
|
||||||
|
<!-- HA - peers -->
|
||||||
|
<div id="ha-peers" class="tab-pane fade in">
|
||||||
|
{{ partial('layout_partials/base_bootgrid_table', formGridPeer)}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/kea/service/reconfigure'}) }}
|
||||||
|
{{ partial("layout_partials/base_dialog",['fields':formDialogSubnet,'id':formGridSubnet['edit_dialog_id'],'label':lang._('Edit Subnet')])}}
|
||||||
|
{{ partial("layout_partials/base_dialog",['fields':formDialogPDPool,'id':formGridPDPool['edit_dialog_id'],'label':lang._('Edit PD Pool')])}}
|
||||||
|
{{ partial("layout_partials/base_dialog",['fields':formDialogReservation,'id':formGridReservation['edit_dialog_id'],'label':lang._('Edit Reservation')])}}
|
||||||
|
{{ partial("layout_partials/base_dialog",['fields':formDialogPeer,'id':formGridPeer['edit_dialog_id'],'label':lang._('Edit Peer')])}}
|
||||||
@ -31,7 +31,7 @@ netconf_srv="${exec_prefix}/sbin/kea-netconf"
|
|||||||
dhcp4={% if not helpers.empty('OPNsense.Kea.dhcp4.general.enabled') %}yes{% else %}no{% endif %}
|
dhcp4={% if not helpers.empty('OPNsense.Kea.dhcp4.general.enabled') %}yes{% else %}no{% endif %}
|
||||||
|
|
||||||
# Start DHCPv6 server?
|
# Start DHCPv6 server?
|
||||||
dhcp6=no
|
dhcp6={% if not helpers.empty('OPNsense.Kea.dhcp6.general.enabled') %}yes{% else %}no{% endif %}
|
||||||
|
|
||||||
# Start DHCP DDNS server?
|
# Start DHCP DDNS server?
|
||||||
dhcp_ddns=no
|
dhcp_ddns=no
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
{% if not helpers.empty('OPNsense.Kea.dhcp4.general.interfaces') and not helpers.empty('OPNsense.Kea.dhcp4.general.enabled') %}
|
{% if (
|
||||||
|
not helpers.empty('OPNsense.Kea.dhcp4.general.interfaces') and not helpers.empty('OPNsense.Kea.dhcp4.general.enabled')
|
||||||
|
) or (
|
||||||
|
not helpers.empty('OPNsense.Kea.dhcp6.general.interfaces') and not helpers.empty('OPNsense.Kea.dhcp6.general.enabled')
|
||||||
|
) %}
|
||||||
kea_enable="YES"
|
kea_enable="YES"
|
||||||
kea_setup="/usr/local/sbin/pluginctl -c kea_sync"
|
kea_setup="/usr/local/sbin/pluginctl -c kea_sync"
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user