diff --git a/netsim/ansible/templates/bgp/ios.macro.j2 b/netsim/ansible/templates/bgp/ios.macro.j2 index b2c851137..7d3787cc5 100644 --- a/netsim/ansible/templates/bgp/ios.macro.j2 +++ b/netsim/ansible/templates/bgp/ios.macro.j2 @@ -8,7 +8,8 @@ {% macro neighbor_global(n,ip) %} neighbor {{ ip }} remote-as {{ n.as }} {% if n.local_as is defined %} - neighbor {{ ip }} local-as {{ n.local_as }}{% if n.replace_global_as|default(True) %} no-prepend replace-as{% endif +%} + neighbor {{ ip }} local-as {{ n.local_as }}{% + if n.replace_global_as|default(True) and n.type != 'localas_ibgp' %} no-prepend replace-as{% endif +%} {% endif %} neighbor {{ ip }} description {{ n.name }} {% if n.type == 'ibgp' %} @@ -20,14 +21,14 @@ #} {% macro neighbor_af(n,ip,bgp) %} neighbor {{ ip }} activate -{% if n.type == 'ibgp' %} +{% if 'ibgp' in n.type %} {% if bgp.community.ibgp|default([]) %} neighbor {{ ip }} send-community {{ community(bgp.community.ibgp) }} {% endif %} {% if bgp.next_hop_self is defined and bgp.next_hop_self %} - neighbor {{ ip }} next-hop-self + neighbor {{ ip }} next-hop-self{% if n.type == 'localas_ibgp' %} all{% endif +%} {% endif %} -{% if bgp.rr|default('') and not n.rr|default('') %} +{% if bgp.rr|default('') and (not n.rr|default('') or n.type == 'localas_ibgp') %} neighbor {{ ip }} route-reflector-client {% endif %} {% else %}{# EBGP IPv4 neighbor #} diff --git a/netsim/modules/bgp.py b/netsim/modules/bgp.py index 23e295716..871a05990 100644 --- a/netsim/modules/bgp.py +++ b/netsim/modules/bgp.py @@ -211,6 +211,13 @@ def build_ebgp_sessions(node: Box, sessions: Box, topology: Box) -> None: extra_data[k] = local_as_data session_type = 'localas_ibgp' if neighbor_local_as == node_local_as else 'ebgp' + if session_type == 'localas_ibgp': + if not features.bgp.local_as_ibgp: + common.error( + text=f'You cannot use BGP local-as to create an IBGP session with {ngb_name} on {node.name} (device {node.device})', + category=common.IncorrectValue, + module='bgp') + continue ebgp_data = bgp_neighbor(neighbor,ngb_ifdata,session_type,sessions,extra_data) if not ebgp_data is None: diff --git a/netsim/topology-defaults.yml b/netsim/topology-defaults.yml index 84724b5f7..56f0753fa 100644 --- a/netsim/topology-defaults.yml +++ b/netsim/topology-defaults.yml @@ -27,7 +27,7 @@ addressing: # Global, node and link attributes attributes: global: [ addressing,defaults,groups,links,module,name,nodes,plugin,provider ] - internal: [ input,includes,pools,Provider,Plugin ] + internal: [ input,includes,pools,Provider,Plugin,message ] link: [ bandwidth,bridge,name,prefix,role,type,unnumbered,interfaces,mtu,gateway,vlan_name ] link_internal: [ linkindex,parentindex ] link_no_propagate: [ prefix,interfaces,gateway ] @@ -294,6 +294,7 @@ devices: bgp: local_as: True vrf_local_as: True + local_as_ibgp: True initial: ipv4: unnumbered: False @@ -340,6 +341,7 @@ devices: bgp: local_as: True vrf_local_as: True + local_as_ibgp: True initial: ipv4: unnumbered: True diff --git a/tests/integration/bgp/local-as/ibgp-local-as.yml b/tests/integration/bgp/local-as/ibgp-local-as.yml new file mode 100644 index 000000000..de95bac78 --- /dev/null +++ b/tests/integration/bgp/local-as/ibgp-local-as.yml @@ -0,0 +1,31 @@ +message: | + This lab topology checks the IBGP local-as functionality -- creating BGP direct + session between two autonomous systems and using BGP local-as to make it a + fake IBGP session. + + You should be able to see BGP routes from all four loopbacks on E1 and E2, and + ping between loopback interfaces of E1 and E2. +defaults.device: iosv + +module: [ bgp,ospf ] + +nodes: + r1: + bgp.as: 65000 + bgp.rr: True + r2: + bgp.as: 65001 + bgp.rr: True + e1: + bgp.as: 65000 + e2: + bgp.as: 65001 + +links: +- r1: + bgp.local_as: 666 + r2: + bgp.local_as: 666 + bgp.advertise: True +- r1-e1 +- r2-e2