summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-10-17 22:55:02 +0200
committerChristian Poessinger <christian@poessinger.com>2020-10-17 22:55:02 +0200
commit183130bcfa3c6bf5dbf73c38c1609c7b6bf21d3f (patch)
treec86c0fb198a3d560e6f6749cd803563f92165362
parent60109764cc18ae50802313716ce9197c9bd36e15 (diff)
parentb90041af38c1d872210d3720c258721247d313a5 (diff)
downloadvyos-1x-183130bcfa3c6bf5dbf73c38c1609c7b6bf21d3f.tar.gz
vyos-1x-183130bcfa3c6bf5dbf73c38c1609c7b6bf21d3f.zip
Merge branch 'bridge' of github.com:c-po/vyos-1x into current
* 'bridge' of github.com:c-po/vyos-1x: smoketest: add IPv6 option tests to BasicInterfaceTest ifconfig: T2985: support on demand bridge creation geneve: T1799: add IPv6 CLI options op-mode: add "show arp" command
-rw-r--r--interface-definitions/interfaces-bridge.xml.in2
-rw-r--r--interface-definitions/interfaces-geneve.xml.in6
-rw-r--r--op-mode-definitions/show-protocols-static.xml17
-rw-r--r--python/vyos/configdict.py22
-rw-r--r--python/vyos/ifconfig/bridge.py28
-rw-r--r--python/vyos/ifconfig/interface.py60
-rw-r--r--python/vyos/ifconfig/stp.py70
-rw-r--r--smoketest/scripts/cli/base_interfaces_test.py26
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py6
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py23
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py3
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py3
12 files changed, 141 insertions, 125 deletions
diff --git a/interface-definitions/interfaces-bridge.xml.in b/interface-definitions/interfaces-bridge.xml.in
index 787e856d7..0a777865b 100644
--- a/interface-definitions/interfaces-bridge.xml.in
+++ b/interface-definitions/interfaces-bridge.xml.in
@@ -5,7 +5,7 @@
<tagNode name="bridge" owner="${vyos_conf_scripts_dir}/interfaces-bridge.py">
<properties>
<help>Bridge Interface</help>
- <priority>489</priority>
+ <priority>310</priority>
<constraint>
<regex>^br[0-9]+$</regex>
</constraint>
diff --git a/interface-definitions/interfaces-geneve.xml.in b/interface-definitions/interfaces-geneve.xml.in
index 28df42220..320dfd64d 100644
--- a/interface-definitions/interfaces-geneve.xml.in
+++ b/interface-definitions/interfaces-geneve.xml.in
@@ -28,6 +28,12 @@
#include <include/interface-enable-proxy-arp.xml.i>
</children>
</node>
+ <node name="ipv6">
+ <children>
+ #include <include/ipv6-disable-forwarding.xml.i>
+ #include <include/ipv6-dup-addr-detect-transmits.xml.i>
+ </children>
+ </node>
#include <include/interface-mac.xml.i>
#include <include/interface-mtu-1450-9000.xml.i>
<leafNode name="remote">
diff --git a/op-mode-definitions/show-protocols-static.xml b/op-mode-definitions/show-protocols-static.xml
index 1211a7fe5..2693c4248 100644
--- a/op-mode-definitions/show-protocols-static.xml
+++ b/op-mode-definitions/show-protocols-static.xml
@@ -2,6 +2,23 @@
<interfaceDefinition>
<node name="show">
<children>
+ <node name="arp">
+ <properties>
+ <help>Show Address Resolution Protocol (ARP) information</help>
+ </properties>
+ <command>/usr/sbin/arp -e -n</command>
+ <children>
+ <tagNode name="interface">
+ <properties>
+ <help>Show Address Resolution Protocol (ARP) cache for specified interface</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py -b</script>
+ </completionHelp>
+ </properties>
+ <command>/usr/sbin/arp -e -n -i "$6"</command>
+ </tagNode>
+ </children>
+ </node>
<node name="protocols">
<children>
<node name="static">
diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py
index 02006465c..62df3334c 100644
--- a/python/vyos/configdict.py
+++ b/python/vyos/configdict.py
@@ -194,11 +194,9 @@ def is_member(conf, interface, intftype=None):
interface name -> Interface is a member of this interface
False -> interface type cannot have members
"""
- from vyos.xml import is_tag
- from vyos.xml import is_leaf
-
ret_val = None
intftypes = ['bonding', 'bridge']
+
if intftype not in intftypes + [None]:
raise ValueError((
f'unknown interface type "{intftype}" or it cannot '
@@ -210,19 +208,13 @@ def is_member(conf, interface, intftype=None):
old_level = conf.get_level()
conf.set_level([])
- for it in intftype:
- base = ['interfaces', it]
+ for iftype in intftype:
+ base = ['interfaces', iftype]
for intf in conf.list_nodes(base):
- memberintf = base + [intf, 'member', 'interface']
- if is_tag(memberintf):
- if interface in conf.list_nodes(memberintf):
- ret_val = intf
- break
- elif is_leaf(memberintf):
- if ( conf.exists(memberintf) and
- interface in conf.return_values(memberintf) ):
- ret_val = intf
- break
+ member = base + [intf, 'member', 'interface', interface]
+ if conf.exists(member):
+ tmp = conf.get_config_dict(member, key_mangling=('-', '_'), get_first_key=True)
+ ret_val = {intf : tmp}
old_level = conf.set_level(old_level)
return ret_val
diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py
index c133a56fc..7867a7387 100644
--- a/python/vyos/ifconfig/bridge.py
+++ b/python/vyos/ifconfig/bridge.py
@@ -234,25 +234,33 @@ class BridgeIf(Interface):
if member in interfaces():
self.del_port(member)
- STPBridgeIf = STP.enable(BridgeIf)
tmp = vyos_dict_search('member.interface', config)
if tmp:
for interface, interface_config in tmp.items():
- # if we've come here we already verified the interface
- # does not have an addresses configured so just flush
- # any remaining ones
- Interface(interface).flush_addrs()
+ # if interface does yet not exist bail out early and
+ # add it later
+ if interface not in interfaces():
+ continue
+
+ # Bridge lower "physical" interface
+ lower = Interface(interface)
+
+ # If we've come that far we already verified the interface does
+ # not have any addresses configured by CLI so just flush any
+ # remaining ones
+ lower.flush_addrs()
# enslave interface port to bridge
self.add_port(interface)
- tmp = STPBridgeIf(interface)
# set bridge port path cost
- value = interface_config.get('cost')
- tmp.set_path_cost(value)
+ if 'cost' in interface_config:
+ value = interface_config.get('cost')
+ lower.set_path_cost(value)
# set bridge port path priority
- value = interface_config.get('priority')
- tmp.set_path_priority(value)
+ if 'priority' in interface_config:
+ value = interface_config.get('priority')
+ lower.set_path_priority(value)
# 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()
diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py
index 47ec94bd3..c1f9d14af 100644
--- a/python/vyos/ifconfig/interface.py
+++ b/python/vyos/ifconfig/interface.py
@@ -46,6 +46,7 @@ from vyos.validate import assert_positive
from vyos.validate import assert_range
from vyos.ifconfig.control import Control
+from vyos.ifconfig.stp import STP
from vyos.ifconfig.vrrp import VRRP
from vyos.ifconfig.operational import Operational
from vyos.ifconfig import Section
@@ -167,6 +168,18 @@ class Interface(Control):
'validate': assert_positive,
'location': '/proc/sys/net/ipv6/conf/{ifname}/dad_transmits',
},
+ 'path_cost': {
+ # XXX: we should set a maximum
+ 'validate': assert_positive,
+ 'location': '/sys/class/net/{ifname}/brport/path_cost',
+ 'errormsg': '{ifname} is not a bridge port member'
+ },
+ 'path_priority': {
+ # XXX: we should set a maximum
+ 'validate': assert_positive,
+ 'location': '/sys/class/net/{ifname}/brport/priority',
+ 'errormsg': '{ifname} is not a bridge port member'
+ },
'proxy_arp': {
'validate': assert_boolean,
'location': '/proc/sys/net/ipv4/conf/{ifname}/proxy_arp',
@@ -628,6 +641,28 @@ class Interface(Control):
self._admin_state_down_cnt += 1
return self.set_interface('admin_state', state)
+ def set_path_cost(self, cost):
+ """
+ Set interface path cost, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_cost(4)
+ """
+ self.set_interface('path_cost', cost)
+
+ def set_path_priority(self, priority):
+ """
+ Set interface path priority, only relevant for STP enabled interfaces
+
+ Example:
+
+ >>> from vyos.ifconfig import Interface
+ >>> Interface('eth0').set_path_priority(4)
+ """
+ self.set_interface('path_priority', priority)
+
def set_proxy_arp(self, enable):
"""
Set per interface proxy ARP configuration
@@ -809,24 +844,27 @@ class Interface(Control):
# flush all addresses
self._cmd(f'ip addr flush dev "{self.ifname}"')
- def add_to_bridge(self, br):
+ def add_to_bridge(self, bridge_dict):
"""
Adds the interface to the bridge with the passed port config.
Returns False if bridge doesn't exist.
"""
- # check if the bridge exists (on boot it doesn't)
- if br not in Section.interfaces('bridge'):
- return False
-
+ # drop all interface addresses first
self.flush_addrs()
- # add interface to bridge - use Section.klass to get BridgeIf class
- Section.klass(br)(br, create=False).add_port(self.ifname)
- # TODO: port config (STP)
+ for bridge, bridge_config in bridge_dict.items():
+ # add interface to bridge - use Section.klass to get BridgeIf class
+ Section.klass(bridge)(bridge, create=True).add_port(self.ifname)
- return True
+ # set bridge port path cost
+ if 'cost' in bridge_config:
+ self.set_path_cost(bridge_config['cost'])
+
+ # set bridge port path priority
+ if 'priority' in bridge_config:
+ self.set_path_cost(bridge_config['priority'])
def set_dhcp(self, enable):
"""
@@ -1047,8 +1085,8 @@ class Interface(Control):
# re-add ourselves to any bridge we might have fallen out of
if 'is_bridge_member' in config:
- bridge = config.get('is_bridge_member')
- self.add_to_bridge(bridge)
+ bridge_dict = config.get('is_bridge_member')
+ self.add_to_bridge(bridge_dict)
# remove no longer required 802.1ad (Q-in-Q VLANs)
ifname = config['ifname']
diff --git a/python/vyos/ifconfig/stp.py b/python/vyos/ifconfig/stp.py
deleted file mode 100644
index 5e83206c2..000000000
--- a/python/vyos/ifconfig/stp.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Copyright 2019 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/>.
-
-
-from vyos.ifconfig.interface import Interface
-
-from vyos.validate import assert_positive
-
-
-class STP:
- """
- A spanning-tree capable interface. This applies only to bridge port member
- interfaces!
- """
-
- @classmethod
- def enable (cls, adaptee):
- adaptee._sysfs_set = {**adaptee._sysfs_set, **cls._sysfs_set}
- adaptee.set_path_cost = cls.set_path_cost
- adaptee.set_path_priority = cls.set_path_priority
- return adaptee
-
- _sysfs_set = {
- 'path_cost': {
- # XXX: we should set a maximum
- 'validate': assert_positive,
- 'location': '/sys/class/net/{ifname}/brport/path_cost',
- 'errormsg': '{ifname} is not a bridge port member'
- },
- 'path_priority': {
- # XXX: we should set a maximum
- 'validate': assert_positive,
- 'location': '/sys/class/net/{ifname}/brport/priority',
- 'errormsg': '{ifname} is not a bridge port member'
- },
- }
-
- def set_path_cost(self, cost):
- """
- Set interface path cost, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').set_path_cost(4)
- """
- self.set_interface('path_cost', cost)
-
- def set_path_priority(self, priority):
- """
- Set interface path priority, only relevant for STP enabled interfaces
-
- Example:
-
- >>> from vyos.ifconfig import Interface
- >>> Interface('eth0').set_path_priority(4)
- """
- self.set_interface('path_priority', priority)
diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py
index d94a5d962..c6bb5bd1a 100644
--- a/smoketest/scripts/cli/base_interfaces_test.py
+++ b/smoketest/scripts/cli/base_interfaces_test.py
@@ -228,7 +228,7 @@ class BasicInterfaceTest:
self._mtu_test(vif)
def test_ip_options(self):
- """ test IP options like arp """
+ """ Test interface base IPv4 options """
if not self._test_ip:
return None
@@ -278,3 +278,27 @@ class BasicInterfaceTest:
tmp = read_file(f'/proc/sys/net/ipv4/conf/{interface}/rp_filter')
self.assertEqual('2', tmp)
+
+ def test_ipv6_options(self):
+ """ Test interface base IPv6 options """
+ if not self._test_ipv6:
+ return None
+
+ for interface in self._interfaces:
+ dad_transmits = '10'
+ path = self._base_path + [interface]
+ for option in self._options.get(interface, []):
+ self.session.set(path + option.split())
+
+ # Options
+ self.session.set(path + ['ipv6', 'disable-forwarding'])
+ self.session.set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits])
+
+ self.session.commit()
+
+ for interface in self._interfaces:
+ tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding')
+ self.assertEqual('0', tmp)
+
+ tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/dad_transmits')
+ self.assertEqual(dad_transmits, tmp)
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 9763620ac..ea9bd54d4 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -109,7 +109,7 @@ def get_config(config=None):
# Check if member interface is already member of a bond
tmp = is_member(conf, interface, 'bonding')
- if tmp and tmp != bond['ifname']:
+ if tmp and bond['ifname'] not in tmp:
interface_config.update({'is_bond_member' : tmp})
# Check if member interface is used as source-interface on another interface
@@ -162,11 +162,11 @@ def verify(bond):
raise ConfigError(error_msg + 'it does not exist!')
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 485decb17..4aeb8fc67 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -24,6 +24,7 @@ from vyos.configdict import get_interface_dict
from vyos.configdict import node_changed
from vyos.configdict import is_member
from vyos.configdict import is_source_interface
+from vyos.configdict import dict_merge
from vyos.configverify import verify_dhcpv6
from vyos.configverify import verify_vrf
from vyos.ifconfig import BridgeIf
@@ -69,25 +70,26 @@ def get_config(config=None):
# the default dictionary is not properly paged into the dict (see T2665)
# thus we will ammend it ourself
default_member_values = defaults(base + ['member', 'interface'])
- for interface, interface_config in bridge['member']['interface'].items():
- interface_config.update(default_member_values)
+ for interface in bridge['member']['interface']:
+ bridge['member']['interface'][interface] = dict_merge(
+ default_member_values, bridge['member']['interface'][interface])
# Check if member interface is already member of another bridge
tmp = is_member(conf, interface, 'bridge')
- if tmp and tmp != bridge['ifname']:
- interface_config.update({'is_bridge_member' : tmp})
+ if tmp and bridge['ifname'] not in tmp:
+ bridge['member']['interface'][interface].update({'is_bridge_member' : tmp})
# Check if member interface is already member of a bond
tmp = is_member(conf, interface, 'bonding')
- if tmp: interface_config.update({'is_bond_member' : tmp})
+ if tmp: bridge['member']['interface'][interface].update({'is_bond_member' : tmp})
# Check if member interface is used as source-interface on another interface
tmp = is_source_interface(conf, interface)
- if tmp: interface_config.update({'is_source_interface' : tmp})
+ if tmp: bridge['member']['interface'][interface].update({'is_source_interface' : tmp})
# Bridge members must not have an assigned address
tmp = has_address_configured(conf, interface)
- if tmp: interface_config.update({'has_address' : ''})
+ if tmp: bridge['member']['interface'][interface].update({'has_address' : ''})
return bridge
@@ -105,15 +107,12 @@ def verify(bridge):
if interface == 'lo':
raise ConfigError('Loopback interface "lo" can not be added to a bridge')
- if interface not in interfaces():
- raise ConfigError(error_msg + 'it does not exist!')
-
if 'is_bridge_member' in interface_config:
- tmp = interface_config['is_bridge_member']
+ tmp = next(iter(interface_config['is_bridge_member']))
raise ConfigError(error_msg + f'it is already a member of bridge "{tmp}"!')
if 'is_bond_member' in interface_config:
- tmp = interface_config['is_bond_member']
+ tmp = next(iter(interface_config['is_bond_member']))
raise ConfigError(error_msg + f'it is already a member of bond "{tmp}"!')
if 'is_source_interface' in interface_config:
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 518dbdc0e..f2b580c6f 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -208,7 +208,8 @@ def get_config(config=None):
openvpn['auth_user_pass_file'] = f"/run/openvpn/{openvpn['intf']}.pw"
# check if interface is member of a bridge
- openvpn['is_bridge_member'] = is_member(conf, openvpn['intf'], 'bridge')
+ tmp = is_member(conf, openvpn['intf'], 'bridge')
+ if tmp: openvpn['is_bridge_member'] = next(iter(tmp))
# Check if interface instance has been removed
if not conf.exists('interfaces openvpn ' + openvpn['intf']):
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index f1d885b15..5561514bd 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -462,7 +462,8 @@ def get_config(config=None):
options['tunnel'] = {}
# check for bridges
- options['bridge'] = is_member(config, ifname, 'bridge')
+ tmp = is_member(config, ifname, 'bridge')
+ if tmp: options['bridge'] = next(iter(tmp))
options['interfaces'] = interfaces()
for name in ct: