diff options
Diffstat (limited to 'python/vyos/ifconfig/tunnel.py')
-rw-r--r-- | python/vyos/ifconfig/tunnel.py | 194 |
1 files changed, 55 insertions, 139 deletions
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py index 4122d1a2f..4d1441a29 100644 --- a/python/vyos/ifconfig/tunnel.py +++ b/python/vyos/ifconfig/tunnel.py @@ -1,4 +1,4 @@ -# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-2020 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 @@ -17,9 +17,11 @@ # 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.ifconfig.afi import IP4, IP6 from vyos.validate import assert_list def enable_to_on(value): @@ -61,68 +63,73 @@ class _Tunnel(Interface): }, }} - # use for "options" and "updates" - # If an key is only in the options list, it can only be set at creation time - # the create comand will only be make using the key in options - - # If an option is in the updates list, it can be updated - # upon, the creation, all key not yet applied will be updated - - # multicast/allmulticast can not be part of the create command - - # options matrix: - # with ip = 4, we have multicast - # wiht ip = 6, nothing - # with tunnel = 4, we have tos, ttl, key - # with tunnel = 6, we have encaplimit, hoplimit, tclass, flowlabel - - # TODO: For multicast, it is allowed on IP6IP6 and Sit6RD - # TODO: to match vyatta but it should be checked for correctness - - updates = [] - - create = '' - change = '' - delete = '' - - ip = [] # AFI of the families which can be used in the tunnel - tunnel = 0 # invalid - need to be set by subclasses - def __init__(self, ifname, **config): self.config = deepcopy(config) if config else {} super().__init__(ifname, **config) def _create(self): + create = 'ip tunnel add {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(self.create.format(**self.config), options)) + self._cmd('{} {}'.format(create.format(**self.config), options)) self.set_admin_state('down') - def _delete(self): - self.set_admin_state('down') - cmd = self.delete.format(**self.config) - return self._cmd(cmd) - - def set_interface(self, option, value): - try: - return Interface.set_interface(self, option, value) - except Exception: - pass - - if value == '': - # remove the value so that it is not used - self.config.pop(option, '') + def change_options(self): + change = 'ip tunnel cha {ifname} mode {type}' - if self.change: - self._cmd('{} {} {}'.format( - self.change.format(**self.config), option, value)) - return True + # 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)) @classmethod def get_config(cls): return dict(zip(cls.options, ['']*len(cls.options))) + 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() + '00:50:ab:cd:ef:00' + """ + # we choose 40 random bytes for the MAC address, this gives + # us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A') + tmp = EUI(getrandbits(48)).value + # set locally administered bit in MAC address + tmp |= 0xf20000000000 + # convert integer to "real" MAC address representation + mac = EUI(hex(tmp).split('x')[-1]) + # change dialect to use : as delimiter instead of - + mac.dialect = mac_unix_expanded + return str(mac) + + 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. """ + + # call base class first + super().update(config) + + # Enable/Disable of an interface must always be done at the end of the + # derived class to make use of the ref-counting set_admin_state() + # function. We will only enable the interface if 'up' was called as + # often as 'down'. This is required by some interface implementations + # as certain parameters can only be changed when the interface is + # in admin-down state. This ensures the link does not flap during + # reconfiguration. + state = 'down' if 'disable' in config else 'up' + self.set_admin_state(state) class GREIf(_Tunnel): """ @@ -141,20 +148,8 @@ class GREIf(_Tunnel): }, } - ip = [IP4, IP6] - tunnel = IP4 - default = {'type': 'gre'} - required = ['local', ] # mGRE is a GRE without remote endpoint - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - # GreTap also called GRE Bridge class GRETapIf(_Tunnel): @@ -173,19 +168,8 @@ class GRETapIf(_Tunnel): }, } - ip = [IP4, ] - tunnel = IP4 - default = {'type': 'gretap'} - required = ['local', ] - options = ['local', 'remote', 'ttl',] - updates = ['mtu', ] - - create = 'ip link add {ifname} type {type}' - change = '' - delete = 'ip link del {ifname}' - class IP6GREIf(_Tunnel): """ @@ -196,30 +180,9 @@ class IP6GREIf(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c """ - ip = [IP4, IP6] - tunnel = IP6 - default = {'type': 'ip6gre'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'encaplimit', 'hoplimit', 'tclass', 'flowlabel'] - updates = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname} mode {type}' - delete = 'ip tunnel del {ifname}' - - # using "ip tunnel change" without using "mode" causes errors - # sudo ip tunnel add tun100 mode ip6gre local ::1 remote 1::1 - # sudo ip tunnel cha tun100 hoplimit 100 - # *** stack smashing detected ** *: < unknown > terminated - # sudo ip tunnel cha tun100 local: : 2 - # Error: an IP address is expected rather than "::2" - # works if mode is explicit - class IPIPIf(_Tunnel): """ @@ -232,20 +195,8 @@ class IPIPIf(_Tunnel): # IPIP does not allow to pass multicast, unlike GRE # but the interface itself can be set with multicast - ip = [IP4,] - tunnel = IP4 - default = {'type': 'ipip'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - class IPIP6If(_Tunnel): """ @@ -255,22 +206,9 @@ class IPIP6If(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c """ - ip = [IP4,] - tunnel = IP6 - default = {'type': 'ipip6'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'encaplimit', 'hoplimit', 'tclass', 'flowlabel'] - updates = ['local', 'remote', 'dev', 'encaplimit', - 'hoplimit', 'tclass', 'flowlabel', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip -6 tunnel add {ifname} mode {type}' - change = 'ip -6 tunnel cha {ifname}' - delete = 'ip -6 tunnel del {ifname}' - class IP6IP6If(IPIP6If): """ @@ -279,9 +217,6 @@ class IP6IP6If(IPIP6If): For more information please refer to: https://tools.ietf.org/html/rfc2473 """ - - ip = [IP6,] - default = {'type': 'ip6ip6'} @@ -293,20 +228,8 @@ class SitIf(_Tunnel): https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c """ - ip = [IP6, IP4] - tunnel = IP4 - default = {'type': 'sit'} - required = ['local', 'remote'] - options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key'] - updates = ['local', 'remote', 'dev', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] - - create = 'ip tunnel add {ifname} mode {type}' - change = 'ip tunnel cha {ifname}' - delete = 'ip tunnel del {ifname}' - class Sit6RDIf(SitIf): """ @@ -314,15 +237,8 @@ class Sit6RDIf(SitIf): https://en.wikipedia.org/wiki/IPv6_rapid_deployment """ - - ip = [IP6,] - - required = ['remote', '6rd-prefix'] - # TODO: check if key can really be used with 6RD options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix'] - updates = ['remote', 'ttl', 'tos', - 'mtu', 'multicast', 'allmulticast'] def _create(self): # do not call _Tunnel.create, building fully here |