Page MenuHomeVyOS Platform

tunnel.py
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

tunnel.py

# 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
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
# 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.validate import assert_list
def enable_to_on(value):
if value == 'enable':
return 'on'
if value == 'disable':
return 'off'
raise ValueError(f'expect enable or disable but got "{value}"')
@Interface.register
class _Tunnel(Interface):
"""
_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
"""
definition = {
**Interface.definition,
**{
'section': 'tunnel',
'prefixes': ['tun',],
},
}
default = {
'local' : '',
'remote': '',
'dev' : '',
'ttl' : '',
'tos' : '',
'key' : '',
'raw' : '',
}
options = Interface.options + list(default.keys())
# 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}'
def _create(self):
# add " option-name option-name-value ..." for all options set
options = ' '.join(['{} {}'.format(k, self.config[k])
for k,v in self.config.items() if v and k not in
['ifname', 'type', 'raw']])
if 'raw' in self.config:
options += ' ' + ' '.join(self.config['raw'])
self._cmd('{} {}'.format(self._create_cmd.format(**self.config), options))
self.set_admin_state('down')
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,v in self.config.items() if v and k not in
['ifname', 'type', 'raw']])
if 'raw' in self.config:
options += ' ' + ' '.join(self.config['raw'])
self._cmd('{} {}'.format(change.format(**self.config), 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):
"""
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 = {
**_Tunnel.default,
**{
'type': 'gre',
'raw' : ['pmtudisc'], # parameters that we can pass raw to ip command
},
}
# 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
definition = {
**_Tunnel.definition,
**{
'bridgeable': True,
},
}
default = {
'type': 'gretap',
'local': '',
'remote': '',
'dev': '',
'raw' : ['pmtudisc'], # parameters that we can pass raw to ip command
}
_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 = {
**_Tunnel.default,
**{
'type': 'ip6gre',
'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 = {
**_Tunnel.default,
**{
'type': 'ipip',
'raw' : ['pmtudisc'], # parameters that we can pass raw to ip command
},
}
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 = {
**_Tunnel.default,
**{
'type': 'ipip6',
'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 = {
**_Tunnel.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 = {
**_Tunnel.default,
**{
'type': 'sit',
},
}
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
default = {
**_Tunnel.default,
**{
'type': '6rd',
'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')
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))

File Metadata

Mime Type
text/x-script.python
Expires
Sat, Oct 17, 12:24 AM (6 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4404138
Default Alt Text
tunnel.py (9 KB)

Event Timeline