summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/include/interface/tunnel-remote-multi.xml.i19
-rw-r--r--interface-definitions/include/interface/tunnel-remote.xml.i2
-rw-r--r--interface-definitions/interfaces-vxlan.xml.in2
-rw-r--r--interface-definitions/vpn_ipsec.xml.in2
-rw-r--r--python/vyos/ifconfig/vxlan.py19
-rw-r--r--python/vyos/util.py8
-rwxr-xr-xsmoketest/scripts/cli/test_interfaces_vxlan.py36
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py1
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py32
9 files changed, 98 insertions, 23 deletions
diff --git a/interface-definitions/include/interface/tunnel-remote-multi.xml.i b/interface-definitions/include/interface/tunnel-remote-multi.xml.i
new file mode 100644
index 000000000..f672087a4
--- /dev/null
+++ b/interface-definitions/include/interface/tunnel-remote-multi.xml.i
@@ -0,0 +1,19 @@
+<!-- include start from interface/tunnel-remote-multi.xml.i -->
+<leafNode name="remote">
+ <properties>
+ <help>Tunnel remote address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Tunnel remote IPv4 address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Tunnel remote IPv6 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/interface/tunnel-remote.xml.i b/interface-definitions/include/interface/tunnel-remote.xml.i
index 1ba9b0382..2a8891b85 100644
--- a/interface-definitions/include/interface/tunnel-remote.xml.i
+++ b/interface-definitions/include/interface/tunnel-remote.xml.i
@@ -1,4 +1,4 @@
-<!-- include start from rip/tunnel-remote.xml.i -->
+<!-- include start from interface/tunnel-remote.xml.i -->
<leafNode name="remote">
<properties>
<help>Tunnel remote address</help>
diff --git a/interface-definitions/interfaces-vxlan.xml.in b/interface-definitions/interfaces-vxlan.xml.in
index 4c3c3ac71..0546b4199 100644
--- a/interface-definitions/interfaces-vxlan.xml.in
+++ b/interface-definitions/interfaces-vxlan.xml.in
@@ -98,7 +98,7 @@
</leafNode>
#include <include/source-address-ipv4-ipv6.xml.i>
#include <include/source-interface.xml.i>
- #include <include/interface/tunnel-remote.xml.i>
+ #include <include/interface/tunnel-remote-multi.xml.i>
#include <include/interface/vrf.xml.i>
#include <include/vni.xml.i>
</children>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 7b5074112..58179b1c3 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -311,7 +311,7 @@
</completionHelp>
<valueHelp>
<format>ikev1</format>
- <description>Use IKEv1 for key exchange [DEFAULT]</description>
+ <description>Use IKEv1 for key exchange</description>
</valueHelp>
<valueHelp>
<format>ikev2</format>
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index 0c5282db4..516a19f24 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2022 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -68,6 +68,16 @@ class VXLANIf(Interface):
'vni' : 'id',
}
+ # IPv6 flowlabels can only be used on IPv6 tunnels, thus we need to
+ # ensure that at least the first remote IP address is passed to the
+ # tunnel creation command. Subsequent tunnel remote addresses can later
+ # be added to the FDB
+ remote_list = None
+ if 'remote' in self.config:
+ # skip first element as this is already configured as remote
+ remote_list = self.config['remote'][1:]
+ self.config['remote'] = self.config['remote'][0]
+
cmd = 'ip link add {ifname} type {type} dstport {port}'
for vyos_key, iproute2_key in mapping.items():
# dict_search will return an empty dict "{}" for valueless nodes like
@@ -82,3 +92,10 @@ class VXLANIf(Interface):
self._cmd(cmd.format(**self.config))
# interface is always A/D down. It needs to be enabled explicitly
self.set_admin_state('down')
+
+ # VXLAN tunnel is always recreated on any change - see interfaces-vxlan.py
+ if remote_list:
+ for remote in remote_list:
+ cmd = f'bridge fdb append to 00:00:00:00:00:00 dst {remote} ' \
+ 'port {port} dev {ifname}'
+ self._cmd(cmd.format(**self.config))
diff --git a/python/vyos/util.py b/python/vyos/util.py
index 1767ff9d3..4526375df 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -774,6 +774,14 @@ def dict_search_recursive(dict_object, key):
for x in dict_search_recursive(j, key):
yield x
+def get_bridge_fdb(interface):
+ """ Returns the forwarding database entries for a given interface """
+ if not os.path.exists(f'/sys/class/net/{interface}'):
+ return None
+ from json import loads
+ tmp = loads(cmd(f'bridge -j fdb show dev {interface}'))
+ return tmp
+
def get_interface_config(interface):
""" Returns the used encapsulation protocol for given interface.
If interface does not exist, None is returned.
diff --git a/smoketest/scripts/cli/test_interfaces_vxlan.py b/smoketest/scripts/cli/test_interfaces_vxlan.py
index 9278adadd..f34b99ea4 100755
--- a/smoketest/scripts/cli/test_interfaces_vxlan.py
+++ b/smoketest/scripts/cli/test_interfaces_vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -18,8 +18,9 @@ import unittest
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Interface
+from vyos.util import get_bridge_fdb
from vyos.util import get_interface_config
-
+from vyos.template import is_ipv6
from base_interfaces_test import BasicInterfaceTest
class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
@@ -33,6 +34,8 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
'vxlan10': ['vni 10', 'remote 127.0.0.2'],
'vxlan20': ['vni 20', 'group 239.1.1.1', 'source-interface eth0'],
'vxlan30': ['vni 30', 'remote 2001:db8:2000::1', 'source-address 2001:db8:1000::1', 'parameters ipv6 flowlabel 0x1000'],
+ 'vxlan40': ['vni 40', 'remote 127.0.0.2', 'remote 127.0.0.3'],
+ 'vxlan50': ['vni 50', 'remote 2001:db8:2000::1', 'remote 2001:db8:2000::2', 'parameters ipv6 flowlabel 0x1000'],
}
cls._interfaces = list(cls._options)
# call base-classes classmethod
@@ -55,21 +58,34 @@ class VXLANInterfaceTest(BasicInterfaceTest.TestCase):
ttl = 20
for interface in self._interfaces:
options = get_interface_config(interface)
+ bridge = get_bridge_fdb(interface)
vni = options['linkinfo']['info_data']['id']
self.assertIn(f'vni {vni}', self._options[interface])
- if any('link' in s for s in self._options[interface]):
+ if any('source-interface' in s for s in self._options[interface]):
link = options['linkinfo']['info_data']['link']
self.assertIn(f'source-interface {link}', self._options[interface])
- if any('local6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['local6']
- self.assertIn(f'source-address {local6}', self._options[interface])
-
- if any('remote6' in s for s in self._options[interface]):
- remote = options['linkinfo']['info_data']['remote6']
- self.assertIn(f'remote {remote}', self._options[interface])
+ # Verify source-address setting was properly configured on the Kernel
+ if any('source-address' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'source-address' in s:
+ address = s.split()[-1]
+ if is_ipv6(address):
+ tmp = options['linkinfo']['info_data']['local6']
+ else:
+ tmp = options['linkinfo']['info_data']['local']
+ self.assertIn(f'source-address {tmp}', self._options[interface])
+
+ # Verify remote setting was properly configured on the Kernel
+ if any('remote' in s for s in self._options[interface]):
+ for s in self._options[interface]:
+ if 'remote' in s:
+ for fdb in bridge:
+ if 'mac' in fdb and fdb['mac'] == '00:00:00:00:00:00':
+ remote = fdb['dst']
+ self.assertIn(f'remote {remote}', self._options[interface])
if any('group' in s for s in self._options[interface]):
group = options['linkinfo']['info_data']['group']
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 4d3ebc587..f4dba9d4a 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -22,7 +22,6 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
from vyos.configdict import node_changed
-from vyos.configdict import leaf_node_changed
from vyos.configdict import is_member
from vyos.configdict import is_source_interface
from vyos.configdict import has_vlan_subinterface_configured
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 1f097c4e3..85604508e 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -34,8 +34,8 @@ 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
+ 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
@@ -70,8 +70,7 @@ def verify(vxlan):
if 'group' in vxlan:
if 'source_interface' not in vxlan:
- raise ConfigError('Multicast VXLAN requires an underlaying interface ')
-
+ raise ConfigError('Multicast VXLAN requires an underlaying interface')
verify_source_interface(vxlan)
if not any(tmp in ['group', 'remote', 'source_address'] for tmp in vxlan):
@@ -108,15 +107,33 @@ def verify(vxlan):
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'
+
verify_mtu_ipv6(vxlan)
verify_address(vxlan)
return None
-
def generate(vxlan):
return None
-
def apply(vxlan):
# Check if the VXLAN interface already exists
if vxlan['ifname'] in interfaces():
@@ -132,7 +149,6 @@ def apply(vxlan):
return None
-
if __name__ == '__main__':
try:
c = get_config()