Add bridgedomain settings.

Bridges can be created with default settings, with specific settings,
and they can be sync'd at runtime with all of the settings in this
change.

Notably missing are two features:
- unknown unicast flooding into specific interfaces (as opposed to
  on/off on the bridge)
- learn-limit, which does not have an API getter, only a setter.
This commit is contained in:
Pim van Pelt
2022-04-05 12:01:13 +00:00
parent f53f7d5095
commit fdb732142a
8 changed files with 155 additions and 9 deletions

View File

@ -81,6 +81,38 @@ def bvi_unique(yaml, bviname):
return n<2 return n<2
def get_settings(yaml, ifname):
ifname, iface = get_by_name(yaml, ifname)
if not iface:
return None
settings = {
'learn': True,
'unicast-flood': True,
'unknown-unicast-flood': True,
'unicast-forward': True,
'arp-termination': False,
'arp-unicast-forward': False,
'mac-age-minutes': 0, ## 0 means disabled
}
if 'settings' in iface:
if 'learn' in iface['settings']:
settings['learn'] = iface['settings']['learn']
if 'unicast-flood' in iface['settings']:
settings['unicast-flood'] = iface['settings']['unicast-flood']
if 'unknown-unicast-flood' in iface['settings']:
settings['unknown-unicast-flood'] = iface['settings']['unknown-unicast-flood']
if 'unicast-forward' in iface['settings']:
settings['unicast-forward'] = iface['settings']['unicast-forward']
if 'arp-termination' in iface['settings']:
settings['arp-termination'] = iface['settings']['arp-termination']
if 'arp-unicast-forward' in iface['settings']:
settings['arp-unicast-forward'] = iface['settings']['arp-unicast-forward']
if 'mac-age-minutes' in iface['settings']:
settings['mac-age-minutes'] = int(iface['settings']['mac-age-minutes'])
return settings
def validate_bridgedomains(yaml): def validate_bridgedomains(yaml):
result = True result = True
msgs = [] msgs = []

View File

@ -32,6 +32,16 @@ bridgedomain:
mtu: int(min=128,max=9216,required=False) mtu: int(min=128,max=9216,required=False)
bvi: str(matches='loop[0-9]+',required=False) bvi: str(matches='loop[0-9]+',required=False)
interfaces: list(str(),required=False) interfaces: list(str(),required=False)
settings: include('bridgedomain-settings',required=False)
---
bridgedomain-settings:
learn: bool(required=False)
unicast-flood: bool(required=False)
unknown-unicast-flood: bool(required=False)
unicast-forward: bool(required=False)
arp-termination: bool(required=False)
arp-unicast-forward: bool(required=False)
mac-age-minutes: int(min=0,max=255,required=False)
--- ---
loopback: loopback:
description: str(exclude='\'"',len=64,required=False) description: str(exclude='\'"',len=64,required=False)

View File

@ -49,3 +49,19 @@ class TestBridgeDomainMethods(unittest.TestCase):
def test_get_bridgedomains(self): def test_get_bridgedomains(self):
ifs = bridgedomain.get_bridgedomains(self.cfg) ifs = bridgedomain.get_bridgedomains(self.cfg)
self.assertEqual(len(ifs), 6) self.assertEqual(len(ifs), 6)
def test_get_settings(self):
settings = bridgedomain.get_settings(self.cfg, "bd1")
self.assertIsNone(settings)
settings = bridgedomain.get_settings(self.cfg, "bd10")
self.assertTrue(settings['learn'])
self.assertTrue(settings['unknown-unicast-flood'])
self.assertTrue(settings['unicast-flood'])
self.assertEqual(settings['mac-age-minutes'], 0)
settings = bridgedomain.get_settings(self.cfg, "bd11")
self.assertTrue(settings['learn'])
self.assertFalse(settings['unknown-unicast-flood'])
self.assertFalse(settings['unicast-flood'])
self.assertEqual(settings['mac-age-minutes'], 10)

View File

