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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 212 additions and 22 deletions

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

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

View file

@ -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);
}
}

View file

@ -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");
}
}

View file

@ -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>

View file

@ -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">

View file

@ -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
{
}

View file

@ -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>

View file

@ -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>

View file

@ -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 %}