Kea / Dhcp - DHCPv4 replacement, initial minimal for https://github.com/opnsense/core/issues/6971

This commit contains the basic features of our new DHCPv4 server, it certainly needs additional testing as currently we only validated the configuration format is valid. The aim is to keep the json templates as simple as possible.

For now we keep the kea-control-agent disabled, we probably need it later, but we don't want to expose a listener without using it.
This commit is contained in:
Ad Schellevis 2023-10-17 20:59:58 +02:00
parent fc5536732b
commit f90f16fc09
21 changed files with 952 additions and 0 deletions

20
plist
View File

@ -25,6 +25,7 @@
/usr/local/etc/inc/plugins.inc.d/dpinger.inc
/usr/local/etc/inc/plugins.inc.d/ipfw.inc
/usr/local/etc/inc/plugins.inc.d/ipsec.inc
/usr/local/etc/inc/plugins.inc.d/kea.inc
/usr/local/etc/inc/plugins.inc.d/loopback.inc
/usr/local/etc/inc/plugins.inc.d/monit.inc
/usr/local/etc/inc/plugins.inc.d/netflow.inc
@ -401,6 +402,12 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVip.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVlan.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Interfaces/forms/dialogVxlan.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/Api/Dhcpv4Controller.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/forms/dialogReservation.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/dialogSubnet.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Kea/forms/generalSettings.xml
/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/StatusController.php
@ -699,6 +706,11 @@
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/Vlan.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/VxLan.php
/usr/local/opnsense/mvc/app/models/OPNsense/Interfaces/VxLan.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Kea/FieldTypes/KeaPoolsField.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/Menu/Menu.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/Migrations/M1_0_0.php
@ -813,6 +825,7 @@
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vip.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vlan.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Interface/vxlan.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Kea/dhcpv4.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/OpenVPN/cso.volt
@ -1139,6 +1152,7 @@
/usr/local/opnsense/service/conf/actions.d/actions_interface.conf
/usr/local/opnsense/service/conf/actions.d/actions_ipfw.conf
/usr/local/opnsense/service/conf/actions.d/actions_ipsec.conf
/usr/local/opnsense/service/conf/actions.d/actions_kea.conf
/usr/local/opnsense/service/conf/actions.d/actions_monit.conf
/usr/local/opnsense/service/conf/actions.d/actions_netflow.conf
/usr/local/opnsense/service/conf/actions.d/actions_openssh.conf
@ -1204,6 +1218,11 @@
/usr/local/opnsense/service/templates/OPNsense/IPFW/rules.macro
/usr/local/opnsense/service/templates/OPNsense/IPsec/+TARGETS
/usr/local/opnsense/service/templates/OPNsense/IPsec/reqid_events.conf
/usr/local/opnsense/service/templates/OPNsense/Kea/+TARGETS
/usr/local/opnsense/service/templates/OPNsense/Kea/kea-ctrl-agent.conf
/usr/local/opnsense/service/templates/OPNsense/Kea/kea-dhcp4.conf
/usr/local/opnsense/service/templates/OPNsense/Kea/keactrl.conf
/usr/local/opnsense/service/templates/OPNsense/Kea/rc.conf.d
/usr/local/opnsense/service/templates/OPNsense/Macros/interface.macro
/usr/local/opnsense/service/templates/OPNsense/Monit/+TARGETS
/usr/local/opnsense/service/templates/OPNsense/Monit/monitrc
@ -1250,6 +1269,7 @@
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/firewall.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/gateways.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/ipsec.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/kea.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/lighttpd.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/monit.conf
/usr/local/opnsense/service/templates/OPNsense/Syslog/local/ntpd.conf

View File

@ -0,0 +1,50 @@
<?php
/*
* Copyright (C) 2023 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.
*/
function kea_services()
{
$services[] = [
'description' => gettext('KEA DHCPv4 server'),
'pidfile' => '/var/run/kea/kea-dhcp4.kea-dhcp4.pid',
'configd' => [
'restart' => ['kea restart'],
'start' => ['kea start'],
'stop' => ['kea stop'],
],
'name' => 'kea-dhcpv4',
];
return $services;
}
function kea_syslog()
{
$logfacilities = [];
$logfacilities['kea'] = ['facility' => ['kea-dhcp4', 'kea-dhcp6', 'kea-ctrl-agent']];
return $logfacilities;
}

