Apstra DC Fabric as a solution

In my last blog post, I already mentioned HPE Juniper Apstra as a multi-vendor management solution for data center networks and demonstrated how the system can be operated together with the associated fabric switches in a ContainerLab topology.

This post, which is the first part of a series of articles, aims to provide a better understanding of Apstra as a solution and the DC fabrics it manages from a network engineer’s perspective. Even though Apstra, as a so-called ‘intent-based networking’ solution, enables a high degree of abstraction in the provisioning and operation of DC networks and does not require in-depth networking expertise, I believe it is essential to understand as precisely as possible which designs and, ultimately, which configurations at the device level underpin the overall solution.

Demo Topology

In this series, I will only touch briefly on modelling fabric topolgies in Apstra, as I believe there is already enough documentation and plenty of examples available on this subject. The focus here is much more on gaining an understanding of the underlying design, configurations and specific features through practical examples, so that we can better assess the key mechanics of the solution, as well as its strengths and weaknesses.

💡 Tip
As Apstra generally supports a variety of designs and vendors, I would like to point out at the beginning that this series primarily refers to the Standard Datacenter Reference Design based on a 3-stage Clos Fabric in combination with the vendor HPE Juniper and Apstra in Version 6.1.2.

The following figure shows the demo topology I used in Apstra: Apstra Demo Topology

I am using a simple and straightforward 3-stage Clos fabric with the following features:

  • Spine layer consisting of 2 spine switches
  • Leaf layer consisting of 2 racks
    • Rack 1 with a leaf pair consisting of Leaf1 and Leaf2, to which the dual-homed Host1 is connected
    • Rack 2 with the standalone Leaf3, to which two single-homed hosts are connected

Part 1 – Underlay Network

This blog post begins by focusing on the underlay network.

IP Addressing

Based on the demo topology, Apstra implements the following underlay design, including IP addressing on the devices:

Apstra Underlay IP-Adressing

All internal physical fabric links are configured as Layer 3 point-to-point links, as shown here in the example of the Spine1 configuration:

interfaces {
    ge-0/0/0 {
        description facing_leaf1:ge-0/0/0;
        mtu 9192;
        unit 0 {
            family inet {
                mtu 9170;
                address 172.16.0.0/31;
            }
        }
    }
    ge-0/0/1 {
        description facing_leaf2:ge-0/0/0;
        mtu 9192;
        unit 0 {
            family inet {
                mtu 9170;
                address 172.16.0.2/31;
            }
        }
    }
    ge-0/0/2 {
        description facing_leaf3:ge-0/0/0;
        mtu 9192;
        unit 0 {
            family inet {
                mtu 9170;
                address 172.16.0.4/31;
            }
        }
    }
    [...]
    lo0 {
        unit 0 {
            family inet {
                address 203.0.113.0/32;
            }
        }
    }
}

By default, an MTU of 9192 bytes is set on the physical interfaces and 9170 bytes on the logical L3 interfaces (unit 0) to enable jumbo frames in the overlay services at a later stage.

Each fabric switch is also assigned a loopback interface (lo0), whose IP address serves as the router ID for BGP. On the leaf switches, this interface also functions as a VTEP.

Underlay Routing

The main task of the underlay network is to ensure end-to-end reachability of the loopback interfaces of all leaf switches, as these will later be used as VTEPs for the VXLAN-based overlay services.

Apstra uses External BGP (eBGP) as the dynamic routing protocol in the underlay. Specifically, an eBGP session with the IPv4 address family is established on each P2P link between the spine and leaf switches:

Apstra Underlay Routing
📝 Note
It is worth noting here the use of individual ASNs even in the spine layer, which can result in valid but suboptimal BGP routing paths between two leaf loopbacks (VTEPs) potentially passing through other leaf switches in certain failure scenarios (see also the following blog post on valley-free routing). Apstra resolves this issue in its own way by using custom BGP policies (see below in the Policies section)

All BGP configurations relevant to the underlay can be found under the group l3clos-l (for all leaf switches) or l3clos-s (for all spine switches).

Taking Leaf1 as an example, you can see that both BGP multipath and multiple-as are set at group level. This is necessary to enable load balancing across all available links on the control plane based on “Equal-Cost-Multipath” (ECMP). If these options were not set, only one of the available paths would be used in each case, in accordance with standard eBGP logic.

protocols {
    [...]
    bgp {
        group l3clos-l {
            type external;
            multipath {
                multiple-as;
            }
            bfd-liveness-detection {
                minimum-interval 1000;
                multiplier 3;
            }
            neighbor 172.16.0.0 {
                description facing_spine1;
                local-address 172.16.0.1;
                family inet {
                    unicast;            
                }
                export ( LEAF_TO_SPINE_FABRIC_OUT && BGP-AOS-Policy );
                peer-as 64512;
            }
            neighbor 172.16.0.6 {
                description facing_spine2;
                local-address 172.16.0.7;
                family inet {
                    unicast;
                }
                export ( LEAF_TO_SPINE_FABRIC_OUT && BGP-AOS-Policy );
                peer-as 64513;
            }
            vpn-apply-export;
        }
        [...]
        log-updown;
        graceful-restart {
            dont-help-shared-fate-bfd-down;
        }
        multipath;
    }

For all eBGP neighbours in the underlay, only the IPv4 address family is enabled using family inet unicast, so that all underlay routes can be found in the default routing table inet.0. In addition, BFD is enabled with a detection time of 3x1000ms to address indirect link failures. However, in the event of direct link failures (“Interface Down”), the corresponding eBGP session is terminated immediately.

To ensure that the ECMP principle is actually implemented at the forwarding plane level, the forwarding table export policy PFE-LB is also enabled under the routing-options. Furthermore, ecmp-fast-reroute is used to optimise the switchover time in the event of spine-leaf link failures.

routing-options {
    router-id 203.0.113.2;
    autonomous-system 64514;
    graceful-restart;
    forwarding-table {
        export PFE-LB;
        ecmp-fast-reroute;
        chained-composite-next-hop {
            ingress {
                evpn;
            }
        }                               
    }
}
[...]
policy-options {
    [...]
    policy-statement PFE-LB {
        then {
            load-balance per-packet;
        }
    }
    [...]
}

The additional configuration option chained-composite-next-hop ingress evpn on all leaf switches is primarily intended to optimise overlay services, but it utilises the underlay VTEP next-hop entries by enabling hierarchical next-hop resolution for EVPN routes.

💡 Tip
This Juiper-specific configuration option significantly improves scalability, convergence speed and the efficient utilisation of hardware/TCAM resources in VXLAN-based leaf-spine fabrics, as not every single EVPN route needs to be mapped individually in the forwarding table. Other manufacturers, such as Cisco or Arista, already enable similar mechanisms implicitly in their forwarding logic.

Furthermore, graceful-restart is enabled globally with the additional option dont-help-shared-fate-bfd-down in the BGP configuration. This enables routes to be preserved and BGP sessions to be restored smoothly during control plane restarts, whilst at the same time, if a peer or link actually fails and this is detected by BFD, all affected routes are immediately withdrawn.

📝 Note
This additional feature is only useful if BFD actually operates independently of the control plane and is implemented, for example, in hardware or the forwarding plane.

Policy

Leaf-Switches

As can be seen in the configuration excerpt above, the following two export policies are also enabled for the underlay eBGP sessions on all leaf switches:

  • LEAF_TO_SPINE_FABRIC_OUT
  • BGP-AOS-Policy

Due to the logical AND combination of the policies in the BGP configuration export ( LEAF_TO_SPINE_FABRIC_OUT && BGP-AOS-Policy ), only routes that match both policies are propagated.

In detail, the relevant policies are as follows:

policy-options {
    policy-statement AllPodNetworks {
        term AllPodNetworks-10 {
            from {
                family inet;
                protocol direct;
            }
            then {
                community add DEFAULT_DIRECT_V4;
                accept;
            }
        }
        term AllPodNetworks-100 {
            then reject;                
        }
    }
    policy-statement BGP-AOS-Policy {
            term BGP-AOS-Policy-10 {
                from policy AllPodNetworks;
                then accept;
            }
            term BGP-AOS-Policy-100 {
                then reject;
            }
        }
    }
    policy-statement LEAF_TO_SPINE_FABRIC_OUT {
            term LEAF_TO_SPINE_FABRIC_OUT-10 {
                from {
                    protocol bgp;
                    community FROM_SPINE_FABRIC_TIER;
                }
                then reject;
            }
            term LEAF_TO_SPINE_FABRIC_OUT-20 {
                then accept;
            }
        }
    }
    community DEFAULT_DIRECT_V4 members [ 3:20007 21001:26000 ];
    community FROM_SPINE_FABRIC_TIER members 0:15;

These export policies ensure that a leaf switch only propagates its own directly connected IPv4 underlay routes (loopbacks / P2P links) to the spines, whilst at the same time preventing the routes learned from one spine from being propagated to the other spine (-> zigzag-routing). This additional policy-based safeguard is also necessary in this case because both spines use different ASNs, and valley-free routing cannot be guaranteed based on the AS path.

Spine Switches

The policies for the spine switches are slightly different:

  • SPINE_TO_LEAF_FABRIC_OUT
  • BGP-AOS-Policy
policy-options {
    policy-statement AllPodNetworks {
        term AllPodNetworks-10 {
            from {
                family inet;
                protocol direct;
            }
            then {
                community add DEFAULT_DIRECT_V4;
                accept;
            }
        }
        term AllPodNetworks-100 {
            then reject;
        }
    }
    policy-statement BGP-AOS-Policy {
        term BGP-AOS-Policy-10 {
            from policy AllPodNetworks;
            then accept;
        }
        term BGP-AOS-Policy-20 {
            from protocol bgp;
            then accept;
        }
        term BGP-AOS-Policy-100 {
            then reject;
        }
    }
    policy-statement SPINE_TO_LEAF_FABRIC_OUT {
        term SPINE_TO_LEAF_FABRIC_OUT-10 {
            then {
                community add FROM_SPINE_FABRIC_TIER;
                accept;
            }
        }
    }
    community DEFAULT_DIRECT_V4 members [ 1:20007 21001:26000 ];
    community FROM_SPINE_FABRIC_TIER members 0:15;
}