@ -105,17 +105,24 @@ BridgeDomains are required to be named `bdN` where N in [1, 16777216). Note that
* ***interfaces***: A list of zero or more interfaces or sub-interfaces that are bridge * ***interfaces***: A list of zero or more interfaces or sub-interfaces that are bridge
members. If the bridge has a `BVI`, it MUST NOT appear in this list. Bridges are allowed to members. If the bridge has a `BVI`, it MUST NOT appear in this list. Bridges are allowed to
exist with no member interfaces. exist with no member interfaces.
* ***settings***: A map of bridge-domain settings to further manipulate its behavior:
* ***learn***: A boolean that turns learning on/off. Default True.
* ***unicast-flood***: A boolean that turns unicast flooding on/off. Default True.
* ***unknown-unicast-flood***: A boolean that turns unknown unicast flooding on/off.
Default True.
* ***unicast-forward***: A boolean that turns unicast forwarding on/off. Default True.
* ***arp-termination***: A boolean that turns termination and response of ARP Requests
on/off. Default False.
* ***arp-unicast-forward***: A boolean that turns L2 arp-unicast forwarding on/off.
Default False.
* ***mac-age-minutes***: An integer between [0,256) that drives the ARP timeout on the
bridge in minutes, where 0 means do not age out, which is the default.
Any member sub-interfaces that are added, will automatically be configured to tag-rewrite the Any member sub-interfaces that are added, will automatically be configured to tag-rewrite the
number of tags they have, so a simple dot1q sub-interface will be configured as `pop 1`, while number of tags they have, so a simple dot1q sub-interface will be configured as `pop 1`, while
a QinQ or QinAD sub-interface will be configured as `pop 2`. Conversely, when interfaces are a QinQ or QinAD sub-interface will be configured as `pop 2`. Conversely, when interfaces are
removed from the bridge, their tag-rewriting will be disabled. removed from the bridge, their tag-rewriting will be disabled.
*Caveat*: Currently, bridgedomains are always created with their default attributes in VPP, that
is to say with learning and unicast forwarding turned on, unknown-unicast flooding enabled,
and ARP terminating and aging turned off. In a future release, `vppcfg` will give more
configuration options.
Examples: Examples:
``` ```
bridgedomains: bridgedomains:
@ -124,9 +131,20 @@ bridgedomains:
bvi: loop1 bvi: loop1
interfaces: [ BondEthernet0.500, HundredGigabitEthernet12/0/1, vxlan_tunnel1 ] interfaces: [ BondEthernet0.500, HundredGigabitEthernet12/0/1, vxlan_tunnel1 ]
bd11: bd11:
description: "No member interfaces, default 1500 byte MTU" description: "No members, default 1500 byte MTU, with (default) settings"
settings:
learn: True
unicast-flood: True
unknown-unicast-flood: True
unicast-forward: True
arp-termination: False
arp-unicast-forward: False
mac-age-minutes: 0
``` ```
*Caveat*: The flooding of unknown-unicast can be turned on or off, but flooding to a specific interface
(as opposed to all interfaces which is the default), is not supported.
### BondEthernets ### BondEthernets
BondEthernets are required to be named `BondEthernetN` (note the camelcase) where N in BondEthernets are required to be named `BondEthernetN` (note the camelcase) where N in

View File

@ -75,6 +75,9 @@ bridgedomains:
mtu: 2000 mtu: 2000
bvi: loop2 bvi: loop2
interfaces: [ BondEthernet0.500, BondEthernet0.501 ] interfaces: [ BondEthernet0.500, BondEthernet0.501 ]
settings:
mac-age-minutes: 10
learn: False
bd11: bd11:
mtu: 1500 mtu: 1500

View File

@ -18,6 +18,16 @@ bridgedomain:
mtu: int(min=128,max=9216,required=False) mtu: int(min=128,max=9216,required=False)
bvi: str(matches='loop[0-9]+',required=False) bvi: str(matches='loop[0-9]+',required=False)
interfaces: list(str(),required=False) interfaces: list(str(),required=False)
settings: include('bridgedomain-settings',required=False)
---
bridgedomain-settings:
learn: bool(required=False)
unicast-flood: bool(required=False)
unknown-unicast-flood: bool(required=False)
unicast-forward: bool(required=False)
arp-termination: bool(required=False)
arp-unicast-forward: bool(required=False)
mac-age-minutes: int(min=0,max=255,required=False)
--- ---
loopback: loopback:
description: str(exclude='\'"',len=64,required=False) description: str(exclude='\'"',len=64,required=False)

View File

@ -51,9 +51,13 @@ bridgedomains:
bvi: loop0 bvi: loop0
interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ] interfaces: [ GigabitEthernet1/0/0, GigabitEthernet1/0/1, BondEthernet0 ]
bd11: bd11:
description: "Bridge Domain 11, with sub-interfaces" description: "Bridge Domain 11, with sub-interfaces and settings"
mtu: 2000 mtu: 2000
interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100, BondEthernet0.100 ] interfaces: [ GigabitEthernet2/0/0.100, GigabitEthernet2/0/1.100, BondEthernet0.100 ]
settings:
mac-age-minutes: 10
unicast-flood: False
unknown-unicast-flood: False
bd12: bd12:
description: "Bridge Domain 12, invalid because it has Gi1/0/0 as well" description: "Bridge Domain 12, invalid because it has Gi1/0/0 as well"
mtu: 9000 mtu: 9000

