Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T6841: firewall: improve config parsing for ZBF when using VRFs and interfaces attached to VRFs #4180

Draft
wants to merge 1 commit into
base: current
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 48 additions & 9 deletions data/templates/firewall/nftables-zone.j2
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
{% endif %}
{% for zone_name, zone_conf in zone.items() %}
{% if 'local_zone' not in zone_conf %}
oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
{% if 'name' in zone_conf.interface %}
oifname { {{ zone_conf.interface.name | join(',') }} } counter jump VZONE_{{ zone_name }}
{% endif %}
{% if 'vrf' in zone_conf.interface %}
{% for vrf_name in zone_conf.interface.vrf %}
oifname { {{ zone_conf['vrf_interfaces'][vrf_name] }} } counter jump VZONE_{{ zone_name }}
#oifname { {{ zone_conf.interface.vrf | join(',') }} } counter jump VZONE_{{ zone_name }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
}
Expand Down Expand Up @@ -40,8 +48,15 @@
iifname lo counter return
{% if zone_conf.from is vyos_defined %}
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return

{% if 'name' in zone[from_zone].interface %}
iifname { {{ zone[from_zone].interface.name | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface.name | join(",") }} } counter return
{% endif %}
{% if 'vrf' in zone[from_zone].interface %}
iifname { {{ zone[from_zone].interface.vrf | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface.vrf | join(",") }} } counter return
{% endif %}
{% endfor %}
{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
Expand All @@ -50,23 +65,47 @@
oifname lo counter return
{% if zone_conf.from_local is vyos_defined %}
{% for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall[fw_name] is vyos_defined %}
oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
oifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% if 'name' in zone[from_zone].interface %}
oifname { {{ zone[from_zone].interface.name | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
oifname { {{ zone[from_zone].interface.name | join(",") }} } counter return
{% endif %}
{% if 'vrf' in zone[from_zone].interface %}
{% for vrf_name in zone[from_zone].interface.vrf %}
oifname { {{ zone[from_zone]['vrf_interfaces'][vrf_name] }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
oifname { {{ zone[from_zone]['vrf_interfaces'][vrf_name] }} } counter return
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{{ zone_conf | nft_default_rule('zone_' + zone_name, family) }}
}
{% else %}
chain VZONE_{{ zone_name }} {
iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }}
{% if 'name' in zone_conf.interface %}
iifname { {{ zone_conf.interface.name | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }}
{% endif %}
{% if 'vrf' in zone_conf.interface %}
iifname { {{ zone_conf.interface.vrf | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6) }}
{% endif %}
{% if zone_conf.intra_zone_filtering is vyos_defined %}
iifname { {{ zone_conf.interface | join(",") }} } counter return
{% if 'name' in zone_conf.interface %}
iifname { {{ zone_conf.interface.name | join(",") }} } counter return
{% endif %}
{% if 'vrf' in zone_conf.interface %}
iifname { {{ zone_conf.interface.vrf | join(",") }} } counter return
{% endif %}
{% endif %}
{% if zone_conf.from is vyos_defined %}
{% for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall[fw_name] is vyos_defined %}
{% if zone[from_zone].local_zone is not defined %}
iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface | join(",") }} } counter return
{% if 'name' in zone[from_zone].interface %}
iifname { {{ zone[from_zone].interface.name | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface.name | join(",") }} } counter return
{% endif %}
{% if 'vrf' in zone[from_zone].interface %}
iifname { {{ zone[from_zone].interface.vrf | join(",") }} } counter jump NAME{{ suffix }}_{{ from_conf.firewall[fw_name] }}
iifname { {{ zone[from_zone].interface.vrf | join(",") }} } counter return
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
Expand Down
45 changes: 30 additions & 15 deletions interface-definitions/firewall.xml.in
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -464,24 +464,39 @@
</node>
</children>
</tagNode>
<leafNode name="interface">
<node name="interface">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<node name="interface">
<node name="member">

I agree with the idea to separate interface and VRF options but I have reservations about this syntax. The problem is that a VRF is not an interface, any VRF may include as many interfaces as it wants.

So the parent node should be named something else than interface. I think member might be a good generic word.

<properties>
<help>Interface associated with zone</help>
<valueHelp>
<format>txt</format>
<description>Interface associated with zone</description>
</valueHelp>
<valueHelp>
<format>vrf</format>
<description>VRF associated with zone</description>
</valueHelp>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
<path>vrf name</path>
</completionHelp>
<multi/>
</properties>
</leafNode>
<children>
<leafNode name="name">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<leafNode name="name">
<leafNode name="interface">

<properties>
<help>Interface associated with zone</help>
<valueHelp>
<format>txt</format>
<description>Interface associated with zone</description>
</valueHelp>
<completionHelp>
<script>${vyos_completion_dir}/list_interfaces</script>
</completionHelp>
<multi/>
</properties>
</leafNode>
<leafNode name="vrf">
<properties>
<help>VRF associated with zone</help>
<valueHelp>
<format>vrf</format>
<description>VRF associated with zone</description>
</valueHelp>
<completionHelp>
<path>vrf name</path>
</completionHelp>
<multi/>
</properties>
</leafNode>
</children>
</node>
<node name="intra-zone-filtering">
<properties>
<help>Intra-zone filtering</help>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<!-- include start from include/version/firewall-version.xml.i -->
<syntaxVersion component='firewall' version='17'></syntaxVersion>
<syntaxVersion component='firewall' version='18'></syntaxVersion>
<!-- include end -->
4 changes: 3 additions & 1 deletion python/vyos/utils/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ def get_vrf_members(vrf: str) -> list:
answer = json.loads(output)
for data in answer:
if 'ifname' in data:
interfaces.append(data.get('ifname'))
# Skip PIM interfaces which appears in VRF
if 'pim' not in data.get('ifname'):
interfaces.append(data.get('ifname'))
except:
pass
return interfaces
Expand Down
94 changes: 93 additions & 1 deletion smoketest/scripts/cli/test_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ def test_timeout_sysctl(self):
def test_zone_basic(self):
self.cli_set(['firewall', 'ipv4', 'name', 'smoketest', 'default-action', 'drop'])
self.cli_set(['firewall', 'ipv6', 'name', 'smoketestv6', 'default-action', 'drop'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'eth0'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'name', 'eth0'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'interface', 'name', 'eth0'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'member', 'interface', 'eth0'])

self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'from', 'smoketest-local', 'firewall', 'name', 'smoketest'])
self.cli_set(['firewall', 'zone', 'smoketest-eth0', 'intra-zone-filtering', 'firewall', 'ipv6-name', 'smoketestv6'])
self.cli_set(['firewall', 'zone', 'smoketest-local', 'local-zone'])
Expand Down Expand Up @@ -963,6 +963,98 @@ def test_zone_basic(self):
self.verify_nftables(nftables_search, 'ip vyos_filter')
self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')