The spine policies ensure that the spine propagates both its own directly connected routes and all routes learned from leaf switches via BGP to other leaves, tagging them with a community so that leaves do not propagate these routes back to the spines. This also ensures valley-free routing and prevents leaf switches from being used as transit devices.

Verification

Finally, we will look at how end-to-end underlay routing and ECMP load balancing can be verified. For verification purposes, I will again use Leaf1 as an example and examine the following outputs:

  • show bgp summary
  • show route table inet.0
  • show route forwarding-table
show bgp summary             

Threading mode: BGP I/O
Default eBGP mode: advertise - accept, receive - accept
Groups: 2 Peers: 4 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
inet.0               
                      16         10          0          0          0          0
[...]
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
172.16.0.0            64512        105        102       0       0       45:10 Establ
  inet.0: 5/8/8/0
172.16.0.6            64513         15         11       0       0        4:30 Establ
  inet.0: 5/8/8/0
[...]

As shown in the output, the two eBGP sessions to the spine switches with different ASNs and the IPv4 Unicast address family are active (Established), and active packets and routes are being exchanged.

show route table inet.0 

inet.0: 13 destinations, 21 routes (13 active, 0 holddown, 0 hidden)
Restart Complete
Limit/Threshold: 1048576/1048576 destinations
+ = Active Route, - = Last Active, * = Both

[...]
172.16.0.1/32      *[Local/0] 00:50:03
                       Local via ge-0/0/0.0
[...]
172.16.0.7/32      *[Local/0] 00:50:03
                       Local via ge-0/0/1.0
[...]
203.0.113.0/32     *[BGP/170] 00:47:49, localpref 100
                      AS path: 64512 I, validation-state: unverified
                    >  to 172.16.0.0 via ge-0/0/0.0
203.0.113.1/32     *[BGP/170] 00:07:10, localpref 100
                      AS path: 64513 I, validation-state: unverified
                    >  to 172.16.0.6 via ge-0/0/1.0
203.0.113.2/32     *[Direct/0] 00:50:20
                    >  via lo0.0
203.0.113.3/32     *[BGP/170] 00:07:03, localpref 100, from 172.16.0.0
                      AS path: 64512 64515 I, validation-state: unverified
                       to 172.16.0.0 via ge-0/0/0.0
                    >  to 172.16.0.6 via ge-0/0/1.0
                    [BGP/170] 00:07:03, localpref 100
                      AS path: 64513 64515 I, validation-state: unverified
                    >  to 172.16.0.6 via ge-0/0/1.0
203.0.113.4/32     *[BGP/170] 00:07:02, localpref 100, from 172.16.0.0
                      AS path: 64512 64516 I, validation-state: unverified
                       to 172.16.0.0 via ge-0/0/0.0
                    >  to 172.16.0.6 via ge-0/0/1.0
                    [BGP/170] 00:07:02, localpref 100
                      AS path: 64513 64516 I, validation-state: unverified
                    >  to 172.16.0.6 via ge-0/0/1.0

The default routing table inet.0 contains all underlay routing information. It is clear that the loopback IPs of the other leaf switches (203.0.113.3/32 and 203.0.113.4/32) are received and are available via two AS paths (= two spines).

show route forwarding-table 
Routing table: default.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    rjct       36     1
0.0.0.0/32         perm     0                    dscd       34     1
[...]
203.0.113.1/32     user     1 172.16.0.6         ucst      578     8 ge-0/0/1.0
203.0.113.2/32     intf     0 203.0.113.2        locl      512     1
203.0.113.3/32     user     0                    ulst  1048574     3
                              172.16.0.0         ucst      577     8 ge-0/0/0.0
                              172.16.0.6         ucst      578     8 ge-0/0/1.0
203.0.113.4/32     user     0                    ulst  1048574     3
                              172.16.0.0         ucst      577     8 ge-0/0/0.0
                              172.16.0.6         ucst      578     8 ge-0/0/1.0
[...]
255.255.255.255/32 perm     0                    bcst       32     1

This output also allows us to verify the forwarding table and the implementation of ECMP load balancing. Here, too, we can see that the two loopback IPs of the remote leaves are each reachable via two active next hops in the underlay.

Conclusion

I hope this first part of the series provides a good overview and a better understanding of the design and implementation of the underlay network in Apstra-managed DC fabrics with Juniper fabric switches. I think it already illustrates how certain design decisions (e.g., different spine ASNs) impact the configuration and its complexity (e.g., ensuring loop- and valley-free routing through custom BGP policies).

In the next part of the series, I will go into more detail about the implementation of the EVPN overlay.