diff options
Diffstat (limited to 'python/vyos')
-rw-r--r-- | python/vyos/ifconfig/bridge.py | 12 | ||||
-rw-r--r-- | python/vyos/ifconfig/interface.py | 18 | ||||
-rw-r--r-- | python/vyos/ifconfig/tunnel.py | 194 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireguard.py | 29 | ||||
-rw-r--r-- | python/vyos/ifconfig/wireless.py | 29 |
5 files changed, 113 insertions, 169 deletions
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index 7c77e050a..e6cda4adb 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -309,15 +309,12 @@ class BridgeIf(Interface): vlan_filter = 1 cmd = f'bridge vlan del dev {interface} vid 1' self._cmd(cmd) - vlan_del.add(1) vlan_id = interface_config['native_vlan'] + if vlan_id != 1: + vlan_del.add(1) cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master' self._cmd(cmd) vlan_add.add(vlan_id) - else: - cmd = f'bridge vlan del dev {interface} vid 1' - self._cmd(cmd) - vlan_del.add(1) if 'allowed_vlan' in interface_config: vlan_filter = 1 @@ -325,6 +322,11 @@ class BridgeIf(Interface): cmd = f'bridge vlan add dev {interface} vid {vlan} master' self._cmd(cmd) vlan_add.add(vlan) + + if vlan_filter: + if 'native_vlan' not in interface_config: + cmd = f'bridge vlan del dev {interface} vid 1' + self._cmd(cmd) for vlan in vlan_del: diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index 893623284..39b80ce08 100644 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -942,6 +942,15 @@ class Interface(Control): # method to apply()? self._config = config + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed. Skip if bond member. + if 'is_bond_member' not in config: + mac = config.get('hw_id') + if 'mac' in config: + mac = config.get('mac') + if mac: + self.set_mac(mac) + # Update interface description self.set_alias(config.get('description', '')) @@ -1058,15 +1067,6 @@ class Interface(Control): for addr in tmp: self.del_ipv6_eui64_address(addr) - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed. Skip if bond member. - if 'is_bond_member' not in config: - mac = config.get('hw_id') - if 'mac' in config: - mac = config.get('mac') - if mac: - self.set_mac(mac) - # Manage IPv6 link-local addresses tmp = dict_search('ipv6.address.no_default_link_local', config) # we must check explicitly for None type as if the key is set we will 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 diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py index da3bd4e89..ac6dc2109 100644 --- a/python/vyos/ifconfig/wireguard.py +++ b/python/vyos/ifconfig/wireguard.py @@ -17,6 +17,9 @@ import os import time from datetime import timedelta +from netaddr import EUI +from netaddr import mac_unix_expanded +from random import getrandbits from hurry.filesize import size from hurry.filesize import alternative @@ -169,6 +172,30 @@ class WireGuardIf(Interface): ['port', 'private_key', 'pubkey', 'psk', 'allowed_ips', 'fwmark', 'endpoint', 'keepalive'] + 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 @@ -221,7 +248,7 @@ class WireGuardIf(Interface): # Endpoint configuration is optional if {'address', 'port'} <= set(peer): - if is_ipv6(config['address']): + if is_ipv6(peer['address']): cmd += ' endpoint [{address}]:{port}' else: cmd += ' endpoint {address}:{port}' diff --git a/python/vyos/ifconfig/wireless.py b/python/vyos/ifconfig/wireless.py index deca68bf0..37703d242 100644 --- a/python/vyos/ifconfig/wireless.py +++ b/python/vyos/ifconfig/wireless.py @@ -23,10 +23,8 @@ class WiFiIf(Interface): default = { 'type': 'wifi', - 'phy': '', - 'wds': 'off', + 'phy': 'phy0' } - definition = { **Interface.definition, **{ @@ -35,19 +33,12 @@ class WiFiIf(Interface): 'bridgeable': True, } } - options = Interface.options + \ ['phy', 'op_mode'] - _command_set = {**Interface._command_set, **{ - '4addr': { - 'shellcmd': 'iw dev {ifname} set 4addr {value}', - }, - }} - def _create(self): # all interfaces will be added in monitor mode - cmd = 'iw phy {phy} interface add {ifname} type monitor 4addr {wds}' \ + cmd = 'iw phy {phy} interface add {ifname} type monitor' \ .format(**self.config) self._cmd(cmd) @@ -59,20 +50,28 @@ class WiFiIf(Interface): .format(**self.config) self._cmd(cmd) - def set_4aadr_mode(self, state): - return self.set_interface('4addr', state) - 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. """ - self.set_4aadr_mode('on' if 'wds' in config else 'off') + # We can not call add_to_bridge() until wpa_supplicant is running, thus + # we will remove the key from the config dict and react to this specal + # case in thie derived class. + # re-add ourselves to any bridge we might have fallen out of + bridge_member = '' + if 'is_bridge_member' in config: + bridge_member = config['is_bridge_member'] + del config['is_bridge_member'] # call base class first super().update(config) + # re-add ourselves to any bridge we might have fallen out of + if bridge_member: + self.add_to_bridge(bridge_member) + # 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 |