def test_zone_with_vrf(self):
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE1-to-LOCAL', 'default-action', 'accept'])
self.cli_set(['firewall', 'ipv4', 'name', 'ZONE2_to_ZONE1', 'default-action', 'continue'])
self.cli_set(['firewall', 'ipv6', 'name', 'LOCAL_to_ZONE2_v6', 'default-action', 'drop'])
self.cli_set(['firewall', 'zone', 'LOCAL', 'from', 'ZONE1', 'firewall', 'name', 'ZONE1-to-LOCAL'])
self.cli_set(['firewall', 'zone', 'LOCAL', 'local-zone'])
self.cli_set(['firewall', 'zone', 'ZONE1', 'from', 'ZONE2', 'firewall', 'name', 'ZONE2_to_ZONE1'])
self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'name', 'eth1'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'name', 'eth1'])
self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'interface', 'eth1'])

self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'name', 'eth2'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'name', 'eth2'])
self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'interface', 'eth2'])

self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'vrf', 'VRF-1'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'ZONE1', 'interface', 'vrf', 'VRF-1'])
self.cli_set(['firewall', 'zone', 'ZONE1', 'member', 'vrf', 'VRF-1'])

self.cli_set(['firewall', 'zone', 'ZONE2', 'from', 'LOCAL', 'firewall', 'ipv6-name', 'LOCAL_to_ZONE2_v6'])
self.cli_set(['firewall', 'zone', 'ZONE2', 'interface', 'name', 'vtun66'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'ZONE2', 'interface', 'name', 'vtun66'])
self.cli_set(['firewall', 'zone', 'ZONE2', 'member', 'interface', 'vtun66'])

