From c078dac303d1e427f9612f7ff1996800f5076b47 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 24 Nov 2019 10:40:56 +0100 Subject: geneve: T1799: add Generic Network Virtualization Encapsulation --- interface-definitions/interfaces-geneve.xml | 92 +++++++++++++++++ python/vyos/ifconfig.py | 39 +++++++- src/conf_mode/interfaces-geneve.py | 150 ++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 interface-definitions/interfaces-geneve.xml create mode 100755 src/conf_mode/interfaces-geneve.py 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 @@ + + + + + + + Generic Network Virtualization Encapsulation (GENEVE) + 460 + + gnv[0-9]+$ + + GENEVE interface must be named gnvN + + gnvN + GENEVE interface name + + + + + + IP address + + ipv4net + IPv4 address and prefix length + + + ipv6net + IPv6 address and prefix length + + + + + + + + + + Interface description + + ^.{1,256}$ + + Interface description too long (limit 256 characters) + + + + + Disable interface + + + + + + Maximum Transmission Unit (MTU) + + 1450-9000 + Maximum Transmission Unit + + + + + MTU must be between 1450 and 9000 + + + + + Remote address of GENEVE tunnel + + ipv4 + Remote address of GENEVE tunnel + + + + + + + + + Virtual Network Identifier + + 0-16777214 + GENEVE virtual network identifier + + + + + + + + + + + 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 . + +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) -- cgit v1.2.3 From 9fc971e6945ed97a88a441053cae6a5f9b57ee00 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 24 Nov 2019 10:41:15 +0100 Subject: geneve: T1799: support bridging --- data/interface-types.json | 3 ++- src/completion/list_interfaces.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/data/interface-types.json b/data/interface-types.json index f1862b882..f174d3c39 100644 --- a/data/interface-types.json +++ b/data/interface-types.json @@ -14,5 +14,6 @@ "wireless": "wlan", "wirelessmodem": "wlm", "input": "ifb", - "pppoe": "pppoe" + "pppoe": "pppoe", + "geneve": "gnv" } diff --git a/src/completion/list_interfaces.py b/src/completion/list_interfaces.py index 47eeaf00c..f336968a6 100755 --- a/src/completion/list_interfaces.py +++ b/src/completion/list_interfaces.py @@ -36,8 +36,9 @@ elif args.bridgeable: wireless = vyos.interfaces.list_interfaces_of_type("wireless") tunnel = vyos.interfaces.list_interfaces_of_type("tunnel") wireless = vyos.interfaces.list_interfaces_of_type("wireless") + geneve = vyos.interfaces.list_interfaces_of_type("geneve") - interfaces = eth + bond + l2tpv3 + openvpn + vxlan + wireless + tunnel + interfaces = eth + bond + l2tpv3 + openvpn + vxlan + tunnel + wireless + geneve elif args.bondable: eth = vyos.interfaces.list_interfaces_of_type("ethernet") -- cgit v1.2.3 From 762e0922eec583c011c5fb834ad8a22971bc96b5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 24 Nov 2019 10:51:54 +0100 Subject: geneve: T1799: add IPv4 routing parameters --- interface-definitions/interfaces-geneve.xml | 26 ++++++++++++++++++++++++++ src/conf_mode/interfaces-geneve.py | 15 +++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/interface-definitions/interfaces-geneve.xml b/interface-definitions/interfaces-geneve.xml index 3a529c363..4d3897330 100644 --- a/interface-definitions/interfaces-geneve.xml +++ b/interface-definitions/interfaces-geneve.xml @@ -48,6 +48,32 @@ + + + IPv4 routing parameters + + + + + ARP cache entry timeout in seconds + + 1-86400 + ARP cache entry timout in seconds (default 30) + + + + + ARP cache entry timeout must be between 1 and 86400 seconds + + + + + Enable proxy-arp on this interface + + + + + Maximum Transmission Unit (MTU) diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py index 94f88ad6d..b11ef3594 100755 --- a/src/conf_mode/interfaces-geneve.py +++ b/src/conf_mode/interfaces-geneve.py @@ -32,6 +32,8 @@ default_config_data = { 'description': '', 'disable': False, 'intf': '', + 'ip_arp_cache_tmo': 30, + 'ip_proxy_arp': 0, 'mtu': 1450, 'remote': '' } @@ -66,6 +68,14 @@ def get_config(): if conf.exists('disable'): geneve['disable'] = True + # ARP cache entry timeout in seconds + if conf.exists('ip arp-cache-timeout'): + geneve['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) + + # Enable proxy-arp on this interface + if conf.exists('ip enable-proxy-arp'): + geneve['ip_proxy_arp'] = 1 + # Maximum Transmission Unit (MTU) if conf.exists('mtu'): geneve['mtu'] = int(conf.return_value('mtu')) @@ -124,6 +134,11 @@ def apply(geneve): # Maximum Transfer Unit (MTU) v.set_mtu(geneve['mtu']) + # configure ARP cache timeout in milliseconds + v.set_arp_cache_tmo(geneve['ip_arp_cache_tmo']) + # Enable proxy-arp on this interface + v.set_proxy_arp(geneve['ip_proxy_arp']) + # Configure interface address(es) - no need to implicitly delete the # old addresses as they have already been removed by deleting the # interface above -- cgit v1.2.3