summaryrefslogtreecommitdiff
path: root/src/conf_mode/interfaces-bridge.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/interfaces-bridge.py')
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py156
1 files changed, 89 insertions, 67 deletions
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 93c6db97e..3ff339f0f 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
@@ -20,45 +20,28 @@ from copy import deepcopy
from sys import exit
from netifaces import interfaces
-from vyos.ifconfig import BridgeIf
+from vyos.ifconfig import BridgeIf, Section
from vyos.ifconfig.stp import STP
-from vyos.configdict import list_diff
+from vyos.configdict import list_diff, interface_default_data
+from vyos.validate import is_member, has_address_configured
from vyos.config import Config
+from vyos.util import cmd, get_bridge_member_config
from vyos import ConfigError
default_config_data = {
- 'address': [],
- 'address_remove': [],
+ **interface_default_data,
'aging': 300,
'arp_cache_tmo': 30,
- 'description': '',
'deleted': False,
- 'dhcp_client_id': '',
- 'dhcp_hostname': '',
- 'dhcp_vendor_class_id': '',
- 'dhcpv6_prm_only': False,
- 'dhcpv6_temporary': False,
- 'disable': False,
- 'disable_link_detect': 1,
'forwarding_delay': 14,
'hello_time': 2,
- 'ip_disable_arp_filter': 1,
- 'ip_enable_arp_accept': 0,
- 'ip_enable_arp_announce': 0,
- 'ip_enable_arp_ignore': 0,
- 'ipv6_autoconf': 0,
- 'ipv6_eui64_prefix': '',
- 'ipv6_forwarding': 1,
- 'ipv6_dup_addr_detect': 1,
'igmp_querier': 0,
'intf': '',
- 'mac' : '',
'max_age': 20,
'member': [],
'member_remove': [],
'priority': 32768,
- 'stp': 0,
- 'vrf': ''
+ 'stp': 0
}
def get_config():
@@ -160,9 +143,21 @@ def get_config():
if conf.exists('ipv6 address autoconf'):
bridge['ipv6_autoconf'] = 1
- # Get prefix for IPv6 addressing based on MAC address (EUI-64)
+ # Get prefixes for IPv6 addressing based on MAC address (EUI-64)
if conf.exists('ipv6 address eui64'):
- bridge['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
+ bridge['ipv6_eui64_prefix'] = conf.return_values('ipv6 address eui64')
+
+ # Determine currently effective EUI64 addresses - to determine which
+ # address is no longer valid and needs to be removed
+ eff_addr = conf.return_effective_values('ipv6 address eui64')
+ bridge['ipv6_eui64_prefix_remove'] = list_diff(eff_addr, bridge['ipv6_eui64_prefix'])
+
+ # Remove the default link-local address if set.
+ if conf.exists('ipv6 address no-default-link-local'):
+ bridge['ipv6_eui64_prefix_remove'].append('fe80::/64')
+ else:
+ # add the link-local by default to make IPv6 work
+ bridge['ipv6_eui64_prefix'].append('fe80::/64')
# Disable IPv6 forwarding on this interface
if conf.exists('ipv6 disable-forwarding'):
@@ -176,28 +171,29 @@ def get_config():
if conf.exists('mac'):
bridge['mac'] = conf.return_value('mac')
+ # Find out if MAC has changed - if so, we need to delete all IPv6 EUI64 addresses
+ # before re-adding them
+ if ( bridge['mac'] and bridge['intf'] in Section.interfaces(section='bridge')
+ and bridge['mac'] != BridgeIf(bridge['intf'], create=False).get_mac() ):
+ bridge['ipv6_eui64_prefix_remove'] += bridge['ipv6_eui64_prefix']
+
+ # to make IPv6 SLAAC and DHCPv6 work with forwarding=1,
+ # accept_ra must be 2
+ if bridge['ipv6_autoconf'] or 'dhcpv6' in bridge['address']:
+ bridge['ipv6_accept_ra'] = 2
+
# Interval at which neighbor bridges are removed
if conf.exists('max-age'):
bridge['max_age'] = int(conf.return_value('max-age'))
# Determine bridge member interface (currently configured)
for intf in conf.list_nodes('member interface'):
- # cost and priority initialized with linux defaults
- # by reading /sys/devices/virtual/net/br0/brif/eth2/{path_cost,priority}
- # after adding interface to bridge after reboot
- iface = {
- 'name': intf,
- 'cost': 100,
- 'priority': 32
- }
-
- if conf.exists('member interface {} cost'.format(intf)):
- iface['cost'] = int(conf.return_value('member interface {} cost'.format(intf)))
-
- if conf.exists('member interface {} priority'.format(intf)):
- iface['priority'] = int(conf.return_value('member interface {} priority'.format(intf)))
-
- bridge['member'].append(iface)
+ # defaults are stored in util.py (they can't be here as all interface
+ # scripts use the function)
+ memberconf = get_bridge_member_config(conf, bridge['intf'], intf)
+ if memberconf:
+ memberconf['name'] = intf
+ bridge['member'].append(memberconf)
# Determine bridge member interface (currently effective) - to determine which
# interfaces is no longer assigend to the bridge and thus can be removed
@@ -228,30 +224,40 @@ def verify(bridge):
raise ConfigError(f'VRF "{vrf_name}" does not exist')
conf = Config()
- for br in conf.list_nodes('interfaces bridge'):
- # it makes no sense to verify ourself in this case
- if br == bridge['intf']:
- continue
-
- for intf in bridge['member']:
- tmp = conf.list_nodes('interfaces bridge {} member interface'.format(br))
- if intf['name'] in tmp:
- raise ConfigError('Interface "{}" belongs to bridge "{}" and can not be enslaved.'.format(intf['name'], bridge['intf']))
-
- # the interface must exist prior adding it to a bridge
for intf in bridge['member']:
+ # the interface must exist prior adding it to a bridge
if intf['name'] not in interfaces():
- raise ConfigError('Can not add non existing interface "{}" to bridge "{}"'.format(intf['name'], bridge['intf']))
+ raise ConfigError((
+ f'Cannot add nonexistent interface "{intf["name"]}" '
+ f'to bridge "{bridge["intf"]}"'))
if intf['name'] == 'lo':
raise ConfigError('Loopback interface "lo" can not be added to a bridge')
- # bridge members are not allowed to be bond members, too
- for intf in bridge['member']:
- for bond in conf.list_nodes('interfaces bonding'):
- if conf.exists('interfaces bonding ' + bond + ' member interface'):
- if intf['name'] in conf.return_values('interfaces bonding ' + bond + ' member interface'):
- raise ConfigError('Interface {} belongs to bond {}, can not add it to {}'.format(intf['name'], bond, bridge['intf']))
+ # bridge members aren't allowed to be members of another bridge
+ for br in conf.list_nodes('interfaces bridge'):
+ # it makes no sense to verify ourself in this case
+ if br == bridge['intf']:
+ continue
+
+ tmp = conf.list_nodes(f'interfaces bridge {br} member interface')
+ if intf['name'] in tmp:
+ raise ConfigError((
+ f'Cannot add interface "{intf["name"]}" to bridge '
+ f'"{bridge["intf"]}", it is already a member of bridge "{br}"!'))
+
+ # bridge members are not allowed to be bond members
+ tmp = is_member(conf, intf['name'], 'bonding')
+ if tmp:
+ raise ConfigError((
+ f'Cannot add interface "{intf["name"]}" to bridge '
+ f'"{bridge["intf"]}", it is already a member of bond "{tmp}"!'))
+
+ # bridge members must not have an assigned address
+ if has_address_configured(conf, intf['name']):
+ raise ConfigError((
+ f'Cannot add interface "{intf["name"]}" to bridge '
+ f'"{bridge["intf"]}", it has an address assigned!'))
return None
@@ -281,10 +287,10 @@ def apply(bridge):
br.set_arp_announce(bridge['ip_enable_arp_announce'])
# configure ARP ignore
br.set_arp_ignore(bridge['ip_enable_arp_ignore'])
+ # IPv6 accept RA
+ br.set_ipv6_accept_ra(bridge['ipv6_accept_ra'])
# IPv6 address autoconfiguration
br.set_ipv6_autoconf(bridge['ipv6_autoconf'])
- # IPv6 EUI-based address
- br.set_ipv6_eui64_address(bridge['ipv6_eui64_prefix'])
# IPv6 forwarding
br.set_ipv6_forwarding(bridge['ipv6_forwarding'])
# IPv6 Duplicate Address Detection (DAD) tries
@@ -315,12 +321,16 @@ def apply(bridge):
if bridge['dhcpv6_temporary']:
br.dhcp.v6.options['dhcpv6_temporary'] = True
+ if bridge['dhcpv6_pd']:
+ br.dhcp.v6.options['dhcpv6_pd'] = br['dhcpv6_pd']
+
# assign/remove VRF
br.set_vrf(bridge['vrf'])
- # Change interface MAC address
- if bridge['mac']:
- br.set_mac(bridge['mac'])
+ # Delete old IPv6 EUI64 addresses before changing MAC
+ # (adding members to a fresh bridge changes its MAC too)
+ for addr in bridge['ipv6_eui64_prefix_remove']:
+ br.del_ipv6_eui64_address(addr)
# remove interface from bridge
for intf in bridge['member_remove']:
@@ -328,8 +338,20 @@ def apply(bridge):
# add interfaces to bridge
for member in bridge['member']:
+ # if we've come here we already verified the interface doesn't
+ # have addresses configured so just flush any remaining ones
+ cmd(f'ip addr flush dev "{member["name"]}"')
br.add_port(member['name'])
+ # Change interface MAC address
+ if bridge['mac']:
+ br.set_mac(bridge['mac'])
+
+ # Add IPv6 EUI-based addresses (must be done after adding the
+ # 1st bridge member or setting its MAC)
+ for addr in bridge['ipv6_eui64_prefix']:
+ br.add_ipv6_eui64_address(addr)
+
# up/down interface
if bridge['disable']:
br.set_admin_state('down')
@@ -347,9 +369,9 @@ def apply(bridge):
for member in bridge['member']:
i = STPBridgeIf(member['name'])
# configure ARP cache timeout
- i.set_arp_cache_tmo(bridge['arp_cache_tmo'])
+ i.set_arp_cache_tmo(member['arp_cache_tmo'])
# ignore link state changes
- i.set_link_detect(bridge['disable_link_detect'])
+ i.set_link_detect(member['disable_link_detect'])
# set bridge port path cost
i.set_path_cost(member['cost'])
# set bridge port path priority