View File

@ -0,0 +1,100 @@
<?php
/*
* Copyright (C) 2023 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;
class Dhcpv4Controller extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'dhcpv4';
protected static $internalModelClass = 'OPNsense\Kea\KeaDhcpv4';
/**
* @inheritdoc
*/
public function getAction()
{
$data = parent::getAction();
return [
self::$internalModelName => [
'general' => $data[self::$internalModelName]['general']
]
];
}
public function searchSubnetAction()
{
return $this->searchBase("subnets.subnet4", ['subnet'], "subnet");
}
public function setSubnetAction($uuid)
{
return $this->setBase("subnet4", "subnets.subnet4", $uuid);
}
public function addSubnetAction()
{
return $this->addBase("subnet4", "subnets.subnet4");
}
public function getSubnetAction($uuid = null)
{
return $this->getBase("subnet4", "subnets.subnet4", $uuid);
}
public function delSubnetAction($uuid)
{
return $this->delBase("subnets.subnet4", $uuid);
}
public function searchReservationAction()
{
return $this->searchBase("reservations.reservation", ['subnet', 'hw_address', 'description'], "hw_address");
}
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);
}
}

View File

@ -0,0 +1,45 @@
<?php
/*
* Copyright (C) 2023 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\ApiMutableServiceControllerBase;
use OPNsense\Core\Backend;
class ServiceController extends ApiMutableServiceControllerBase
{
protected static $internalServiceClass = '\OPNsense\Kea\KeaDhcpv4';
protected static $internalServiceTemplate = 'OPNsense/Kea';
protected static $internalServiceEnabled = 'general.enabled';
protected static $internalServiceName = 'kea';
/**
* TODO: overwrite when implementing KeaDhcpv6 as well. Both services share the same rc script
* protected function serviceEnabled() {}
*/
}

View File

