Configuring BGP
This document describes the device data model parameters one should consider when creating a BGP device configuration template. For a wider picture, please see the contributing new devices document.
Most of the document assumes you already created an Ansible task list that is able to deploy device configuration from a template. If you plan to use Ansible modules to build initial device configuration, see Using Ansible Configuration Modules for more details.
Notes:
The device configuration template (in Jinja2 format) should be stored in
netsim/templates/bgp/<nos>.j2with nos being the value of netlab_device_type or ansible_network_os variable (see Using Your Devices with Ansible Playbooks for more details.Most of the data model attributes are optional. Use
if sth is defined,sth|default(value)orif 'sth' in ifdatain your Jinja2 templates to check for the presence of optional attributes. Try to be consistent ;)
Device Features
You can use the following features.bgp device features to specify which BGP features your configuration template implements:
activate_af – device supports selective activation of IPv4 AF on IPv4 BGP session and IPv6 AF on IPv6 BGP session
community – Granular control of BGP community propagation see
netsim/devices/frr.ymlornetsim/devices/eos.ymlfor an example.confederation – device supports BGP confederations
ipv6_lla – device supports unnumbered (interface) EBGP sessions
local_as – device supports local_as feature
local_as_ibgp – the local_as feature can be used to convert an EBGP session into an IBGP session
rfc8950 – device supports extended next hop functionality that can be used to implement RFC8950-style IPv6 next hops for IPv4 AF
vrf_local_as – the local_as feature is supported for BGP VRF sessions
import – the list of protocols that can be redistributed into BGP. Add vrf to the list if the device supports redistribution into VRF BGP instances.
Supporting Multiple Address Families
The BGP data model assumes all devices handle dual-stack deployments and provides bgp.ipv4 and bgp.ipv6 attributes to indicate which address families you should configure.
BGP neighbors data can contain ipv4 and ipv6 addresses. If a neighbor has both addresses, you have to configure two BGP neighbor sessions, one for IPv4, the other for IPv6.
netlab assumes the IPv4 BGP session carries the IPv4 address family (plus other address families like MPLS/VPN or EVPN) and the IPv6 BGP session carries the IPv6 address family. It’s possible to disable the default address family with the activate BGP neighbor attribute.
Global BGP Parameters
netlab data model assumes a single BGP process running in the global routing table and VRFs. Device-wide parameters are set in bgp dictionary:
bgp.advertise_loopback (boolean) – When set to True, the BGP process should advertise the IPv4/IPv6 prefix assigned to loopback interface. You can ignore this attribute as it sets the bgp.advertise flag on the loopback interface
bgp.as – The BGP AS number (always present)
bgp.community – The BGP communities propagation rules – a dictionary of neighbor types, containing a list of communities (standard, extended, large) to propagate to all neighbors of that type
bgp.confederation.as (optional) – confederation-wide (external) AS
bgp.confederation.peers (optional) – list of other (internal) autonomous systems within the confederation. The local AS is not included in this list.
bgp.import (optional) – List of routing protocols (including static and connected) to be redistributed into BGP.
bgp.ipv4 (optional) – You should configure IPv4 BGP address family
bgp.ipv6 (optional) – You should configure IPv6 BGP address family
bgp.neighbors – A list of IBGP and EBGP neighbors
bgp.next_hop_self – When set to True, the BGP process should set itself as the next hop on EBGP routes propagated as IBGP routes. This attribute is propagated to the neighbor next_hop_self attribute and can be ignored if you configure next-hop-self on individual neighbors.
bgp.originate (optional) – A list of additional IPv4 prefixes that should be advertised by the BGP process. You’ll probably have to create static routes to support these prefixes.
bgp.router_id – The BGP router ID
bgp.rr (optional) – When set to True, the device is a BGP route reflector. This attribute is propagated to the rr_client neighbor attribute and can be ignored if you configure route-reflector-client on individual neighbors.
bgp.rr_cluster_id (optional) – user-configured BGP route-reflector cluster ID. If you need a cluster ID, use
bgp.rr_cluster_id|default(bgp.router_id)
Interface BGP Parameters
There’s a single BGP-related interface parameter:
bgp.advertise (optional) – advertise the prefix assigned to the interface in the BGP process. Used to advertise stub networks instead of doing redistribution of connected prefixes.
BGP Neighbors
Each entry in the bgp.neighbors list describes an adjacent device that could be an endpoint of IBGP or EBGP sessions. A single adjacent device might have an IPv4 and an IPv6 BGP session, which would usually have to be configured as two distinct BGP neighbors.
A description of a BGP neighbor might have these parameters:
activate.ipv4 (optional) – Activate the IPv4 AF on the IPv4 BGP session.
activate.ipv6 (optional) – Activate the IPv6 AF on the IPv6 BGP session.
as – Remote autonomous system
ipv4 (optional) – IPv4 address of an IPv4 BGP neighbor
ipv6 (optional) – IPv6 address of an IPv6 BGP neighbor
ipv4_rfc8950 (optional) – Activate IPv4 address family over IPv6 BGP session using RFC 8950 next hops
local_as (optional) – Local autonomous system (when using local-as functionality)
local_if (optional) – Local interface name for “unnumbered” BGP neighbors (EBGP sessions over IPv6 LLA configured as interface sessions)
name – Neighbor name (used in descriptions)
next_hop_self (optional) – use “next-hop-self” for this neighbor. Can contain ebgp (change next hop for EBGP routes) or always (change next hop for all routes, including reflected IBGP routes).
rr_client (optional) – the neighbor is a route-reflector client
replace_global_as (optional) – for neighbors with local_as, replace true AS with the local AS (typically results in no-prepend replace-as parameters of the neighbor local-as configuration command).
type – Neighbor type: ibgp, ebgp, confed_ebgp or localas_ibgp. The confed_ebgp type is used for intra-confederation EBGP sessions, the localas_ibgp type is used when the local_as turns an EBGP session into a fake IBGP session.
_source_intf (optional) – The BGP session has to originate from the specified source interface. You can use ipv4, ipv6, or ifname attributes of the _source_intf to configure update-source.
BGP Configuration Framework
The global BGP configuration should configure the BGP process and the address families. The configuration template should then use the bgp.neighbors list to configure individual BGP sessions, and activate those sessions within the address families. Finally, the template should originate the local prefixes. The sample code is taken from the FRRouting BGP template:
router bgp {{ bgp.as }}
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
bgp default show-hostname
bgp default show-nexthop-hostname
{% if bgp.confederation is defined %}
bgp confederation identifier {{ bgp.confederation.as }}
bgp confederation peers {{ bgp.confederation.peers|join(' ') }}
{% endif %}
{% if bgp.router_id|ipv4 %}
bgp router-id {{ bgp.router_id }}
{% endif %}
{% if bgp.rr_cluster_id|default(False) %}
bgp cluster-id {{ bgp.rr_cluster_id }}
{% endif %}
!
{# Create neighbors #}
{# Create AFs #}
{#
Activate neighbors, set AF attributes
#}
Configure Neighbors
BGP neighbors usually have to be defined within the BGP process and then activated within address families. Iterate over IPv4/IPv6 AFs, then over BGP neighbors, and configure neighbors with matching address families:
{% for af in ['ipv4','ipv6'] %}
{% for n in bgp.neighbors if n[af] is defined %}
{% set peer = n[af] %}
neighbor {{ peer }} remote-as {{ n.as }}
neighbor {{ peer }} description {{ n.name }}
{% if n._source_intf is defined %}
neighbor {{ peer }} update-source {{ n._source_intf.ifname }}
{% endif %}
{% if n.local_as is defined %}
neighbor {{ peer }} local-as {{ n.local_as }} {{ 'no-prepend replace-as' if n.replace_global_as|default(True) else '' }}
{% endif %}
{% if af == 'ipv6' and n.ipv4_rfc8950|default(False) %}
neighbor {{ peer }} capability extended-nexthop
{% endif %}
!
{% endfor %}
{% endfor %}
Configure Address Families
After configuring the BGP neighbors, you should configure individual address families defined in the bgp data structure. Iterate over IPv4/IPv6 and create address families as needed. Within each address family, configure:
Route redistribution (when bgp.import is set)
Prefix origination
Address family parameters of BGP neighbors
{% for af in ['ipv4','ipv6'] if bgp[af] is defined %}
address-family {{ af }} unicast
!
{# Configure redistribution #}
{# Configure prefix origination #}
{# Activate/configure neighbors #}
Configure Prefix Origination
Within each address family, iterate over interfaces and create network statements (or whatever it takes to originate BGP prefixes) if:
The bgp.advertise interface attribute is set
The target address family is configured on the interface
The vrf interface attribute matches the VRF you’re configuring (or is missing for global BGP configuration)
Use netlab_interfaces list to include the loopback interface.
{% for l in netlab_interfaces if l.bgp.advertise|default(False) and l[af] is defined and not 'vrf' in l %}
network {{ l[af]|ipaddr(0) }}
{% endfor %}
For the global IPv4 address family, check the bgp.originate list and advertise the prefixes listed in that list. No such mechanism exists for IPv6 prefixes or VRFs
{% for pfx in bgp.originate|default([]) if af == 'ipv4' %}
network {{ pfx|ipaddr('0') }}
{% endfor %}
!
The extra prefixes configured with bgp.originate usually have to be supported by discard static routes. Configure those static routes outside of the BGP routing process:
{% for pfx in bgp.originate|default([]) %}
ip route {{ pfx|ipaddr('0') }} Null0
{% endfor %}
Activate Neighbors Within Address Families
Use the neighbor activate attribute to figure out if a neighbor needs to be activated/configured within an address family:
{% for n in bgp.neighbors if n.activate[af]|default(False) %}
{% set peer = n[af] %}
neighbor {{ peer }} activate
{# Configure other neighbor attributes #}
{% endfor %}
Some devices configure neighbor attributes (like next_hop_self) within address families, others configure them per-neighbor and apply them to all address families. Whenever possible, configure neighbor attributes within the address families. Neighbor parameters configured within an address family typically include next_hop_self processing, route reflector clients, and BGP community propagation:
{% if n.next_hop_self|default(False) %}
neighbor {{ peer }} next-hop-self{% if n.next_hop_self == 'all' %} force{% endif +%}
{% endif %}
{% if n.rr_client|default(False) %}
neighbor {{ peer }} route-reflector-client
{% endif %}
{% if n.type in bgp.community|default({}) %}
{% for c_type in bgp.community[n.type] %}
neighbor {{ peer }} send-community {{ c_type }}
{% endfor %}
{% endif %}
{% endif %}
Configuring RFC 8950 neighbors
If you want to support RFC 8950-type IPv4 address families, you have to be able to activate/configure the IPv4 address family on an IPv6 neighbor when the neighbor ipv4 attribute is not a valid IPv4 address (string).
Use the following logic within the address family to figure out the neighbor IP address, then check if the peer is defined before trying to configure it. Use peer instead of n[af] in configuration commands.
{% set peer = if n[af] is string
else n.ipv6 if af == 'ipv4' and n.ipv4_rfc8950
else False %}
{% if peer %}
{% endif %}
You might also have to configure extended next hop functionality on non-LLA IPv6 neighbors that have to support RFC 8950 next hops:
{% if af == 'ipv6' and n[af] is string and n.ipv4_rfc8950|default(False) %}
neighbor {{ peer }} capability extended-nexthop
{% endif %}
Configuring Unnumbered EBGP Neighbors
Unnumbered (interface) EBGP neighbors have ipv4 and/or ipv6 attribute set to True and the local_if attribute set to the name of the outgoing interface.
Configure BGP neighbors when ipv4/ipv6 is a string, or when the local_if is defined. Set a variable to the peer name (AF-derived or interface-derived) to simplify the configuration template. FRRouting also requires interface keyword in the neighbor statement:
{% for af in ['ipv4','ipv6'] %}
{% for n in bgp.neighbors if n[af] is defined and (n[af] is string or n.local_if is defined) %}
{% set peer = n[af] if n[af] is string else n.local_if|default('?') %}
neighbor {{ peer }}{{ ' interface' if peer!=n[af] else '' }} remote-as {{ n.as }}
neighbor {{ peer }} description {{ n.name }}
...
{% endfor %}
{% endfor %}
You also have to modify the “set peer name” logic in AF configuration:
{% for n in bgp.neighbors if n.activate[af]|default(False) %}
{% set peer = n[af] if n[af] is string
else n.local_if if n.local_if is defined
else n.ipv6 if af == 'ipv4' and n.ipv4_rfc8950
else False %}
{% if peer %}
neighbor {{ peer }} activate
...
{% endif %}
!
{% endfor %}
Configuring VRF BGP Instances
VRF BGP instances contain most BGP parameters (for example, router_id) in the vrfs.vname.bgp data structure with these exceptions:
ipv4 and ipv6 parameters are not set. You should use the VRF af dictionary to figure out the BGP address families.
Use the global bgp.as parameter to configure the BGP routing process.
bgp.router_id is set only when needed/configured.
Use VRF networks list instead of bgp.advertise interface attributes to configure prefix origination. The networks list contains dictionaries with ipv4 and ipv6 attributes. Each entry might contain one or both attributes.
netlab does not support IBGP or confederation EBGP VRF sessions. You have to be able to configure EBGP and (when implemented) local-as IBGP sessions.
At the very minimum, the VRF bgp data structure contains only the neighbors list.
Use templates similar to the ones above when configuring VRF BGP instances. The neighbor/prefix configuration is almost identical to the global BGP configuration, so it might make sense to define configuration macros to configure BGP neighbors, per-AF BGP neighbor parameters, and prefix origination. See Configuring RIPv2/RIPng in VRFs for a very simple example, or Configuring VRF BGP Instances and Configure VRF BGP Neighbors.
This is how Cisco IOS configures VRFs within the BGP routing process:
router bgp {{ bgp.as }}
{% for vname,vdata in vrfs.items() %}
{% for af in ('ipv4','ipv6') if af in vdata.af|default({}) %}
address-family {{ af }} vrf {{ vname }}
bgp router-id {{ vdata.bgp.router_id|default(bgp.router_id) }}
{{ redistribute.config(vdata.bgp,af=af,ospf_pid=vdata.vrfidx,vrf=vname)|indent(1,first=True) }}
!
{% for n in vdata.networks|default([]) if af in n %}
{{ bgpcfg.bgp_network(af,n[af]) }}
{% endfor %}
!
{% for n in vdata.bgp.neighbors|default([]) if af in n %}
{{ bgpcfg.neighbor_global(n,n[af]) }}
{{ bgpcfg.neighbor_af(n,n[af],bgp) }}
{% endfor %}
{% endfor %}
{% endfor %}