diff --git a/netsim/ansible/templates/bgp/srlinux.macro.j2 b/netsim/ansible/templates/bgp/srlinux.macro.j2 index e6e66de7d..395c65879 100644 --- a/netsim/ansible/templates/bgp/srlinux.macro.j2 +++ b/netsim/ansible/templates/bgp/srlinux.macro.j2 @@ -51,69 +51,7 @@ export-reject-all: False import-reject-all: False -{# - Configure BGP Groups -#} - group: -{% for g in ['ibgp-ipv4','ibgp-ipv6','ebgp','ebgp-unnumbered'] %} -{% set _type = g[:4] %} - - group-name: {{ g }} - admin-state: enable - import-policy: {{ import_policy if 'ebgp' in g else 'accept_all' }} - export-policy: {{ export_policy if 'ebgp' in g else 'accept_all' }} - timers: - connect-retry: 10 - _annotate_connect-retry: "Reduce default 120s to 10s" - minimum-advertisement-interval: 1 - -{# BGP communities #} -{% if bgp.community[ _type ]|default([]) %} -{% set list = bgp.community[ _type ] %} - send-community: - standard: {{ 'standard' in list or 'both' in list }} - large: {{ 'large' in list }} -{% endif %} - -{# iBGP #} -{% if g in ['ibgp-ipv4','ibgp-ipv6'] %} - peer-as: {{ _as }} - ipv4-unicast: - admin-state: {{ 'enable' if g == 'ibgp-ipv4' and 'ipv4' in bgp.address_families['ibgp'] else 'disable' }} - ipv6-unicast: - admin-state: {{ 'enable' if g == 'ibgp-ipv6' or 'ipv6' in bgp.address_families['ibgp'] else 'disable' }} - evpn: - admin-state: {{ 'enable' if evpn is defined and 'ibgp' in evpn.session|default([]) else 'disable' }} -{% if g[5:] in loopback %} - transport: - local-address: {{ loopback[ g[5:] ]|default('')|ipaddr('address') }} -{% endif %} -{% if vrf_bgp.rr|default(0) %} - route-reflector: - cluster-id: {{ vrf_bgp.rr_cluster_id|default(False) or router_id }} - client: True -{% endif %} -{% else %} - -{# eBGP #} - next-hop-self: {{ 'True' if bgp.next_hop_self|default(False) else 'False' }} -{% if g == 'ebgp-unnumbered' %} - local-as: - - as-number: {{ bgp.underlay_as|default(_as) }} - prepend-global-as: false - ipv4-unicast: - advertise-ipv6-next-hops: true - receive-ipv6-next-hops: true -{% else %} - ipv4-unicast: -{% endif %} - admin-state: {{ 'enable' if 'ipv4' in bgp.address_families['ebgp'] else 'disable' }} - ipv6-unicast: - admin-state: {{ 'enable' if 'ipv6' in bgp.address_families['ebgp'] else 'disable' }} -{% endif %} - -{% endfor %} - -{# Configure BGP address families #} +{# Configure BGP address families globally #} {% for af in ['ipv4','ipv6'] if vrf_bgp[af]|default(0) %} {{ af }}-unicast: admin-state: enable @@ -133,29 +71,83 @@ {% endfor %} +{% macro bgp_peer_group(name,type,neighbor,transport_ip) %} +- path: network-instance[name={{vrf}}]/protocols/bgp/group[group-name={{name}}] + val: + admin-state: enable + ipv4-unicast: + admin-state: {{ 'enable' if 'ipv4' in neighbor.activate and neighbor.activate['ipv4'] else 'disable' }} + ipv6-unicast: + admin-state: {{ 'enable' if 'ipv6' in neighbor.activate and neighbor.activate['ipv6'] else 'disable' }} + import-policy: {{ import_policy if 'ebgp' in type else 'accept_all' }} + export-policy: {{ export_policy if 'ebgp' in type else 'accept_all' }} + timers: + connect-retry: 10 + _annotate_connect-retry: "Reduce default 120s to 10s" + minimum-advertisement-interval: 1 +{% if vrf_bgp.community[ type ]|default([]) %} +{% set list = vrf_bgp.community[ type ] %} + send-community: + standard: {{ 'standard' in list }} + large: {{ 'extended' in list }} +{% endif %} +{% if transport_ip %} + transport: + local-address: {{ transport_ip }} +{% endif %} +{% if 'ibgp' in type %} + next-hop-self: {{ vrf_bgp.next_hop_self|default(False) }} + peer-as: {{ neighbor.as }} +{% if vrf_bgp.rr|default(0) %} + route-reflector: + cluster-id: {{ vrf_bgp.rr_cluster_id|default(False) or router_id }} + client: True +{% endif %} +{% endif %} +{% endmacro %} + {# Define IPv4 and IPv6 BGP neighbors #} {% for n in vrf_bgp.neighbors %} {% for af in ['ipv4','ipv6'] if n[af] is defined %} + {% if n[af] is string %} +{# (Re)create peer group #} +{% set peer_group = 'ebgp' if n.type=='ebgp' else 'ibgp-local-as' if n.type=='localas_ibgp' else ('ibgp-'+af) %} +{% set transport_ip = loopback[af]|ipaddr('address') if af in loopback and n.type=='ibgp' else None %} +{{ bgp_peer_group(peer_group,'ibgp' if 'ibgp' in n.type else 'ebgp',n,transport_ip) }} + - path: network-instance[name={{vrf}}]/protocols/bgp/neighbor[peer-address={{n[af]}}] val: description: {{ n.name }} - peer-group: {{ 'ebgp' if n.type=='ebgp' else ('ibgp-'+af) }} -{% if n.type != 'ibgp' %} + peer-group: {{ peer_group }} +{% if 'ebgp' in n.type %} peer-as: {{ n.as }} +{% elif vrf_bgp.rr|default(False) and n.rr|default(False) %} + route-reflector: + client: False # Don't reflect routes between ibgp route reflectors {% endif %} {% if n.local_as is defined %} local-as: - as-number: {{ n.local_as }} -{% if n.type == 'ebgp' %} - prepend-global-as: False # Don't include iBGP AS in eBGP advertisements -{% endif %} + prepend-global-as: {{ not n.replace_global_as|default(True) }} # Don't include iBGP global AS in eBGP advertisements {% endif %} -{% elif n[af]==True and af=='ipv6' %} -{# BGP unnumbered for IPv6 LLA #} + +{% elif n[af]==True and af=='ipv6' and n.type == 'ebgp' %} +{# BGP unnumbered for IPv6 LLA, only supported for EBGP peers #} {% set peer_group = "ebgp-unnumbered" + (('-' + n.local_as|string()) if n.local_as is defined else '') %} +{{ bgp_peer_group(peer_group,'ebgp',n,None) }} +{% if n.local_as is defined %} + local-as: + - as-number: {{ n.local_as }} + prepend-global-as: {{ not n.replace_global_as|default(True) }} # Don't include iBGP AS in eBGP advertisements +{% endif %} +{% if 'ipv4' in n.activate|default([]) and n.activate[ 'ipv4' ] %} + ipv4-unicast: + advertise-ipv6-next-hops: true + receive-ipv6-next-hops: true +{% endif %} - path: network-instance[name={{vrf}}]/ip-forwarding val: @@ -171,23 +163,6 @@ peer-group: "{{ peer_group }}" allowed-peer-as: [ {{ n.as }}..{{ n.as }} ] -{% if peer_group != "ebgp-unnumbered" %} -- path: network-instance[name={{vrf}}]/protocols/bgp/group[group-name={{peer_group}}] - val: - admin-state: enable - import-policy: accept_all - export-policy: accept_all - timers: - connect-retry: 10 - _annotate_connect-retry: "Reduce default 120s to 10s" - minimum-advertisement-interval: 1 - local-as: - - as-number: {{ n.local_as }} - prepend-global-as: false - ipv4-unicast: - advertise-ipv6-next-hops: true - receive-ipv6-next-hops: true -{% endif %} {% endfor %} {% endif %} {% endfor %} diff --git a/netsim/ansible/templates/evpn/srlinux.j2 b/netsim/ansible/templates/evpn/srlinux.j2 index a2851456b..5b87fb6f2 100644 --- a/netsim/ansible/templates/evpn/srlinux.j2 +++ b/netsim/ansible/templates/evpn/srlinux.j2 @@ -12,6 +12,9 @@ updates: - group-name: {{ 'ibgp-ipv4' if type=='ibgp' else 'ebgp' }} # Could create a dedicated group for EVPN only? evpn: admin-state: enable +{% if bgp.rr|default(0) %} + next-hop-self: False +{% endif %} evpn: rapid-update: True {% endfor %} diff --git a/netsim/ansible/templates/initial/srlinux.j2 b/netsim/ansible/templates/initial/srlinux.j2 index 01aecdf59..3b08d04a2 100644 --- a/netsim/ansible/templates/initial/srlinux.j2 +++ b/netsim/ansible/templates/initial/srlinux.j2 @@ -40,7 +40,7 @@ updates: val: {{ ip_addresses(loopback,False,True) }} -{% for l in interfaces|default([]) if (l.type in ['lan','vlan_member','svi'] or l.vlan is not defined) %} +{% for l in interfaces|default([]) %} {% set if_name_index = l.ifname.split('.') %} {% set if_name = if_name_index[0] %} {% set if_index = if_name_index[1] if if_name_index|length > 1 else l.vlan.access_id|default(0) if l.vlan is defined else '0' %} @@ -83,25 +83,27 @@ updates: {% set if_name_index = l.ifname.split('.') %} {% set if_name = if_name_index[0] %} {% set if_index = if_name_index[1] if if_name_index|length > 1 else '0' %} +- path: network-instance[name={{ vrf }}] + val: + type: {{ 'default' if vrf=='default' else 'ip-vrf' }} + interface: - name: {{ if_name }}.{{ if_index }} {% endfor %} {% endmacro %} -{# Make sure the default VRF is called 'default' #} +{# Make sure the default VRF is called 'default', and has system0.0 interface #} - path: network-instance[name=default] val: type: default interface: - name: system0.0 + {{ list_interfaces('default') }} {% if vrfs is defined %} {% for vname,vdata in vrfs.items() %} -- path: network-instance[name={{ vname }}] - val: - type: ip-vrf - # TODO: vdata.rd, vdata.import/export, vdata.af - interface: + +# TODO: vdata.rd, vdata.import/export, vdata.af {{ list_interfaces(vname) }} {% endfor %} diff --git a/netsim/topology-defaults.yml b/netsim/topology-defaults.yml index 050f5d1e7..1137e511b 100644 --- a/netsim/topology-defaults.yml +++ b/netsim/topology-defaults.yml @@ -607,8 +607,6 @@ devices: ansible_network_os: cumulus ansible_connection: paramiko features: - bgp: - activate_af: True initial: ipv4: unnumbered: True @@ -676,10 +674,6 @@ devices: srgb_range_start: 500000 srgb_range_size: 1000 ipv6_sid_offset: 100 - bgp: - address_families: - ibgp: [ ipv4, ipv6 ] - ebgp: [ ipv4, ipv6 ] bfd: # SR Linux supports lower BFD timers than the global default min_tx: 100 min_rx: 100 @@ -704,8 +698,13 @@ devices: svi_interface_name: "irb0.{vlan}" subif_name: "{ifname}.{vlan.access_id}" mixed_trunk: True + bgp: + local_as: True + vrf_local_as: True + local_as_ibgp: True + activate_af: True vxlan: - requires: [ evpn ] + requires: [ evpn, vrf ] ospf: unnumbered: False isis: diff --git a/tests/topology/expected/device-module-defaults.yml b/tests/topology/expected/device-module-defaults.yml index 86929cfdf..b94aad01a 100644 --- a/tests/topology/expected/device-module-defaults.yml +++ b/tests/topology/expected/device-module-defaults.yml @@ -54,9 +54,6 @@ nodes: ipv4: true bgp: address_families: - ebgp: - - ipv4 - - ipv6 ibgp: - ipv4 advertise_loopback: true