self.cli_set(['firewall', 'zone', 'ZONE2', 'interface', 'vrf', 'VRF-2'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cli_set(['firewall', 'zone', 'ZONE2', 'interface', 'vrf', 'VRF-2'])
self.cli_set(['firewall', 'zone', 'ZONE2', 'member', 'vrf', 'VRF-2'])


self.cli_set(['vrf', 'name', 'VRF-1', 'table', '101'])
self.cli_set(['vrf', 'name', 'VRF-2', 'table', '102'])
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vrf', 'VRF-1'])
self.cli_set(['interfaces', 'vti', 'vti1', 'vrf', 'VRF-2'])

self.cli_commit()

nftables_search = [
['chain NAME_ZONE1-to-LOCAL'],
['counter', 'accept', 'comment "NAM-ZONE1-to-LOCAL default-action accept"'],
['chain NAME_ZONE2_to_ZONE1'],
['counter', 'continue', 'comment "NAM-ZONE2_to_ZONE1 default-action continue"'],
['chain VYOS_ZONE_FORWARD'],
['type filter hook forward priority filter + 1'],
['oifname { "eth1", "eth2" }', 'counter packets', 'jump VZONE_ZONE1'],
['oifname "eth0"', 'counter packets', 'jump VZONE_ZONE1'],
['oifname "vtun66"', 'counter packets', 'jump VZONE_ZONE2'],
['oifname "vti1"', 'counter packets', 'jump VZONE_ZONE2'],
['chain VYOS_ZONE_LOCAL'],
['type filter hook input priority filter + 1'],
['counter packets', 'jump VZONE_LOCAL_IN'],
['chain VYOS_ZONE_OUTPUT'],
['type filter hook output priority filter + 1'],
['counter packets', 'jump VZONE_LOCAL_OUT'],
['chain VZONE_LOCAL_IN'],
['iifname { "eth1", "eth2" }', 'counter packets', 'jump NAME_ZONE1-to-LOCAL'],
['iifname "VRF-1"', 'counter packets', 'jump NAME_ZONE1-to-LOCAL'],
['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
['chain VZONE_LOCAL_OUT'],
['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
['chain VZONE_ZONE1'],
['iifname { "eth1", "eth2" }', 'counter packets', 'return'],
['iifname "VRF-1"', 'counter packets', 'return'],
['iifname "vtun66"', 'counter packets', 'jump NAME_ZONE2_to_ZONE1'],
['iifname "vtun66"', 'counter packets', 'return'],
['iifname "VRF-2"', 'counter packets', 'jump NAME_ZONE2_to_ZONE1'],
['iifname "VRF-2"', 'counter packets', 'return'],
['counter packets', 'drop', 'comment "zone_ZONE1 default-action drop"'],
['chain VZONE_ZONE2'],
['iifname "vtun66"', 'counter packets', 'return'],
['iifname "VRF-2"', 'counter packets', 'return'],
['counter packets', 'drop', 'comment "zone_ZONE2 default-action drop"']
]

nftables_search_v6 = [
['chain NAME6_LOCAL_to_ZONE2_v6'],
['counter', 'drop', 'comment "NAM-LOCAL_to_ZONE2_v6 default-action drop"'],
['chain VYOS_ZONE_FORWARD'],
['type filter hook forward priority filter + 1'],
['oifname { "eth1", "eth2" }', 'counter packets', 'jump VZONE_ZONE1'],
['oifname "eth0"', 'counter packets', 'jump VZONE_ZONE1'],
['oifname "vtun66"', 'counter packets', 'jump VZONE_ZONE2'],
['oifname "vti1"', 'counter packets', 'jump VZONE_ZONE2'],
['chain VYOS_ZONE_LOCAL'],
['type filter hook input priority filter + 1'],
['counter packets', 'jump VZONE_LOCAL_IN'],
['chain VYOS_ZONE_OUTPUT'],
['type filter hook output priority filter + 1'],
['counter packets', 'jump VZONE_LOCAL_OUT'],
['chain VZONE_LOCAL_IN'],
['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
['chain VZONE_LOCAL_OUT'],
['oifname "vtun66"', 'counter packets', 'jump NAME6_LOCAL_to_ZONE2_v6'],
['oifname "vti1"', 'counter packets', 'jump NAME6_LOCAL_to_ZONE2_v6'],
['counter packets', 'drop', 'comment "zone_LOCAL default-action drop"'],
['chain VZONE_ZONE1'],
['iifname { "eth1", "eth2" }', 'counter packets', 'return'],
['iifname "VRF-1"', 'counter packets', 'return'],
['counter packets', 'drop', 'comment "zone_ZONE1 default-action drop"'],
['chain VZONE_ZONE2'],
['iifname "vtun66"', 'counter packets', 'return'],
['iifname "VRF-2"', 'counter packets', 'return'],
['counter packets', 'drop', 'comment "zone_ZONE2 default-action drop"']
]

self.verify_nftables(nftables_search, 'ip vyos_filter')
self.verify_nftables(nftables_search_v6, 'ip6 vyos_filter')

def test_flow_offload(self):
self.cli_set(['interfaces', 'ethernet', 'eth0', 'vif', '10'])
self.cli_set(['firewall', 'flowtable', 'smoketest', 'interface', 'eth0.10'])
Expand Down
28 changes: 24 additions & 4 deletions src/conf_mode/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
from vyos.utils.process import call
from vyos.utils.process import cmd
from vyos.utils.process import rc_cmd
from vyos.utils.network import get_vrf_members
from vyos.utils.network import get_interface_vrf
from vyos import ConfigError
from vyos import airbag
from pathlib import Path
Expand Down Expand Up @@ -442,6 +444,7 @@ def verify(firewall):

local_zone = False
zone_interfaces = []
zone_vrf = []

if 'zone' in firewall:
for zone, zone_conf in firewall['zone'].items():
Expand All @@ -458,12 +461,23 @@ def verify(firewall):
local_zone = True

if 'interface' in zone_conf:
found_duplicates = [intf for intf in zone_conf['interface'] if intf in zone_interfaces]
if 'name'in zone_conf['interface']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if 'name'in zone_conf['interface']:
if 'interface' in zone_conf['member']:


if found_duplicates:
raise ConfigError(f'Interfaces cannot be assigned to multiple zones')
for iface in zone_conf['interface']['name']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for iface in zone_conf['interface']['name']:
for iface in zone_conf['member']['interface']:


zone_interfaces += zone_conf['interface']
if iface in zone_interfaces:
raise ConfigError(f'Interfaces cannot be assigned to multiple zones')

iface_vrf = get_interface_vrf(iface)
if iface_vrf != 'default':
Warning(f"Interface {iface} assigned to zone {zone} is in VRF {iface_vrf}. This might not work as expected.")
zone_interfaces += iface

if 'vrf' in zone_conf['interface']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if 'vrf' in zone_conf['interface']:
if 'vrf' in zone_conf['member']:

for vrf in zone_conf['interface']['vrf']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for vrf in zone_conf['interface']['vrf']:
for vrf in zone_conf['member']['vrf']:

if vrf in zone_vrf:
raise ConfigError(f'VRF cannot be assigned to multiple zones')
zone_vrf += vrf

if 'intra_zone_filtering' in zone_conf:
intra_zone = zone_conf['intra_zone_filtering']
Expand Down Expand Up @@ -505,6 +519,12 @@ def generate(firewall):
if 'zone' in firewall:
for local_zone, local_zone_conf in firewall['zone'].items():
if 'local_zone' not in local_zone_conf:
# Get physical interfaces assigned to the zone if vrf is used:
if 'vrf' in local_zone_conf['interface']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if 'vrf' in local_zone_conf['interface']:
if 'vrf' in local_zone_conf['member']:

local_zone_conf['vrf_interfaces'] = {}
for vrf_name in local_zone_conf['interface']['vrf']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for vrf_name in local_zone_conf['interface']['vrf']:
for vrf_name in local_zone_conf['member']['vrf']:

local_zone_conf['vrf_interfaces'][vrf_name] = ','.join(get_vrf_members(vrf_name))
#local_zone_conf['interface']['vrf'][vrf_name] = ''.join(get_vrf_members(vrf_name))
continue

local_zone_conf['from_local'] = {}
Expand Down
Empty file modified src/migration-scripts/firewall/16-to-17
100755 → 100644
Empty file.
36 changes: 36 additions & 0 deletions src/migration-scripts/firewall/17-to-18
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright (C) 2024 VyOS maintainers and contributors
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library. If not, see <http://www.gnu.org/licenses/>.

# From
# set firewall zone <zone> interface <iface>
# To
# set firewall zone <zone> interface name <iface>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# set firewall zone <zone> interface name <iface>
# set firewall zone <zone> member interface <iface>

# or
# set firewall zone <zone> interface vrf <vrf>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# set firewall zone <zone> interface vrf <vrf>
# set firewall zone <zone> member vrf <vrf>



from vyos.configtree import ConfigTree

base = ['firewall', 'zone']

def migrate(config: ConfigTree) -> None:
if not config.exists(base):
# Nothing to do
return

for zone in config.list_nodes(base):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic looks incomplete.

  1. It doesn't seem to delete values from under the old interface node, only duplicates them under the new one.
  2. VRF support was introduced in 1.4.0 already, but the script doesn't handle the case when the value is a VRF name rather than an interface name.

if config.exists(base + [zone, 'interface']):
for iface in config.return_values(base + [zone, 'interface']):
config.set(base + [zone, 'interface', 'name'], value=iface, replace=False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config.set(base + [zone, 'interface', 'name'], value=iface, replace=False)
config.set(base + [zone, 'member', 'interface'], value=iface, replace=False)

Loading