summaryrefslogtreecommitdiff
path: root/python/vyos/ifconfig/tunnel.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/vyos/ifconfig/tunnel.py')
-rw-r--r--python/vyos/ifconfig/tunnel.py269
1 files changed, 120 insertions, 149 deletions
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 6f9d7abbf..e40756cc7 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -1,4 +1,4 @@
-# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2019-2021 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
@@ -16,13 +16,12 @@
# https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/
# https://community.hetzner.com/tutorials/linux-setup-gre-tunnel
-from copy import deepcopy
-
from netaddr import EUI
from netaddr import mac_unix_expanded
from random import getrandbits
from vyos.ifconfig.interface import Interface
+from vyos.util import dict_search
from vyos.validate import assert_list
def enable_to_on(value):
@@ -32,11 +31,10 @@ def enable_to_on(value):
return 'off'
raise ValueError(f'expect enable or disable but got "{value}"')
-
@Interface.register
-class _Tunnel(Interface):
+class TunnelIf(Interface):
"""
- _Tunnel: private base class for tunnels
+ Tunnel: private base class for tunnels
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/tunnel.c
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip6tunnel.c
"""
@@ -49,54 +47,127 @@ class _Tunnel(Interface):
},
}
+ # This table represents a mapping from VyOS internal config dict to
+ # arguments used by iproute2. For more information please refer to:
+ # - https://man7.org/linux/man-pages/man8/ip-link.8.html
+ # - https://man7.org/linux/man-pages/man8/ip-tunnel.8.html
+ mapping = {
+ 'source_address' : 'local',
+ 'source_interface' : 'dev',
+ 'remote' : 'remote',
+ 'parameters.ip.key' : 'key',
+ 'parameters.ip.tos' : 'tos',
+ 'parameters.ip.ttl' : 'ttl',
+ }
+ mapping_ipv4 = {
+ 'parameters.ip.key' : 'key',
+ 'parameters.ip.no_pmtu_discovery' : 'nopmtudisc',
+ 'parameters.ip.ignore_df' : 'ignore-df',
+ 'parameters.ip.tos' : 'tos',
+ 'parameters.ip.ttl' : 'ttl',
+ 'parameters.erspan.direction' : 'erspan_dir',
+ 'parameters.erspan.hw_id' : 'erspan_hwid',
+ 'parameters.erspan.index' : 'erspan',
+ 'parameters.erspan.version' : 'erspan_ver',
+ }
+ mapping_ipv6 = {
+ 'parameters.ipv6.encaplimit' : 'encaplimit',
+ 'parameters.ipv6.flowlabel' : 'flowlabel',
+ 'parameters.ipv6.hoplimit' : 'hoplimit',
+ 'parameters.ipv6.tclass' : 'tclass',
+ }
+
# TODO: This is surely used for more than tunnels
# TODO: could be refactored elsewhere
- _command_set = {**Interface._command_set, **{
- 'multicast': {
- 'validate': lambda v: assert_list(v, ['enable', 'disable']),
- 'convert': enable_to_on,
- 'shellcmd': 'ip link set dev {ifname} multicast {value}',
- },
- 'allmulticast': {
- 'validate': lambda v: assert_list(v, ['enable', 'disable']),
- 'convert': enable_to_on,
- 'shellcmd': 'ip link set dev {ifname} allmulticast {value}',
- },
- }}
-
- _create_cmd = 'ip tunnel add {ifname} mode {type}'
+ _command_set = {
+ **Interface._command_set,
+ **{
+ 'multicast': {
+ 'validate': lambda v: assert_list(v, ['enable', 'disable']),
+ 'convert': enable_to_on,
+ 'shellcmd': 'ip link set dev {ifname} multicast {value}',
+ },
+ 'allmulticast': {
+ 'validate': lambda v: assert_list(v, ['enable', 'disable']),
+ 'convert': enable_to_on,
+ 'shellcmd': 'ip link set dev {ifname} allmulticast {value}',
+ },
+ }
+ }
- def __init__(self, ifname, **config):
- self.config = deepcopy(config) if config else {}
- super().__init__(ifname, **config)
+ def __init__(self, ifname, **kargs):
+ # T3357: we do not have the 'encapsulation' in kargs when calling this
+ # class from op-mode like "show interfaces tunnel"
+ if 'encapsulation' in kargs:
+ self.iftype = kargs['encapsulation']
+ # The gretap interface has the possibility to act as L2 bridge
+ if self.iftype in ['gretap', 'ip6gretap']:
+ # no multicast, ttl or tos for gretap
+ self.definition = {
+ **TunnelIf.definition,
+ **{
+ 'bridgeable': True,
+ },
+ }
+
+ super().__init__(ifname, **kargs)
def _create(self):
- # add " option-name option-name-value ..." for all options set
- options = " ".join(["{} {}".format(k, self.config[k])
- for k in self.options if k in self.config and self.config[k]])
- self._cmd('{} {}'.format(self._create_cmd.format(**self.config), options))
- self.set_admin_state('down')
+ if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
+ mapping = { **self.mapping, **self.mapping_ipv6 }
+ else:
+ mapping = { **self.mapping, **self.mapping_ipv4 }
+
+ cmd = 'ip tunnel add {ifname} mode {encapsulation}'
+ if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
+ cmd = 'ip link add name {ifname} type {encapsulation}'
+ # ERSPAN requires the serialisation of packets
+ if self.iftype in ['erspan', 'ip6erspan']:
+ cmd += ' seq'
+
+ for vyos_key, iproute2_key in mapping.items():
+ # dict_search will return an empty dict "{}" for valueless nodes like
+ # "parameters.nolearning" - thus we need to test the nodes existence
+ # by using isinstance()
+ tmp = dict_search(vyos_key, self.config)
+ if isinstance(tmp, dict):
+ cmd += f' {iproute2_key}'
+ elif tmp != None:
+ cmd += f' {iproute2_key} {tmp}'
+
+ self._cmd(cmd.format(**self.config))
- def change_options(self):
- change = 'ip tunnel change {ifname} mode {type}'
-
- # add " option-name option-name-value ..." for all options set
- options = " ".join(["{} {}".format(k, self.config[k])
- for k in self.options if k in self.config and self.config[k]])
- self._cmd('{} {}'.format(change.format(**self.config), options))
+ self.set_admin_state('down')
- @classmethod
- def get_config(cls):
- return dict(zip(cls.options, ['']*len(cls.options)))
+ def _change_options(self):
+ # gretap interfaces do not support changing any parameter
+ if self.iftype in ['gretap', 'ip6gretap', 'erspan', 'ip6erspan']:
+ return
+
+ if self.config['encapsulation'] in ['ipip6', 'ip6ip6', 'ip6gre']:
+ mapping = { **self.mapping, **self.mapping_ipv6 }
+ else:
+ mapping = { **self.mapping, **self.mapping_ipv4 }
+
+ cmd = 'ip tunnel change {ifname} mode {encapsulation}'
+ for vyos_key, iproute2_key in mapping.items():
+ # dict_search will return an empty dict "{}" for valueless nodes like
+ # "parameters.nolearning" - thus we need to test the nodes existence
+ # by using isinstance()
+ tmp = dict_search(vyos_key, self.config)
+ if isinstance(tmp, dict):
+ cmd += f' {iproute2_key}'
+ elif tmp != None:
+ cmd += f' {iproute2_key} {tmp}'
+
+ self._cmd(cmd.format(**self.config))
def get_mac(self):
"""
Get current interface MAC (Media Access Contrl) address used.
-
NOTE: Tunnel interfaces have no "MAC" address by default. The content
of the 'address' file in /sys/class/net/device contains the
local-ip thus we generate a random MAC address instead
-
Example:
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').get_mac()
@@ -113,113 +184,13 @@ class _Tunnel(Interface):
mac.dialect = mac_unix_expanded
return str(mac)
-class GREIf(_Tunnel):
- """
- GRE: Generic Routing Encapsulation
-
- For more information please refer to:
- RFC1701, RFC1702, RFC2784
- https://tools.ietf.org/html/rfc2784
- https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre.c
- """
-
- default = {'type': 'gre'}
- options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
-
-# GreTap also called GRE Bridge
-class GRETapIf(_Tunnel):
- """
- GRETapIF: GreIF using TAP instead of TUN
-
- https://en.wikipedia.org/wiki/TUN/TAP
- """
-
- # no multicast, ttl or tos for gretap
- default = {'type': 'gretap'}
- options = ['local', 'remote', 'ttl', 'tos', 'key']
-
- _create_cmd = 'ip link add name {ifname} type {type}'
-
- def change_options(self):
- pass
-
-class IP6GREIf(_Tunnel):
- """
- IP6Gre: IPv6 Support for Generic Routing Encapsulation (GRE)
-
- For more information please refer to:
- https://tools.ietf.org/html/rfc7676
- https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c
- """
-
- default = {'type': 'ip6gre'}
- options = ['local', 'remote', 'dev', 'encaplimit',
- 'hoplimit', 'tclass', 'flowlabel']
-
-class IPIPIf(_Tunnel):
- """
- IPIP: IP Encapsulation within IP
-
- For more information please refer to:
- https://tools.ietf.org/html/rfc2003
- """
-
- # IPIP does not allow to pass multicast, unlike GRE
- # but the interface itself can be set with multicast
-
- default = {'type': 'ipip'}
- options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
-
-class IPIP6If(_Tunnel):
- """
- IPIP6: IPv4 over IPv6 tunnel
-
- For more information please refer to:
- https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c
- """
-
- default = {'type': 'ipip6'}
- options = ['local', 'remote', 'dev', 'encaplimit',
- 'hoplimit', 'tclass', 'flowlabel']
-
-class IP6IP6If(IPIP6If):
- """
- IP6IP6: IPv6 over IPv6 tunnel
-
- For more information please refer to:
- https://tools.ietf.org/html/rfc2473
- """
- default = {'type': 'ip6ip6'}
-
-
-class SitIf(_Tunnel):
- """
- Sit: Simple Internet Transition
-
- For more information please refer to:
- https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c
- """
-
- default = {'type': 'sit'}
- options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
-
-class Sit6RDIf(SitIf):
- """
- Sit6RDIf: Simple Internet Transition with 6RD
-
- https://en.wikipedia.org/wiki/IPv6_rapid_deployment
- """
- # TODO: check if key can really be used with 6RD
- options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix']
-
- def _create(self):
- # do not call _Tunnel.create, building fully here
-
- create = 'ip tunnel add {ifname} mode {type} remote {remote}'
- self._cmd(create.format(**self.config))
- self.set_interface('state','down')
+ def update(self, config):
+ """ General helper function which works on a dictionary retrived by
+ get_config_dict(). It's main intention is to consolidate the scattered
+ interface setup code and provide a single point of entry when workin
+ on any interface. """
+ # Adjust iproute2 tunnel parameters if necessary
+ self._change_options()
- set6rd = 'ip tunnel 6rd dev {ifname} 6rd-prefix {6rd-prefix}'
- if '6rd-relay-prefix' in self.config:
- set6rd += ' 6rd-relay-prefix {6rd-relay-prefix}'
- self._cmd(set6rd.format(**self.config))
+ # call base class first
+ super().update(config)