net/tayga: static mapping support (#4986)

Implement support for the 'map' directive in Tayga
that can be used to statically define one-to-one
maps between IPv4 and IPv6 prefixes.

Implements #4606.
This commit is contained in:
Matthias Valvekens
2025-10-24 09:15:42 +02:00
committed by GitHub
parent b31937c1ab
commit 3222e2e1f9
10 changed files with 212 additions and 22 deletions
+1 -2
View File
@@ -1,6 +1,5 @@
PLUGIN_NAME= tayga
PLUGIN_VERSION= 1.2
PLUGIN_REVISION= 2
PLUGIN_VERSION= 1.3
PLUGIN_COMMENT= Tayga NAT64
PLUGIN_DEPENDS= tayga
PLUGIN_MAINTAINER= m.muenz@gmail.com
+4
View File
@@ -7,6 +7,10 @@ networks where dedicated NAT64 hardware would be overkill.
Plugin Changelog
================
1.3
* Static mapping support
1.2
* Custom IPv6 routing option
@@ -0,0 +1,67 @@
<?php
/*
Copyright (C) 2025 Matthias Valvekens <dev@mvalvekens.be>
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\Tayga\Api;
use OPNsense\Base\ApiMutableModelControllerBase;
class MappingController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'staticmapping';
protected static $internalModelClass = '\OPNsense\Tayga\StaticMapping';
public function searchStaticmappingAction()
{
return $this->searchBase('staticmappings.staticmapping', ['enabled', 'v4', 'v6']);
}
public function getStaticmappingAction($uuid = null)
{
return $this->getBase('staticmapping', 'staticmappings.staticmapping', $uuid);
}
public function addStaticmappingAction()
{
return $this->addBase('staticmapping', 'staticmappings.staticmapping');
}
public function delStaticmappingAction($uuid)
{
return $this->delBase('staticmappings.staticmapping', $uuid);
}
public function setStaticmappingAction($uuid)
{
return $this->setBase('staticmapping', 'staticmappings.staticmapping', $uuid);
}
public function toggleStaticmappingAction($uuid)
{
return $this->toggleBase('staticmappings.staticmapping', $uuid);
}
}
@@ -34,5 +34,7 @@ class GeneralController extends \OPNsense\Base\IndexController
{
$this->view->generalForm = $this->getForm("general");
$this->view->pick('OPNsense/Tayga/general');
$this->view->formDialogEditStaticMapping = $this->getForm("dialogEditStaticMapping");
$this->view->formGridStaticMapping = $this->getFormGrid("dialogEditStaticMapping");
}
}
@@ -0,0 +1,34 @@
<form>
<field>
<id>staticmapping.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>This will enable or disable the static mapping</help>
<grid_view>
<width>6em</width>
<type>boolean</type>
<formatter>rowtoggle</formatter>
</grid_view>
</field>
<field>
<id>staticmapping.v4</id>
<label>IPv4 network or address</label>
<type>text</type>
<help>IPv4 network to map. Can overlap with dynamic pool.</help>
</field>
<field>
<id>staticmapping.v6</id>
<label>IPv6 network or address</label>
<type>text</type>
<help>IPv4 network to map. Must not overlap with NAT64 prefix.</help>
</field>
<field>
<id>staticmapping.description</id>
<label>Description</label>
<type>text</type>
<help>Optionally describe the purpose of the mapping.</help>
<grid_view>
<visible>false</visible>
</grid_view>
</field>
</form>
@@ -1,7 +1,7 @@
<model>
<mount>//OPNsense/tayga/general</mount>
<description>Tayga configuration</description>
<version>1.2.0</version>
<version>1.3.0</version>
<items>
<enabled type="BooleanField">
<Default>0</Default>
@@ -10,24 +10,30 @@
<v4address type="NetworkField">
<Default>192.168.255.1</Default>
<Required>Y</Required>
<AddressFamily>ipv4</AddressFamily>
</v4address>
<v4destination type="NetworkField">
<Default>192.168.254.1</Default>
<Required>Y</Required>
<AddressFamily>ipv4</AddressFamily>
</v4destination>
<v6address type="NetworkField">
<Required>N</Required>
<AddressFamily>ipv6</AddressFamily>
</v6address>
<v6destination type="NetworkField">
<Default>2001:db8:1:ffff::1</Default>
<AddressFamily>ipv6</AddressFamily>
<Required>Y</Required>
</v6destination>
<v6prefix type="NetworkField">
<Default>64:ff9b::/96</Default>
<AddressFamily>ipv6</AddressFamily>
<Required>Y</Required>
</v6prefix>
<v4pool type="NetworkField">
<Default>192.168.255.0/24</Default>
<AddressFamily>ipv4</AddressFamily>
<Required>Y</Required>
</v4pool>
<v6routedisabled type="BooleanField">
@@ -0,0 +1,35 @@
<?php
/*
Copyright (C) 2025 Matthias Valvekens <dev@mvalvekens.be>
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\Tayga;
use OPNsense\Base\BaseModel;
class StaticMapping extends BaseModel
{
}
@@ -0,0 +1,24 @@
<model>
<mount>//OPNsense/tayga/staticmapping</mount>
<description>Static NAT64 mappings</description>
<version>1.3.0</version>
<items>
<staticmappings>
<staticmapping type="ArrayField">
<enabled type="BooleanField">
<Required>Y</Required>
<Default>1</Default>
</enabled>
<v4 type="NetworkField">
<Required>Y</Required>
<AddressFamily>ipv4</AddressFamily>
</v4>
<v6 type="NetworkField">
<Required>Y</Required>
<AddressFamily>ipv6</AddressFamily>
</v6>
<description type="DescriptionField"/>
</staticmapping>
</staticmappings>
</items>
</model>
@@ -26,13 +26,23 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
#}
<div class="content-box" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="saveAct" type="button"><b>{{ lang._('Save') }}</b> <i id="saveAct_progress"></i></button>
<!-- Navigation bar -->
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#general">{{ lang._('General') }}</a></li>
<li><a data-toggle="tab" href="#staticmappings">{{ lang._('Static Mappings') }}</a></li>
</ul>
<div class="tab-content content-box">
<div class="tab-pane fade in active" id="general" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
</div>
<div id="staticmappings" class="tab-pane fade in">
{{ partial('layout_partials/base_bootgrid_table', formGridStaticMapping) }}
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogEditStaticMapping,'id': formGridStaticMapping['edit_dialog_id'], 'label':lang._('Edit mapping')])}}
{{ partial('layout_partials/base_apply_button', {'data_endpoint': '/api/tayga/service/reconfigure', 'data_service_widget': 'tayga'}) }}
<script>
$( document ).ready(function() {
@@ -41,21 +51,22 @@ POSSIBILITY OF SUCH DAMAGE.
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
});
ajaxCall(url="/api/tayga/service/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
});
// link save button to API set action
$("#saveAct").click(function(){
saveFormToEndpoint(url="/api/tayga/general/set", formid='frm_general_settings',callback_ok=function(){
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/tayga/service/reconfigure", sendData={}, callback=function(data,status) {
ajaxCall(url="/api/tayga/service/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
});
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
});
});
$("#{{formGridStaticMapping['table_id']}}").UIBootgrid({
'search': '/api/tayga/mapping/search_staticmapping',
'get': '/api/tayga/mapping/get_staticmapping/',
'set': '/api/tayga/mapping/set_staticmapping/',
'add': '/api/tayga/mapping/add_staticmapping/',
'del': '/api/tayga/mapping/del_staticmapping/',
'toggle': '/api/tayga/mapping/toggle_staticmapping/'
});
$("#reconfigureAct").SimpleActionButton({
onPreAction: function() {
const dfObj = $.Deferred();
saveFormToEndpoint("/api/tayga/general/set", 'frm_general_settings', function () { dfObj.resolve(); }, true, function () { dfObj.reject(); });
return dfObj;
}
});
updateServiceControlUI('tayga');
});
</script>
@@ -10,4 +10,12 @@ ipv6-addr {{ OPNsense.tayga.general.v6address }}
prefix {{ OPNsense.tayga.general.v6prefix }}
dynamic-pool {{ OPNsense.tayga.general.v4pool }}
{% if helpers.exists('OPNsense.tayga.staticmapping.staticmappings.staticmapping') %}
{% for mapping in helpers.toList('OPNsense.tayga.staticmapping.staticmappings.staticmapping') %}
{% if mapping.enabled == '1' %}
map {{ mapping.v4 }} {{ mapping.v6 }}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}