View File

@ -623,9 +623,24 @@ class Reconciler():
for ifname in bridgedomain.get_bridgedomains(self.cfg): for ifname in bridgedomain.get_bridgedomains(self.cfg):
ifname, iface = bridgedomain.get_by_name(self.cfg, ifname) ifname, iface = bridgedomain.get_by_name(self.cfg, ifname)
instance = int(ifname[2:]) instance = int(ifname[2:])
settings = bridgedomain.get_settings(self.cfg, ifname)
if instance in self.vpp.cache['bridgedomains']: if instance in self.vpp.cache['bridgedomains']:
continue continue
cli="create bridge-domain %s" % (instance) cli="create bridge-domain %s" % (instance)
if not settings['learn']:
cli += " learn 0"
if not settings['unicast-flood']:
cli += " flood 0"
if not settings['unknown-unicast-flood']:
cli += " uu-flood 0"
if not settings['unicast-forward']:
cli += " forward 0"
if settings['arp-termination']:
cli += " arp-term 1"
if settings['arp-unicast-forward']:
cli += " arp-ufwd 1"
if settings['mac-age-minutes'] > 0:
cli += " mac-age %d" % settings['mac-age-minutes']
self.cli['create'].append(cli); self.cli['create'].append(cli);
return True return True
@ -727,12 +742,48 @@ class Reconciler():
bridge_members = [self.vpp.cache['interfaces'][x].interface_name for x in bridge_sw_if_index_list if x in self.vpp.cache['interfaces']] bridge_members = [self.vpp.cache['interfaces'][x].interface_name for x in bridge_sw_if_index_list if x in self.vpp.cache['interfaces']]
else: else:
## New BridgeDomain ## New BridgeDomain
vpp_bridge = None
bvi_sw_if_index = -1 bvi_sw_if_index = -1
bridge_members = [] bridge_members = []
config_bridge_ifname, config_bridge_iface = bridgedomain.get_by_name(self.cfg, "bd%d"%instance) config_bridge_ifname, config_bridge_iface = bridgedomain.get_by_name(self.cfg, "bd%d"%instance)
if not 'interfaces' in config_bridge_iface: if vpp_bridge:
continue # Sync settings on existing bridge. create_bridgedomain() will have set them for new bridges.
settings = bridgedomain.get_settings(self.cfg, config_bridge_ifname)
if settings['learn'] != vpp_bridge.learn:
cli="set bridge-domain learn %d" % (instance)
if not settings['learn']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['unicast-forward'] != vpp_bridge.forward:
cli="set bridge-domain forward %d" % (instance)
if not settings['unicast-forward']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['unicast-flood'] != vpp_bridge.flood:
cli="set bridge-domain flood %d" % (instance)
if not settings['unicast-flood']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['unknown-unicast-flood'] != vpp_bridge.uu_flood:
cli="set bridge-domain uu-flood %d" % (instance)
if not settings['unknown-unicast-flood']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['arp-termination'] != vpp_bridge.arp_term:
cli="set bridge-domain arp term %d" % (instance)
if not settings['arp-termination']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['arp-unicast-forward'] != vpp_bridge.arp_ufwd:
cli="set bridge-domain arp-ufwd %d" % (instance)
if not settings['arp-unicast-forward']:
cli += " disable"
self.cli['sync'].append(cli);
if settings['mac-age-minutes'] != vpp_bridge.mac_age:
cli="set bridge-domain mac-age %d %d" % (instance, settings['mac-age-minutes'])
self.cli['sync'].append(cli);
if 'bvi' in config_bridge_iface: if 'bvi' in config_bridge_iface:
bviname = config_bridge_iface['bvi'] bviname = config_bridge_iface['bvi']
if bviname in self.vpp.cache['interface_names'] and self.vpp.cache['interface_names'][bviname].sw_if_index == bvi_sw_if_index: if bviname in self.vpp.cache['interface_names'] and self.vpp.cache['interface_names'][bviname].sw_if_index == bvi_sw_if_index:
@ -740,6 +791,8 @@ class Reconciler():
cli="set interface l2 bridge %s %d bvi" % (bviname, instance) cli="set interface l2 bridge %s %d bvi" % (bviname, instance)
self.cli['sync'].append(cli); self.cli['sync'].append(cli);
if not 'interfaces' in config_bridge_iface:
continue
for member_ifname in config_bridge_iface['interfaces']: for member_ifname in config_bridge_iface['interfaces']:
member_ifname, member_iface = interface.get_by_name(self.cfg, member_ifname) member_ifname, member_iface = interface.get_by_name(self.cfg, member_ifname)
if not member_ifname in bridge_members: if not member_ifname in bridge_members: