summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/interfaces-geneve.xml92
-rw-r--r--python/vyos/ifconfig.py39
-rwxr-xr-xsrc/conf_mode/interfaces-geneve.py150
3 files changed, 280 insertions, 1 deletions
diff --git a/interface-definitions/interfaces-geneve.xml b/interface-definitions/interfaces-geneve.xml
new file mode 100644
index 000000000..3a529c363
--- /dev/null
+++ b/interface-definitions/interfaces-geneve.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="interfaces">
+ <children>
+ <tagNode name="geneve" owner="${vyos_conf_scripts_dir}/interfaces-geneve.py">
+ <properties>
+ <help>Generic Network Virtualization Encapsulation (GENEVE)</help>
+ <priority>460</priority>
+ <constraint>
+ <regex>gnv[0-9]+$</regex>
+ </constraint>
+ <constraintErrorMessage>GENEVE interface must be named gnvN</constraintErrorMessage>
+ <valueHelp>
+ <format>gnvN</format>
+ <description>GENEVE interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ip-cidr"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>1450-9000</format>
+ <description>Maximum Transmission Unit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1450-9000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 1450 and 9000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="remote">
+ <properties>
+ <help>Remote address of GENEVE tunnel</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Remote address of GENEVE tunnel</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="vni">
+ <properties>
+ <help>Virtual Network Identifier</help>
+ <valueHelp>
+ <format>0-16777214</format>
+ <description>GENEVE virtual network identifier</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 0-16777214"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py
index 279d948b7..f487e6a5b 100644
--- a/python/vyos/ifconfig.py
+++ b/python/vyos/ifconfig.py
@@ -1609,7 +1609,7 @@ class WireGuardIf(Interface):
class VXLANIf(Interface, ):
"""
The VXLAN protocol is a tunnelling protocol designed to solve the
- problem of limited VLAN IDs (4096) in IEEE 802.1q. With VXLAN the
+ problem of limited VLAN IDs (4096) in IEEE 802.1q. With VXLAN the
size of the identifier is expanded to 24 bits (16777216).
VXLAN is described by IETF RFC 7348, and has been implemented by a
@@ -1668,3 +1668,40 @@ class VXLANIf(Interface, ):
'remote': ''
}
return config
+
+class GeneveIf(Interface, ):
+ """
+ Geneve: Generic Network Virtualization Encapsulation
+
+ For more information please refer to:
+ https://tools.ietf.org/html/draft-gross-geneve-00
+ https://www.redhat.com/en/blog/what-geneve
+ https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/#geneve
+ https://lwn.net/Articles/644938/
+ """
+ def __init__(self, ifname, config=''):
+ if config:
+ self._ifname = ifname
+
+ if not os.path.exists('/sys/class/net/{}'.format(self._ifname)):
+ cmd = 'ip link add name {} type geneve id {} remote {}' \
+ .format(self._ifname, config['vni'], config['remote'])
+ self._cmd(cmd)
+
+ super().__init__(ifname, type='geneve')
+
+ @staticmethod
+ def get_config():
+ """
+ GENEVE interfaces require a configuration when they are added using
+ iproute2. This static method will provide the configuration dictionary
+ used by this class.
+
+ Example:
+ >> dict = GeneveIf().get_config()
+ """
+ config = {
+ 'vni': 0,
+ 'remote': ''
+ }
+ return config
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py
new file mode 100755
index 000000000..94f88ad6d
--- /dev/null
+++ b/src/conf_mode/interfaces-geneve.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 copy import deepcopy
+
+from vyos.configdict import list_diff
+from vyos.config import Config
+from vyos.ifconfig import GeneveIf, Interface
+from vyos.interfaces import get_type_of_interface
+from vyos import ConfigError
+from netifaces import interfaces
+
+default_config_data = {
+ 'address': [],
+ 'deleted': False,
+ 'description': '',
+ 'disable': False,
+ 'intf': '',
+ 'mtu': 1450,
+ 'remote': ''
+}
+
+def get_config():
+ geneve = deepcopy(default_config_data)
+ conf = Config()
+
+ # determine tagNode instance
+ try:
+ geneve['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+ except KeyError as E:
+ print("Interface not specified")
+
+ # Check if interface has been removed
+ if not conf.exists('interfaces geneve ' + geneve['intf']):
+ geneve['deleted'] = True
+ return geneve
+
+ # set new configuration level
+ conf.set_level('interfaces geneve ' + geneve['intf'])
+
+ # retrieve configured interface addresses
+ if conf.exists('address'):
+ geneve['address'] = conf.return_values('address')
+
+ # retrieve interface description
+ if conf.exists('description'):
+ geneve['description'] = conf.return_value('description')
+
+ # Disable this interface
+ if conf.exists('disable'):
+ geneve['disable'] = True
+
+ # Maximum Transmission Unit (MTU)
+ if conf.exists('mtu'):
+ geneve['mtu'] = int(conf.return_value('mtu'))
+
+ # Remote address of GENEVE tunnel
+ if conf.exists('remote'):
+ geneve['remote'] = conf.return_value('remote')
+
+ # Virtual Network Identifier
+ if conf.exists('vni'):
+ geneve['vni'] = conf.return_value('vni')
+
+ return geneve
+
+
+def verify(geneve):
+ if geneve['deleted']:
+ # bail out early
+ return None
+
+ if not geneve['remote']:
+ raise ConfigError('GENEVE remote must be configured')
+
+ if not geneve['vni']:
+ raise ConfigError('GENEVE VNI must be configured')
+
+ return None
+
+
+def generate(geneve):
+ return None
+
+
+def apply(geneve):
+ # Check if GENEVE interface already exists
+ if geneve['intf'] in interfaces():
+ v = GeneveIf(geneve['intf'])
+ # GENEVE is super picky and the tunnel always needs to be recreated,
+ # thus we can simply always delete it first.
+ v.remove()
+
+ if not geneve['deleted']:
+ # GENEVE interface needs to be created on-block
+ # instead of passing a ton of arguments, I just use a dict
+ # that is managed by vyos.ifconfig
+ conf = deepcopy(GeneveIf.get_config())
+
+ # Assign GENEVE instance configuration parameters to config dict
+ conf['vni'] = geneve['vni']
+ conf['remote'] = geneve['remote']
+
+ # Finally create the new interface
+ v = GeneveIf(geneve['intf'], config=conf)
+ # update interface description used e.g. by SNMP
+ v.set_alias(geneve['description'])
+ # Maximum Transfer Unit (MTU)
+ v.set_mtu(geneve['mtu'])
+
+ # Configure interface address(es) - no need to implicitly delete the
+ # old addresses as they have already been removed by deleting the
+ # interface above
+ for addr in geneve['address']:
+ v.add_addr(addr)
+
+ # As the bond interface is always disabled first when changing
+ # parameters we will only re-enable the interface if it is not
+ # administratively disabled
+ if not geneve['disable']:
+ v.set_state('up')
+
+ return None
+
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)