@ -0,0 +1,40 @@
<?php
/*
* Copyright (C) 2023 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;
class DhcpController extends \OPNsense\Base\IndexController
{
public function v4Action()
{
$this->view->pick('OPNsense/Kea/dhcpv4');
$this->view->formGeneralSettings = $this->getForm("generalSettings");
$this->view->formDialogSubnet = $this->getForm("dialogSubnet");
$this->view->formDialogReservation = $this->getForm("dialogReservation");
}
}

View File

@ -0,0 +1,26 @@
<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.hw_address</id>
<label>MAC address</label>
<type>text</type>
<help>MAC/Ether address of the client in question</help>
</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>

View File

@ -0,0 +1,50 @@
<form>
<field>
<id>subnet4.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>subnet4.pools</id>
<label>Pools</label>
<type>textbox</type>
<help>List of pools, one per line in range or subnet format (e.g. 192.168.0.100 - 192.168.0.200 , 192.0.2.64/26</help>
</field>
<field>
<id>subnet4.option_data.routers</id>
<label>Routers (gateway)</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>Default gateways to offer to the clients</help>
</field>
<field>
<id>subnet4.option_data.domain_name_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>
</field>
<field>
<id>subnet4.option_data.ntp_servers</id>
<label>NTP servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>NTP servers to offer to the clients</help>
</field>
<field>
<id>subnet4.option_data.tftp_server_name</id>
<label>TFTP server</label>
<type>text</type>
<help>TFTP server address or fqdn</help>
</field>
<field>
<id>subnet4.option_data.boot_file_name</id>
<label>TFTP bootfile name</label>
<type>text</type>
<help>Boot filename to request</help>
</field>
</form>

View File

@ -0,0 +1,20 @@
<form>
<field>
<id>dhcpv4.general.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable DHCPv4 server.</help>
</field>
<field>
<id>dhcpv4.general.interfaces</id>
<label>Interfaces</label>
<type>select_multiple</type>
<help>Select interfaces to listen on.</help>
</field>
<field>
<id>dhcpv4.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>
</form>

View File

@ -0,0 +1,10 @@
<acl>
<page-dhcp-kea-v4>
<name>Services: DHCP: Kea(v4)</name>
<description>Allow access to the KEA dhcp4 server</description>
<patterns>
<pattern>ui/kea/dhcp/v4</pattern>
<pattern>api/kea/dhcpv4/*</pattern>
</patterns>
</page-dhcp-kea-v4>
</acl>

View File

@ -0,0 +1,66 @@
<?php
/*
* Copyright (C) 2023 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\FieldTypes;
use OPNsense\Base\FieldTypes\BaseField;
use OPNsense\Base\Validators\CallbackValidator;
use OPNsense\Firewall\Util;
class KeaPoolsField extends BaseField
{
/**
* @var bool marks if this is a data node or a container
*/
protected $internalIsContainer = false;
public function getValidators()
{
$validators = parent::getValidators();
if ($this->internalValue != null) {
$validators[] = new CallbackValidator(["callback" => function ($data) {
$messages = [];
foreach (explode("\n", $data) as $entry) {
$parts = array_map('trim', explode('-', $entry));
if (empty($entry)) {
continue;
} elseif (Util::isSubnet($entry)) {
continue;
} elseif (count($parts) == 2 && Util::isIpAddress($parts[0]) && Util::isIpAddress($parts[1])) {
continue;
}
$messages[] = sprintf(gettext('Entry "%s" is not a valid range or subnet.'), $entry);
}
return $messages;
}
]);
}
return $validators;
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
* Copyright (C) 2023 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\BaseModel;
use OPNsense\Core\Config;
class KeaDhcpv4 extends BaseModel
{
}

View File

@ -0,0 +1,81 @@
<model>
<mount>//OPNsense/Kea/dhcp4</mount>
<version>0.0.1</version>
<description>Kea DHCPv4 configuration</description>
<items>
<general>
<enabled type="BooleanField">
<Default>0</Default>
<Required>Y</Required>
</enabled>
<interfaces type="InterfaceField">
<Multiple>Y</Multiple>
</interfaces>
<valid_lifetime type="IntegerField">
<Default>4000</Default>
<Required>Y</Required>
</valid_lifetime>
</general>
<subnets>
<subnet4 type="ArrayField">
<subnet type="NetworkField">
<NetMaskRequired>Y</NetMaskRequired>
<AddressFamily>ipv4</AddressFamily>
<Required>Y</Required>
</subnet>
<option_data>
<domain_name_servers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv4</AddressFamily>
<AsList>Y</AsList>
<FieldSeparator>,</FieldSeparator>
</domain_name_servers>
<routers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv4</AddressFamily>
<AsList>Y</AsList>
<FieldSeparator>,</FieldSeparator>
</routers>
<ntp_servers type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv4</AddressFamily>
<AsList>Y</AsList>
<FieldSeparator>,</FieldSeparator>
</ntp_servers>
<tftp_server_name type="TextField">
<Mask>/^([0-9a-zA-Z.\:\-,_]){0,1024}$/u</Mask>
</tftp_server_name>
<boot_file_name type="TextField">
<Mask>/^([0-9a-zA-Z.\:\-,_]){0,1024}$/u</Mask>
</boot_file_name>
</option_data>
<pools type=".\KeaPoolsField">
</pools>
</subnet4>
</subnets>
<reservations>
<reservation type="ArrayField">
<subnet type="ModelRelationField">
<Model>
<pipes>
<source>OPNsense.Kea.KeaDhcpv4</source>
<items>subnets.subnet4</items>
<display>subnet</display>
</pipes>
</Model>
<ValidationMessage>Related subnet not found</ValidationMessage>
<Required>Y</Required>
</subnet>
<ip_address type="NetworkField">
<NetMaskAllowed>N</NetMaskAllowed>
<AddressFamily>ipv4</AddressFamily>
</ip_address>
<hw_address type="MacAddressField">
<Required>Y</Required>
</hw_address>
<description type="TextField">
</description>
</reservation>
</reservations>
</items>
</model>

View File

@ -0,0 +1,8 @@
<menu>
<Services>
<DHCP VisibleName="DHCP (new)" cssClass="fa fa-bullseye fa-fw">
<KEAv4 order="5" VisibleName="KEA DHCPv4" url="/ui/kea/dhcp/v4"/>
<LogFile order="100" VisibleName="Log File" url="/ui/diagnostics/log/core/kea"/>
</DHCP>
</Services>
</menu>

View File

@ -0,0 +1,145 @@
{#
OPNsense® is Copyright © 2023 by 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() {
let data_get_map = {'frm_generalsettings':"/api/kea/dhcpv4/get"};
mapDataToFormUI(data_get_map).done(function(){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
updateServiceControlUI('kea');
});
$("#grid-subnets").UIBootgrid(
{ search:'/api/kea/dhcpv4/search_subnet',
get:'/api/kea/dhcpv4/get_subnet/',
set:'/api/kea/dhcpv4/set_subnet/',
add:'/api/kea/dhcpv4/add_subnet/',
del:'/api/kea/dhcpv4/del_subnet/'
}
);
$("#grid-reservations").UIBootgrid(
{ search:'/api/kea/dhcpv4/search_reservation',
get:'/api/kea/dhcpv4/get_reservation/',
set:'/api/kea/dhcpv4/set_reservation/',
add:'/api/kea/dhcpv4/add_reservation/',
del:'/api/kea/dhcpv4/del_reservation/'
}
);
$("#reconfigureAct").SimpleActionButton({
onPreAction: function() {
const dfObj = new $.Deferred();
saveFormToEndpoint("/api/kea/dhcpv4/set", 'frm_generalsettings', function(){
dfObj.resolve();
});
return dfObj;
},
onAction: function(data, status) {
updateServiceControlUI('kea');
}
});
});
</script>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#settings" id="settings_tab">{{ lang._('Settings') }}</a></li>
<li><a data-toggle="tab" href="#subnets" id="tab_pools"> {{ lang._('Subnets') }} </a></li>
<li><a data-toggle="tab" href="#reservations" id="tab_pools"> {{ lang._('Reservations') }} </a></li>
</ul>
<div class="tab-content content-box">
<div id="settings" class="tab-pane fade in active">
{{ partial("layout_partials/base_form",['fields':formGeneralSettings,'id':'frm_generalsettings'])}}
</div>
<!-- -->
<div id="subnets" class="tab-pane fade in">
<table id="grid-subnets" class="table table-condensed table-hover table-striped" data-editDialog="DialogSubnet">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="subnet" data-type="string">{{ lang._('Subnet') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary pull-right"><span class="fa fa-fw fa-plus"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<!-- -->
<div id="reservations" class="tab-pane fade in">
<table id="grid-reservations" class="table table-condensed table-hover table-striped" data-editDialog="DialogReservation">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="subnet" data-type="string">{{ lang._('Subnet') }}</th>
<th data-column-id="hw_address" data-type="string">{{ lang._('MAC') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary pull-right"><span class="fa fa-fw fa-plus"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<section class="page-content-main">
<div class="content-box">
<div class="col-md-12">
<br/>
<button class="btn btn-primary" id="reconfigureAct"
data-endpoint='/api/kea/service/reconfigure'
data-label="{{ lang._('Apply') }}"
data-error-title="{{ lang._('Error reconfiguring DHCPv4') }}"
type="button"
></button>
<br/><br/>
</div>
</div>
</section>
{{ partial("layout_partials/base_dialog",['fields':formDialogSubnet,'id':'DialogSubnet','label':lang._('Edit Subnet')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogReservation,'id':'DialogReservation','label':lang._('Edit Reservation')])}}

View File

@ -0,0 +1,24 @@
[stop]
command:/usr/local/etc/rc.d/kea stop
parameters:
type:script
message:stop kea daemon
[start]
command:/usr/local/etc/rc.d/kea start
parameters:
type:script
message:start kea daemon
[restart]
command:/usr/local/etc/rc.d/kea restart
parameters:
type:script
message:restart kea daemon
description:Restart kea daemon
[status]
command:/usr/local/sbin/pluginctl -s kea-dhcpv4 status
parameters:
type:script_output
message:get kea daemon status

View File

@ -0,0 +1,4 @@
kea-dhcp4.conf:/usr/local/etc/kea/kea-dhcp4.conf
rc.conf.d:/etc/rc.conf.d/kea
keactrl.conf:/usr/local/etc/kea/keactrl.conf
kea-ctrl-agent.conf:/usr/local/etc/kea/kea-ctrl-agent.conf

View File

@ -0,0 +1,105 @@
// This is a basic configuration for the Kea Control Agent.
//
// This is just a very basic configuration. Kea comes with large suite (over 30)
// of configuration examples and extensive Kea User's Guide. Please refer to
// those materials to get better understanding of what this software is able to
// do. Comments in this configuration file sometimes refer to sections for more
// details. These are section numbers in Kea User's Guide. The version matching
// your software should come with your Kea package, but it is also available
// in ISC's Knowledgebase (https://kea.readthedocs.io; the direct link for
// the stable version is https://kea.readthedocs.io/).
//
// This configuration file contains only Control Agent's configuration.
// If configurations for other Kea services are also included in this file they
// are ignored by the Control Agent.
{
// This is a basic configuration for the Kea Control Agent.
// RESTful interface to be available at http://127.0.0.1:8000/
"Control-agent": {
"http-host": "127.0.0.1",
// If enabling HA and multi-threading, the 8000 port is used by the HA
// hook library http listener. When using HA hook library with
// multi-threading to function, make sure the port used by dedicated
// listener is different (e.g. 8001) than the one used by CA. Note
// the commands should still be sent via CA. The dedicated listener
// is specifically for HA updates only.
"http-port": 8000,
// Specify location of the files to which the Control Agent
// should connect to forward commands to the DHCPv4, DHCPv6
// and D2 servers via unix domain sockets.
"control-sockets": {
"dhcp4": {
"socket-type": "unix",
"socket-name": "/var/run/kea4-ctrl-socket"
},
"dhcp6": {
"socket-type": "unix",
"socket-name": "/var/run/kea6-ctrl-socket"
},
"d2": {
"socket-type": "unix",
"socket-name": "/var/run/kea-ddns-ctrl-socket"
}
},
// Specify hooks libraries that are attached to the Control Agent.
// Such hooks libraries should support 'control_command_receive'
// hook point. This is currently commented out because it has to
// point to the existing hooks library. Otherwise the Control
// Agent will fail to start.
"hooks-libraries": [
// {
// "library": "/usr/local/lib/kea/hooks/control-agent-commands.so",
// "parameters": {
// "param1": "foo"
// }
// }
],
// Logging configuration starts here. Kea uses different loggers to log various
// activities. For details (e.g. names of loggers), see Chapter 18.
"loggers": [
{
// This specifies the logging for Control Agent daemon.
"name": "kea-ctrl-agent",
"output_options": [
{
// Specifies the output file. There are several special values
// supported:
// - stdout (prints on standard output)
// - stderr (prints on standard error)
// - syslog (logs to syslog)
// - syslog:name (logs to syslog using specified name)
// Any other value is considered a name of the file
"output": "syslog"
// Shorter log pattern suitable for use with systemd,
// avoids redundant information
// "pattern": "%-5p %m\n"
// This governs whether the log output is flushed to disk after
// every write.
// "flush": false,
// This specifies the maximum size of the file before it is
// rotated.
// "maxsize": 1048576,
// This specifies the maximum number of rotated files to keep.
// "maxver": 8
}
],
// This specifies the severity of log messages to keep. Supported values
// are: FATAL, ERROR, WARN, INFO, DEBUG
"severity": "INFO",
// If DEBUG level is specified, this value is used. 0 is least verbose,
// 99 is most verbose. Be cautious, Kea can generate lots and lots
// of logs if told to do so.
"debuglevel": 0
}
]
}
}

View File

@ -0,0 +1,64 @@
{%- if not helpers.empty('OPNsense.Kea.dhcp4.general.interfaces') and not helpers.empty('OPNsense.Kea.dhcp4.general.enabled') -%}
{%- set reservation_fields = ({
'hw-address': 'hw_address',
'ip-address': 'ip_address'
}) -%}
{%- set general = OPNsense.Kea.dhcp4.general -%}
{
"Dhcp4": {
"valid-lifetime": {{general.valid_lifetime}},
"interfaces-config": {
"interfaces": ["{{helpers.physical_interfaces(general.interfaces.split(','))|join('","')}}"]
},
"lease-database": {
"type": "memfile",
"persist": true
},
"control-socket": {
"socket-type": "unix",
"socket-name": "/var/run/kea4-ctrl-socket"
},
"loggers": [
{
"name": "kea-dhcp4",
"output_options": [
{
"output": "syslog"
}
],
"severity": "INFO"
}
],
"subnet4": [
{% for subnet in helpers.toList('OPNsense.Kea.dhcp4.subnets.subnet4') %}
{
"id": {{loop.index}},
"subnet": "{{subnet.subnet}}",
"option-data": [
{% for od_attr in subnet.option_data if subnet.option_data[od_attr]|length > 1 %}
{
"name": "{{od_attr.replace('_','-')}}",
"data": {{subnet.option_data[od_attr]|tojson}}
}{% if not loop.last %},{% endif +%}
{% endfor %}
],
"pools": [
{% for pool in (subnet.pools|default('')).split("\n") if pool|length > 1%}
{ "pool": "{{pool}}" }{% if not loop.last %},{% endif +%}
{% endfor %}
],
"reservations": [
{% for reservation in helpers.toList('OPNsense.Kea.dhcp4.reservations.reservation') if reservation.subnet == subnet['@uuid'] %}
{
{% for res_key, res_prop in reservation_fields.items() if reservation[res_prop]|length > 1 %}
"{{res_key}}": "{{reservation[res_prop]}}"{% if not loop.last %},{% endif +%}
{% endfor %}
}{% if not loop.last %},{% endif +%}
{% endfor %}
]
}{% if not loop.last %},{% endif +%}
{% endfor %}
]
}
}
{%- endif -%}

View File

@ -0,0 +1,46 @@
#!/bin/sh
# This is a configuration file for keactrl script which controls
# the startup, shutdown, reconfiguration and gathering the status
# of the Kea's processes.
# Note that control agent must be launched after servers and netconf last.
# shellcheck disable=SC2034
# SC2034: ... appears unused. Verify use (or export if used externally).
# prefix holds the location where the Kea is installed.
prefix="/usr/local"
# Location of Kea configuration files.
kea_dhcp4_config_file="${prefix}/etc/kea/kea-dhcp4.conf"
kea_dhcp6_config_file="${prefix}/etc/kea/kea-dhcp6.conf"
kea_dhcp_ddns_config_file="${prefix}/etc/kea/kea-dhcp-ddns.conf"
kea_ctrl_agent_config_file="${prefix}/etc/kea/kea-ctrl-agent.conf"
kea_netconf_config_file="${prefix}/etc/kea/kea-netconf.conf"
# Location of Kea binaries.
exec_prefix="${prefix}"
dhcp4_srv="${exec_prefix}/sbin/kea-dhcp4"
dhcp6_srv="${exec_prefix}/sbin/kea-dhcp6"
dhcp_ddns_srv="${exec_prefix}/sbin/kea-dhcp-ddns"
ctrl_agent_srv="${exec_prefix}/sbin/kea-ctrl-agent"
netconf_srv="${exec_prefix}/sbin/kea-netconf"
# Start DHCPv4 server?
dhcp4=yes
# Start DHCPv6 server?
dhcp6=no
# Start DHCP DDNS server?
dhcp_ddns=no
# Start Control Agent?
ctrl_agent=no
# Start Netconf?
netconf=no
# Be verbose?
kea_verbose=no

View File

@ -0,0 +1,5 @@
{% if not helpers.empty('OPNsense.Kea.dhcp4.general.interfaces') and not helpers.empty('OPNsense.Kea.dhcp4.general.enabled') %}
kea_enable="YES"
{% else %}
kea_enable="NO"
{% endif %}

View File

@ -0,0 +1,6 @@
###################################################################
# Local syslog-ng configuration filter definition [kea].
###################################################################
filter f_local_kea {
program("kea-dhcp4") or program("kea-dhcp6") or program("kea-ctrl-agent");
};