瀏覽代碼

SDN/bird: Add area support for OSPF

  Area 51 is now used for OOBM

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
Maximilian Wilhelm 1 年之前
父節點
當前提交
a1ffcb8721
共有 2 個文件被更改,包括 84 次插入43 次删除
  1. 55 40
      _modules/ffho_net.py
  2. 29 3
      bird/IGP.conf

+ 55 - 40
_modules/ffho_net.py

@@ -1246,27 +1246,32 @@ def get_router_id (node_config, node_id):
 # 100 Mbit/s  |   1000
 #    VPN      |  10000
 #
-def get_ospf_interface_config (node_config, grains_id):
-	ospf_node_config = node_config.get ('ospf', {})
-
-	ospf_interfaces = {}
+def get_ospf_config (node_config, grains_id):
+	ospf_config = {
+		# <area> : {
+		#	<iface> : {
+		#		config ...
+		#	}
+		# }
+	}
 
 	for iface, iface_config in node_config.get ('ifaces', {}).items ():
 		# By default we don't speak OSPF on interfaces
 		ospf_on = False
+		area = 0
 
 		# Defaults for OSPF interfaces
-		ospf_config = {
+		ospf_iface_cfg = {
 			'stub' : True,			# Active/Passive interface
 			'cost' : 12345,
 			# 'type' 			# Area type
 		}
 
 		# OSPF configuration for interface present?
-		ospf_config_pillar = iface_config.get ('ospf', {})
+		ospf_iface_cfg_pillar = iface_config.get ('ospf', {})
 
 		# Should be completely ignore this interface?
-		if ospf_config_pillar.get ('ignore', False):
+		if ospf_iface_cfg_pillar.get ('ignore', False):
 			continue
 
 		# Ignore interfaces without any IPs configured
@@ -1276,80 +1281,87 @@ def get_ospf_interface_config (node_config, grains_id):
 		# Wireless Local Links (WLL)
 		if re.search (r'^vlan90\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = True
-			ospf_config['cost'] = 10
-			ospf_config['desc'] = "Wireless Local Link (WLL)"
+			ospf_iface_cfg['stub'] = True
+			ospf_iface_cfg['cost'] = 10
+			ospf_iface_cfg['desc'] = "Wireless Local Link (WLL)"
 
 		# Local Gigabit Ethernet based connections (PTP or L2 subnets), cost 10
 		elif re.search (r'^(br-?|br\d+\.|vlan)10\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 100
-			ospf_config['desc'] = "Wired Gigabit connection"
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 100
+			ospf_iface_cfg['desc'] = "Wired Gigabit connection"
 
 		# 10/20 Gbit/s Dark Fiber connection
 		elif re.search (r'^vlan12\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 10
-			ospf_config['desc'] = "Wired 10Gb/s connection"
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 10
+			ospf_iface_cfg['desc'] = "Wired 10Gb/s connection"
 
 		# VLL connection
 		elif re.search (r'^vlan15\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 200
-			ospf_config['desc'] = "VLL connection"
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 200
+			ospf_iface_cfg['desc'] = "VLL connection"
 
 		# WBBL connection
 		elif re.search (r'^vlan20\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 1000
-			ospf_config['desc'] = "WBBL connection"
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 1000
+			ospf_iface_cfg['desc'] = "WBBL connection"
 
 		# Legacy WBBL connection
 		elif re.search (r'^vlan22\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 1000
-			ospf_config['desc'] = "WBBL connection"
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 1000
+			ospf_iface_cfg['desc'] = "WBBL connection"
 
 		# Management Vlans
 		elif re.search (r'^vlan30\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = True
-			ospf_config['cost'] = 10
+			ospf_iface_cfg['stub'] = True
+			ospf_iface_cfg['cost'] = 10
+
+		# Management X-Connects
+		elif re.search (r'^vlan32\d\d$', iface):
+			ospf_on = True
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 10
+			area = 51
 
 		# OPS Vlans
 		elif re.search (r'^vlan39\d\d$', iface):
 			ospf_on = True
-			ospf_config['stub'] = True
-			ospf_config['cost'] = 10
+			ospf_iface_cfg['stub'] = True
+			ospf_iface_cfg['cost'] = 10
 
 		# Active OSPF on OpenVPN tunnels, cost 10000
 		elif iface.startswith ('ovpn-'):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 10000
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 10000
 
 			# Inter-Core links should have cost 5000
 			if iface.startswith ('ovpn-cr') and grains_id.startswith ('cr'):
-				ospf_config['cost'] = 5000
+				ospf_iface_cfg['cost'] = 5000
 
 			# OpenVPN tunnels to EdgeRouters
 			elif iface.startswith ('ovpn-er-'):
-				ospf_config['type'] = 'broadcast'
+				ospf_iface_cfg['type'] = 'broadcast'
 
 		# Active OSPF on Wireguard tunnels, cost 10000
 		elif iface.startswith ('wg-'):
 			ospf_on = True
-			ospf_config['stub'] = False
-			ospf_config['cost'] = 10000
+			ospf_iface_cfg['stub'] = False
+			ospf_iface_cfg['cost'] = 10000
 
 			# Inter-Core links should have cost 5000
 			if iface.startswith ('wg-cr') and grains_id.startswith ('cr'):
-				ospf_config['cost'] = 5000
+				ospf_iface_cfg['cost'] = 5000
 
 		# OSPF explicitly enabled for interface
 		elif 'ospf' in iface_config:
@@ -1362,13 +1374,16 @@ def get_ospf_interface_config (node_config, grains_id):
 			continue
 
 		# Explicit OSPF interface configuration parameters take precendence over generated ones
-		for attr, val in ospf_config_pillar.items ():
-			ospf_config[attr] = val
+		for attr, val in ospf_iface_cfg_pillar.items ():
+			ospf_iface_cfg[attr] = val
 
 		# Store interface configuration
-		ospf_interfaces[iface] = ospf_config
+		if area not in ospf_config:
+			ospf_config[area] = {}
+
+		ospf_config[area][iface] = ospf_iface_cfg
 
-	return ospf_interfaces
+	return ospf_config
 
 
 # Return (possibly empty) subset of Traffic Engineering entries from 'te' pillar entry

+ 29 - 3
bird/IGP.conf

@@ -8,6 +8,7 @@
 {%- if 'stub_router' in ospf_node_config and ospf_node_config['stub_router'] in [ True, 'yes'] %}
   {%- do ospf_node_config.update ({'stub_router': 'yes'}) %}
 {%- endif %}
+{%- set ospf_config = salt['ffho_net.get_ospf_config'](node_config, grains['id']) %}
 
 {%- if proto == 'v6' %}
 # Bloody workaround for bird6's unwillingness to read !LL IPv6 addresses from lo
@@ -56,9 +57,8 @@ protocol ospf IGP {
 			stub yes;
 		};
 
-{%- set ospf_interface_config = salt['ffho_net.get_ospf_interface_config'](node_config, grains['id']) %}
-{%- for iface in ospf_interface_config|sort %}
-  {%- set config = ospf_interface_config.get (iface) %}
+{%- for iface in ospf_config.get(0, {})|sort %}
+  {%- set config = ospf_config[0][iface] %}
 
   {#- Interface description? #}
   {%- set desc = salt['pillar.get']('nodes:' ~ grains['id'] ~ ':ifaces:' ~ iface ~ ':desc', "") %}
@@ -124,4 +124,30 @@ protocol ospf IGP {
 		};
 {%- endif %}
 	};
+
+
+{#- Interfaces for non-backbone areas (OOBM e.g.) #}
+{%- for area in ospf_config if area != 0 %}
+	# Area {{ area }}
+  {%- set area_ifaces = ospf_config[area] %}
+	area {{ area }} {
+  {%- for iface in area_ifaces|sort %}
+    {%- set iface_config = area_ifaces[iface] %}
+		interface "{{ iface }}" {
+    {%- if 'desc' in iface_config %}
+			# {{ iface_config.get ('desc') }}
+    {%- endif %}
+    {%- for attr in iface_config|sort if attr not in ['desc'] %}
+      {%- set value = iface_config.get (attr) %}
+      {#- 'is bool' only introduced in Jinja 2.11 #}
+      {%- if salt['ffho.is_bool'](value) %}
+        {%- set value = 'yes' if value else 'no' %}
+      {%- endif %}
+			{{ attr }} {{ value }};
+    {%- endfor %}
+		};
+
+  {%- endfor %}
+	};
+{%- endfor %}
 }