diff --git a/src/opnsense/service/modules/addons/template_helpers.py b/src/opnsense/service/modules/addons/template_helpers.py
index a3fd46c19..f4e811b15 100644
--- a/src/opnsense/service/modules/addons/template_helpers.py
+++ b/src/opnsense/service/modules/addons/template_helpers.py
@@ -135,6 +135,23 @@ class Helpers(object):
"""
return self._template_in_data['__uuid__'].get(uuid, {})
+ def physical_interface(self, name):
+ """
+ :param name: interface technical name [lan, wan, opt]
+ :return: device name (e.g. em0), input name when not found
+ """
+ return self.getNodeByTag('interfaces.'+name+'.if') or name
+
+ def physical_interfaces(self, names):
+ """
+ :param names: list of interface technical names [lan, wan, opt]
+ :return: device names (e.g. ['em0']), skips missing entries
+ """
+ result = []
+ for name in names:
+ result.append(self.getNodeByTag('interfaces.'+name+'.if'))
+ return list(filter(None, result))
+
@staticmethod
def getIPNetwork(network):
"""
diff --git a/src/opnsense/service/templates/OPNsense/Filter/filter_tables.conf b/src/opnsense/service/templates/OPNsense/Filter/filter_tables.conf
index 575c753c8..9fb1ef429 100644
--- a/src/opnsense/service/templates/OPNsense/Filter/filter_tables.conf
+++ b/src/opnsense/service/templates/OPNsense/Filter/filter_tables.conf
@@ -5,7 +5,6 @@
{% endif %}
{% set new_style_aliases = 0 %}
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{% if helpers.exists('OPNsense.Firewall.Alias.aliases.alias') %}
{% set new_style_aliases = OPNsense.Firewall.Alias.aliases.alias|length %}
{% for alias in helpers.toList('OPNsense.Firewall.Alias.aliases.alias') %}
@@ -23,7 +22,7 @@
{% elif alias.content %}
{{ alias.content|e|encode_idna }}
{% endif %}{% if alias.interface and alias.type == 'dynipv6host' %}
- {{ physical_interface(alias.interface)|default('LAN')}}
+ {{ helpers.physical_interface(alias.interface)|default('LAN')}}
{% endif %}{% if alias.proto %}
{{ alias.proto|e }}
{% endif %}{% if alias.updatefreq %}
diff --git a/src/opnsense/service/templates/OPNsense/IDS/rc.conf.d b/src/opnsense/service/templates/OPNsense/IDS/rc.conf.d
index dd9280c23..d03c6f8b8 100644
--- a/src/opnsense/service/templates/OPNsense/IDS/rc.conf.d
+++ b/src/opnsense/service/templates/OPNsense/IDS/rc.conf.d
@@ -1,5 +1,3 @@
-{# Macro import #}
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{% if not helpers.empty('OPNsense.IDS.general.enabled') %}
suricata_setup="/usr/local/opnsense/scripts/suricata/setup.sh"
suricata_enable="YES"
@@ -14,7 +12,7 @@ suricata_netmap="YES"
{% set addFlags=[] %}
{% for intfName in OPNsense.IDS.general.interfaces.split(',') %}
{# store additional interfaces to addFlags #}
-{% do addFlags.append(physical_interface(intfName)) %}
+{% do addFlags.append(helpers.physical_interface(intfName)) %}
{% endfor %}
suricata_interface="{{ addFlags|join(' ') }}"
{% endif %}
diff --git a/src/opnsense/service/templates/OPNsense/IDS/suricata.yaml b/src/opnsense/service/templates/OPNsense/IDS/suricata.yaml
index 2f9550d8a..b1bc0efc9 100644
--- a/src/opnsense/service/templates/OPNsense/IDS/suricata.yaml
+++ b/src/opnsense/service/templates/OPNsense/IDS/suricata.yaml
@@ -1,5 +1,3 @@
-{# Macro import #}
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
%YAML 1.1
---
@@ -1710,11 +1708,11 @@ netmap:
{% if helpers.exists('OPNsense.IDS.general.interfaces') %}
{% for intfName in OPNsense.IDS.general.interfaces.split(',') %}
- - interface: {{physical_interface(intfName)}}
- copy-iface: {{physical_interface(intfName)}}^
+ - interface: {{helpers.physical_interface(intfName)}}
+ copy-iface: {{helpers.physical_interface(intfName)}}^
- - interface: {{physical_interface(intfName)}}^
- copy-iface: {{physical_interface(intfName)}}
+ - interface: {{helpers.physical_interface(intfName)}}^
+ copy-iface: {{helpers.physical_interface(intfName)}}
{% endfor %}
{% endif %}
diff --git a/src/opnsense/service/templates/OPNsense/IPFW/ipfw.conf b/src/opnsense/service/templates/OPNsense/IPFW/ipfw.conf
index 4e555bc74..b80ac6b65 100644
--- a/src/opnsense/service/templates/OPNsense/IPFW/ipfw.conf
+++ b/src/opnsense/service/templates/OPNsense/IPFW/ipfw.conf
@@ -1,5 +1,4 @@
{# Macro import #}
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{% from 'OPNsense/IPFW/rules.macro' import convert_address %}
{# collect interfaces list (with / without captive portal enabled) #}
{% set cp_interface_list = [] %}
@@ -157,12 +156,12 @@ add 60000 return via any
{% if helpers.exists('OPNsense.TrafficShaper.rules.rule') %}
{% for rule in helpers.toList('OPNsense.TrafficShaper.rules.rule', 'sequence', 'int') %}
{% if helpers.getUUIDtag(rule.target) in ['pipe','queue'] %}
-{% if physical_interface(rule.interface) and rule.enabled|default('0') == '1' %}
+{% if helpers.physical_interface(rule.interface) and rule.enabled|default('0') == '1' %}
{% if helpers.getUUID(rule.target).enabled|default('0') == '1' %}
{% if helpers.getUUIDtag(rule.target) == 'pipe' or
helpers.getUUID(helpers.getUUID(rule.target).pipe).enabled|default('0') == '1'
%}
-{% if rule.interface2 and physical_interface(rule.interface2) %}
+{% if rule.interface2 and helpers.physical_interface(rule.interface2) %}
{# 2 interface defined, use both to match packets (2 rules) #}
{% if rule.direction == 'in' or not rule.direction %}
add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{
@@ -170,11 +169,11 @@ add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{
if rule.source_not|default('0') == '1' %}not {% endif %}{{ convert_address(rule, 'source') }} to {%
if rule.destination_not|default('0') == '1' %}not {% endif %}{{convert_address(rule, 'destination')
}} src-port {{ rule.src_port }} dst-port {{ rule.dst_port }} recv {{
- physical_interface(rule.interface) }} {%
+ helpers.physical_interface(rule.interface) }} {%
if rule.proto.split('_')[1]|default('') == 'ack' %} {{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %}{%
if rule.iplen|default('') != '' %} iplen 1-{{ rule.iplen }}{% endif %}{%
if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %}
- xmit {{physical_interface(rule.interface2)
+ xmit {{helpers.physical_interface(rule.interface2)
}} // {{ (rule['@uuid'] + " " + rule.interface + " -> " + rule.interface2 + ": " + helpers.getUUID(rule.target).description)[0:78] }}
{% endif %}
{% if rule.direction == 'out' or not rule.direction %}
@@ -187,7 +186,7 @@ add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{
if rule.proto.split('_')[1]|default('') == 'ack' %} {{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %}{%
if rule.iplen|default('') != '' %} iplen 1-{{ rule.iplen }}{% endif %}{%
if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %}
- recv {{physical_interface(rule.interface2)
+ recv {{helpers.physical_interface(rule.interface2)
}} // {{ (rule['@uuid'] + " " + rule.interface2 + " -> " + rule.interface + ": " + helpers.getUUID(rule.target).description)[0:78] }}
{% endif %}
{% else %}
@@ -200,7 +199,7 @@ add {{loop.index + 60000}} {{ helpers.getUUIDtag(rule.target) }} {{
if rule.proto.split('_')[1]|default('') == 'ack' %}{{ rule.proto.split('_')[2]|default('') }} tcpflags ack {% endif %} {%
if rule.iplen|default('') != '' %} iplen 1-{{ rule.iplen }}{% endif %}{%
if rule.dscp|default('') != '' %} dscp {{ rule.dscp }}{% endif %} via {{
- physical_interface(rule.interface)
+ helpers.physical_interface(rule.interface)
}} // {{ (rule['@uuid'] + " " + rule.interface + ": " + helpers.getUUID(rule.target).description)[0:78] }}
{% endif %}
{% endif %}
diff --git a/src/opnsense/service/templates/OPNsense/Monit/monitrc b/src/opnsense/service/templates/OPNsense/Monit/monitrc
index e8286bbce..7861d6805 100644
--- a/src/opnsense/service/templates/OPNsense/Monit/monitrc
+++ b/src/opnsense/service/templates/OPNsense/Monit/monitrc
@@ -1,6 +1,5 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{% if helpers.exists('OPNsense.monit.general') %}
{% if helpers.exists('OPNsense.monit.general.httpdEnabled') and OPNsense.monit.general.httpdEnabled|default('0') == '1' %}
{% set httpdCredentials = OPNsense.monit.general.httpdUsername ~ ':"' ~ OPNsense.monit.general.httpdPassword ~ '"' %}
@@ -111,7 +110,7 @@ check {{ service.type }} {{ service.name }} {{ address }}
{% if service.address|default('') != '' %}
{% set address = "address " ~ service.address %}
{% elif service.interface|default('') != '' %}
-{% set interface = "interface " ~ physical_interface(service.interface) %}
+{% set interface = "interface " ~ helpers.physical_interface(service.interface) %}
{% endif %}
check {{ service.type }} {{ service.name }} {{ address }} {{ interface }}
{% elif service.type == 'system' %}
diff --git a/src/opnsense/service/templates/OPNsense/Netflow/netflow.conf b/src/opnsense/service/templates/OPNsense/Netflow/netflow.conf
index adec028a9..92752caab 100644
--- a/src/opnsense/service/templates/OPNsense/Netflow/netflow.conf
+++ b/src/opnsense/service/templates/OPNsense/Netflow/netflow.conf
@@ -2,7 +2,6 @@
# Automatic generated configuration for netflow.
# Do not edit this file manually.
#
-{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{%
if helpers.exists('OPNsense.Netflow.capture.interfaces')
and
@@ -14,12 +13,12 @@
%}
netflow_interfaces="{% for interface in OPNsense.Netflow.capture.interfaces.split(',')
%}{{
- physical_interface(interface)
+ helpers.physical_interface(interface)
}} {%
endfor%}"
netflow_egress_only="{% if OPNsense.Netflow.capture.egress_only %}{% for interface in OPNsense.Netflow.capture.egress_only.split(',')
%}{{
- physical_interface(interface)
+ helpers.physical_interface(interface)
}} {%
endfor%} {%endif%}"
netflow_version="{%if OPNsense.Netflow.capture.version == 'v9' %}9{% else %}5{%endif%}"
diff --git a/src/opnsense/service/templates/OPNsense/Sample/example_simple_page.txt b/src/opnsense/service/templates/OPNsense/Sample/example_simple_page.txt
index 3a76ba422..3ff2520d6 100644
--- a/src/opnsense/service/templates/OPNsense/Sample/example_simple_page.txt
+++ b/src/opnsense/service/templates/OPNsense/Sample/example_simple_page.txt
@@ -18,9 +18,11 @@ list of configured network interfaces :
{% endfor %}
+Physical interface connected to "lan" : {{ helpers.physical_interface('lan') }} or both lan and wan {{ helpers.physical_interfaces(['lan', 'wan']) }}
+
and a short list of firewall rules created (multiple rule items in filter section):
-{% for item in filter.rule%}
+{% for item in filter.rule %}
descr : {{ item.descr }}
type : {{ item.type }}
interface : {{ item.interface }}
diff --git a/src/opnsense/service/tests/template.py b/src/opnsense/service/tests/template.py
index 93f696edd..22ebdb596 100644
--- a/src/opnsense/service/tests/template.py
+++ b/src/opnsense/service/tests/template.py
@@ -96,6 +96,7 @@ class TestTemplateMethods(unittest.TestCase):
generated_filenames = self.tmpl.generate('OPNsense/Sample')
self.assertEqual(len(generated_filenames), 4, 'number of output files not 4')
+ @unittest.skip("Very fragile test, only works on clean install")
def test_all(self):
""" Test if all expected templates are created, can only find test for static defined cases.
Calls "generate *" and compares that to all defined templates in all +TARGET files