summaryrefslogtreecommitdiff
path: root/src/conf_mode/interfaces-vxlan.py
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2023-12-30 23:25:20 +0100
committerChristian Breunig <christian@breunig.cc>2023-12-31 23:49:48 +0100
commit4ef110fd2c501b718344c72d495ad7e16d2bd465 (patch)
treee98bf08f93c029ec4431a3b6ca078e7562e0cc58 /src/conf_mode/interfaces-vxlan.py
parent2286b8600da6c631b17e1d5b9b341843e50f9abf (diff)
downloadvyos-1x-4ef110fd2c501b718344c72d495ad7e16d2bd465.tar.gz
vyos-1x-4ef110fd2c501b718344c72d495ad7e16d2bd465.zip
T5474: establish common file name pattern for XML conf mode commands
We will use _ as CLI level divider. The XML definition filename and also the Python helper should match the CLI node. Example: set interfaces ethernet -> interfaces_ethernet.xml.in set interfaces bond -> interfaces_bond.xml.in set service dhcp-server -> service_dhcp-server-xml.in
Diffstat (limited to 'src/conf_mode/interfaces-vxlan.py')
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py236
1 files changed, 0 insertions, 236 deletions
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
deleted file mode 100755
index 4251e611b..000000000
--- a/src/conf_mode/interfaces-vxlan.py
+++ /dev/null
@@ -1,236 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019-2023 VyOS maintainers and contributors
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import os
-
-from sys import exit
-from netifaces import interfaces
-
-from vyos.base import Warning
-from vyos.config import Config
-from vyos.configdict import get_interface_dict
-from vyos.configdict import leaf_node_changed
-from vyos.configdict import is_node_changed
-from vyos.configdict import node_changed
-from vyos.configverify import verify_address
-from vyos.configverify import verify_bridge_delete
-from vyos.configverify import verify_mtu_ipv6
-from vyos.configverify import verify_mirror_redirect
-from vyos.configverify import verify_source_interface
-from vyos.configverify import verify_bond_bridge_member
-from vyos.ifconfig import Interface
-from vyos.ifconfig import VXLANIf
-from vyos.template import is_ipv6
-from vyos.utils.dict import dict_search
-from vyos import ConfigError
-from vyos import airbag
-airbag.enable()
-
-def get_config(config=None):
- """
- Retrive CLI config as dictionary. Dictionary can never be empty, as at least
- the interface name will be added or a deleted flag
- """
- if config:
- conf = config
- else:
- conf = Config()
- base = ['interfaces', 'vxlan']
- ifname, vxlan = get_interface_dict(conf, base)
-
- # VXLAN interfaces are picky and require recreation if certain parameters
- # change. But a VXLAN interface should - of course - not be re-created if
- # it's description or IP address is adjusted. Feels somehow logic doesn't it?
- for cli_option in ['parameters', 'gpe', 'group', 'port', 'remote',
- 'source-address', 'source-interface', 'vni']:
- if is_node_changed(conf, base + [ifname, cli_option]):
- vxlan.update({'rebuild_required': {}})
- break
-
- # When dealing with VNI filtering we need to know what VNI was actually removed,
- # so build up a dict matching the vlan_to_vni structure but with removed values.
- tmp = node_changed(conf, base + [ifname, 'vlan-to-vni'], recursive=True)
- if tmp:
- vxlan.update({'vlan_to_vni_removed': {}})
- for vlan in tmp:
- vni = leaf_node_changed(conf, base + [ifname, 'vlan-to-vni', vlan, 'vni'])
- vxlan['vlan_to_vni_removed'].update({vlan : {'vni' : vni[0]}})
-
- # We need to verify that no other VXLAN tunnel is configured when external
- # mode is in use - Linux Kernel limitation
- conf.set_level(base)
- vxlan['other_tunnels'] = conf.get_config_dict([], key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True)
-
- # This if-clause is just to be sure - it will always evaluate to true
- ifname = vxlan['ifname']
- if ifname in vxlan['other_tunnels']:
- del vxlan['other_tunnels'][ifname]
- if len(vxlan['other_tunnels']) == 0:
- del vxlan['other_tunnels']
-
- return vxlan
-
-def verify(vxlan):
- if 'deleted' in vxlan:
- verify_bridge_delete(vxlan)
- return None
-
- if int(vxlan['mtu']) < 1500:
- Warning('RFC7348 recommends VXLAN tunnels preserve a 1500 byte MTU')
-
- if 'group' in vxlan:
- if 'source_interface' not in vxlan:
- raise ConfigError('Multicast VXLAN requires an underlaying interface')
- verify_source_interface(vxlan)
-
- if not any(tmp in ['group', 'remote', 'source_address', 'source_interface'] for tmp in vxlan):
- raise ConfigError('Group, remote, source-address or source-interface must be configured')
-
- if 'vni' not in vxlan and dict_search('parameters.external', vxlan) == None:
- raise ConfigError('Must either configure VXLAN "vni" or use "external" CLI option!')
-
- if dict_search('parameters.external', vxlan) != None:
- if 'vni' in vxlan:
- raise ConfigError('Can not specify both "external" and "VNI"!')
-
- if 'other_tunnels' in vxlan:
- # When multiple VXLAN interfaces are defined and "external" is used,
- # all VXLAN interfaces need to have vni-filter enabled!
- # See Linux Kernel commit f9c4bb0b245cee35ef66f75bf409c9573d934cf9
- other_vni_filter = False
- for tunnel, tunnel_config in vxlan['other_tunnels'].items():
- if dict_search('parameters.vni_filter', tunnel_config) != None:
- other_vni_filter = True
- break
- # eqivalent of the C foo ? 'a' : 'b' statement
- vni_filter = True and (dict_search('parameters.vni_filter', vxlan) != None) or False
- # If either one is enabled, so must be the other. Both can be off and both can be on
- if (vni_filter and not other_vni_filter) or (not vni_filter and other_vni_filter):
- raise ConfigError(f'Using multiple VXLAN interfaces with "external" '\
- 'requires all VXLAN interfaces to have "vni-filter" configured!')
-
- if not vni_filter and not other_vni_filter:
- other_tunnels = ', '.join(vxlan['other_tunnels'])
- raise ConfigError(f'Only one VXLAN tunnel is supported when "external" '\
- f'CLI option is used and "vni-filter" is unset. '\
- f'Additional tunnels: {other_tunnels}')
-
- if 'gpe' in vxlan and 'external' not in vxlan:
- raise ConfigError(f'VXLAN-GPE is only supported when "external" '\
- f'CLI option is used.')
-
- if 'source_interface' in vxlan:
- # VXLAN adds at least an overhead of 50 byte - we need to check the
- # underlaying device if our VXLAN package is not going to be fragmented!
- vxlan_overhead = 50
- if 'source_address' in vxlan and is_ipv6(vxlan['source_address']):
- # IPv6 adds an extra 20 bytes overhead because the IPv6 header is 20
- # bytes larger than the IPv4 header - assuming no extra options are
- # in use.
- vxlan_overhead += 20
-
- # If source_address is not used - check IPv6 'remote' list
- elif 'remote' in vxlan:
- if any(is_ipv6(a) for a in vxlan['remote']):
- vxlan_overhead += 20
-
- lower_mtu = Interface(vxlan['source_interface']).get_mtu()
- if lower_mtu < (int(vxlan['mtu']) + vxlan_overhead):
- raise ConfigError(f'Underlaying device MTU is to small ({lower_mtu} '\
- f'bytes) for VXLAN overhead ({vxlan_overhead} bytes!)')
-
- # Check for mixed IPv4 and IPv6 addresses
- protocol = None
- if 'source_address' in vxlan:
- if is_ipv6(vxlan['source_address']):
- protocol = 'ipv6'
- else:
- protocol = 'ipv4'
-
- if 'remote' in vxlan:
- error_msg = 'Can not mix both IPv4 and IPv6 for VXLAN underlay'
- for remote in vxlan['remote']:
- if is_ipv6(remote):
- if protocol == 'ipv4':
- raise ConfigError(error_msg)
- protocol = 'ipv6'
- else:
- if protocol == 'ipv6':
- raise ConfigError(error_msg)
- protocol = 'ipv4'
-
- if 'vlan_to_vni' in vxlan:
- if 'is_bridge_member' not in vxlan:
- raise ConfigError('VLAN to VNI mapping requires that VXLAN interface '\
- 'is member of a bridge interface!')
-
- vnis_used = []
- for vif, vif_config in vxlan['vlan_to_vni'].items():
- if 'vni' not in vif_config:
- raise ConfigError(f'Must define VNI for VLAN "{vif}"!')
- vni = vif_config['vni']
- if vni in vnis_used:
- raise ConfigError(f'VNI "{vni}" is already assigned to a different VLAN!')
- vnis_used.append(vni)
-
- if dict_search('parameters.neighbor_suppress', vxlan) != None:
- if 'is_bridge_member' not in vxlan:
- raise ConfigError('Neighbor suppression requires that VXLAN interface '\
- 'is member of a bridge interface!')
-
- verify_mtu_ipv6(vxlan)
- verify_address(vxlan)
- verify_bond_bridge_member(vxlan)
- verify_mirror_redirect(vxlan)
-
- # We use a defaultValue for port, thus it's always safe to use
- if vxlan['port'] == '8472':
- Warning('Starting from VyOS 1.4, the default port for VXLAN '\
- 'has been changed to 4789. This matches the IANA assigned '\
- 'standard port number!')
-
- return None
-
-def generate(vxlan):
- return None
-
-def apply(vxlan):
- # Check if the VXLAN interface already exists
- if 'rebuild_required' in vxlan or 'delete' in vxlan:
- if vxlan['ifname'] in interfaces():
- v = VXLANIf(vxlan['ifname'])
- # VXLAN is super picky and the tunnel always needs to be recreated,
- # thus we can simply always delete it first.
- v.remove()
-
- if 'deleted' not in vxlan:
- # Finally create the new interface
- v = VXLANIf(**vxlan)
- v.update(vxlan)
-
- return None
-
-if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- generate(c)
- apply(c)
- except ConfigError as e:
- print(e)
- exit(1)